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 "run the test suite; if anything is red, diagnose it and open a draft PR with the fix"

Or set a default task in your environment:

AFK_DAEMON_TASK="review any PRs opened since the last run and post findings as a comment" 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 full test suite and send me a summary on Telegram"

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 "triage today's new issues and draft a reply to each"

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_TOKENS4096Deprecated / inert — use AFK_MAX_OUTPUT_TOKENS

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 tests" \
  --command "run the test suite; if it fails, diagnose the failure and open a fix PR" \
  --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 PR review" \
  --command "/review --auto" \
  --cron "0 9 * * 1-5" \
  --notify always     # always | failure | never

Pull queue (afk queue)

afk queue lets you enqueue tasks that a daemon running in --trigger pull mode will drain. Tasks are persisted as JSON files in ~/.afk/state/queue/ and consumed one-by-one on the daemon's poll interval (30 seconds).

Subcommands

SubcommandDescription
afk queue add <command>Enqueue a command for the pull-trigger daemon to execute
afk queue listList all pending queued tasks in FIFO order
afk queue remove <id>Remove a pending task by id
afk queue clearRemove all pending tasks from the queue

add accepts one option:

FlagDescription
--notify-on <mode>When to send a Telegram notification: failure | always | never

Typical workflow

# 1. Start the daemon in pull mode (no cron needed)
afk daemon --trigger pull

# 2. Enqueue tasks from another terminal (or a script) while the daemon runs
afk queue add "/review 1234 --post github" --notify-on always
afk queue add "/diagnose the failing integration test on main" --notify-on failure

# Tasks are picked up and executed in order on the next poll (≤30s)

The daemon prints ✔ Daemon in pull mode on start and processes each queued file in sequence-number order, deleting it after the task completes.

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.

On this page