Automation Quickstart¶
sygen automation systems:
| System | Trigger | Execution Context | Output |
|---|---|---|---|
Sessions (/session) | user command | named session (persistent) | chat notification |
Delegated tasks (TaskHub) | task tool scripts (/tasks/* API) | shared background task runtime | chat notification + parent-session injection |
| Cron jobs | schedule | isolated task folder | chat result |
| Webhooks | HTTP POST | wake or isolated cron_task | chat result |
| Heartbeat | interval | active main session | chat alert (non-ACK only) |
| Cleanup | daily hour | filesystem maintenance | no chat message |
Named sessions (/session)¶
/session <prompt> starts a named background session. The chat is free immediately; a message is sent when the task completes. Sessions persist and support follow-ups.
Key properties:
- auto-generates memorable compact names (e.g.
swiftfox,tallnewt) - supports provider isolation:
/session @codex <prompt> - follow-up in foreground:
@session-name <message> - follow-up in background:
/session @session-name <message> - session management:
/sessions(list, end, end all) /stopcancels all sessions for the chat/stop_allon the main agent also aborts active work across other agents (sub-agent fallback is local-only)- max 5 concurrent tasks, max 10 user-created sessions per chat
/statusshows active background tasks/sessionbackground timeout usesconfig.timeouts.background
Inter-agent sessions (ia-<sender>) use a deterministic registry path and are not created through /session.
Restart behavior:
- active asyncio task objects are lost on restart,
- persisted named sessions can still be resumed,
- startup recovery automatically retries safe named sessions that were
runningbefore restart when Telegram is the primary transport - Matrix-primary startup currently has no equivalent auto-recovery pipeline
Status values for named-session runs: ok, error:timeout, error:cli, error:internal, aborted.
Delegated tasks (TaskHub, /tasks)¶
Delegated tasks are separate from /session:
- persisted in
~/.sygen/tasks.json - task folders in
~/.sygen/workspace/tasks/<task_id>/ - managed via
/taskscommand (running/waiting/finished + cancel/cleanup controls) - created/resumed/cancelled/deleted through task tools (
tools/task_tools/*.py) overInternalAgentAPI /tasks/* - timeout source:
config.tasks.timeout_seconds
Result flow:
- task completion/failure is posted to the chat
- result is injected into parent agent's current active session (
handle_task_result) - task questions (
ask_parent.py) are posted and injected viahandle_task_question - forum-topic tasks route back to the originating topic via
thread_id/SYGEN_TOPIC_ID
Cron jobs¶
Cron jobs run in ~/.sygen/workspace/cron_tasks/<task_folder>/.
Each run is a fresh one-shot subprocess in the task folder. It does not reuse the main chat session.
Typical task folder:
~/.sygen/workspace/cron_tasks/weather-report/
CLAUDE.md
AGENTS.md
TASK_DESCRIPTION.md
weather-report_MEMORY.md
scripts/
Rule files are kept in sync automatically (CLAUDE.md, AGENTS.md, GEMINI.md) based on newest mtime per directory.
Rule-file sync behavior (all workspace directories, recursive):
- files:
CLAUDE.md,AGENTS.md,GEMINI.md - source of truth per directory: newest file by mtime
- sync runs at init (
sync_rule_files) and continuously (watch_rule_files, every 10s) - result: edits are propagated to older existing sibling rule files
- missing siblings are generally not auto-created, except cron task folders where
ensure_task_rule_files(...)backfills missing provider rule files
Runtime behavior:
- dependency lock (
dependency_queue) - quiet-hour check (only when
job.quiet_*is set; no fallback to global heartbeat quiet hours) - folder check
- resolve task overrides (
provider/model/reasoning/cli_parameters) - build provider command (
claude,codex, orgemini) - execute with timeout (
cli_timeout) - parse output
- send result callback to chat when the run actually executes and produces a callback path
- routing: UNICAST to originating chat when the job has
chat_idset; BROADCAST to all users whenchat_idis zero/unset - if UNICAST delivery fails (e.g. transport unavailable), the bus cascading fallback delivers to an available transport with an explanation
- persist status (
last_run_status,last_run_at)
Per-job override fields in cron_jobs.json:
{
"provider": "gemini",
"model": "gemini-2.5-pro",
"reasoning_effort": null,
"cli_parameters": ["--debug"],
"quiet_start": 22,
"quiet_end": 7,
"dependency": "nightly-reports"
}
Notes:
reasoning_effortis only used for Codex models that support it.- task
cli_parametersare task-level only (no merge with global provider args). - cron status includes
error:cli_not_found_<provider>for missing provider binaries. error:folder_missingupdateslast_run_statusbut does not emit a result callback.- quiet-hour skips do not emit result callbacks and do not update
last_run_status.
Script mode¶
For cron tasks that just need to run a script and send its output — without LLM reasoning — use script_mode. The script is executed directly via subprocess, and its stdout is delivered to Telegram. No LLM agent is spawned, no tokens are consumed.
{
"id": "business-dashboard",
"title": "Business Dashboard",
"schedule": "0 19 * * *",
"task_folder": "business-dashboard",
"script_mode": true,
"script": "scripts/dashboard.py"
}
The script path is relative to the task folder (cron_tasks/<task_folder>/).
Runtime behavior in script mode:
- dependency lock (same as agent mode)
- quiet-hour check (same as agent mode)
- resolve script path:
cron_tasks/<task_folder>/<script> - execute:
python3 <script>withcwdset to the task folder - capture stdout as result text; if exit code ≠ 0, stderr is appended
- deliver result to chat (same routing as agent mode)
[SILENT]marker in stdout suppresses delivery (same as agent mode)- persist status:
success,error, orerror:timeout
Script mode fields:
| Field | Type | Default | Description |
|---|---|---|---|
script_mode | bool | false | Enable direct script execution |
script | string | null | Script path relative to task folder |
When script_mode is false or absent, the job uses the standard LLM agent path. All other job fields (provider, model, cli_parameters, etc.) are ignored in script mode.
Webhooks¶
Server route: POST /hooks/{hook_id}
Validation order:
- rate limit
- content type (
application/json) - JSON object body
- hook exists
- hook enabled
- auth (
bearerorhmac) - accept and dispatch asynchronously (
202)
Modes:
wake: inject rendered prompt into active Telegram chat flowcron_task: run isolated one-shot execution in task folder
Prompt payload is wrapped with safety markers before execution.
cron_task mode supports the same override/quiet/dependency fields as cron jobs.
Quiet-hour behavior in cron_task mode:
- only
hook.quiet_start/hook.quiet_endare considered - no fallback to global heartbeat quiet hours
Typical status values:
successerror:no_responseerror:no_task_foldererror:folder_missingerror:cli_not_found_claudeerror:cli_not_found_codexerror:cli_not_found_geminierror:timeouterror:exit_<code>skipped:quiet_hours
Current transport limitation:
- webhook
wakedepends on a configured wake handler and is currently wired by Telegram startup only - Matrix-primary setups do not provide that handler right now, so
wakereturnserror:no_wake_handler - for Matrix-only deployments, use
cron_taskmode instead ofwake
Heartbeat¶
Heartbeat runs only when heartbeat.enabled=true.
Observer behavior:
- interval loop (
interval_minutes) - quiet-hour suppression in
user_timezone - busy-chat skip via
ProcessRegistry.has_active - stale process cleanup hook before each tick
heartbeat_flow behavior:
- uses read-only active session lookup,
- skips if no session or no provider-compatible session,
- enforces cooldown via
last_active, - sends heartbeat prompt with
resume_session, - suppresses pure ACK token responses,
- updates session metrics only for non-ACK alerts.
Default ACK token: HEARTBEAT_OK.
Default prompt asks the model to review memory + cron context and either send something useful or respond exactly with HEARTBEAT_OK.
Group targets¶
group_targets allows heartbeat to run in specific group chats/topics in addition to private user chats. Each target supports per-target overrides for prompt, ack token, interval, and quiet hours.
{
"heartbeat": {
"enabled": true,
"interval_minutes": 30,
"quiet_start": 21,
"quiet_end": 8,
"group_targets": [
{
"chat_id": -1001234567890,
"topic_id": 42,
"interval_minutes": 60,
"prompt": "Check project status and report if anything needs attention.",
"quiet_start": 23,
"quiet_end": 6
},
{
"chat_id": -1009876543210
}
]
}
}
Per-target override fields (all optional, fall back to global heartbeat values when unset):
promptack_tokeninterval_minutesquiet_start/quiet_end
Lifecycle note:
- heartbeat/cleanup config values hot-reload
- observer start/stop does not hot-toggle
- if disabled at startup, changing
enabledtotruerequires restart
Cleanup¶
Cleanup runs once per day at cleanup.check_hour (in user_timezone), checked hourly.
Deletes old files (recursive) from:
workspace/telegram_files/(Telegram media)workspace/matrix_files/(Matrix media)workspace/output_to_user/workspace/api_files/
Retention windows:
cleanup.media_files_dayscleanup.output_to_user_dayscleanup.api_files_days
Cleanup also prunes empty subdirectories after deletion, so dated upload folders are removed once empty.
Config blocks¶
{
"heartbeat": {
"enabled": false,
"interval_minutes": 30,
"cooldown_minutes": 5,
"quiet_start": 21,
"quiet_end": 8,
"ack_token": "HEARTBEAT_OK"
},
"cleanup": {
"enabled": true,
"media_files_days": 30,
"output_to_user_days": 30,
"api_files_days": 30,
"check_hour": 3
},
"webhooks": {
"enabled": false,
"host": "127.0.0.1",
"port": 8742,
"rate_limit_per_minute": 30
}
}
Cron jobs and webhook entries are stored in cron_jobs.json / webhooks.json.