agentafk
Guides

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 daemon

The 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 daemon

The 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 allowlist

Start the bot:

afk telegram start

Once 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,98765432

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

VariableTypeDefaultWhat it limits
AFK_MAX_BUDGET_USDnumber5.00Cumulative USD spend ceiling for the session
AFK_TASK_BUDGETnumber100000Per-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:

VariableDefaultWhat it limits
AFK_MAX_OUTPUT_TOKENSprovider defaultOutput tokens per turn only
AFK_MAX_TOKENS4096Total 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:

TriggerBehavior
cronFires on the cron expression only (default)
sessionstartFires once when the daemon boots
bothFires 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 | never

Putting 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 daemon

The 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 doctor before leaving the daemon unattended — it validates keys, paths, and provider connectivity.
  • afk status reports 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-setup skill.