Unattended Runs
Run Agent AFK safely while you're away: daemon mode, Telegram supervision, budget caps, and scheduled tasks.
Agent AFK is built for work that happens while you're away from the keyboard. This guide covers the four pieces that make long, headless runs safe: the daemon, Telegram supervision, spend limits, and scheduled tasks.
The daemon
afk daemon runs a headless agent session — no REPL, no interactive prompts, no permission dialogs:
afk daemonThe daemon accepts a task at startup and runs until it reaches a terminal state. All the same tools are available (Bash, file ops, web fetch, subagents), and bypass permissions are enabled by default so no tool prompt can wedge the session.
Pass a task inline:
afk daemon --task "refactor the auth module and run tests"Or set a default task in your environment:
AFK_DAEMON_TASK="triage open issues and file a daily summary" afk daemonThe working directory for spawned sessions defaults to the current directory. Override with AFK_DAEMON_CWD.
Running once
--once runs a single task and exits when it reaches a terminal state, rather than looping:
afk daemon --once --task "run the test suite and report"This is useful in CI or cron where you want a bounded execution.
Telegram supervision
Pairing the daemon with Telegram gives you push notifications the moment work lands in a terminal state — no polling, no tail-following a log file.
Setup (one-time):
afk telegram setup # walks through bot token + chat ID allowlistStart the bot:
afk telegram startOnce configured, the send_telegram built-in tool is available to every agent session. The daemon calls it automatically on task completion; your own prompts and skills can call it too. The tool is safe to invoke unconditionally — it returns an error (does not throw) if Telegram is not configured.
Recipients are gated by AFK_TELEGRAM_ALLOWED_CHAT_IDS. Set this to a comma-separated list of chat IDs that are allowed to interact:
AFK_TELEGRAM_ALLOWED_CHAT_IDS=12345678,98765432See the Telegram surface guide for full bot setup.
Budget caps
Two environment variables set hard spending limits. The daemon aborts a turn if either ceiling is breached:
| Variable | Type | Default | What it limits |
|---|---|---|---|
AFK_MAX_BUDGET_USD | number | 5.00 | Cumulative USD spend ceiling for the session |
AFK_TASK_BUDGET | number | 100000 | Per-task cumulative token ceiling |
Source: docs/env-registry.md (Model category).
Set them in your shell, in ~/.afk/config/afk.env, or pass --max-budget-usd / --task-budget per-session:
# Environment
export AFK_MAX_BUDGET_USD=2.00
export AFK_TASK_BUDGET=50000
# Persist to afk.env (these are non-secret, so `afk config env set` works directly)
afk config env set AFK_MAX_BUDGET_USD 2.00
# Or per-run
afk daemon --max-budget-usd 2.00 --task "..."When the session's cumulative spend crosses the ceiling, the turn aborts and you'll see Hit the budget cap in the output. Raise AFK_MAX_BUDGET_USD or unset it for the session to remove the limit.
A per-output-token cap is also available if you need tighter control:
| Variable | Default | What it limits |
|---|---|---|
AFK_MAX_OUTPUT_TOKENS | provider default | Output tokens per turn only |
AFK_MAX_TOKENS | 4096 | Total tokens per turn (input + output) |
Scheduled tasks
afk schedule manages a persistent list of tasks the daemon runs on a cron expression. Tasks are stored in ~/.afk/config/schedules.json and live-synced to a running daemon when the port file is available.
# Add a task
afk schedule add \
--name "nightly test run" \
--command "run the test suite and file a summary" \
--cron "0 2 * * *"
# List all tasks
afk schedule list
# Disable without removing
afk schedule disable <id>
# Re-enable
afk schedule enable <id>
# Remove permanently
afk schedule remove <id>
# Inspect recent runs
afk schedule logs <id>Source: src/cli/commands/schedule.ts.
Trigger modes
Pass --trigger to schedule add to control when the task fires:
| Trigger | Behavior |
|---|---|
cron | Fires on the cron expression only (default) |
sessionstart | Fires once when the daemon boots |
both | Fires at boot and on the cron expression |
--sessionstart-cooldown-ms (env: AFK_SESSIONSTART_COOLDOWN_MS) adds a delay between task fires so the daemon doesn't hammer on rapid restarts.
Notification policy
Control when Telegram notifications fire per task:
afk schedule add \
--name "daily digest" \
--command "..." \
--cron "0 9 * * *" \
--notify always # always | failure | neverPutting it together
A complete setup for a nightly unattended run:
# ~/.afk/config/afk.env
ANTHROPIC_API_KEY=sk-ant-...
TELEGRAM_BOT_TOKEN=1234567890:ABC...
AFK_TELEGRAM_ALLOWED_CHAT_IDS=12345678
AFK_MAX_BUDGET_USD=3.00
AFK_TASK_BUDGET=150000
# Add a nightly task
afk schedule add \
--name "nightly report" \
--command "check open PRs, run tests, and send a summary via Telegram" \
--cron "0 3 * * *" \
--notify always
# Start the daemon (it will pick up the schedule)
afk daemonThe daemon reads the schedule file at startup, runs tasks at their cron times, and pushes a Telegram notification on completion. If spend hits AFK_MAX_BUDGET_USD, the turn aborts and the notification surfaces the error.
Tips
- Run
afk doctorbefore leaving the daemon unattended — it validates keys, paths, and provider connectivity. afk statusreports bypass-mode state, model, and connection health.- Logs write to
~/.afk/logs/. The daemon state directory is~/.afk/state/daemon/. - To install the daemon as a persistent macOS service that survives reboots, use the
/service-setupskill.