Reference
Environment Variables
Complete reference for all environment variables recognized by agent-afk, grouped by category.
All environment variables recognized by agent-afk are listed here, grouped by functional category. Most are optional; required variables are noted in the description. Variables can be set in your shell, a .env file, or ~/.afk/config/afk.env — that last file is loaded at startup (shell env takes precedence).
Authentication
| Variable | Type | Description | Default |
|---|---|---|---|
AFK_IMAGE_API_KEY | string | Dedicated OpenAI API key for the image_generate tool. Checked before OPENAI_API_KEY to keep image billing separate from chat completions. When unset, OPENAI_API_KEY is used as a fallback. Get a key from https://platform.openai.com/api-keys. | — |
AFK_LOCAL_API_KEY | string | Placeholder API key for local Anthropic-compatible servers (vllm-mlx, etc.). Set when AFK_LOCAL_BASE_URL is configured. | local |
ANTHROPIC_API_KEY | string | Anthropic API key. Tier-1 credential — overrides keychain OAuth and CLAUDE_CODE_OAUTH_TOKEN. | — |
CLAUDE_CODE_OAUTH_TOKEN | string | Claude Code OAuth token. Tier-2 credential — used when ANTHROPIC_API_KEY is unset; falls back to keychain. | — |
CODEX_API_KEY | string | Fallback OpenAI API key for the openai-compatible provider, read after OPENAI_API_KEY. Legacy name from the removed @openai/codex-sdk integration — prefer OPENAI_API_KEY. | — |
EXA_API_KEY | string | Exa (exa.ai) search API key, enabling web_scrape search mode. Free tier (20k requests/month) available at https://exa.ai. When unset, search mode returns an actionable error; markdown and raw modes are unaffected. | — |
OPENAI_API_KEY | string | OpenAI API key for the openai-compatible provider (gpt-, o1, o3*, o4* models). | — |
XAI_API_KEY | string | xAI API key for the xai provider (Grok models) in API-key mode. Metered; distinct from SuperGrok / SuperGrok Heavy / X Premium+ OAuth. | — |
Model
| Variable | Type | Description | Default |
|---|---|---|---|
AFK_COMPACT_KEEP_LAST_TURNS | number | Number of recent turns the compactor keeps verbatim during /compact. Default tuned in compact-handler.ts. | — |
AFK_COMPACT_MODEL | string | Model id or slot alias used by the /compact summarizer and auto-compaction. Accepts any model on any supported provider (anthropic, openai, xai). Cross-provider compaction is supported: set to a gpt-* id on a Claude session (or a claude-* id on an OpenAI session) and AFK will route the summarize call to that provider using its own credentials. The transcript is sent to the target provider — a one-time privacy warning is emitted on first cross-provider use. Requires that provider's credentials to be available (ANTHROPIC_API_KEY / OPENAI_API_KEY / XAI_API_KEY, or ChatGPT-subscription OAuth from codex login). When unset: Anthropic sessions fall back to claude-haiku-class; OpenAI API-key sessions (api.openai.com) fall back to gpt-6-luna; ChatGPT-subscription (chatgpt-oauth) and custom-baseURL sessions (local runners, proxies) fall back to the session model. Also drives ghost-text suggestions on Claude sessions (see AFK_SUGGEST_MODEL to override that independently). | — |
AFK_COMPACT_SHRINK_FRACTION | number | Context-fullness fraction (0–1, exclusive) at/above which /compact and auto-compaction relax the keep-window so a short-but-full session (few turns, huge tool exchanges) can still be summarized instead of no-oping on turn count. Default 0.7 (see shared/compaction.ts DEFAULT_COMPACT_SHRINK_THRESHOLD). | — |
AFK_CONNECT_RETRY_BUDGET_MS | number | Experimental streaming connection retry wall budget in milliseconds. Positive finite value enables capped jittered backoff before the stream opens; unset or invalid preserves legacy retry counts. In-flight attempts retain existing timeouts. | — |
AFK_CONTEXT_GUARD_DISABLE | boolean | Disable the temporary within-turn context pressure guard (1/true/yes/on). Restores unguarded tool-loop behavior; ordinary compaction and round caps remain enabled. | 0 |
AFK_CONTEXT_GUARD_PCT | number | Temporary within-turn context guard operational threshold as percent of the effective provider-route limit (catalog metadata for subscription routes; contextLimitFor() for API-key routes). Default 95. Not a documented model capacity or guaranteed server cutoff. Valid range 1–99. | 95 |
AFK_DEFAULT_SUBAGENT_MODEL | string | Override the default model used when a subagent is dispatched without an explicit model. | — |
AFK_DISABLE_PROMPT_CACHE | boolean | Disable Anthropic prompt caching when set to 1/true/yes/on. Unset = caching enabled. | 0 |
AFK_EFFORT | string | Effort hint guiding adaptive-thinking depth, forwarded as Anthropic output_config.effort (model-gated; ignored where unsupported). Accepts low | medium | high | xhigh | max. | — |
AFK_LOCAL_BASE_URL | string | Base URL for a self-hosted Anthropic-compatible server. When set, routes traffic away from api.anthropic.com. | — |
AFK_MAX_BUDGET_USD | number | Opt-in cumulative USD budget ceiling for the session. Aborts the turn when the running cost crosses this. Unset by default (no cap applied). | — |
AFK_MAX_CONCURRENT_AGENTS | number | Maximum number of simultaneously-running agents across the entire session tree. Part of the delegation budget: prevents rate-limit (429) cascades from too many parallel children. Accepted range 1-64; unset or unparseable means no concurrent limit. Works alongside AFK_MAX_CONCURRENT_CHILDREN_PER_AGENT and AFK_MAX_TOTAL_AGENTS. | — |
AFK_MAX_CONCURRENT_CHILDREN_PER_AGENT | number | Maximum number of child agents a single agent may have running concurrently (per-agent concurrent child cap). Part of the delegation budget: guards against a single agent fanning out too many simultaneous children. Accepted range 1-20; unset or unparseable means no per-agent child limit. Works alongside AFK_MAX_CONCURRENT_AGENTS and AFK_MAX_TOTAL_AGENTS. | — |
AFK_MAX_NESTING_DEPTH | number | Maximum sub-agent/skill nesting depth; 0 disables nested delegation entirely (the agent, skill, AND compose tools all refuse). A top-level session is depth 0, so the default 3 permits three generations of forked descendants (depth 1 → 2 → 3) and refuses the agent and skill tools at depth 3. Resolved once at the root of each session and propagated down through child AgentConfig, so descendants inherit the root value rather than re-reading the environment. Raise with care: depth is a fan-out exponent (a width-N wave at depth D reaches ~N^D concurrent children), so values above 4 invite provider rate-limit (429) cascades. Accepted range 0-6; unset, empty, unparseable, negative, or out-of-range input falls back to the default. An explicit programmatic maxDepth (SubagentExecutorContext / SkillExecutorContext) still wins. | 3 |
AFK_MAX_OUTPUT_TOKENS | number | Cap on output tokens per turn. When unset, falls back to the model output ceiling (AFK's own per-model limit, 64k–128k — NOT a small provider default), so every request advertises the full ceiling by default. An over-ceiling value is clamped down with a warning. | — |
AFK_MAX_TOKENS | number | Deprecated and inert: not read by the generation path. Use AFK_MAX_OUTPUT_TOKENS (or --max-output-tokens) to cap per-response output tokens; falls back to the model output ceiling when unset. | 4096 |
AFK_MAX_TOOL_USE_ITERATIONS | number | Opt-in ceiling on tool-use rounds per turn for TOP-LEVEL (non-subagent) sessions, on both providers. Mirrors the maxToolUseIterations config key / max_tool_use_iterations tool param. Unset, non-numeric, or <=0 means unlimited (the default — zero behavior change): a top-level turn ends only when the model stops calling tools, the abort signal fires, the provider errors, or the dollar budget trips. A positive integer N makes top-level turns wind down gracefully after N tool rounds (one tools-stripped final round). An explicit config/CLI value wins over this env default. Does NOT affect subagent forks — they keep their own non-zero anti-hang default (SUBAGENT_DEFAULT_MAX_TOOL_USE_ITERATIONS) regardless of this var. | 0 |
AFK_MAX_TOTAL_AGENTS | number | Maximum total agents spawned across the entire session tree (lifetime, not concurrent). Part of the delegation budget: absolute ceiling preventing runaway recursive delegation. Accepted range 1-200; unset or unparseable means no total limit. Works alongside AFK_MAX_CONCURRENT_CHILDREN_PER_AGENT and AFK_MAX_CONCURRENT_AGENTS. | — |
AFK_MICROCOMPACT_DELEGATION_BYTES | number | Byte threshold for delegation tool results (agent/compose/skill). These results carry pre-compressed subagent findings that are expensive to re-derive -- clearing them destroys irreplaceable synthesized knowledge. A delegation tool result must be at least this large before microcompaction considers clearing it. Default 16384 (16KB, vs 2KB for ordinary tools). Set to match AFK_MICROCOMPACT_TOOL_RESULT_BYTES to disable the elevated threshold. | 16384 |
AFK_MICROCOMPACT_KEEP_LAST | number | Number of the most-recent tool_result blocks that tool-result microcompaction keeps intact regardless of size, so the agent does not lose the tool output it is actively reasoning over. Older results are the safe ones to trim. Default 4 (see shared/compaction.ts DEFAULT_MICROCOMPACT_KEEP_LAST). Values <= 0 protect nothing. | 4 |
AFK_MICROCOMPACT_TOOL_RESULT_BYTES | number | Byte threshold (tool_result content length) at/above which a tool_result block becomes a microcompaction candidate. When /compact and auto-compaction would otherwise no-op on a short-but-full session, microcompaction clears large/old tool_result CONTENT in place (largest first) — replacing it with a short placeholder, never removing the block — to reclaim context deterministically (no LLM call). Blocks below this size are left intact. Default 2048 (see shared/compaction.ts DEFAULT_MICROCOMPACT_TOOL_RESULT_BYTES). | 2048 |
AFK_MODEL | string | Default model for agent turns. Accepts slot names (local, small, medium, large), fixed-identity aliases (opus, sonnet, haiku, fable), or full model IDs. Migration: AFK_MODEL=sonnet now pins the fixed Sonnet identity rather than following a rebound medium tier. | medium |
AFK_MODEL_LARGE | string | Bind the "large" capability tier (most capable) to a model id/alias. Overrides afk.config.json models.large. | — |
AFK_MODEL_LARGE_API_KEY | string | Per-slot API key for the "large" tier (Stage 2). Overrides global credentials for this tier only. | — |
AFK_MODEL_LARGE_BASE_URL | string | Per-slot endpoint base URL for the "large" tier (Stage 2). Anthropic Messages base or OpenAI-compatible base per the tier provider. | — |
AFK_MODEL_LOCAL | string | Bind the "local" capability tier (cheapest/fastest, user-configured) to a model id. Overrides afk.config.json models.local. Point at a local Ollama, LM Studio, or any OpenAI-compatible shim. | — |
AFK_MODEL_LOCAL_API_KEY | string | Per-slot API key for the "local" tier. Overrides global credentials for this tier only. | — |
AFK_MODEL_LOCAL_BASE_URL | string | Per-slot endpoint base URL for the "local" tier. Anthropic Messages base or OpenAI-compatible base per the tier provider. | — |
AFK_MODEL_MEDIUM | string | Bind the "medium" capability tier (general-use) to a model id/alias. Overrides afk.config.json models.medium. | — |
AFK_MODEL_MEDIUM_API_KEY | string | Per-slot API key for the "medium" tier (Stage 2). Overrides global credentials for this tier only. | — |
AFK_MODEL_MEDIUM_BASE_URL | string | Per-slot endpoint base URL for the "medium" tier (Stage 2). Anthropic Messages base or OpenAI-compatible base per the tier provider. | — |
AFK_MODEL_SMALL | string | Bind the "small" capability tier (cheap/fast) to a model id/alias. Overrides afk.config.json models.small. | — |
AFK_MODEL_SMALL_API_KEY | string | Per-slot API key for the "small" tier (Stage 2). Overrides global credentials for this tier only. | — |
AFK_MODEL_SMALL_BASE_URL | string | Per-slot endpoint base URL for the "small" tier (Stage 2). Anthropic Messages base or OpenAI-compatible base per the tier provider. | — |
AFK_MODEL_STALL_TIMEOUT_MS | number | Progress-aware POST-first-byte stall window (ms) for the anthropic-direct streaming loop. Bounds how long a stream that has ALREADY produced content may then go completely silent. Every streamed output event resets the window, so this is NOT a total-round cap: a legitimately long, actively-streaming round (large code emission, extended thinking) survives indefinitely, while a stream wedged mid-flight is aborted and surfaces as a real terminal error instead of hanging. Complements AFK_MODEL_TTFB_TIMEOUT_MS, which governs only the window BEFORE the first token; this one takes over after it. Default 1200000 (20min) — above the 18.5min maximum post-first-byte stream duration observed across 86,076 rounds in 12,381 local traces (p99 122s, p99.9 234s), so no healthy round trips it. Set to 0 to disable (issue #762). | 1200000 |
AFK_MODEL_TTFB_TIMEOUT_MS | number | Per-ROUND time-to-first-token budget (ms) for the anthropic-direct streaming loop. Bounds how long a round may stall BEFORE its first streamed CONTENT token (a text/thinking delta or tool_use); the connection-level message_start and keep-alive pings do NOT count. Once a content token streams, the timer is cleared and the rest of the response is governed instead by the progress-aware AFK_MODEL_STALL_TIMEOUT_MS window, so a normal slow call (below the bound) and any actively-streaming extended-thinking response are never aborted. The budget is divided into 3 shorter first-byte ATTEMPTS (each ~2/3 of this value, so the worst-case wall time per round is unchanged from the previous 2-attempt regime while a transient stall gets 3 chances instead of 2). NOTE: a request whose FIRST token takes longer than the per-attempt bound — e.g. a very large opus_1m prefill — is aborted and re-driven, then surfaces as an error once the budget is spent (raise this value or set 0 for such workloads); this trims the degrading-call tail instead of a silent ~10-min hang on the SDK default. Default 180000 (180s ≈ 2× the measured p99 ttfb), i.e. 3 × 120s. Set to 0 to disable. | 180000 |
AFK_OPENAI_BASE_URL | string | Base URL override for the OpenAI-compatible provider. Used for local shims (mlx_lm.server, Ollama, vLLM, LM Studio). The OpenAI SDK appends /chat/completions itself — a value ending in /chat/completions will be stripped at config-load time with a one-shot warning. | — |
AFK_OPENAI_USE_RESPONSES | boolean | Opt the OpenAI-compatible provider into the OpenAI Responses API instead of Chat Completions for API-key sessions. Truthy values: 1, true, yes, on. The ChatGPT-subscription OAuth path uses Responses automatically regardless of this flag. | — |
AFK_OVERLOAD_PAUSE_MS | number | Wall-clock ceiling (ms) for the bounded pause after a mid-stream overload (529) exhausts its retry budget. Overrides the per-surface default for ALL surfaces: 0 disables the pause (fail fast). Interactive surfaces (cli/repl/telegram) default to 600000; daemon/cron default to 0 so an always-on runner never silently parks on upstream capacity. | — |
AFK_PROMPT_CACHE_TTL | string | TTL for Anthropic prompt-cache blocks. Accepts 5m or 1h. | 1h |
AFK_PROVIDER | string | Force provider selection (anthropic | anthropic-direct | openai | openai-compatible | openai-codex | xai | xai-oauth). Overrides the model-name heuristic. Same surface as the --provider CLI flag; CLI flag wins when both are set. | — |
AFK_RATE_LIMIT_ADMISSION_DISABLED | boolean | Bypass the process-wide rate-limit admission gate (issue #941). Set to 1 to skip pre-request capacity checks. Default 0 (gate active). OAuth subscription accounts already pass through automatically when no per-minute headers are returned. | 0 |
AFK_RATE_LIMIT_STAGGER_MAX_MS | number | Jitter ceiling (ms) for the rate-limit admission bucket: each waiter at a window boundary wakes at reset + random(0..ceiling) to avoid re-storming the API. Default 500. Set to 0 in tests for deterministic timing. | 500 |
AFK_SUBAGENT_AUTO_RESUME_ON_USAGE_LIMIT | boolean | When set to 1/true, forked sub-agents that hit an OAuth usage-limit 429 will park and wait (up to 2 hours) for a keychain account hot-swap before surfacing the error — the same behaviour as top-level interactive sessions. When false (the default), forks fail fast so the parent session can decide how to recover. Ignored for daemon-surface sessions, because no human can switch accounts there. Callers may override per-fork via config.autoResumeOnUsageLimit; an explicit value always wins over this env var. | 0 |
AFK_SUBAGENT_IDLE_TIMEOUT_MS | number | Forked-subagent progress-aware idle-watchdog window in ms; 0 disables the watchdog; an explicit per-fork config.idleTimeoutMs still wins. Fires when a forked child produces no observable output event for this window, aborting the same controller the wall-clock timeout targets so partial output is preserved and the run classifies as a failure. This is distinct from AFK_SUBAGENT_TIMEOUT_MS, the blunt wall-clock that bounds total turn time: the idle watchdog is the tighter first-to-fire bound and never fires while the stream is legitimately parked on a provider-communicated backoff (OAuth pause, or a rate-limit event carrying a retry-after), extending the deadline for the pause window instead. Default 480000 (8 min) clears the worst-case transient-429 backoff the watchdog is currently blind to (about 363s) with roughly 2 min of margin, while staying materially tighter than the 45-min wall-clock. Unset, empty, or unparseable input falls back to the default; a negative value is treated as invalid and also falls back. Set to 0 to disable the idle watchdog for a whole session (the wall-clock still applies). v1 applies to forked sub-agent turns only, not top-level or daemon sessions. | 480000 |
AFK_SUBAGENT_RESULT_CAP_BYTES | number | Foreground subagent result size cap in bytes. When a subagent's final message exceeds this threshold, the full output is spilled to a sidecar file (state/sessions/<id>/subagent-handoffs/<subagentId>.txt) and the parent receives a head+tail slice with a read_file pointer. Prevents large subagent outputs from bloating parent context. Set to 0 to disable the cap entirely. Default 32768 (32KB). Note: sidecar files contain unredacted subagent output. No automatic GC covers subagent-handoffs/ today; retention is tied to session-directory cleanup. | 32768 |
AFK_SUBAGENT_TIMEOUT_MS | number | Foreground forked-subagent wall-clock budget in ms; 0 disables the cap; explicit per-fork config.timeoutMs and the 60-min background mode still win. Bounds how long a single forked child turn may run before withTimeout aborts its controller (cascading through the AbortGraph to descendants) and the parent receives a legible TimeoutError tool_result instead of hanging. Default 2700000 (45 min ≈ headroom over the longest healthy review/research agent observed in production). Unset, empty, or unparseable input falls back to the default; a negative value is treated as invalid and also falls back. Set to 0 to opt a whole session back into unbounded child turns. Does NOT affect the background dispatch budget (SUBAGENT_BACKGROUND_TIMEOUT_MS) or a per-fork config.timeoutMs — both take precedence. | 2700000 |
AFK_SUGGEST_ENABLED | boolean | Enable the LLM-backed ghost-text suggestion tier in the interactive REPL. On by default. Set to 0/false/no/off to disable. | 1 |
AFK_SUGGEST_GHOST | boolean | Enable REPL ghost-text inline suggestions (Tier-1 history/dropdown + optional Tier-2 LLM). 1 = on (default), 0 = off. Set 0/false/off/no to disable all ghost text. Tier-2 LLM is separately gated by AFK_SUGGEST_ENABLED. | 1 |
AFK_SUGGEST_MODEL | string | Override the small model used for REPL ghost-text suggestions. Falls back to AFK_COMPACT_MODEL or haiku-class for anthropic, or the session model for other providers. | — |
AFK_SUGGEST_PROMPT | boolean | Enable LLM-generated empty-prompt suggestions — a proposed next action shown as ghost text when the prompt is blank, accepted with Tab or Right-arrow. Fires only after a turn has completed in the session: the startup prompt (and the prompt right after /clear) is left clean because there is no session context to ground a proposal in. On by default. Set to 0/false/no/off to disable. Requires AFK_SUGGEST_ENABLED (it reuses the same suggestion model and provider). | 1 |
AFK_SYSTEM_PROMPT | string | Raw operator-overlay prompt. Highest-priority overlay (over afk.config.json and AFK.md). Appended on top of the framework base (system-prompt.md) under an "# Operator configuration" header — it augments, never replaces, the base. | — |
AFK_TASK_BUDGET | number | Per-task token budget ceiling. Aborts when cumulative usage would exceed it. | 100000 |
AFK_TEMPERATURE | number | Numeric temperature override for model sampling. Provider default if unset. | — |
AFK_THINKING | string | Extended-thinking mode. Accepts adaptive | disabled | max | enabled:<N> | enabled:max. Defaults to the model-appropriate mode when unset (adaptive on current models). | adaptive |
AFK_TIMEOUT_MS | number | Per-turn timeout in milliseconds. Provider/SDK default if unset. | — |
AFK_USAGE_LEDGER_DISABLED | boolean | Set to 1 to stop publishing/reading the cross-process usage ledger (namespace usage in state/kv/kv.db). Usage surfaces (afk usage, get_runtime_state, fan-out notice, daemon budget gate) then see only this process's in-memory quota cache. Default 0 (ledger active). | 0 |
AFK_VISION_MODELS | string | Comma-separated override for image (vision) capability detection on the openai-compatible provider. Each token force-enables a model id by exact or substring match (e.g. "qwen2.5-vl" matches a local VL id); prefix a token with "!" to force-disable. Use to send images to a local vision-language model AFK does not recognise by name, or to blacklist a mis-detected id. Built-in detection already covers gpt-4o/4.1/5.x, o1/o3/o4-mini, Claude, and common VL families. | — |
AFK_WORKSPACE_DISABLED | boolean | Disable the shared agent workspace (WorkspaceStore + workspace_publish/workspace_query tools + preamble injection). When set to 1, subagents do not get workspace tools and no workspace preamble is injected at fork time — each agent works in full isolation as before v5.133. Used as the control arm of the file-read deduplication A/B experiment. Default: workspace enabled (unset or 0). | 0 |
AFK_XAI_BASE_URL | string | Base URL for xAI API-key mode. Default https://api.x.ai/v1. The OpenAI SDK appends /chat/completions. | — |
AFK_XAI_GROK_CLIENT_VERSION | string | Override the semver sent as x-grok-client-version to the xAI OAuth CLI chat proxy. Use only if the proxy raises its required Grok CLI version before agent-afk is updated; invalid values fall back to the official Grok Build version file or built-in compatible default. | — |
AFK_XAI_OAUTH_BASE_URL | string | Base URL for xAI SuperGrok / SuperGrok Heavy / X Premium+ OAuth inference. Default https://cli-chat-proxy.grok.com/v1 (subscription path). Some accounts work on https://api.x.ai/v1 with OAuth — override if needed. Distinct from AFK_XAI_BASE_URL (API-key mode). | — |
CLAUDE_MODEL | string | Legacy alias for AFK_MODEL — supported for back-compat with pre-AFK_* deployments. | — |
OPENAI_BASE_URL | string | Standard OpenAI SDK base URL override. When set without AFK_OPENAI_BASE_URL, the OpenAI SDK routes the client to this endpoint while AFK's own baseURL option remains undefined. AFK reads this variable to correctly classify the effective endpoint for decisions such as compaction model selection — without it, AFK would see an undefined baseURL and incorrectly treat the session as targeting api.openai.com. | — |
Routing
| Variable | Type | Description | Default |
|---|---|---|---|
AFK_AUTO_ROUTING | boolean | Auto-route bare slash inputs to matching skills. Applies to interactive, chat, and telegram surfaces. The afk web surface also inherits the interactive value (autoRouting.interactive), so this setting covers web sessions too. | — |
AFK_INTERNAL | boolean | Tier gate. Set to exactly 1 to unlock — only the literal string "1" unlocks (other truthy values like "true"/"yes" leave the tier locked). When unlocked, skills tagged audience: 'internal' (e.g. /audit-fit, harvest/distill plugins) become visible at end-user surfaces (slash-command list, --help, tab-complete, system-prompt skill manifest). Default unset = public tier — internal skills are hidden. Not an access-control boundary; it gates surfacing, not the underlying registry. | — |
Browser
| Variable | Type | Description | Default |
|---|---|---|---|
AFK_BROWSER_ALLOWED_DOMAINS | string | Comma-separated allowlist of URL host globs. When set, browser_open and any navigation that targets a host outside the list returns status: blocked_by_policy. Unset means no allowlist (permissive). Patterns use simple * glob matching against the URL host. Combines with AFK_BROWSER_BLOCKED_DOMAINS — block wins. | — |
AFK_BROWSER_BACKEND | string | Browser provider backend: playwright (Playwright, the only supported backend) or auto (default, resolves to playwright). The legacy value agent-browser is accepted but deprecated — it resolves to playwright and emits a warning. | auto |
AFK_BROWSER_BLOCKED_DOMAINS | string | Comma-separated blocklist of URL host globs. Browser navigation that matches any entry returns status: blocked_by_policy regardless of the allowlist. | — |
AFK_BROWSER_CONFIG | string | Absolute path to an alternate browser config file. Overrides the default ~/.afk/config/browser.json lookup. Useful for per-project overrides in CI. | — |
AFK_BROWSER_DEFAULT_PROFILE | string | Name of the persistent session-vault profile the agent reuses for browser sessions. The context restores its login from (and saves it back to) ~/.afk/state/browser/<profile>/storageState.json, so a human runs afk browser login --profile <name> once and the agent reuses that authenticated session across unattended runs. Unset defaults to default (a fresh, empty profile — identical to pre-vault behavior). Allowed charset: [A-Za-z0-9_-], max 128 chars. | default |
AFK_BROWSER_DOM_SNAPSHOTS | boolean | Phase 2 opt-in: when set to 1, every browser_act writes a gzipped DOM snapshot sidecar under ~/.afk/state/witness/<sid>/browser/dom-snapshots/. Off by default because snapshots are large; useful for post-mortem analysis of failed actions. | — |
AFK_BROWSER_HEADLESS | boolean | Override the default headless mode for native browser-control tools. 1/true forces headless; 0/false forces headed. When unset the default is headless on every AFK surface — the CLI entrypoint reports surface afk, which is in the headless set — so watching the agent work in a visible window is opt-in via AFK_BROWSER_HEADLESS=0, not implied by running the REPL. Note headed and headless use different chromium downloads (chromium-* vs chromium_headless_shell-*). | — |
AFK_SESSION_ID | string | Override the browser session ID used by the native browser-control tools. Defaults to 'default' for single-session use. Subagents inherit the parent's session by default. Set this when running multiple concurrent AFK processes that should each manage an isolated browser context. | default |
MCP (Model Context Protocol)
| Variable | Type | Description | Default |
|---|---|---|---|
AFK_ALLOW_PROJECT_MCP | boolean | Opt-in to loading + spawning MCP servers declared in <cwd>/.mcp.json. Fail-closed: when unset (or 0), project-local servers are NOT spawned; set to a truthy value (1/true/yes/on) to load them. A project-local .mcp.json spawns arbitrary commands on session start, so it is off by default to prevent code execution when entering an untrusted repo (issue #571). Skipped servers are listed in a startup warning with the opt-in instruction. | 0 |
AFK_MCP_HEALTHCHECK | boolean | Enable an optional post-connect liveness probe that re-issues tools/list to every connected MCP server in parallel after the initial connect burst. OFF by default — the extra round-trip adds cold-start latency (issue #1751). Set to a truthy value (1/true/yes/on) to enable. | — |
Daemon
| Variable | Type | Description | Default |
|---|---|---|---|
AFK_DAEMON_BUDGET_GATE_DISABLED | boolean | Set to 1 to disable the subscription-usage budget gate for daemon/cron agent tasks. When unset (default) the daemon checks Claude subscription utilization before starting each agent task and skips the run if any window is at or above AFK_DAEMON_BUDGET_SKIP_PCT. Shell and builtin tasks are never gated regardless of this setting. | 0 |
AFK_DAEMON_BUDGET_SKIP_PCT | number | Subscription-usage skip threshold (0–100 integer) for the daemon budget gate. When any Claude subscription window's utilization is at or above this percentage, scheduled agent tasks are skipped with a Telegram notice. Default 90 (90%). Set to 100 to disable the gate entirely (no usage fetch is made and every task passes). Requires AFK_DAEMON_BUDGET_GATE_DISABLED=0 (default). | 90 |
AFK_DAEMON_CWD | string | Working directory used by the daemon process for spawned agent sessions. | — |
AFK_DAEMON_HOST | string | Bind address for the daemon control HTTP surface. Defaults to 127.0.0.1 (loopback only). The control surface is unauthenticated, so bind a non-loopback address such as 0.0.0.0 only on a trusted or firewalled network. Overridden by the --host flag. | — |
AFK_DAEMON_SHELL_TIMEOUT_MS | number | Wall-clock timeout in milliseconds for executor:shell scheduled tasks. Defaults to 2700000 (45 minutes), matching the agent executor budget (AFK_SUBAGENT_TIMEOUT_MS). The child process is killed on timeout; the telemetry errorMessage will read "daemon shell timeout after NNNs" to distinguish a daemon-imposed kill from a process or network failure. | — |
AFK_DAEMON_TASK | string | Default task description for the daemon. Falls back to afk.config.json daemon.task. | — |
AFK_DAEMON_TASK_ID | string | Task identifier the daemon uses to scope its state directory and telemetry. | — |
AFK_EVENTS_GITHUB_POLL_MS | number | Poll interval in milliseconds for afk events github. Overrides the --interval flag. Must be >= 5000. Default 60000 (1 minute). Part of #3896 (event-triggered runs). | 60000 |
AFK_SESSIONSTART_COOLDOWN_MS | number | Cooldown in milliseconds between SessionStart trigger fires in the daemon. Prevents thundering-herd on rapid restarts. | — |
AFK_TOOL_HEALTH_DISABLE | boolean | Disable the tool-health daemon builtin entirely. Set to "1" to skip registration. | — |
AFK_WEB_HOST | string | Bind address for the afk web browser surface. Defaults to 127.0.0.1. Unlike the daemon control surface, a non-loopback bind is REFUSED unless AFK_WEB_TOKEN (or --token) is also set, because this surface can submit prompts and approve tool use. Overridden by --host. | 127.0.0.1 |
AFK_WEB_PORT | number | Port for the afk web browser surface. Defaults to 4141; falls back to an ephemeral port when taken. Overridden by --port. | 4141 |
AFK_WEB_TOKEN | string | Bearer token for the afk web surface. When unset, a random per-run token is minted and printed in the startup URL. Setting this explicitly is also what permits a non-loopback bind. | — |
Telegram
| Variable | Type | Description | Default |
|---|---|---|---|
AFK_TELEGRAM_ALLOWED_CHAT_IDS | string | Comma-separated list of Telegram chat IDs allowed to interact with the bot. Required when the bot is running. | — |
AFK_TELEGRAM_BOT_TOKEN | string | Alternative env var name for the Telegram bot token, accepted by the setup wizard. | — |
AFK_TELEGRAM_CWD | string | Override the working directory used by the Telegram bot when spawning agent sessions. | — |
AFK_TELEGRAM_NOTIFY_MODE | string | Outbound notification fan-out: primary (default — one chat), broadcast (every allowed chat), or custom (afk.config.json telegram.notify.targets). The afk.config.json telegram.notify.mode takes precedence. | — |
AFK_TELEGRAM_PRIMARY_CHAT_ID | string | Default chat ID for outbound notifications (primary-mode routing). When unset, notifications go to the first private/DM chat in AFK_TELEGRAM_ALLOWED_CHAT_IDS. The afk.config.json telegram.notify block takes precedence. | — |
AFK_TELEGRAM_SESSION_IDLE_MS | number | Milliseconds of inactivity after which an idle AgentSession is closed and its memory freed. sessionData (model/cwd preferences) is kept in-memory and reloaded on demand from disk. Default: 14400000 (4 hours). | 14400000 |
AFK_TELEGRAM_TAG_ONLY_CHAT_IDS | string | Comma-separated list of Telegram chat IDs where the bot answers only when addressed (a reply to the bot, an @mention of the bot, or a text_mention resolving to the bot). Slash-commands are unaffected; chats not listed behave as usual. The afk.config.json telegram.tagOnlyChats block takes precedence. Requires Telegram privacy mode OFF (BotFather /setprivacy Disable) for non-addressed group messages to reach the bot. | — |
AFK_TRANSCRIBE_API_KEY | string | Bearer token for the AFK_TRANSCRIBE_API_URL transcription endpoint. Leave unset for unauthenticated local servers. | — |
AFK_TRANSCRIBE_API_URL | string | OpenAI-compatible /audio/transcriptions endpoint URL used to transcribe inbound Telegram voice and audio messages. When unset, voice/audio messages receive a refusal reply naming this variable. Supports any OpenAI-compatible server including local Whisper deployments. | — |
AFK_TRANSCRIBE_MODEL | string | Model name forwarded to the AFK_TRANSCRIBE_API_URL endpoint (default: whisper-1). Change when using an endpoint that expects a different model identifier. | whisper-1 |
AFK_TRANSCRIBE_TIMEOUT_MS | number | Timeout in milliseconds for the transcription API POST request (default: 60000). Increase for slow local Whisper servers or large audio files. | 60000 |
TELEGRAM_BOT_TOKEN | string | Telegram bot token from @BotFather. Required to run the Telegram bot surface. | — |
TELEGRAM_DATA_DIR | string | Override the directory where Telegram bot state is stored. Defaults to ~/.afk/state/telegram/. | — |
TELEGRAM_VERBOSE | boolean | Set to a truthy value ('1'/'true'/'yes'/'on', case-insensitive) to log per-message details from the Telegram bot — chat IDs, message text, latency. | — |
Worktrees
| Variable | Type | Description | Default |
|---|---|---|---|
AFK_WORKTREE_AUTONAME | boolean | Auto-rename worktree branches based on the first user message in interactive mode. 1 = on (default), 0 = off. | 1 |
AFK_WORKTREE_BASE | string | Override the base git ref for worktrees created with --worktree. By default AFK bases worktrees on the remote's default branch (e.g. origin/main), fetched fresh. Set this to pin a different ref, or to HEAD to base on the local checkout. Overridden per-session by --worktree-base. | — |
AFK_WORKTREE_BOOT_PRUNE | boolean | When set, the daemon prunes stale worktrees at boot in addition to the cron-driven sweep. | — |
AFK_WORKTREE_BRANCH_PREFIX | string | Branch-name prefix for AFK-managed worktrees. Default afk/. Set to empty string to drop the prefix. | afk/ |
AFK_WORKTREE_MAX_AGE_CLEAN | number | Maximum age (in days) before a clean worktree is auto-pruned. Default 14. | 14 |
AFK_WORKTREE_MAX_AGE_DIRTY | number | Maximum age (in days) before a dirty worktree is auto-pruned. Default 30. | 30 |
AFK_WORKTREE_ON_EXIT | string | Clean-worktree quit policy for interactive --worktree sessions: ask, keep, or remove. | — |
AFK_WORKTREE_PRUNE_DISABLE | boolean | Disable the worktree prune job entirely. Useful for long-running tests. | — |
AFK_WORKTREE_SWEEP_ROOT | string | Override the root directory under which AFK worktrees are tracked for pruning. | — |
Paths
| Variable | Type | Description | Default |
|---|---|---|---|
AFK_COMPANION_PRIMER | string | Opt-in: absolute path to a single companion-primer file. When set, its content is bounded (capped, fenced as <companion-primer>) and appended to the system prompt at session start for top-level sessions (chat/REPL/telegram/daemon), as lower-authority "reflections, not facts" context. Unset (default) = no-op. Only the one named file is ever read — never a directory or repo walk. | — |
AFK_FRAMEWORK_DIR | string | Override the AFK agent-framework directory used for telemetry and briefs. Default: $AFK_HOME/agent-framework/. | — |
AFK_HOME | string | Override the AFK home directory. Default: ~/.afk/. | ~/.afk |
AFK_STATE_DIR | string | Override the entire AFK state tier (sessions/, todos/, transcripts/, memory/, daemon/, etc.), not just one subdirectory. Must be an absolute path (not /). Default: $AFK_HOME/state/. | — |
CODEX_HOME | string | Codex configuration, plugin, and skill directory used for imports. Defaults to ~/.codex. | — |
Process
| Variable | Type | Description | Default |
|---|---|---|---|
__CFBundleIdentifier | string | macOS-only, set by the OS: bundle id of the app that launched this process (e.g. the terminal). Read by the real-Chrome probe to name the app that needs Files & Folders access to Google Chrome on macOS 27+. A hint only — under tmux it reflects the app that started the tmux server. | — |
AFK_DISABLE_BASH_INTERPRETER_GUARD | boolean | Skip ONLY the bash interpreter-eval denylist (python -c, node -e, sh -c, ...) when set to 1, leaving the rest of path-approval intact. Applies on interactive surfaces (REPL/Telegram), where the denylist is active but your workflow legitimately runs interpreter one-liners. The restricted-root substring check is unaffected. Default: denylist active on interactive surfaces; contexts with no grant manager already fail open (opt in with AFK_FORCE_BASH_INTERPRETER_GUARD=1). To disable all of path-approval + bash restriction instead, use AFK_DISABLE_PATH_APPROVAL=1. | 0 |
AFK_DISABLE_PATH_APPROVAL | boolean | Skip the path-approval + bash-restriction hooks entirely when set to 1. Use for headless flows that need wide-open file access (CI scripts, batch jobs). Default: hooks enabled. Note: the interpreter denylist (python -c, node -e, sh -c, ...) fires wherever the session provider is wired as the grant manager (every production surface) and fails OPEN only for contexts with none; opt those into it with AFK_FORCE_BASH_INTERPRETER_GUARD=1, or set this var to 1 to disable all of path-approval. | 0 |
AFK_FORCE_BASH_INTERPRETER_GUARD | boolean | Apply the bash interpreter-eval denylist (python -c, node -e, sh -c, ...) even to tool calls whose context carries no grant manager. Every production provider wires itself as the grant manager on every surface (REPL, Telegram, afk chat, daemon, subagents), so there the denylist is already active; without one it fails open. Set to 1 to force it on for those contexts too. Overridden by AFK_DISABLE_BASH_INTERPRETER_GUARD=1. Default: off. | 0 |
AFK_MAX_CONCURRENT_BACKGROUND_JOBS | number | Maximum background subagent jobs running simultaneously in one registry. Default 10; accepted range 1-64; out-of-range or unparseable input falls back to the default. | 10 |
AFK_MAX_CONCURRENT_SAFE_TOOL_CALLS | number | Maximum concurrency-safe tool calls run simultaneously within one dispatcher batch. Default 8; accepted range 1-32; out-of-range or unparseable input falls back to the default. | 8 |
AFK_MAX_CONCURRENT_SUBAGENT_CALLS | number | Maximum subagent calls run simultaneously from one compose/DAG layer or skill wave. Default 8; accepted range 1-32; out-of-range or unparseable input falls back to the default. | 8 |
AFK_MAX_CONCURRENT_SUBAGENT_CALLS_ANTHROPIC | number | Per-provider override for AFK_MAX_CONCURRENT_SUBAGENT_CALLS applied to fan-out waves whose tasks run on the Anthropic provider (anthropic-direct). When set, takes precedence over the global AFK_MAX_CONCURRENT_SUBAGENT_CALLS for those waves. Unset = use the global value. Accepted range 1-32; out-of-range or unparseable input falls back to the global. Part of #3886. | — |
AFK_MAX_CONCURRENT_SUBAGENT_CALLS_OPENAI | number | Per-provider override for AFK_MAX_CONCURRENT_SUBAGENT_CALLS applied to fan-out waves whose tasks run on the OpenAI-compatible provider (openai-compatible). When set, takes precedence over the global AFK_MAX_CONCURRENT_SUBAGENT_CALLS for those waves. Unset = use the global value. Accepted range 1-32; out-of-range or unparseable input falls back to the global. Part of #3886. | — |
AFK_SESSION_TMPDIR_DISABLE | boolean | Disable per-session private temp dirs. By default every session and subagent gets its own directory under <os tmpdir>/afk-<uid>/ (removed on close) injected as TMPDIR/TMP/TEMP into bash and test_run child processes, so one session cleaning its temp files cannot delete a concurrent session's. Set to 1 to share the inherited TMPDIR instead. | — |
AFK_SHELL_WRAPPER | boolean | Set to 1 or true by the optional afk shell wrapper function (installed via afk shell-init). Signals that the parent shell has the wrapper active so the post-exit cd can fire. | — |
AGENT_SURFACE | string | Process-level surface identity propagated to subprocesses (e.g. Python plugin scripts) that cannot read the in-memory AgentConfig.surface field. The CLI entrypoint defaults it to 'afk' via ??=; no surface overrides it at runtime today, so the env var is effectively a static 'afk' constant. Per-surface discrimination ('cli', 'telegram', 'daemon') lives on AgentConfig.surface instead. The browser headless-routing check in src/browser/config.ts reads this as a secondary fallback after AFK_BROWSER_HEADLESS. | afk |
APPDATA | string | Windows %APPDATA% (Roaming). Used for VS Code/Cursor settings discovery and credential denylist paths. | — |
ASCIINEMA_REC | boolean | Set to 1 by asciinema rec while a session is being recorded. Triggers capture-mode. | — |
CHROME_CONFIG_HOME | string | Linux only. When set, Chrome stores its profile under $CHROME_CONFIG_HOME/<channel-dir> instead of $XDG_CONFIG_HOME/<channel-dir>. Mirrors the override that chrome-devtools-mcp/Puppeteer apply when locating DevToolsActivePort so the real-Chrome probe checks the same path. | — |
CI | string | Standard CI-detection convention. Auto-set by GitHub Actions, CircleCI, etc. Used to switch off TTY-only UX. | — |
COLORFGBG | string | Terminal-set "foreground;background" color hint (e.g. "15;0"), read only for AFK_THEME=auto detection. The trailing field is the background color index; >= 7 is treated as a light background, otherwise dark. Not set by AFK — emitted by some terminals (rxvt, Konsole, iTerm2). Absent or unparseable => dark. | — |
COLORTERM | string | OS-level terminal color-depth hint. Accepted values: truecolor or 24bit (24-bit color), 256color (256-color). Set by the outer terminal emulator, not by AFK. Read by chalk's supports-color to set its initial color level; also read by configureColor() to recommend FORCE_COLOR=3 for tmux truecolor users on Node ≤ 24. | — |
EDITOR | string | Standard POSIX env var naming the user's preferred editor (with optional flags). Consulted by the /editor slash command AFTER VISUAL, as the standard fallback. No default editor is assumed when both are unset — /editor prints a hint telling the user to set one. | — |
FORCE_COLOR | string | Standard Node convention. Force-enable ANSI color output even when stdout is not a TTY. | — |
HOME | string | Standard Unix home directory. Used as the fallback when AFK_HOME is unset. | — |
LOCALAPPDATA | string | Windows %LOCALAPPDATA%. Used for browser credential denylist paths (Chrome, Firefox, Edge, etc.). | — |
MSYSTEM | string | Set by Git Bash environments (e.g. MINGW64). Signals that bash.exe is already on PATH in resolve-shell.ts. | — |
NO_COLOR | string | Standard convention (https://no-color.org). When set to any non-empty value, disables ANSI color output. | — |
NO_UPDATE_NOTIFIER | boolean | Disable the update-available notifier on CLI startup. Standard convention shared with many Node CLIs. | — |
NODE_ENV | string | Standard Node environment marker. test | development | production. Used by routing-telemetry.ts to suppress test-time writes. | — |
PAGER | string | Standard POSIX env var naming the user's preferred pager (with optional flags). Used by /transcript to render the full session in a scrollable viewer; falls back to less -R when unset. | — |
PATH | string | System PATH. Read for executable resolution (git, gh, etc.) in tool handlers. | — |
SCRIPT | string | Set by script(1) on BSD/macOS/Linux to the typescript filename while a terminal session is being recorded. Presence of a non-empty value triggers capture-mode. | — |
SHELL | string | Standard POSIX env var pointing to the user's login shell binary. Used by shell-init and worktree commands to auto-detect the correct shell syntax for emitted wrapper code. | — |
SystemRoot | string | Windows system root directory (typically C:\Windows). Used to identify WSL bash.exe shim paths in resolve-shell.ts. | — |
TMUX | string | OS-level tmux session identifier. Set automatically by tmux to the socket path and session info (e.g. /tmp/tmux-501/default,12345,0) inside any tmux pane. Not set by AFK. Read by configureColor() to detect a tmux environment, and by peer messaging to derive a default session name (#S:#I); for truecolor support on Node ≤ 24, set FORCE_COLOR=3 in your shell or ~/.afk/config/afk.env. | — |
TMUX_PANE | string | tmux pane identifier, set automatically by tmux inside any pane (e.g. %0). Used by AFK to target the correct pane when composing a tmux display-message label. | — |
USER | string | Unix login name of the current user. Used as a default approver identity in improve/approve. | — |
USERNAME | string | Windows login name of the current user. Fallback after USER when identifying the approver. | — |
USERPROFILE | string | Windows %USERPROFILE%. Used for credential denylist paths (.ssh, .aws, .gnupg) on win32. | — |
VISUAL | string | Standard POSIX env var naming the user's preferred full-screen editor (with optional flags). Consulted FIRST by the /editor slash command (and its key chord) to compose a long prompt externally; takes precedence over EDITOR. No fallback editor is assumed — if neither VISUAL nor EDITOR is set, /editor prints a hint instead of guessing. | — |
VITEST | string | Set automatically by Vitest. Used at runtime to short-circuit code paths that should not fire in tests. | — |
XDG_CONFIG_HOME | string | Linux XDG base-dir spec override for per-user config files. When set (and CHROME_CONFIG_HOME is not), Chrome stores its profile under $XDG_CONFIG_HOME/<channel-dir>. Mirrors the override that chrome-devtools-mcp/Puppeteer apply when locating DevToolsActivePort. | — |
Debug
| Variable | Type | Description | Default |
|---|---|---|---|
AFK_DEBUG | boolean | Enable verbose debug logging across the codebase. Accepts 1 to enable. | — |
AFK_DEBUG_CLIPBOARD | boolean | Debug bracketed-paste and image-paste handling in the interactive REPL. | — |
AFK_DEBUG_COMPOSITOR | boolean | Gate compositor phase-boundary traces to stderr; any truthy value enables. | — |
AFK_DUMP_PROMPT | string | Write the resolved system prompt to a file at startup. Accepts a path or 1 for default location. | — |
AFK_MESSAGE_JOURNAL_DISABLED | boolean | Disable the per-session message journal (state/sessions/<id>/journal.jsonl + blobs/). The journal records the full conversation, including full tool results, and is the source for --resume and /fork. Set to 1 to skip journal writes; resume then falls back to the text-only sidecar history. | — |
AFK_PREEXISTING_LEDGER_DISABLE | boolean | Set to 1 to disable the pre-existing-defect SessionEnd hook. When set, the hook will not scan session turns or append records to ~/.afk/agent-framework/preexisting-ledger.jsonl. Useful in test environments or when the ledger is not desired. | — |
AFK_RUN_RECEIPT_DISABLED | boolean | Disable the post-session run receipt (state/receipts/<label>.json and .md). Set to 1 to skip receipt writes; the underlying witness trace is unaffected. Receipts are also implicitly off when AFK_TRACE_DISABLED=1 (no trace to summarize). | — |
AFK_SESSION_LEDGER_DISABLED | boolean | Disable the per-session durable event ledger (state/sessions/<id>/events.jsonl). Set to 1 to skip ledger writes; live cross-surface watching (e.g. the Telegram /watch command) will report no activity for sessions started while disabled. | — |
AFK_SESSION_MAX_AGE_DAYS | number | Evict a session sidecar file once it is older than this many days (based on savedAt). Default 30. | 30 |
AFK_SESSION_MAX_COUNT | number | Count-based safety valve: evict oldest session sidecars first once the total exceeds this number. Default 1000. | 1000 |
AFK_SESSION_RETENTION_DISABLE | boolean | Disable the session sidecar retention sweep entirely, so no state/sessions/*.json file is ever evicted. | — |
AFK_SKILL_STREAM_VERBOSE | boolean | Verbose streaming output when a skill is dispatched. Logs sub-agent setup, intermediate events, and final result. | — |
AFK_SUBAGENT_LOG | boolean | Opt-in per-subagent conversation log. Writes OutputEvent JSONL to state/subagent-logs/<sessionLabel>/<subagentId>.jsonl for both foreground and background subagents. Powers /tasks:view replay. OFF by default (raw tool arguments are written without redaction). Set to 1 to enable. | — |
AFK_TELEGRAM_TRACE | boolean | Set to 1 to dump raw bridge traffic between the agent and the Telegram bot — debugging only. | — |
AFK_TRACE_DISABLED | boolean | Disable the agent trace subsystem entirely. Set to 1 to skip trace file writes. | — |
AFK_WITNESS_MAX_AGE_DAYS | number | Evict a witness session directory once its newest content is older than this many days. Default 30. | 30 |
AFK_WITNESS_MAX_BYTES | number | Aggregate byte cap for the witness tree; oldest session directories are evicted first once exceeded. Default 2147483648 (2 GiB). | 2147483648 |
AFK_WITNESS_RETENTION_DISABLE | boolean | Disable the witness-tree retention sweep entirely, so no session directory is ever evicted. | — |
AGENT_AFK_ASCII | boolean | Force the interactive REPL tool-lane renderer to ASCII-only glyphs instead of the default Unicode box-drawing set. Accepts 1/true/yes (case-insensitive). Useful for terminals whose font lacks ┃├╰├ glyphs. | — |
DEBUG | string | Standard Node debug-package convention. When set to 1, enables verbose logging in several modules alongside AFK_DEBUG. | — |
Display
| Variable | Type | Description | Default |
|---|---|---|---|
AFK_CENTER_CONTENT | boolean | When set to "1" (or any truthy value), content surfaces (tool-lane overlay, scrollback blocks, input line, spinner, and OODA stage rail) are horizontally centered by prepending a left margin equal to Math.floor((terminalWidth - contentMeasure) / 2). No-op when the terminal is at or below the content measure — the common 80–100 column case. Default off (empty string). Opt-in: set AFK_CENTER_CONTENT=1 to enable. | `` |
AFK_SMOKE_TEXT | boolean | When set to "1" (or true/yes/on), streamed assistant prose in the interactive REPL condenses out of faint smoke (speck, then haze, then a dim letter, then the real letter) instead of popping in. Needs a 256-color or truecolor terminal. It stays off for NO_COLOR, non-TTY output, AFK_PLAIN_OUTPUT, Telegram, the daemon, and AFK_REDUCED_MOTION=1, and it skips code fences and tables. Default off. Set AFK_SMOKE_TEXT=0 or unset it to disable. | `` |
AFK_STREAM_BUFFER_MS | number | Input buffer window for TUI streaming in milliseconds. When set to a positive value, incoming tokens are micro-batched before parsing and rendering, producing smoother visual output. The first token after idle always passes through immediately (leading-edge). 0 = disabled (every token is parsed individually). Reasonable range: 8-50. | 0 |
Miscellaneous
| Variable | Type | Description | Default |
|---|---|---|---|
AFK_BANNER_PLAIN | boolean | Suppress the ANSI-colored banner at REPL startup. Useful for non-TTY captures and CI logs. | — |
AFK_BASH_PREVIEW_HEAD_LINES | number | Number of leading non-empty lines shown in the bash output head block of the TUI outcome preview. 0 (default) disables the head block entirely. Accepted range 0–200; non-integer, negative, or out-of-range input falls back to the default. When head + tail >= total non-empty lines, all lines are shown without a hidden-line notice. Keep preview preferences separate from model-context caps (AFK_BASH_PREVIEW_TAIL_LINES). | 0 |
AFK_BASH_PREVIEW_TAIL_LINES | number | Number of trailing non-empty lines shown in the bash output tail block of the TUI outcome preview. Default 7 (compact tail-first preview). Accepted range 0–200; non-integer, negative, or out-of-range input falls back to the default. When head + tail >= total non-empty lines, all lines are shown without a hidden-line notice. Keep preview preferences separate from model-context caps (AFK_BASH_PREVIEW_HEAD_LINES). | 7 |
AFK_BG_AUTO_DELIVER | boolean | Auto-deliver background subagent results into the model context on the next user turn (interactive REPL). On by default. Set to 0, false, off, or no (case-insensitive) to disable, restoring the manual /bgsub:join retrieval flow. | 1 |
AFK_DEMO_CLEAN | boolean | Explicit opt-in to capture-mode. When set to 1, suppresses high-frequency repaint drivers (spinner ticker, live thinking-preview) so recorded artifacts contain each state once instead of once per timer tick. | — |
AFK_DIFF_LINES | number | Maximum number of diff lines shown in the inline diff render during write_file tool calls. Set to 0 for no cap. Non-integer values are silently ignored and the default applies. | — |
AFK_DISABLE_SPINE_UPDATE | boolean | Disable the SPINE.md SessionEnd hook when set to 1. The hook runs a single LLM call at the end of each top-level session to classify architectural signals in the git diff against SPINE.md. Set to 1 to opt out globally (useful in CI or when the LLM call is unwanted). | 0 |
AFK_EVAL_STALENESS_DAYS | number | Number of days without a completed eval-run before the ground-state pre-flight surfaces a staleness warning. The guard reads the most recent timestamp from the eval-runs index ($AFK_HOME/agent-framework/improve/eval-runs/.index.jsonl) and emits a warning finding when the gap exceeds this threshold. Default: 7. Set to 0 to disable the guard. | 7 |
AFK_FRAMEWORK_PROMPT_FILE | string | Absolute path to a replacement for the framework base system prompt (system-prompt.md). When set, loadSystemPrompt() reads this file instead of the bundled system-prompt.md. Unset = byte-identical default behaviour. Fail-closed: a relative path or an unreadable file is a hard error with no bundled-prompt fallback — a silent fallback would turn an A/B run into A/A. This error reaches EVERY surface that builds a system prompt. At startup (CLI, Telegram) it crashes the process with a clear message. Mid-session (/afk-md reload) it surfaces an error and leaves the REPL usable. Mid-server (afk web session-create) it rejects the request with an actionable message and keeps other sessions running. Use only with afk whatif --env AFK_FRAMEWORK_PROMPT_FILE=<path> — never set it persistently in afk.env (a stale value there blocks every surface until removed). | — |
AFK_GOBLIN_MASCOT | boolean | Reacting goblin mini-sprite in the reserved footer band while the agent runs tools (3 rows, animated). 1 = on, unset/0 = off (default). Claims terminal rows, so it is opt-in. | — |
AFK_GOBLIN_SPINNER | boolean | Goblin-themed working spinner (olive frames + goblin verbs) while the agent runs tools. 1 = on (default), 0 = classic dim spinner. | — |
AFK_IMAGE_ALLOW_DAEMON | string | Set to "1" to allow image_generate in daemon/cron sessions. Blocked by default to prevent unattended API spend. | — |
AFK_IMAGE_SESSION_LIMIT | string | Maximum number of images the image_generate tool may produce per session. Prevents runaway spend in autonomous loops. Default: 10. | — |
AFK_LEASE_TTL_MS | number | Lease TTL in milliseconds for durable task execution (issue #1411). A leased task whose lease expires before it completes is recovered and re-enqueued (or dead-lettered if maxAttempts is exhausted). Default: 600000 (10 minutes). | — |
AFK_LSP_DIAGNOSTICS | boolean | Enable opt-in LSP features (#3904). When set to 1: (phase 1) after every successful edit_file or write_file call the handler queries typescript-language-server (must be on PATH) for diagnostics on the touched file and appends any errors/warnings to the tool result; (phase 2) enables the code_symbols tool for go-to-definition and find-references. Off by default. A missing or slow server never fails or stalls the edit — the query times out silently. TypeScript / JavaScript only in this phase. | 0 |
AFK_LSP_DIAGNOSTICS_TIMEOUT_MS | number | Hard timeout in milliseconds for the post-edit LSP diagnostics query (AFK_LSP_DIAGNOSTICS=1). If the language server does not respond within this window the query is abandoned silently and the edit result is unchanged. Default 1500 (1.5 s) — intentionally low so a slow server does not stall every edit; raise when diagnostics are valuable enough to wait for. | 1500 |
AFK_MEMORY_EVIDENCE_GATE | boolean | Evidence gate for durable memory writes. When enabled, a codebase fact (memory_update category "convention") stored without an evidence citation is recalled as [unverified], and memory_search results carry a verification verdict. User preferences and agent reflections are never gated. On by default. Set to 0 to disable. | 1 |
AFK_MEMORY_GC_MIN_AGE_DAYS | number | Minimum age in days for a never-accessed fact to become a GC candidate. Only meaningful when AFK_MEMORY_GC_SWEEP_ENABLE=1. Default 30. Age is counted only for facts created since tracking began. Must be a positive number; invalid values fall back to the default. | 30 |
AFK_MEMORY_GC_SWEEP_ENABLE | boolean | Enable the periodic soft-delete GC sweep for the fact archive. When set to 1, never-accessed facts older than AFK_MEMORY_GC_MIN_AGE_DAYS are marked superseded (soft-deleted) at most once every 24 hours at session start. Off by default — enable only when archive noise becomes measurable. "preference" facts and facts predating this database's tracking-start marker are always excluded regardless of this setting. | 0 |
AFK_NOTIFY | boolean | Emit a desktop completion notification (OSC 9) on turn completion, for terminals that map OSC 9 to system notifications (iTerm2, kitty, WezTerm). Opt-in and off by default (intrusive). 1 = on. TTY-only. | — |
AFK_OTLP_ENDPOINT | string | OTLP/HTTP endpoint URL for live trace export (e.g. http://localhost:4318/v1/traces). When set, afk trace export-otlp POSTs the converted OTLP/JSON payload to this endpoint after converting the witness trace. A dead or unreachable endpoint never blocks or fails the command — the error is printed to stderr and the command still exits 0. Equivalent to passing --endpoint <url> on the command line; the flag takes precedence when both are provided. | — |
AFK_OTLP_HEADERS | string | Comma-separated list of Key=Value HTTP headers appended to every OTLP/HTTP push request made by afk trace export-otlp when AFK_OTLP_ENDPOINT or --endpoint is set. Typical use: bearer tokens and tenant identifiers required by managed collectors (e.g. Grafana Cloud, Honeycomb). Values that contain a literal comma must be percent-encoded (%2C). Example: Authorization=Bearer <tok>,X-Org=acme. | — |
AFK_PEER_INBOUND | string | How this REPL session handles incoming peer messages. 'accept' (default) delivers messages to the model at the next idle turn. 'hold' writes them to held/ for /inbox review. 'off' ignores incoming messages. Invalid values fall back to 'accept' with a one-time warning emitted via debugLog (visible under AFK_DEBUG=1). | accept |
AFK_PEER_POLL_MS | number | Polling interval in milliseconds for the peer inbox notifier when fs.watch is unavailable or unreliable. Default 1000. | 1000 |
AFK_PLAIN_OUTPUT | boolean | Force the interactive REPL to fully behave like a non-TTY surface for rendering purposes, even when stdout/stdin ARE a TTY: append-only plain-stdout output instead of the TerminalCompositor live overlay (both the persistent between-turn compositor AND the per-turn StreamRenderer overlay), AND the input surface downgrades to the simple non-TTY line reader instead of the fancy compositor-backed input box. Same code path already used for non-TTY surfaces (pipes, CI). Full opt-out escape hatch for tmux/SSH/multiplexer sessions where cursor-up redraws and DECSTBM scroll regions misbehave — trades the live overlay and fancy input UX for reliability. Opt-in — default TTY behavior (live overlay + fancy input) is unchanged unless this var is set. Truthy values: 1, true (case-insensitive). | — |
AFK_PROSE_MEASURE | string | Maximum line length (columns) for prose-only blocks (paragraphs, list items, blockquotes) in the interactive REPL. Code fences use the wider AFK_TEXT_MEASURE (default 100). When AFK_TEXT_MEASURE is explicitly set, it overrides this value for backward compatibility. Accepts a positive integer (minimum 20), or full | off | none | 0 to disable. Unparseable or below-minimum values fall back to the default. | 80 |
AFK_READ_DENYLIST | string | Colon-separated list of additional absolute paths the read_file/grep/glob/list_directory tools refuse to read, and that the bash-restriction hook refuses to let a shell command reference on interactive surfaces (REPL, Telegram) — on headless surfaces (afk chat, daemon) bash fails open regardless of this list. A leading ~/ is expanded ( | — |
AFK_READONLY_GIT_TOOL | boolean | Opt-in (#3754). When set to 1/true/yes/on, read-only child agents (read-only skills such as ground-state, named agents with bash: read-only such as git-investigator, and compose nodes running such agents) lose the generic bash tool and get the structured git_read tool instead (status, log, diff, show, grep, tag/branch listing, blame, ls-files; argv-only, fixed option allowlist). Top-level sessions (no tool allowlist) also see git_read while set. Default: off (read-only children keep classifier-gated bash). | 0 |
AFK_REMOTE_DEPTH | string | Set to 1 by the remote_agent tool on every afk run it launches on another host over SSH (inlined into the remote command, since sshd drops forwarded env by default). When set to a non-empty value other than 0, remote_agent refuses to dispatch, so a remote agent can never fan out to further hosts. Not meant to be set by hand. | — |
AFK_SDK_SURFACE_WARN | string | Controls the one-time warning emitted when an SDK session (AgentSession / query() / queryText()) discovers skills but has no skillExecutor wired, meaning the model cannot invoke them. Set to 0 to silence the warning (useful when the reduced surface is intentional). | — |
AFK_SHELL_PASSTHROUGH | boolean | Enable the interactive REPL !cmd / !&cmd shell-passthrough feature. On by default. Set to 0, false, off, or no (case-insensitive) to disable, so inputs beginning with ! are sent to the model as literal text instead of being executed as shell commands. Equivalent to the --no-shell-passthrough flag. | 1 |
AFK_SHOW_DIFFS | boolean | Show inline diffs in the tool-lane output for edit/write tool calls. 1 = on, 0 = off. | — |
AFK_SPINNER_TIPS | boolean | Show rotating tips in the loading spinner during long calls. 1 = on, 0 = off. State-specific hints (e.g. the wait_for queue-to-stop hint) still show. | — |
AFK_STEER_MODE | string | Controls when a typed-ahead REPL message is delivered to the model during a multi-round turn. "boundary" (opt-in): drain the compositor queue at every tool-round boundary, so a correction typed during tool execution reaches the model on the NEXT tool round rather than waiting for the whole turn to end. "turn-end" (default): existing behaviour — the message is delivered as its own turn once the current turn finishes. The "boundary" mode is gated behind this env var during soak (issue #3897). Note: the user-yield contract (wait_for / exit_plan_mode) relies on "turn-end" semantics; those tools tell the model "end your turn so the message is delivered first", which is only accurate in "turn-end" mode. In "boundary" mode the message arrives mid-turn and the yield notice is redundant but harmless — the tool still returns early, the model still sees the message. | turn-end |
AFK_STOP_HOOK_MAX_CONTINUATIONS | number | Maximum number of same-turn continuation rounds a blocking Stop hook may trigger before the turn ends normally (issue #2714). Each continuation re-enters the model loop with the block reason as a framework user message, giving the agent another chance to address the hook. 0 disables continuation entirely (the turn ends on first block). Default: 2. | 2 |
AFK_TERM_TITLE | boolean | Set the terminal/tab title (OSC 2) to reflect afk state — "afk — <cwd> · running" during a turn, "afk — <cwd>" when idle, cleared on exit. 1 = on (default when stdout is a TTY), 0 = leave the title alone. TTY-only. | — |
AFK_TEXT_MEASURE | string | Maximum line length (columns) for unbordered streamed text in the interactive REPL: assistant prose, thinking blocks, tool-lane text, and subagent text. Display-only — affects wrapping, never behavior. Bordered elements (cards, error boxes) already cap at 100; this applies the same ceiling to the unbordered surfaces, which previously scaled to the full terminal width. Accepts a positive integer (minimum 20), or full | off | none | 0 to disable capping and restore full-width wrapping. Unparseable or below-minimum values fall back to the default. No-op on terminals at or below the measure, so narrow terminals are unaffected. | 100 |
AFK_THEME | string | TUI color palette for the interactive REPL and all CLI rendering: dark | light | umber | auto. Display-only — swaps the semantic color palette, never behavior (cost/latency unaffected). auto detects from COLORFGBG and falls back to dark; umber matches the Umber terminal, is dark-only, and is never chosen by auto. Overridden per-launch by --theme and mutable mid-session via /theme. Precedence: --theme flag > this env > config theme > auto-detect > dark. Invalid values are ignored (dark). | dark |
AFK_THINKING_UI | string | Default thinking-display mode for the interactive REPL: summary | live | digest | off. Display-only — controls how extended-thinking blocks render, never whether thinking runs (cost/latency unaffected). Overridden per-launch by --thinking-ui and mutable mid-session via /thinking. Precedence: --thinking-ui flag > this env > interactive.thinkingUi config > live. Invalid values are ignored. | live |
AFK_TURN_SEPARATOR | boolean | Render a dim horizontal rule between conversation turns in the REPL. 0 = off, unset/1 = on (default). TTY-only: piped/one-shot output (afk chat) never emits the rule. | — |
AFK_UNPROVEN_DIAGNOSIS_GATE | boolean | Opt-in Stop hook that prevents a turn from closing with an unproven "external / root cause unknown" diagnosis. When set to 1, the hook scans the final assistant text for cause-unknown language ("root cause" near "not found|unknown", "something else in", "likely upstream") AND checks for the absence of instrumentation tool calls (hash/manifest checks, bypass reruns, counter/log insertions). When both conditions are met it injects a correction asking the agent to run the elimination ladder (byte-verify installs, bypass wrapper, clean-env repeat, instrument the path) before closing. Fires at most once per turn. Does not fire when instrumentation evidence is already present, or on subagent turns. Default: 0 (off). | 0 |
AFK_USER_CARD_MAX_ROWS | number | Maximum number of visual rows emitted by renderUserCard before collapsing the remainder into a dim "…(N lines collapsed)" summary row. Defaults to 24. Non-integer or non-positive values are silently ignored and the default applies. | — |
AFK_VITALS | boolean | Per-round "[vitals]" harness note appended to each tool-result turn so the model stays aware of fast-changing state: local wall-clock time and elapsed turn time every round, time left before the soft deadline when one is set, context-window fill once it reaches 50%, and (root Anthropic sessions only) Claude subscription usage once it reaches 80%. Set to 0 to disable. Default: on. | 1 |
AFK_WAVE_MANIFEST_DISABLED | boolean | Disable the wave manifest system entirely. When set to 1, no manifest is written for parallel subagent waves, and no resumption offer is made at session start. | 0 |
AFK_WAVE_MANIFEST_TTL_HOURS | number | Time-to-live for wave manifests in hours. Manifests older than this are deleted on reconciliation and by the witness sweep. Default 48 (two days). | 48 |
AFK_WAVE_RESUME_UNATTENDED | boolean | When set to 1, surface wave resumption offers even on non-interactive surfaces (daemon, one-shot chat). By default, offers are only made on interactive surfaces (REPL, Telegram). | 0 |
AFK_WEB_ALLOW_PRIVATE_HOSTS | boolean | Opt out of the web_scrape SSRF egress guard. When unset (default) the guard is ACTIVE: the markdown, raw, and headless-render paths refuse loopback (127/8, ::1), link-local (169.254/16 — including the 169.254.169.254 cloud instance-metadata endpoint), RFC1918 (10/8, 172.16/12, 192.168/16), carrier-grade NAT (100.64/10), IPv6 unique-local (fc00::/7), 0.0.0.0/8, and the IPv4-mapped/compatible IPv6 forms of all of those. Hostnames are resolved and the RESOLVED addresses are classified (DNS-rebinding guard), and the check is re-applied on every redirect hop. Set to 1/true to allow private-host access — needed only to scrape a local dev server. Enabling it restores a model-reachable SSRF path (issue #575). | — |
AFK_WHATIF_ALLOW_MCP | boolean | When set to a truthy value (1/true/yes/on) inside a what-if episode (AFK_WHATIF_EPISODE=1), keep MCP servers enabled. By default, MCP servers are disabled inside episodes to prevent side effects from spawning external processes. Set this flag only when the change under test specifically concerns MCP server behaviour. | `` |
AFK_WHATIF_EPISODE | boolean | When set to "1" or "true", the process is a sandboxed what-if episode. The episode gate (src/agent/whatif-episode-gate.ts) intercepts every PreToolUse call: read-only tools execute normally; the first side-effecting action is RECORDED as the decision but NOT executed. Episode processes are spawned by afk whatif --verify as isolated subprocesses with their own AFK_HOME / AFK_STATE_DIR pointing at the sandbox. Do not set this manually in production — it disables delegation, MCP (unless AFK_WHATIF_ALLOW_MCP=1), and all write tools. | `` |
AFK_WHATIF_KEEP_CONTEXT_HOOKS | boolean | When set to a truthy value (1/true/yes/on) inside a what-if episode (AFK_WHATIF_EPISODE=1), keep SessionStart and UserPromptSubmit config/plugin hooks enabled. By default, these hooks are disabled inside episodes so that both the baseline and candidate arms see byte-identical first user messages, preventing cwd- or recency-sensitive hook output from confounding the measured delta. Set this flag only when the change under test specifically concerns context-injecting hook behaviour. | `` |
AFK_WHATIF_TOOL_LOG | string | Absolute path of a JSONL file the episode gate appends every tool request to (one JSON line per call). Each line has the shape {"ts":<ms>,"tool":<name>,"input":<raw input>,"verdict":"executed"|"recorded","subagent":<bool>}. Ignored when AFK_WHATIF_EPISODE is not set. The file is created if absent; write errors are swallowed so a full disk never disrupts tool execution. | — |
AFK_WRITE_DENYLIST | string | Comma-separated list of additional path globs that the write_file tool refuses to write to. | — |
AFK_WRITE_DIFF | boolean | Show a diff preview before each write_file tool call. Defaults provider-controlled when unset. | — |