Skip to content

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)
  • /stop cancels all sessions for the chat
  • /stop_all on 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
  • /status shows active background tasks
  • /session background timeout uses config.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 running before 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 /tasks command (running/waiting/finished + cancel/cleanup controls)
  • created/resumed/cancelled/deleted through task tools (tools/task_tools/*.py) over InternalAgentAPI /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 via handle_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:

  1. dependency lock (dependency_queue)
  2. quiet-hour check (only when job.quiet_* is set; no fallback to global heartbeat quiet hours)
  3. folder check
  4. resolve task overrides (provider/model/reasoning/cli_parameters)
  5. build provider command (claude, codex, or gemini)
  6. execute with timeout (cli_timeout)
  7. parse output
  8. send result callback to chat when the run actually executes and produces a callback path
  9. routing: UNICAST to originating chat when the job has chat_id set; BROADCAST to all users when chat_id is zero/unset
  10. if UNICAST delivery fails (e.g. transport unavailable), the bus cascading fallback delivers to an available transport with an explanation
  11. 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_effort is only used for Codex models that support it.
  • task cli_parameters are task-level only (no merge with global provider args).
  • cron status includes error:cli_not_found_<provider> for missing provider binaries.
  • error:folder_missing updates last_run_status but 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:

  1. dependency lock (same as agent mode)
  2. quiet-hour check (same as agent mode)
  3. resolve script path: cron_tasks/<task_folder>/<script>
  4. execute: python3 <script> with cwd set to the task folder
  5. capture stdout as result text; if exit code ≠ 0, stderr is appended
  6. deliver result to chat (same routing as agent mode)
  7. [SILENT] marker in stdout suppresses delivery (same as agent mode)
  8. persist status: success, error, or error: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:

  1. rate limit
  2. content type (application/json)
  3. JSON object body
  4. hook exists
  5. hook enabled
  6. auth (bearer or hmac)
  7. accept and dispatch asynchronously (202)

Modes:

  • wake: inject rendered prompt into active Telegram chat flow
  • cron_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_end are considered
  • no fallback to global heartbeat quiet hours

Typical status values:

  • success
  • error:no_response
  • error:no_task_folder
  • error:folder_missing
  • error:cli_not_found_claude
  • error:cli_not_found_codex
  • error:cli_not_found_gemini
  • error:timeout
  • error:exit_<code>
  • skipped:quiet_hours

Current transport limitation:

  • webhook wake depends on a configured wake handler and is currently wired by Telegram startup only
  • Matrix-primary setups do not provide that handler right now, so wake returns error:no_wake_handler
  • for Matrix-only deployments, use cron_task mode instead of wake

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):

  • prompt
  • ack_token
  • interval_minutes
  • quiet_start / quiet_end

Lifecycle note:

  • heartbeat/cleanup config values hot-reload
  • observer start/stop does not hot-toggle
  • if disabled at startup, changing enabled to true requires 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_days
  • cleanup.output_to_user_days
  • cleanup.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.