Skip to content

config.toml

Complete CompozyOS config.toml schema, defaults, valid values, merge behavior, and annotated examples.

For people running agent work10 pages in this section

config.toml is the main CompozyOS runtime desired-state file. CompozyOS loads the built-in defaults, then overlays global and workspace files when they exist. The daemon only treats a change as runtime truth after ConfigApplyService advances the active generation and writes a config_apply_records audit row.

FileScopeLoaded when
$COMPOZY_HOME/config.tomlGlobalEvery config load. Defaults to ~/.compozy/config.toml.
$COMPOZY_HOME/mcp.jsonGlobal MCP sidecarAfter global TOML.
<workspace>/.compozy/config.tomlWorkspace overlayOnly when a workspace root is resolved for the session or command.
<workspace>/.compozy/mcp.jsonWorkspace MCP sidecarAfter workspace TOML.

Unknown TOML keys are errors. Use the supported sections below. Retired tables are the one exception: CompozyOS archives them on load instead of failing; see Archived retired settings.

Quick Reference

SectionPurposeDefault
[daemon]UDS socket, process-memory reports, and task-run escalation from subprocess health.socket = "$COMPOZY_HOME/daemon.sock", memory reports every 5m, escalation after 3 failed checks
[daemon.reload_timeouts]Live reload timeout budgets for provider and MCP subsystem applies.providers 5s, MCP 10s
[daemon.load]Agent CPU priority and load-aware admission for background work.niceness 10, threshold 1.5, max wait 2m
[http]HTTP and SSE bind address.host = "localhost", port = 2123
[app]Desktop app update-check policy.checks enabled every 6 hours
[attention]Live operator notification delivery and per-workspace mute policy.toasts and sound on, system notifications off, no muted workspaces
[terminal]Live terminal process, retention, recording, and capacity policy.user shell, 1 MiB scrollback, 24h idle TTL, recording off, caps 8/32/16
[shell.sessions]Default session order and grouping for windows that have not chosen their own.creation order, no grouping
[shell.appearance]Interface and Code fonts every connected Web and desktop client paints with.Inter and Geist Mono
[user_scope]Working directory for work that belongs to no project (the User scope).work_dir = "~"
[cmd_palette]Command fallback targets, personalization, and workspace aliases.agent fallback target, personalization on, no aliases
[window_manager]Live virtual-desktop, focus, snap, layout, transition, and shortcut defaults.windows tile beside focus, flush gaps, adaptive stacks, 50-entry history
[defaults]Default agent and session runtime (provider, model, reasoning effort) resolution.agent = "general", provider = ""
[roles.coordinator]Coordinator routing plus coordinator-owned session limits.disabled, builtin coordinator, TTL 2 hours, 5 children, 5 active sessions/workspace
[roles.auto_title]Automatic-title session routing.enabled, inherits the invoking agent
[limits]Daemon-level concurrently running agent cap.max_concurrent_agents = 20
[session.limits]Deprecated. timeout has no effect and is removed in v0.5.0.timeout = "0s"
[session.supervision]Activity heartbeat, progress, hung-turn watchdog, and idle runtime release controls.heartbeat 30 seconds, progress 10 minutes, quiet 30 minutes, stop grace 10 minutes, idle release 30m
[session.derive]Size bounds of every transcript replay into a new agent session.max_replay_bytes = 131072, max_message_bytes = 16384
[subagents]Size cap of the answer a subagent returns to its parent session.result_max_chars = 60000
[agents.soul]Optional SOUL.md parsing, body limits, and compact projection budget.enabled, 32 KiB body, 2 KiB compact projection
[agents.heartbeat]Optional HEARTBEAT.md policy bounds, wake cadence/limits, and health timing.enabled, 32 KiB body, 5 min/30 min intervals, 25 wakes per cycle, 168 h retention
[permissions]Default permission mode for tool risk and cross-workspace requests.mode = "approve-all"
[worktrees]Managed placement, per-run branch namespace, bootstrap, and Git discovery freshness.$COMPOZY_HOME/worktrees, run/, no copy/setup, 10m setup, 30s discovery
[tools]Tool registry lifecycle, hosted MCP enablement, and result budget defaults.enabled, hosted MCP enabled, 256 KiB result default
[tools.hosted_mcp]Hosted MCP session bind nonce lifecycle.30 seconds
[tools.clarify]Timeout for one live agent clarification question.No automatic expiration (omitted or 0s); 5m restores the previous bound
[tools.policy]External tool source defaults, approval timeout, and trusted sources.external tools disabled, 120 second approval timeout, no trusted sources
[mcp.oauth]Global MCP OAuth client metadata and callback identity.CompozyOS public CIMD; loopback callback on port 2123
[[mcp_servers]]Top-level MCP servers passed to agents.empty list
[providers.<name>]Built-in provider override or custom provider definition.empty map plus built-ins
[model_catalog.sources.models_dev]models.dev enrichment source (cross-provider).enabled, https://models.dev/api.json, 24 h TTL, 10 s timeout
[provider_usage]Experimental provider usage cadence, per-provider opt-out, and Keychain consent.enabled, 5m refresh, all supported providers, no Keychain access
[marketplace.catalog]Curated marketplace feed location and fetch timing.CompozyOS main catalog, 1 h TTL, 10 s timeout
[[marketplace.plugin_sources]]Plugin marketplace registrations and preset choices.Preset defaults; custom registrations enabled by default
[observability]Event summary retention and global byte cap.enabled, 7 days, 1 GiB
[observability.transcripts]Transcript segment sizing and per-session cap.enabled, 1 MiB segments, 256 MiB per session
[log]Structured log level and daemon-owned rotation.level = "info", 10 MiB, 5 backups, 30 days, compression off
[redact]Additive secret heuristics for agent-visible content, logs, and persisted events.enabled = true
[skills]Skill discovery, source roots, polling, disable list, and installed-skill trust gates.enabled, agents on, no custom sources, poll every 3 seconds
[extensions.trust]Operator policy for non-curated extension installs.allow_unverified = true; per-install consent still required
[extensions.sources.*]Published extension source availability and endpoints.GitHub and git enabled, https://api.github.com
[extensions.dev]CLI-side extension dev-loop poll cadence.watch_interval = "2s"
[extensions.resources]Resource kinds, scope ceiling, and rate limits for extension-published resources.unrestricted kinds and scope, rate limits off
[automation]Automation scheduler defaults.enabled, host time zone for new configs, 5 concurrent jobs
[[automation.jobs]]Scheduled automation jobs.empty list
[[automation.triggers]]Event-driven automation triggers.empty list
[loops]Daemon reconciliation cadence for terminal Loop execution records.reconcile_interval = "1m"
[loops.defaults.delivery]Global/workspace seed defaults for delivery Loops, including node lifecycle policy.iteration cap 50, retry attempts 3, silence window 30m, fan-out 4
[loops.defaults.watch]Global/workspace seed defaults for watch-source Loops, including lifecycle policy.no iteration cap, retry attempts 3, silence window 30m, fan-out 2
[loops.breaker]Reload-scoped target breaker shared across Loop kinds.5 consecutive transport failures, 60s probe interval
[loops.inputs.<loop-name>]Global/workspace defaults for declared inputs of one Loop.empty map; per-key origin remains explicit
[goals]Goal defaults and durable session-event relay controls.20 turns, context ratio 0.8, relay batch 50 every 100 ms
[autonomy]Task-kernel autonomy defaults (unblock-loop breaker).block recurrence limit 2
[task.orchestration]Bounds for active workspace runs, summaries, context, scheduler health, and runtime.16 active runs/workspace, 4 KiB summaries, 8 KiB context, prior 5/recent 50 events
[task.orchestration.profile]Defaults and gates for task execution profiles.inherit coordinator/worker; provider overrides allowed
[task.orchestration.review]Defaults and bounds for the post-terminal review gate.policy none, max rounds 3, max attempts 2, timeout 20m, failure block_task
[task.recovery]Gates agent access to task-run force recovery verbs.agents may force release, force fail, and retry
[[hooks.declarations]]Config-defined runtime hooks.empty list
[gateway]Remote gateway ceiling, listener ports, and bounded security tunables.disabled; both tier ports assigned by the operating system

Load And Merge Order

OrderSourceMerge behavior
1Built-in defaultsBase config.
2$COMPOZY_HOME/config.tomlScalar fields replace defaults. TOML MCP and provider MCP entries merge by name.
3$COMPOZY_HOME/mcp.jsonSame-name MCP servers replace the whole server object.
4<workspace>/.compozy/config.tomlWorkspace overlay applies over global config.
5<workspace>/.compozy/mcp.jsonSame-name MCP servers replace the whole server object after workspace TOML.
6Path normalization and validation~ is expanded for daemon.socket; invalid values fail startup.

TOML [[mcp_servers]] entries merge fields by name: non-empty command, non-empty args, and new env keys overlay the existing server. JSON sidecars replace same-name servers as whole objects.

Daemon boot uses the home config for boot-time daemon settings. The general config loader applies global and workspace MCP sidecars for session and runtime resolution.

Archived retired settings

A config.toml that still carries settings for features CompozyOS removed does not fail on those settings. Whenever the loader reads the global file, a profile file, or a workspace file, it archives these retired settings in place:

  • the memory table and all of its sub-tables, in any TOML form (table headers, dotted keys, or inline tables);
  • the Dream, memory extractor, memory controller, and checkpoint summary role tables under roles;
  • the CompozyOS-side compaction table under session;
  • every [[automation.triggers]] entry for the removed memory-consolidation event;
  • the compaction_reason and compaction_strategy keys of a hook matcher. The hook declaration and its other matcher keys stay active; see Hook declarations.

The migration guide lists the exact keys and the upgrade steps.

For each file that holds any of them, the loader:

  1. removes the retired settings from the live file and leaves every other setting and comment as it was;
  2. appends the removed values to the end of the same file as a commented block. A blank line and the line # Archived retired memory and compaction settings; these values are inactive. open the block, and every archived TOML line starts with # . A retired matcher key is archived next to the name of the hook it came from;
  3. rewrites the file atomically, only after the remaining settings decode;
  4. logs a config.retired_keys_archived warning once, with the path of the file and a keys field that lists the archived settings, sorted and comma-separated, for example keys=hooks.declarations.matcher.compaction_reason,memory,roles.dream.

The archived values stay in the file as comments and no longer apply. The next load finds nothing to archive, leaves the file byte for byte unchanged, and logs nothing. The archive step is a compatibility shim that CompozyOS removes in v0.6.0.

Archiving these settings never stops the load. The rewrite is guarded against concurrent edits: just before it writes, the loader re-reads the file and requires the same file with the same bytes. If an editor, another process, or another daemon changed or replaced the file in between, or the loader cannot write the file or its directory (for example a read-only directory), nothing is written and your file stays exactly as it was. The loader logs a config.retired_keys_archive_failed warning with the path of the file and the reason, and loads the rest of the file with the retired settings inactive. They are only inactive, not removed: the file keeps them until an archive succeeds, so treat the warning as something to act on. Make the file or directory writable, or finish editing, and the next load archives the current content and logs config.retired_keys_archived. The daemon starts either way.

Setting a retired key with compozy config set is still refused.

Complete Annotated Example

# ~/.compozy/config.toml

[daemon]
# Path to the Unix domain socket used by CompozyOS CLI commands.
socket = "~/.compozy/daemon.sock"
# Emit daemon process-memory snapshots every five minutes. Set to "0s" to disable.
memory_report_interval = "5m"
# Escalate a task-bound session after three consecutive failed subprocess health checks.
# Set to 0 to keep status and doctor evidence without changing task-run state.
subprocess_health_escalation_threshold = 3

[daemon.reload_timeouts]
# Provider runtime reload budget. Valid range: 1s to 60s.
providers = "5s"
# MCP runtime reload budget. Valid range: 1s to 60s.
mcp = "10s"

[daemon.load]
# Run agent process trees (and the tests, linters, and builds they launch) at niceness 10.
# Set to 0 to keep the daemon's priority.
agent_niceness = 10
# Delay new background sessions and prompts while the 1-minute load average exceeds
# 1.5x the logical CPU count. Set to 0 to disable load sampling.
admission_threshold = 1.5
# Longest one background admission waits before it proceeds anyway.
admission_max_wait = "2m"

[http]
# HTTP/SSE API bind address.
host = "localhost"
port = 2123

[app]
# Check for desktop app and app-owned runtime updates.
update_check = true
# Check cadence. Valid range: 15m to 168h.
update_check_interval = "15m"

[attention]
toasts = true
sound = true
system = false

[terminal]
default_shell = ""
shell_integration = true
scrollback_bytes = 1048576
detached_ttl = "24h"
exit_retention = "15m"
recording = false
recording_retention_days = 30
max_per_workspace = 8
max_per_daemon = 32
max_subscribers = 16

[shell.sessions]
# Session ordering: "created", "name", "last_activity", "attention", or "manual".
sort = "created"
# Session grouping: "none", "project" (under All projects), or "profile" (under All profiles).
group_by = "none"
# Deprecated in v0.4.0, removed in v0.5.0: "workspace" or "all-workspaces".
# scope = "all-workspaces"

[user_scope]
# Working directory for User-scope work. Absolute after ~ expansion; must exist.
work_dir = "~"

[cmd_palette]
# The current runtime accepts "agent". Use [] to disable the fallback row.
fallback_targets = ["agent"]
# Keep workspace ranking signals and recents enabled.
personalization = true

[cmd_palette.aliases]
# Aliases are workspace vocabulary used by the palette and CLI.
"session.new" = "new"

[window_manager]
new_window_policy = "tab"
small_viewport_policy = "stack"
focus_policy = "click_directional"
focus_wrap = false
focus_follows_pointer = false
raise_on_focus = true
drag_away_policy = "window"
group_move_modifier = "alt"
swap_modifier = "shift"
history_limit = 50
# Retain at most this many prior routes per window when a new route is pushed.
nav_stack_limit = 50
# Retain at most this many newest closed-window entries for reopen.
closed_entry_limit = 20
desktop_transition = "slide"

[window_manager.gaps]
inner = 0
top = 0
right = 0
bottom = 0
left = 0

[window_manager.snap]
edge_band = 32
corner_reach = 150
exit_slack = 16
repeat_ratios = [0.5, 0.666667, 0.333333]

[window_manager.bindings]
top_center = "zoom"
bottom_center = "zoom"

[defaults]
# Agent name used when a command or API request does not specify one.
agent = "general"
# Provider used by AGENT.md files that omit provider.
provider = "claude"
# Optional: model and reasoning effort for sessions that resolve that provider.
model = "claude-opus-5"
reasoning_effort = "high"

[roles.coordinator]
# Coordinator is a virtual builtin when agent is omitted. It is disabled by default.
enabled = false
ttl = "2h"
max_children = 5
max_active_sessions_per_workspace = 5

[roles.auto_title]
# Use a small model for the bounded automatic-title pass.
enabled = true
provider = "claude"
model = "claude-haiku-4-5-20251001"
reasoning_effort = "low"

[[roles.auto_title.fallback_chain]]
# Fallback routes are tried in declaration order before a session is accepted.
provider = "cursor"
model = "grok-4.6"
reasoning_effort = "high"
speed = "fast"
acp_options = [{ id = "thinking", bool_value = true }]

[limits]
# Positive daemon-wide agent concurrency bound.
max_concurrent_agents = 20

[session.limits]
# Deprecated: no session enforces this key; it is removed in v0.5.0.
# Use session.supervision.idle_release_after for idle sessions.
timeout = "0s"

[session.busy_input]
# Busy follow-ups steer by default; queue defers them until the current turn ends.
default_mode = "steer"
queue_cap = 10
max_text_bytes = 65536

[session.messaging]
# Agent-to-agent messages (compozy__session_prompt) one sender session may send to one target per window.
# -1 disables the budget.
rate_limit_messages = 30
rate_limit_window = "10m"
# An armed reply watch with no answer after this long wakes its sender once with an `expired` outcome.
# "24h" is the default; "0s" never expires a watch, so a sender waiting on a long-running target stays awake.
reply_watch_ttl = "24h"
# A queued message from another session waiting on a turn that has run this long is injected into the live
# turn when the provider supports steering. "0s" disables promotion.
queue_promote_after = "2m"

[session.supervision]
# Short heartbeats update durable liveness only. They are not persisted as session events.
activity_heartbeat_interval = "30s"
# 0 disables periodic progress events.
progress_notify_interval = "10m"
# Silence in an in-flight turn before one warning. 0 disables the hung-turn watchdog.
quiet_after = "30m"
# 0 disables automatic stop after the warning.
stop_grace = "10m"
# Idle time before the agent process of a resumable idle session is released. 0 disables it.
idle_release_after = "30m"
# Grace after an explicit prompt-budget cancellation.
timeout_cancel_grace = "30s"

[session.derive]
# Hard upper bound, in bytes, of the transcript JSON replayed into any new agent session
# (resume, runtime or model replacement, account fallback, recovery, continue, fork).
# Oldest messages are dropped first; the 8 most recent messages are kept while they fit.
max_replay_bytes = 131072
# Per-message cap on replayed message bodies; longer bodies end with " [content truncated]".
max_message_bytes = 16384

[subagents]
# Cap, in characters, of the `result` a subagent returns to its parent (1000–1000000).
# The full answer stays in the subagent's own session.
result_max_chars = 60000

[agents.soul]
# Disabling pauses prompt injection but keeps inspect/validate/write/delete/history/rollback available.
enabled = true
# Hard cap for SOUL.md body bytes after normalization.
max_body_bytes = 32768
# Compact /agent/context projection budget. Must be <= max_body_bytes.
context_projection_bytes = 2048

[agents.heartbeat]
# Disabling pauses wake decisions but keeps managed authoring available for repair.
enabled = true
# Bounded HEARTBEAT.md body size (snapshot uses normalized body).
max_body_bytes = 32768
# Compact projection budget for status surfaces.
context_projection_bytes = 4096
# Hard floor for authored preferences.min_interval. Files below this are clamped.
min_interval = "5m"
# Default cadence baseline used when authored files omit min_interval.
default_interval = "30m"
# Cooldown between wakes for a single session.
wake_cooldown = "1m"
# Maximum scheduler-cycle wakes across the daemon.
max_wakes_per_cycle = 25
# When true, wake only sessions in active runtime state.
active_session_only = true
# When true, authored active_hours/quiet_windows preferences are honored within bounds.
allow_active_hours_preferences = true
# Retention for agent_heartbeat_wake_events audit rows. Minimum 1h.
wake_event_retention = "168h"
# Idle age before metadata-only presence is treated as stale for wake eligibility.
session_health_stale_after = "2m"
# Minimum interval between session.health.update.after hook fires for the same session.
session_health_hook_min_interval = "1m"

[permissions]
# Valid values: deny-all, approve-reads, approve-all.
mode = "approve-all"
# Opt in to the provider's unrestricted ACP mode when available (Codex and Claude Code).
provider_full_access = true

[worktrees]
# Empty uses $COMPOZY_HOME/worktrees/<workspace>/<name> for new managed checkouts.
root = ""
# Lowercase, slash-terminated namespace for task per-run branches.
run_branch_namespace = "run/"
# Relative Git pathspecs for ignored, untracked files copied during managed creation.
copy_list = []
# Empty skips setup; a non-empty command runs inside the new checkout.
setup_command = ""
setup_timeout = "10m"
teardown_command = ""
teardown_timeout = "10m"
discovery_cache_ttl = "30s"

[tools]
# Disables registry dispatch and hosted MCP exposure when false.
enabled = true
# Enables CompozyOS-hosted local MCP projection for session-visible tools.
hosted_mcp_enabled = true
# Outer result cap; a smaller tool descriptor limit wins.
default_max_result_bytes = 262144

[tools.artifacts]
# Global retained-artifact count ceiling across workspaces.
max_count = 200
# Global retained-artifact byte ceiling across workspaces.
max_bytes = 1073741824
# Maximum retained-artifact age.
max_age = "720h"

[tools.hosted_mcp]
# Non-secret launch correlation nonce lifetime before UDS peer binding,
# and the post-release window for one replacement proxy bind.
bind_nonce_ttl_seconds = 30

[tools.clarify]
# Omitted entirely: no automatic expiration (the default).
# timeout = "0s"  # explicit unbounded: identical to omitted.
# timeout = "5m"  # finite: falls back after 5 minutes, as before.
# Manage with `compozy config get|set|unset tools.clarify.timeout --scope user`
# (or the `compozy__config_*` native tools); user scope only, restart-required.

[tools.policy]
# Valid values: disabled, ask, enabled.
external_default = "disabled"
# Bounded wait for daemon-mediated approval flows.
approval_timeout_seconds = 120
# External read-only sources trusted for approve-reads. Entries use kind:owner.
trusted_sources = []

[[mcp_servers]]
name = "filesystem"
command = "npx"
args = ["-y", "@modelcontextprotocol/server-filesystem", "/Users/you/work"]
env = { LOG_LEVEL = "info" }

[[mcp_servers]]
name = "remote-docs"
transport = "http"
url = "https://mcp.example.com/mcp"

[mcp_servers.auth]
registration = "auto"
scopes = ["mcp.read", "mcp.write"]

[providers.claude]
# Overrides the built-in Claude provider command and records native auth diagnostics.
command = "npx -y @agentclientprotocol/claude-agent-acp@latest"
auth_mode = "native_cli"
auth_status_command = "claude auth status"
auth_login_command = "claude auth login"

[providers.claude.models]
# Pre-session catalog defaults consumed by the daemon-owned model catalog.
default = "claude-sonnet-5"

[providers.claude.models.reasoning]
apply = "acp_option"

[[providers.claude.models.curated]]
id = "claude-sonnet-5"
display_name = "Claude Sonnet 5"

[[providers.claude.models.curated]]
id = "claude-fable-5"
display_name = "Claude Fable 5"
featured = true
release_date = "2026-06-09"

[model_catalog.sources.models_dev]
# Optional models.dev enrichment source for catalog metadata.
enabled = true
endpoint = "https://models.dev/api.json"
ttl = "24h"
timeout = "10s"

[provider_usage]
# Experimental. Topbar usage meter and daemon-owned usage collection.
enabled = true
refresh_interval = "5m"

[provider_usage.providers.claude]
# macOS only: consent to read Claude Code's Keychain login on an explicit refresh.
keychain_access = false

[marketplace.catalog]
# Base directory containing mcp.json, extensions.json, and skills.json.
base_url = "https://raw.githubusercontent.com/compozy/compozy/main/catalog"
# A browse after this interval refreshes the affected kind before serving it.
ttl = "1h"
# Per-request deadline for fetching one catalog document.
timeout = "10s"

[[providers.claude.mcp_servers]]
name = "github"
command = "npx"
args = ["-y", "@modelcontextprotocol/server-github"]
secret_env = { GITHUB_TOKEN = "env:GITHUB_TOKEN" }

[observability]
enabled = true
retention_days = 7
max_global_bytes = 1073741824

[observability.transcripts]
enabled = true
segment_bytes = 1048576
max_bytes_per_session = 268435456

[log]
level = "info"
max_size_mb = 10
max_backups = 5
max_age_days = 30
compress_backups = false

[redact]
# Detect likely credentials in content before logs, streams, or event storage receive them.
# Changing this value requires a daemon restart.
enabled = true

[skills]
enabled = true
# Folder conventions to scan besides CompozyOS's own (always active).
# Valid presets: "agents" (default on), "claude".
sources = ["agents"]
# Extra skills-only directories. Absolute or ~ paths at user and profile scope;
# workspace configs may also use workspace-relative paths.
custom_sources = []
disabled_skills = ["experimental-skill"]
poll_interval = "3s"
allowed_marketplace_hooks = ["clawhub:@team/format-hook"]

[extensions.trust]
allow_unverified = true

[extensions.sources.github]
enabled = true
base_url = "https://api.github.com"

[extensions.sources.git]
enabled = true

[extensions.dev]
watch_interval = "2s"

[extensions.resources]
allowed_kinds = ["window_layout"]
max_scope = "workspace"

[automation]
enabled = true
timezone = "America/Sao_Paulo" # Example: choose your local IANA zone
max_concurrent_jobs = 5
default_fire_limit = { max = 12, window = "1h" }

[automation.suggestions]
pending_cap = 5

[[automation.jobs]]
scope = "workspace"
name = "daily-doc-check"
agent = "docs"
workspace = "workspace-id-or-path"
prompt = "Review changed docs and report stale links."
enabled = true
fire_limit = { max = 2, window = "24h" }

[automation.jobs.schedule]
mode = "cron"
expr = "0 9 * * 1-5"

[automation.jobs.retry]
strategy = "backoff"
max_retries = 3
base_delay = "2s"

[[automation.triggers]]
scope = "workspace"
workspace = "workspace-id-or-path"
name = "failed-session-summary"
agent = "general"
prompt = "Summarize why session {{ index .Data \"session_id\" }} stopped."
event = "session.stopped"
enabled = true

[automation.triggers.filter]
kind = "session.stopped"
data.stop_reason = "error"

[loops]
# Positive duration between reconciliation cycles. Startup reconciliation always runs.
reconcile_interval = "1m"

[loops.defaults.delivery]
# Global/workspace defaults that seed new delivery loop runs.
iteration_cap = 50
fan_out_width = 4

[loops.defaults.delivery.retry]
# Mechanical node families inherit this policy. 0 disables; maximum 10.
max_attempts = 3
backoff_base = "1s"
backoff_max = "30s"

[loops.defaults.delivery.liveness]
# 0 disables silence attention. Silence never auto-kills or auto-pauses a node.
silence_window = "30m"

[loops.defaults.delivery.resume]
death_streak_limit = 3

[loops.defaults.delivery.predicates]
# CEL warns at 80% of this runtime cost and stops at the limit.
cost_limit = 10000

[loops.defaults.delivery.waits]
admission_attempts = 3
admission_retry_interval = "60s"

[loops.defaults.delivery.requests]
# Seeds asks that omit expires. Empty leaves expiry author-owned.
expire_after = "72h"

[loops.defaults.delivery.admission]
tombstone_horizon = "168h"

[[loops.defaults.delivery.autopause]]
# Ordered, first match wins. Rules are not agent-mutable.
match = "class == 'transport' && attempt >= 2"
action = "pause"

[loops.defaults.delivery.no_progress]
# Generation-hash no-progress window. Compile-time ceiling: 30.
window = 3

[loops.defaults.delivery.gates]
# Gate revision seed default. Compile-time ceiling: 64.
max_revisions = 10

[loops.defaults.delivery.runtime_defaults.worker]
provider = "codex"
model = "gpt-5.4"
reasoning = "high"
speed = "fast"

[loops.defaults.delivery.runtime_defaults.judge]
provider = "claude"
model = "opus"
speed = "normal"

[[loops.defaults.delivery.runtime_rules]]
[loops.defaults.delivery.runtime_rules.match]
type = "frontend"
complexity = "high"
[loops.defaults.delivery.runtime_rules.runtime]
model = "gpt-5.5-codex"
speed = "normal"

[loops.defaults.delivery.budget]
# 0 means unlimited. When set, budgets are enforced with halt or escalate.
tokens = 0
wall_clock_sec = 0
on_exceeded = "halt"

[loops.defaults.watch]
# Definitions containing watch-source use watch defaults. watch-events alone does not.
iteration_cap = 0
fan_out_width = 2

[loops.defaults.watch.retry]
max_attempts = 3
backoff_base = "1s"
backoff_max = "30s"

[loops.defaults.watch.liveness]
silence_window = "30m"

[loops.defaults.watch.resume]
death_streak_limit = 3

[loops.defaults.watch.predicates]
cost_limit = 10000

[loops.defaults.watch.waits]
admission_attempts = 3
admission_retry_interval = "60s"

[loops.defaults.watch.requests]
expire_after = "72h"

[loops.defaults.watch.admission]
tombstone_horizon = "168h"

[loops.defaults.watch.no_progress]
window = 2

[loops.defaults.watch.runtime_defaults.worker]
provider = "codex"
model = "gpt-5.4-mini"

[loops.defaults.watch.runtime_defaults.judge]
provider = "claude"
model = "sonnet"

[loops.defaults.watch.budget]
tokens = 0
wall_clock_sec = 0
on_exceeded = "halt"

[loops.breaker]
# Shared per workspace, target family, and target; only transport failures count.
threshold = 5
probe_interval = "60s"

[loops.inputs.implement-tasks]
# Explicit false remains a present value.
auto_commit = false
# Per-task is the default; set orchestrated to delegate through bounded workers.
mode = "per-task"
# Runtime input values are partial. Omitted fields keep resolving through lower layers.
orchestrator_runtime = { provider = "codex", model = "gpt-5.6-sol", reasoning = "high", speed = "fast" }
backend_runtime = { provider = "codex", model = "gpt-5.6-sol" }
frontend_runtime = { provider = "claude", model = "sonnet" }
default_runtime = { model = "gpt-5.6-terra" }

[loops.inputs.review-and-fix]
auto_commit = false

[goals]
# Defaults for newly started Goals. Active Runs keep their pinned values.
max_turns = 20
context_nudge_ratio = 0.8
# Durable session-event relay controls. Applied when the daemon starts.
outbox_batch_size = 50
outbox_poll_interval = "2s"

[autonomy]
# Same-kind re-block count that escalates a task to needs_attention. 0 disables the breaker.
block_recurrence_limit = 2

[task.orchestration]
# Bounds for task summaries, context bundles, scheduler health, and runtime watchdogs.
summary_max_bytes = 4096
context_body_max_bytes = 8192
context_prior_attempts = 5
context_recent_events = 50
spawn_failure_limit = 5
scheduler_bad_tick_threshold = 6
scheduler_bad_tick_cooldown = "5m"
# Zero disables the per-task max-runtime watchdog default. Tasks may still set a per-task override.
default_max_runtime = "0s"
designated_run_max = 5
# Maximum active worker and coordinator runs per workspace. 0 disables the limit.
max_active_runs_per_workspace = 16
# Default wall-clock deadline for action nodes that omit their own timeout.
action_run_timeout = "30m"

[task.orchestration.profile]
# Defaults that apply when a task does not persist its own execution profile.
default_coordinator_mode = "inherit"
default_worker_mode = "inherit"
# inherit has workspace-root semantics unless a higher task policy supplies a mode.
default_worktree_mode = "inherit"
# Gate task-level provider/model overrides on the profile.
allow_task_provider_override = true

[task.orchestration.review]
# Defaults for the post-terminal review gate. See the review-gate docs for end-to-end behavior.
default_policy = "none"
max_rounds = 3
max_review_attempts = 2
timeout = "20m"
rapid_terminal_window = "2m"
rapid_terminal_limit = 3
missing_work_max_items = 20
missing_work_item_max_bytes = 512
reason_max_bytes = 2048
review_text_max_bytes = 12000
next_round_guidance_max_bytes = 4096
failure_policy = "block_task"

[task.recovery]
# When true, agent identities may use force release, force fail, retry, and bulk recovery surfaces.
# Set false to reserve those verbs for non-agent operator identities.
allow_agent_force = true

[[hooks.declarations]]
name = "record-session-start"
event = "session.post_create"
mode = "async"
required = false
priority = 500
timeout = "5s"
command = "printf"
args = ["session started\n"]
env = { HOOK_LOG = "1" }

[hooks.declarations.matcher]
agent_name = "general"

[gateway]
enabled = false
private_port = 0
public_port = 0

[gateway.pairing]
ttl = "5m"
max_pending = 8

[gateway.stream_ticket]
ttl = "30s"

[gateway.auth.rate_limit]
window = "60s"
max_fails = 10

[gateway.verify]
timeout = "10s"
public_dns_resolver = "1.1.1.1:853"

[daemon]

shutdown_grace defaults to "30s" and accepts durations from "0s" through "5m". On a requested restart it gives admitted turns time to finish before CompozyOS records interruptions and stops provider processes concurrently. The total shutdown budget also reserves time for the session termination ladder and resource cleanup; the relaunch helper waits through that budget. Explicit daemon stop does not wait for restart continuation and never auto-resumes work. Changing this setting requires a restart.

FieldTypeDefaultValid valuesDescription
socketstring path$COMPOZY_HOME/daemon.sockNon-empty path. ~ is expanded.Unix domain socket used by CLI IPC and the UDS API server.
memory_report_intervalduration5m0s to disable, or any positive duration.Cadence for daemon process-memory snapshots in logs and runtime.memory.
subprocess_health_escalation_thresholdinteger30 or a positive integer.Failed health checks before CompozyOS moves a task-bound nonterminal run to needs_attention; 0 disables the transition.

The daemon captures a baseline snapshot at startup, periodic snapshots at this cadence, and one final snapshot during joined shutdown. Each [memory] log record and the runtime.memory item from compozy doctor -o json reports Go heap allocation, heap in-use bytes, goroutines, uptime, and resident memory when the operating system exposes it. resident_memory_kind distinguishes current resident memory from the peak value available on macOS. Setting the interval to 0s starts no sampling worker; doctor reports the feature as disabled. Changing this field is restart-required.

subprocess_health_escalation_threshold consumes the health verdict exposed by an active ACP subprocess. At the configured consecutive-failure count, CompozyOS moves the exact task run bound to that session from queued, claimed, starting, or running to needs_attention. Repeated failed verdicts do not emit another transition, and a completed, failed, or canceled run remains terminal. An unexpected ACP process exit triggers the same transition immediately when the value is positive. Set the field to 0 when you want compozy status and compozy doctor evidence without task mutation. Changing this field is restart-required.

[daemon.load]

These fields keep many concurrent agents from saturating the host and starving the desktop UI.

FieldTypeDefaultValid valuesDescription
agent_nicenessinteger100 through 19. 0 disables.CPU niceness applied to each agent's process group on macOS and Linux. Ignored on Windows.
admission_thresholdfloat1.50 to disable, or a positive number.Normalized load (1-minute load average divided by logical CPUs) above which background work waits.
admission_max_waitduration2m1s through 30m when the threshold is enabled; otherwise 0s+.Longest one background session start or prompt waits for load to drop before it proceeds anyway.

CompozyOS starts each agent in its own process group and applies agent_niceness to that group right after launch, so every tool, test runner, linter, and build the agent starts inherits the lower priority. On an idle machine the agent still gets all available CPU; the niceness only matters when processes compete. Raising priority back requires root, so the value is fixed for the lifetime of the agent process. If the operating system refuses the change, the session still starts and the daemon logs one warning.

When admission_threshold is above zero, the daemon samples the load average every five seconds. While normalized load exceeds the threshold, new sessions and prompts started by scheduled or triggered automations, Loops, and scheduler wakes wait until load drops or admission_max_wait elapses, then proceed. Prompts and sessions you start yourself, and manual automation runs, are never delayed. compozy doctor -o json reports the current state in the runtime.load item: the sampled and normalized load, whether background work is waiting, and how many admissions were delayed. Platforms without a load average (Windows) report the feature as unavailable and never delay work. Changing any field is restart-required.

[http]

FieldTypeDefaultValid valuesDescription
hoststringlocalhostNon-empty host or bind address.HTTP/SSE server host.
portinteger21231 through 65535HTTP/SSE server port.

[app]

The daemon is the sole consumer of both keys. It schedules read-only checks for the runtime and, when installed, the desktop app. The shell does not read config.toml; it observes the durable update operation when an app apply is staged.

FieldTypeDefaultValid valuesDescription
update_checkbooleantruetrue, falseEnables daemon-scheduled host update checks.
update_check_intervalduration15m15m to 168hTime between daemon-scheduled checks.

With automatic checks enabled, a running daemon checks the active release channel at startup and every 15 minutes by default. Settings and the menubar read the shared snapshot every minute when idle, so a successful discovery appears within about 16 minutes of publication. Explicit interval settings are preserved; network failures and GitHub rate limits can delay discovery.

Settings → General → Updates shows the last successful check time. Check for updates calls POST /api/settings/update/check over HTTP or UDS, bypassing the normal release cache without installing anything. All Settings clients share a one-minute minimum between upstream attempts, including failed attempts. GitHub Retry-After and rate-limit reset deadlines extend that wait. GET /api/settings/update remains the cached status read; runtime.checked_at is optional until a check succeeds. Manual checks remain available when automatic checks are disabled.

[attention]

Attention delivery settings are global and live-applied as one validated candidate. Operators can use Settings → Notifications; agents can use compozy config get|set attention.toasts|sound|system. The title count is always on and has no config key.

FieldTypeDefaultValidationDescription
toastsbooleantruetrue or falseAllows in-app notification toasts.
soundbooleantruetrue or falseAllows the built-in notification sound.
systembooleanfalsetrue or falseAllows supported clients to use system notifications while not in view.

Workspace mutes are profile-owned rows, not config.toml fields. Read or replace the default profile's set with GET/PATCH /api/settings/attention?scope=user; use scope=profile&profile=<name> for another profile. Removing a workspace deletes its mute rows from every profile. Unmuting never replays notifications that were suppressed.

[terminal]

Terminal settings apply live as one validated candidate. Operators and agents can read or change them with compozy config get|set|unset terminal.<key> -o json; the native compozy__config_* mutation surface does not expose these paths.

FieldTypeDefaultValidationDescription
default_shellstring""Empty, a bare executable name, or an absolute path.Shell selected for later interactive terminals.
shell_integrationbooleantruetrue or falseEnables authenticated command-boundary markers.
scrollback_bytesinteger1048576Positive.Bounded in-memory scrollback bytes per terminal.
detached_ttlduration24hPositive.Reclaims an idle terminal with no viewers.
exit_retentionduration15mPositive.Keeps an exited terminal readable before expiry.
recordingbooleanfalsetrue or falseStarts recording for new terminals created by terminal open operations through supported CLI, API, and native-tool surfaces.
recording_retention_daysinteger30Positive.Retains saved recordings for this many days.
max_per_workspaceinteger8Positive.Per-profile, per-workspace live-terminal cap.
max_per_daemoninteger32Positive.Daemon-global live-terminal cap.
max_subscribersinteger16Positive.Concurrent viewer cap per terminal.

Workspace overlays may set all ten keys. Profile overlays may set nine; they reject terminal.max_per_daemon with profile_config_key_denied because one daemon owns that global cap. Changing recording affects new terminals created by later terminal open operations and does not start recording an existing terminal.

[shell.sessions]

Shell session preferences are global and live-applied as one validated candidate. They are the default order and grouping of session windows: each window keeps its own Sort by and Group by, shared by its List and Kanban views and remembered across reloads, and a window that has not chosen yet follows these values. The Web never writes them, so a choice in one window never moves another. Operators and agents set them with compozy config get|set shell.sessions.<key> or GET/PATCH /api/settings/shell.

FieldTypeDefaultValid valuesDescription
sortstringcreatedcreated, name, last_activity, attention, manualNewest created first, name A–Z, recent activity, attention urgency, or manual order.
group_bystringnonenone, project, profileGroups the session list by project (under All projects) or profile (under All profiles).
scopestringworkspaceworkspace, all-workspacesDeprecated. Legacy breadth intent; kept through v0.4.0, removed in v0.5.0.

Created keeps parent and nested child rows still while agents work. Name orders the visible title case-insensitively using Unicode lowercase, then creation time descending and ID descending; unnamed sessions sort as New session. Created uses creation time descending, then ID descending. When the automatic title arrives after the first turn, Name may move the row once; subsequent renames may move it again. Created stays in place. Last activity and Attention first remain opt-in choices. An unset sort uses Created; an explicitly persisted last_activity or attention remains unchanged, even if it matched the previous default. No user-state migration or rewrite is performed. manual applies only where the list shows exactly one project and profile; elsewhere it displays as Last activity and stays stored.

group_by = "project" applies only under All projects and profile only under All profiles. An inapplicable value renders as none and is not rewritten. Unknown values fail with shell_config_invalid. Groups come from server facets, so a group appears even when none of its sessions are on the loaded page.

scope deprecation

Which projects you see is now decided by the topbar globe (All projects), not by a config key. Through v0.4.0, scope stays stored and is returned as written. Old clients and scripts may keep writing it; every write returns a deprecation warning naming shell.sessions.group_by and the globe. While group_by is unset, scope = "all-workspaces" derives group_by = "project"; an explicit group_by always wins. GET /api/settings/shell reports warnings and legacy_scope: { value, explicit, revision }. The Web turns on All projects only for an explicit all-workspaces written after it last looked, never from the default.

In v0.5.0 the loader turns scope = "all-workspaces" into group_by = "project" when grouping is unset, drops scope with a logged notice, and the next apply removes it from config.toml. A leftover scope key never blocks the daemon from starting. To migrate now:

compozy config unset shell.sessions.scope
compozy config set shell.sessions.group_by project

All fields have the live lifecycle. The daemon stores them in global config.toml, so the preference survives browser restarts without browser-local storage. Workspace overlays and workspace-scoped CLI or native-tool writes are rejected.

[shell.appearance]

The Interface and Code fonts chosen in Settings › Appearance › Fonts. They are global and live-applied: every Web and desktop client connected to this CompozyOS repaints with them, and a client that reconnects later loads them. A family that is not installed on one device falls back to the built-in stack there.

FieldTypeDefaultValid valuesDescription
ui_fontstring""Empty, or one family name: letters, digits, spaces, -_.'; at most 128 charsInterface font. Empty uses Inter.
mono_fontstring""Same as ui_fontCode blocks, diffs, paths, and the terminal. Empty uses Geist Mono.

system-ui and ui-monospace select the platform's own faces; JetBrains Mono Variable ships with the app and downloads only once chosen. Invalid values fail with shell_config_invalid.

compozy config set shell.appearance.mono_font "JetBrains Mono Variable"
compozy config unset shell.appearance.mono_font

[cmd_palette]

Command-palette settings are global desired state with workspace overrides. Changes apply live as one validated candidate. The typed Settings surface exposes the agent fallback and personalization; aliases remain available through compozy config and config.toml.

FieldTypeDefaultValidationDescription
fallback_targetsstring array["agent"]Empty, or the single supported value agent.Enables or disables the agent fallback row.
personalizationbooleantruetrue or falseControls workspace personalization desired state.

With fallback_targets = ["agent"], a non-empty query without a strong result can show Ask agent: '<query>'. Selecting it with Enter creates a new session with the workspace's default agent and uses the query as the opening prompt. The query is not sent before Enter. Set fallback_targets = [], or turn off Agent fallback in Settings → Palette, to remove the row.

[cmd_palette.aliases] maps a command ID to one workspace alias. An alias contains 1–32 Unicode characters with no whitespace and must be unique in the workspace. Use compozy cmd-palette alias set|clear, or send aliases through PATCH /api/settings/window-manager, for an atomic update. The same endpoint accepts shortcut changes and returns 409 shortcut_conflict, 409 alias_conflict, or 422 invalid_alias without a partial write.

[window_manager]

Window-manager defaults are global desired state and may be overridden by the same typed fields in a workspace config overlay. Every field is live-applied as one validated candidate; an invalid field writes/applies nothing and leaves the previous active generation in place. Operators can use Settings › Layouts, while agents can use compozy config or the typed GET/PATCH /api/settings/window-manager surface and inspect config apply history.

FieldTypeDefaultValid valuesDescription
new_window_policystringtabtab, beside_focus, floatingInitial structural placement.
small_viewport_policystringstackstack, rejectProjection behavior when minimum sizes cannot fit.
focus_policystringclick_directionalclick_directional, directionalEnabled focus inputs.
focus_wrapbooleanfalsetrue or falseWhether directional focus wraps at a desktop boundary.
focus_follows_pointerbooleanfalsetrue or falseWhether pointer entry changes client-local focus.
raise_on_focusbooleantruetrue or falseWhether focusing a floating window raises it.
drag_away_policystringwindowwindow, groupDefault detach scope for a tiled drag.
group_move_modifierstringaltalt, control, meta, shift, noneModifier that temporarily moves the complete group.
swap_modifierstringshiftalt, control, meta, shift, noneModifier that turns an occupied drop into a window swap.
history_limitinteger501 through 500Maximum workspace-scoped layout undo entries.
nav_stack_limitinteger501 through 200Write-time cap for each window's prior-route stack.
closed_entry_limitinteger201 through 100Write-time cap for retained closed-window entries.
desktop_transitionstringslideslide, crossfade, instantDesktop transition; reduced motion remains authoritative.

nav_stack_limit applies when compozy window navigate --mode push records the current route. closed_entry_limit applies when a non-minimize close records a reopen entry. Lowering either live limit does not remove existing state; the next relevant mutation retains only the newest values.

[window_manager.gaps]

inner, top, right, bottom, and left are integer CSS pixels from 0 through 64. All five default to 0, so tiled windows meet flush at shared seams and fill the work area. Clients apply them to the measured work area; normalized durable geometry never stores pixels. An explicit [window_manager.gaps] table keeps its values.

[window_manager.snap]

FieldTypeDefaultValidation
edge_bandinteger324 through 128 pixels.
corner_reachinteger15016 through 512 pixels.
exit_slackinteger160 through 64 pixels.
repeat_ratiosnumber array[0.5, 0.666667, 0.333333]One to eight unique finite ratios, each 0.1 through 0.9.

[window_manager.bindings] and shortcuts

top_center and bottom_center accept zoom, reserved, or none, and both default to zoom. reserved blocks that edge-center snap. A target-less stack action is invalid because stacking requires an occupied target window. [window_manager.shortcuts] maps action IDs to a chord string or an array of chords. An empty string or array disables an action. desktop.switch accepts ranges through Digit1..9; window.tab.jump accepts Digit1..8. Range members expand before full-map validation, and any duplicate rejects the whole candidate. See Keyboard shortcuts for defaults, examples, and the Terminal preset. Named any/landscape/portrait layout profiles are declarative window_layout resources, not a second config source.

[window_manager.global_shortcuts]

This table maps any command-palette command ID to one desktop-global chord. It defaults palette.summon.global = "meta+shift+Space". The daemon validates and persists the intended map; each Electron shell reports its own registration status. Use compozy cmd-palette bind|unbind --global for atomic changes and Global hotkeys for failure and browser-mode behavior.

[defaults]

FieldTypeDefaultValid valuesDescription
agentstringgeneralEmpty, or after trimming ^[a-z][a-z0-9_-]{0,105}$ (maximum 106 characters).Agent selected when a command or API request omits an agent. Empty keeps the next resolution fallback available.
providerstringemptyBuilt-in provider name or configured provider key.Provider selected when an AGENT.md omits provider. Empty is valid until an agent without provider is resolved.
modelstringemptyA model ID of provider. Requires provider.Model for sessions whose provider comes from defaults.provider and whose agent omits model. Empty uses the provider's models.default.
reasoning_effortstringemptyAn effort identifier such as low, medium, high, or max. Requires provider.Reasoning for those same sessions when the model is not agent-authored. Empty uses the model's curated default. Skipped for a provider that applies no reasoning effort.

model and reasoning_effort belong to provider. An agent that names its own provider never inherits them, and an explicit session runtime (--provider, --model, --reasoning-effort, or the API runtime fields) wins over them. A config layer that changes provider drops the model and reasoning_effort inherited from lower layers, and writing a different provider with compozy config set defaults.provider or Settings removes that layer's saved model and reasoning, so a model chosen for one provider never runs on another. compozy install replaces both with its own model choice. Existing configs without these keys resolve exactly as before.

Settings → General → Session defaults edits this section for the active profile: the agent, a runtime selector for provider, model and reasoning (Automatic (recommended) stores an empty provider), and the browser-local Smooth streaming preference. The former Settings → Defaults page and its /settings/defaults links redirect there. Changes need a daemon restart, like every defaults.* key. Choose Use provider default to keep the provider while clearing its model and reasoning overrides, or Use automatic to clear the provider as well.

[roles.<role>]

[roles] is the closed routing roster for daemon-owned background work. It is not an extension registry: only the two sections below are accepted. Global $COMPOZY_HOME/config.toml values apply to every workspace unless the selected workspace's .compozy/config.toml overlays them.

RoleDefaultEmpty agent resolutionExtra policy owned here
coordinatoroffbuiltin coordinatorttl, child cap, workspace active-session cap
auto_titleoninvoking contextnone

coordinator is a CompozyOS-owned virtual identity. It resolves without an authored AGENT.md, does not appear in public agent catalogs, and is reserved from create, update, duplicate, and extension agent publication. Its embedded prompt is fixed; selecting a custom agent changes routing, not the builtin prompt. Automatic titling runs under a fixed 60-second deadline that is not configurable.

Session-backed role fields

These fields apply to coordinator and auto_title.

FieldTypeDefaultValid valuesDescription
enabledbooleanrole-specifictrue or falseAdmits new work for the role. Disabling a role does not stop an already accepted session.
agentstringemptyEmpty, or after trimming ^[a-z][a-z0-9_-]{0,105}$ (maximum 106 characters)Selects an authored agent. Empty resolves to the builtin or invoking-context behavior in the roster above.
providerstringemptyEmpty or a configured provider keyOptional provider override applied after agent resolution.
modelstringemptyEmpty or an exact provider model IDOptional model override.
reasoning_effortstringemptynone, minimal, low, medium, high, xhigh, max, ultraOptional reasoning override.
speedstringemptyEmpty, normal, or fastOptional speed override; fast must be valid for the resolved model and provider strategy.
acp_optionsarray of typed tablesemptyEach entry has id and exactly one of value_id or bool_valueOptional provider-advertised configuration overrides, merged by option ID.
fallback_chainarray of route tablesemptyEach entry requires provider and modelOrdered fallback routes; each entry may also set reasoning_effort, speed, acp_options, and command.

Each route table accepts these keys:

KeyTypeDefaultDescription
providerstring—Required. A configured provider key.
modelstring—Required. The model to run on this route.
reasoning_effortstringemptyOptional reasoning override for this route.
speedstringemptyOptional speed override for this route.
acp_optionsarray of typed tablesemptyOptional provider-advertised configuration for this route.
commandstring""Optional account for this route. Same grammar as providers.<name>.command. Empty inherits: the agent's command on its own provider, otherwise the route provider's command.

command selects which account (seat) the route launches. It is parsed without a shell: shell-style quoting splits the arguments, and leading NAME=value tokens become private environment for the launched process. Values are forwarded literally, so ~ and $HOME are not expanded — write absolute paths. Parsing is the only validation: a command made only of assignments fails with roles.<role>.fallback_chain[<i>].command: command is missing an executable, and unbalanced quotes fail with …command: parse command: …. There is no limit on the number of routes or on command length.

Fallback chains replace as a whole when a workspace overlay declares them, including command on every route. A session-backed consumer may advance before ACP accepts a session. Automatic titles also advance when the provider refuses a turn before producing any output, after the refused child stops. A turn that produced output stays on its route, and a failed child stop prevents another attempt. Immediately before each fallback attempt, CompozyOS emits role.fallback.used with the role, the fallback's one-based attempt, provider, model, and provider_command_fingerprint (a sha256: fingerprint of the route's command when it sets one). The raw command never appears in events or logs. The event records an attempt, not success.

# Second Claude seat first, then another provider as the last resort.
[[roles.auto_title.fallback_chain]]
provider = "claude"
model = "haiku-4-5"
command = "CLAUDE_CONFIG_DIR=/Users/ada/.claude-work claude --acp"

[[roles.auto_title.fallback_chain]]
provider = "cursor"
model = "grok-4.6"
reasoning_effort = "high"
speed = "fast"
acp_options = [{ id = "thinking", bool_value = true }]

compozy roles show auto_title prints each route with its command, and compozy logs --type role.fallback.used shows which route each attempt used by fingerprint.

[roles.coordinator]

Coordinator routing uses the shared session-backed fields plus these coordinator-owned safety limits. Task creation alone does not start a coordinator; an executable workspace run must be enqueued, the role must be enabled, and no healthy workspace coordinator may already exist.

FieldTypeDefaultValid valuesDescription
ttlduration2h1m through 24hManaged coordinator session lifetime.
max_childreninteger51 through 5Maximum concurrently held safe-spawn children.
max_active_sessions_per_workspaceinteger5Positive integerActive coordinator and spawned-session cap for one workspace.

Role changes are classified live and affect later invocations without a daemon restart. Inspect the resolved roster and per-field provenance with:

compozy roles list --workspace <workspace> -o json
compozy roles show auto_title --workspace <workspace> -o json

HTTP and UDS expose the same GET /api/roles and GET /api/roles/{role} projections. A missing authored agent remains visible as role_agent_not_found; an unknown role returns role_unknown.

[limits]

FieldTypeDefaultValid valuesDescription
max_concurrent_agentsinteger20Positive integer.Daemon-wide cap for concurrently running agent subprocesses.

[subagents]

[subagents]
result_max_chars = 60000

result_max_chars limits the result returned by the subagent tools to 1,000–1,000,000 characters (default 60,000). The complete answer stays in the child session transcript. A truncated result includes a hint to use compozy__session_history on child_session_id. Workspace overlays override the global value only for that workspace. The service reads the current value when the subagent settles; changes apply to subsequent results without a restart. Subagents have no depth, count, or TTL caps.

[session.restart]

FieldTypeDefaultValid valuesDescription
auto_continuebooleantruetrue, falseContinue eligible interrupted turns after daemon restart or crash.
max_attemptsinteger31–10Maximum automatic recovery attempts per session inside the window.
windowduration"5m""1s"–"1h"Rolling window for the durable restart attempt budget.

These keys are additive: omitted values receive defaults without rewriting existing files; explicit auto_continue = false is preserved. They follow the restart-required settings lifecycle and can be managed through CLI, HTTP/UDS, and the native config tools. See restart recovery for eligibility, pending-interaction handling, and external-effect limits.

[session.limits]

session.limits.timeout is deprecated and has no effect: no session is ended or stopped when it elapses. It stays readable and writable through v0.4 so existing files and scripts keep loading, and a non-zero value logs a deprecation warning at load. It is removed in v0.5.0. To free the agent of an idle session, use session.supervision.idle_release_after.

FieldTypeDefaultValid valuesDescription
timeoutduration0sZero or positive Go duration.Deprecated; not enforced. Removed in v0.5.0. Use idle_release_after.

[session.attachments]

Session attachments are stored under $COMPOZY_HOME/session-attachments/<workspace_id>/<session_id>/. CompozyOS uses the detected file type rather than a client-supplied Content-Type. Image bytes are stored as received in v1, including EXIF metadata.

[session.attachments]
max_file_bytes = 10485760
max_files_per_prompt = 10
allowed_mime = ["image/png", "image/jpeg", "image/webp", "application/pdf", "text/markdown", "text/plain"]

[session.attachments.retention]
max_count = 200
max_bytes = 1073741824
max_age = "720h"
FieldTypeDefaultDescription
max_file_bytesinteger10485760Maximum stored bytes for one attachment.
max_files_per_promptinteger10Maximum attachment references on one prompt.
allowed_mimearray of stringsix v1 typesExact detected MIME types accepted by the store.
retention.max_countinteger200Global retained attachment count ceiling.
retention.max_bytesinteger1073741824Global retained byte ceiling.
retention.max_ageduration720hMaximum retained age before sweep.

All session.attachments.* changes are restart-required. A sweep runs at startup, during store access, and periodically. Archiving or clearing a conversation keeps its attachments; deleting a session removes its attachment directory, and unregistering a workspace removes that workspace's complete attachment subtree. Support bundles exclude attachment files and their home-tree entries.

Manage attachment settings

All six session.attachments.* paths are agent-mutable. Use compozy config get|set|unset with -o json to inspect, change, or remove a global value; add --scope workspace --workspace <workspace> to write the workspace overlay. The same paths are available through the typed compozy__config_get, compozy__config_set, and compozy__config_unset tools; resolve the live descriptor before a native-tool mutation so its scope and input type are explicit.

compozy config get session.attachments.max_file_bytes --workspace <workspace> -o json
compozy config set session.attachments.max_file_bytes 20971520 --scope workspace --workspace <workspace> -o json
compozy config set session.attachments.allowed_mime '["image/png","application/pdf","text/plain"]' --scope workspace --workspace <workspace> -o json
compozy config unset session.attachments.max_file_bytes --scope workspace --workspace <workspace> -o json

The other mutable paths are session.attachments.max_files_per_prompt, session.attachments.retention.max_count, session.attachments.retention.max_bytes, and session.attachments.retention.max_age. Each bound must stay positive; allowed_mime must be a non-empty, duplicate-free subset of the six v1 MIME types. A rejected write reports validation rather than changing the desired state. A successful structured response reports lifecycle="restart-required" and next_action="restart-daemon"; restart the daemon, then read the path again to confirm the active generation.

[session.supervision]

Session supervision uses fresh work evidence across live sessions. quiet_after and stop_grace are a hung-turn watchdog: silence accrues only while a turn is in flight and waiting on the agent, so a session waiting for you is never warned or stopped for inactivity. idle_release_after silently releases the agent process of an idle session whose agent supports ACP session/load; the session stays active and the next prompt binds a runtime again. See session lifecycle for signal validity, quiet warnings, unknown-source attention, the stop ladder, and idle runtime release.

Settings → General → Pause idle agents after edits idle_release_after (Never, 15 minutes, 30 minutes, 1 hour, 4 hours, or 1 day; any other configured duration is shown as-is). The general settings HTTP/UDS DTO carries it as config.idle_release_after; omit the field on update to keep the current value. Changing it requires a daemon restart.

FieldTypeDefaultValid valuesDescription
activity_heartbeat_intervalduration30sPositive Go duration.Metadata heartbeat cadence; actual agent progress remains fresh for twice this interval.
progress_notify_intervalduration10mZero or positive.Periodic runtime_progress cadence; zero disables it.
idle_release_afterduration30mZero or positive.Idle time before a resumable idle session's agent process is released. Zero disables it.
prompt_deadlineduration0sZero or positive.Optional total prompt budget.
quiet_afterduration30mZero or positive.Silence in an in-flight turn before one warning. Zero disables warning and auto stop.
stop_graceduration10mZero or positive.Continued turn silence after the warning before stopping. Zero disables only auto stop.
timeout_cancel_graceduration30sPositive Go duration.Grace after explicit prompt-budget cancellation.

The released inactivity_warning_after and inactivity_timeout keys remain readable and writable through the v0.4 compatibility window and emit a deprecation warning. Remove them before v0.5.0. A file using only those keys retains their independent absolute timers, including disabled warnings and equal warning/stop thresholds. Setting either new timing key selects the new zero semantics; explicit new values override the corresponding translated legacy value. With ordinary positive legacy thresholds, migrate the warning threshold to quiet_after and their difference to stop_grace.

[session.derive]

[session.derive] bounds every transcript replay into a new agent session: resuming a session without native session loading, replacing its runtime or model, account fallback, prompt recovery, and compozy session continue or fork (including the Web "Continue with another agent…" action). CompozyOS never compacts a session itself and the agent owns its live context window, so the replay is the only context CompozyOS rebuilds, and it is bounded. Native compaction is observed, not performed, by CompozyOS; see Session compaction.

The bound applies to the serialized transcript JSON and is never exceeded: CompozyOS caps each message body first, then drops the oldest messages, keeping the 8 most recent while they fit, and finally drops even those down to one. When messages are dropped:

  • the earliest user message stays pinned ahead of the omission note;
  • the omission note [N earlier messages omitted to fit the context budget] states how many were dropped;
  • the replay header adds Earlier messages were omitted. Read them with the compozy__session_history tool (session_id: <id>) when you need them. only when the session's tool surface includes compozy__session_history. For a derived child, <id> is the source session.

Every replay header also tells the agent that the files and git state in the workspace are authoritative and must be inspected before acting on the transcript. Continue and fork responses report the omission as truncated and omitted_count. Bounding never fails a rebuild.

FieldTypeDefaultValid valuesDescription
max_replay_bytesinteger1310724096 or more.Hard upper bound of the replayed transcript, in bytes.
max_message_bytesinteger163841024 through max_replay_bytes.Per-message cap; a cut body ends with [content truncated].

Invalid values fail config validation with session.derive.max_replay_bytes must be at least 4096 or session.derive.max_message_bytes must be between 1024 and session.derive.max_replay_bytes. The two bounds are checked together against the effective values, so lowering max_replay_bytes below the max_message_bytes default of 16384 also requires lowering max_message_bytes. For example, max_replay_bytes = 8192 alone fails with the second error; set both keys:

[session.derive]
max_replay_bytes = 8192
max_message_bytes = 4096

A workspace .compozy/config.toml may override either key for sessions in that workspace.

[subagents]

[subagents] controls subagents: child agent sessions an agent delegates one task to with compozy__subagent_delegate. Subagents have no depth, count, or TTL limits; recursion is bounded by narrowing-only permissions, the active-turn requirement, and the stop cascade. The one setting caps how much of a subagent's answer reaches the parent, so one subagent cannot flood the parent's context.

FieldTypeDefaultValid valuesDescription
result_max_charsinteger600001000 through 1000000Truncation point of result in compozy__subagent_status, CLI, and HTTP/UDS payloads. A cut result sets result_truncated: true; the full text stays in the subagent's session transcript.

The value is read when a subagent settles, so a reload applies to subagents that finish afterwards. An invalid value fails config validation with the key path, for example subagents.result_max_chars must be between 1000 and 1000000: 999. A workspace .compozy/config.toml may override it for sessions in that workspace.

[agents.soul]

[agents.soul] controls the optional SOUL.md authored persona artifact. Config owns body limits, compact projection budget, and the enabled toggle. SOUL.md cannot redefine any of these keys, and forbidden operational fields in SOUL.md are rejected. See Agent Soul.

FieldTypeDefaultValid valuesDescription
enabledbooleantruetrue or falseWhen false, CompozyOS stops injecting Soul into prompts and /agent/context but keeps inspect/validate/write/delete/history/rollback available.
max_body_bytesinteger32768Positive integer.Maximum normalized SOUL.md body size. Files beyond this fail validation with oversized_body.
context_projection_bytesinteger2048Positive integer; must be <= max_body_bytes.Compact /agent/context.soul projection budget.

Workspace overlay merges scalar fields predictably; setting any key zero-clamps to the validated positive value or fails with a typed validation error. Disabling at runtime does not clear stored snapshots; managed authoring keeps working so operators can repair a broken file.

[agents.heartbeat]

[agents.heartbeat] controls the optional HEARTBEAT.md advisory wake policy and the runtime health/wake-audit subsystem. Config owns cadence, rate limits, retention, and stale thresholds; authored files express bounded preferences only. See Agent Heartbeat.

FieldTypeDefaultValid valuesDescription
enabledbooleantruetrue or falseWhen false, CompozyOS disables wake decisions but keeps managed authoring available so operators can repair a broken policy.
max_body_bytesinteger32768Positive integer; capped at the runtime hard limit.Maximum normalized HEARTBEAT.md body size; oversized files fail validation.
context_projection_bytesinteger4096Positive integer; must be <= max_body_bytes.Compact projection budget for status responses.
min_intervalduration5mPositive Go duration; must be <= default_interval.Hard floor for authored preferences.min_interval. Files below this are clamped with heartbeat_preference_clamped.
default_intervalduration30mPositive Go duration.Default cadence baseline used when authored files omit min_interval.
wake_cooldownduration1mPositive Go duration.Cooldown between wakes targeting the same session.
max_wakes_per_cycleinteger25Positive integer.Daemon-wide cap on wakes per scheduler cycle. Excess wakes return heartbeat_rate_limited.
active_session_onlybooleantruetrue or falseWhen true, only sessions reported as active and attachable are wake-eligible.
allow_active_hours_preferencesbooleantruetrue or falseWhen false, authored active_hours/quiet_windows are ignored with heartbeat_preference_ignored.
wake_event_retentionduration168hPositive Go duration; minimum 1h.Retention for agent_heartbeat_wake_events audit rows. Cleanup runs at boot and during scheduler maintenance cycles.
session_health_stale_afterduration2mPositive Go duration.Idle age before metadata-only presence becomes stale for wake eligibility.
session_health_hook_min_intervalduration1mPositive Go duration.Minimum interval between session.health.update.after hook fires for the same session; metadata-only touches are coalesced.

[agents.heartbeat] does not own task-run lease heartbeat (task_runs + ClaimNextRun + HeartbeatRunLease), [session.supervision] activity heartbeat, or other runtime policy greet presence. Those four authorities remain independent.

HEARTBEAT.md snapshot digest covers the normalized body, canonical frontmatter JSON, and a canonical digest of the resolved [agents.heartbeat] config subset. Editing any field that affects parsing, preference resolution, eligibility, retention, or coalescing invalidates the digest and triggers a fresh resolution at the next wake decision.

[permissions]

FieldTypeDefaultValid valuesDescription
modestringapprove-alldeny-all, approve-reads, approve-allDefault permission policy used when an agent definition does not override permissions.
provider_full_accessbooleanfalsetrue, falseRequest an unrestricted ACP mode for new approve-all sessions on supported providers; enable it only in the global config.

The mode also decides cross-workspace requests from agent sessions: approve-all crosses without asking, approve-reads asks at the native-tool boundary and denies elsewhere, and deny-all never crosses and never prompts. There is no separate workspace-access key. See Permissions.

[worktrees]

These values belong to the parent workspace. A workspace overlay changes later creates and Git discovery for that workspace; it does not move existing Worktrees or create a separate Worktree config scope.

FieldTypeDefaultValid valuesDescription
rootstring$COMPOZY_HOME/worktreesEmpty or absolute path.Central parent for new managed Worktrees, grouped by workspace and name.
run_branch_namespacestringrun/Lowercase slash-terminated namespace.Prefix for per-run branches.
copy_liststring arrayemptyNon-empty relative Git pathspecs.Matching ignored, untracked files copied without overwriting files.
setup_commandstringemptyShell command or empty.Bootstrap command run inside a new managed Worktree.
teardown_commandstringemptyShell command or empty.Cleanup command before removing a managed Worktree.
setup_timeoutduration10mPositive Go duration.Deadline for setup_command.
teardown_timeoutduration10mPositive Go duration.Deadline for teardown_command.
discovery_cache_ttlduration30sPositive Go duration.Freshness window for Git discovery and cached local status.

The section applies live. New creation and later discovery read the active generation; accepted creations and existing paths keep their recorded decisions. Adoption does not run the copy or setup steps. See Worktree configuration for the full bootstrap flow.

[tools]

Tool registry configuration is loaded before runtime policy and dispatch consume it. These keys only establish defaults and parse-time guardrails; dispatch still revalidates visibility, permission, availability, hooks, and approval state per call. When enabled = true and hosted MCP is enabled, a bare managed session receives the full availability-gated callable catalog unless effective policy narrows it. The startup prompt and hosted MCP metadata still teach the search → info → invoke loop through canonical discovery tools.

FieldTypeDefaultValid valuesDescription
enabledbooleantruetrue or false.When false, disables CompozyOS-owned registry dispatch and hosted MCP exposure.
hosted_mcp_enabledbooleantruetrue or false.Allows CompozyOS to expose the local hosted MCP proxy for session-visible tools.
default_max_result_bytesinteger2621440 through 16777216.Outer cap for tool results and Loop action results; a smaller descriptor wins.

The effective tool-result cap is the lower positive value of this setting and the tool descriptor. A descriptor can tighten the configured bound but cannot raise it. Loop action completion uses the same configured outer bound; 0 selects the 16 MiB platform ceiling rather than unlimited storage. Accepted action results above 16 KiB are stored by content reference and read through the task-run result paging surface. No separate Settings control or additional config key is required.

[tools.artifacts]

When a post-hook, redacted tool result exceeds its effective result cap, CompozyOS retains the canonical JSON envelope and returns a bounded preview plus an opaque artifact URI. Retention is global across the daemon, while every read remains bound to the workspace that produced the result. The daemon snapshots these settings at boot; config changes require a restart.

FieldTypeDefaultValid valuesDescription
max_countinteger200Positive integer.Maximum retained oversized tool-result artifacts across the daemon.
max_bytesinteger1073741824Positive integer.Maximum combined retained bytes across the daemon.
max_ageduration720hPositive Go duration.Maximum artifact age before deterministic retention cleanup removes it.

[tools.hosted_mcp]

FieldTypeDefaultValid valuesDescription
bind_nonce_ttl_secondsinteger301 through 300 seconds.Lifetime for the non-secret hosted MCP bind nonce before UDS peer binding, and the post-release window that admits one replacement proxy bind.

[tools.clarify]

The daemon snapshots this global setting at boot. A structured config write records desired state with lifecycle="restart-required"; it does not change a question already waiting for an answer.

FieldTypeDefaultValid valuesDescription
timeoutdurationomitted / 0s (unbounded)0s or 1s through 24hOmitted or 0s: the question waits until answered, canceled, or stopped with its context. A finite value keeps the existing deadline, fallback answer {choice:null,text:"",fallback:true}, and timed_out outcome.

[tools.policy]

FieldTypeDefaultValid valuesDescription
external_defaultstringdisableddisabled, ask, enabled.Default executable policy for extension, MCP, and dynamic tools before explicit tool/source grants.
approval_timeout_secondsinteger1201 through 600 seconds.Maximum wait for daemon-mediated approval flows.
trusted_sourcesstring array[]mcp:<server> or extension:<name>.External read-only sources trusted for approve-reads; MCP entries must reference configured MCP servers.

trusted_sources does not store credentials. Remote MCP OAuth tokens remain in the MCP auth store, and hosted MCP bind nonces are transient session launch state.

[mcp.oauth]

These global values identify CompozyOS to authorization servers that support a client metadata document. They are restart-required because an in-flight authorization is bound to its callback identity.

FieldTypeDefaultDescription
client_metadata_urlstring URLhttps://compozy.com/.well-known/mcp-client.jsonPublic client metadata document used for CIMD.
redirect_uristring URLhttp://127.0.0.1:2123/api/mcp/oauth/callbackOptional override for the callback URI used by new authorization flows.

The published CompozyOS document lists the default callback only. If you change the daemon port or use a self-hosted callback, publish a matching metadata document and configure both fields together. When the active callback is not listed, CompozyOS skips CIMD and may use the provider's one DCR fallback.

[[mcp_servers]]

Top-level MCP servers are merged with provider, agent, and skill MCP servers during session startup. They can point at local stdio processes or remote Streamable HTTP MCP endpoints.

FieldTypeDefaultValid valuesDescription
namestringrequiredNon-empty after trimming.Stable server key used for merge and replacement.
transportstringinferredstdio, http.Transport kind. Command servers default to stdio; URL servers default to http.
commandstringrequired for stdioNon-empty command.Executable command for a local stdio MCP server. Invalid for remote transports.
urlstring URLrequired for remoteAbsolute URL.Remote MCP endpoint for http. Invalid for stdio.
authobjectemptyOAuth 2.1 PKCE settings.Remote auth metadata and client settings. Invalid for stdio.
argsstring arrayemptyStrings.Process arguments passed to the command.
envstring mapemptyString keys and values.Literal environment values passed to the MCP server. CompozyOS does not perform shell expansion.
secret_envstring mapemptyenv:NAME or vault:mcp/** refs.Scope-qualified secret environment bindings for stdio servers. Values are resolved at launch and redacted from diagnostics.

OAuth-enabled remote servers store only metadata in config. Token material is encrypted in the CompozyOS Vault and is exposed through API, settings, CLI, logs, and the agent-callable compozy__mcp_status / compozy__mcp_auth_status tools only as redacted status. Authorization and logout are daemon-mediated operator flows available through CLI, HTTP, and UDS. The browser callback is HTTP-only and enabled only when the daemon binds to a loopback host.

auth fieldTypeRequiredDescription
registrationstringnoauto (default) or pre_registered.
issuer_urlstring URLconditionalRequired for pre_registered; must match discovery.
client_idstringconditionalRequired for pre_registered.
client_secret_refstringnoScope-qualified vault:mcp/**/oauth/client-secret ref for pre_registered.
scopesstring arraynoUnique requested OAuth scopes.

With registration = "auto", CompozyOS resolves protected-resource metadata, then CIMD, then makes one Dynamic Client Registration fallback attempt. A pre-registered server must include issuer_url and client_id. Authenticate with:

compozy mcp auth login remote-docs
compozy mcp auth login remote-docs --manual
compozy mcp auth status remote-docs
compozy mcp auth logout remote-docs

compozy mcp auth login remote-docs --scope workspace --workspace <workspace-id>

auth login starts the daemon-owned PKCE session and prints the live authorization URL. --manual accepts the full redirect URL, which is the supported path for remote operators and non-loopback HTTP binds. A command succeeds only after status confirms both authenticated and token_present.

Agents can read the same redacted status from inside a session through compozy__mcp_status or compozy__mcp_auth_status. These tools never trigger an OAuth browser flow; missing or expired authentication is repaired through the operator login/logout commands above. Auth-blocked MCP tools are omitted from callable discovery while status/probe output explains the repair path.

For workspace-scoped servers, five consecutive confirmed permanent discovery failures mark the MCP runtime as dead. Settings, status, doctor, and compozy__mcp_status expose the redacted diagnostic; last-known tool descriptors remain visible through compozy__tool_info with reason backend_dead during the same daemon lifetime instead of disappearing from the catalog. CompozyOS suppresses ordinary attempts while the mark is active. After 60 seconds, the next runtime MCP status or discovery access may run one recovery probe; a successful probe clears the mark without restarting the daemon. Doctor reads the durable mark without starting that probe. Recovery is automatic, so there is no manual clear or revive command.

[providers.<name>]

Provider keys override built-ins with the same name or create a custom provider. Built-ins are claude, codex, gemini, opencode, copilot, cursor, kimi-cli, qwen-code, pi, openrouter, zai, moonshot, vercel-ai-gateway, and xai. The retired built-ins hermes, openclaw, blackbox, cline, goose, junie, kiro, openhands, qoder, mistral, groq, and minimax keep resolving with a provider.retired_builtin warning until v0.5.0; declare them here with their own command to keep using them. See Retired built-in providers.

FieldTypeDefaultValid valuesDescription
commandstringBuilt-in command or empty for custom providers.Required after built-in plus override resolution.ACP launch command for this provider.
display_namestringBuilt-in label or empty.Any string.Operator-facing label shown in settings and provider pickers.
modelstableBuilt-in defaults or empty.Nested model config block (see below).Pre-session model defaults, curated metadata, and optional discovery wiring.
harnessstringacp unless a built-in sets pi_acp.acp, pi_acp.Launch strategy. pi_acp routes the provider through Pi's ACP adapter.
runtime_providerstringProvider key.Harness-specific provider id.Downstream provider id used by Pi and other harnesses.
transportstringempty.Harness-specific string.Optional Pi models override transport/API family.
base_urlstringempty.URL string.Optional Pi models override base URL for custom gateways.
auth_modestringbound_secret only when credential slots are configured; otherwise native_cli.native_cli, bound_secret, none.Declares whether auth belongs to the provider CLI, CompozyOS secret binding, or no auth.
none_securitystringlocal_transport.local_transport, external_identity, public_readonly.Safety rationale required for auth_mode = "none".
env_policystringfiltered.filtered, isolated.Controls which daemon environment variables the provider subprocess inherits.
home_policystringoperator.operator, isolated.Controls whether native CLI state comes from the operator home or a CompozyOS provider home.
auth_status_commandstringempty.Shell-style command string.Optional status probe run by compozy provider auth status <provider>.
auth_login_commandstringempty unless a built-in provider defines one.Shell-style command string.Write-only login command executed locally by compozy provider auth login <provider>. Reads return only its safe login descriptor.
session_mcpbooleantrue unless a provider disables it.true, false.Enables CompozyOS session MCP injection for providers that support it.
credential_slotsarrayempty.See below.Bound secret refs injected into provider subprocess environment variables at launch.
mcp_serversarray of MCP server objectsempty.Same shape as [[mcp_servers]].Provider-specific MCP servers merged after top-level config and before agent MCP servers.

auth_login_command accepts writes in config.toml, through compozy config set, or through the compozy__config_set native tool. It is never a readable configuration value: the set response and compozy config show, list, get, and diff redact it. Provider status, doctor, Settings, HTTP, and UDS likewise never expose the command, its arguments, or its resolved path.

Instead, they expose a safe login descriptor: configured, source = "auth_login_command", an executable basename, presence (present, missing, or unknown), and a recommended_action when recovery is needed (install_cli, login, bind_secret, retry, inspect, or no_retry).

The flat keys default_model, supported_models, and supports_reasoning_effort are no longer accepted. Config that still sets them is rejected with a deterministic hard-cut error citing the exact path. Move every value into [providers.<id>.models] below.

Provider aliases are small and explicit. Aliases such as claude-code, ai-gateway, vercel, kimi, glm, x.ai, grok, open-code, and qwen resolve to the canonical provider ID before config is applied. Model aliases and unknown-to-default fallback were removed: model values must be the exact IDs accepted by the active adapter. Runtime diagnostics, session metadata, and catalog rows preserve that canonical value.

The old providers.<id>.aliases config key was removed for v1. Reference providers by canonical name only; config files that set aliases = [...] under a provider are rejected during load.

auth_mode = "none" means No auth required and is only valid when the provider needs no CompozyOS or provider-owned credential. Set none_security to state why that is safe: local-only transport, external identity enforcement, or public read-only access. No-auth providers cannot define credential_slots, auth_status_command, or auth_login_command; those keys are rejected during config validation.

[providers.<id>.models]

Pre-session model defaults and curated metadata are owned by the daemon-owned model catalog. The catalog merges builtin defaults, the operator config, the optional models.dev enrichment source, live provider discovery sources, and extension model sources, and exposes the result through HTTP, UDS, CLI, the OpenAI-compatible projection, the Host API, and the web. Active ACP configOptions continue to govern model and reasoning controls inside a running session, so curated entries are metadata, never an allowlist.

FieldTypeDefaultValid valuesDescription
models.defaultstringBuilt-in default or empty.Any non-blank model id.Model used when an agent omits model; it need not appear in models.curated, but the active adapter must accept it.
models.curatedarrayomitted; no explicit set.See entry fields below.Curated model entries projected as config rows. Not an allowlist; manual model ids stay valid.
models.curated[].idstringrequired per entry.Unique provider model id.Provider model identifier sent to the runtime.
models.curated[].display_namestringempty.Any string.Optional human label.
models.curated[].context_windowintegerempty.Positive integer.Context window in tokens.
models.curated[].max_input_tokensintegerempty.Positive integer.Maximum input tokens.
models.curated[].max_output_tokensintegerempty.Positive integer.Maximum output tokens.
models.curated[].supports_toolsbooleanempty.true, false.Whether the model supports tool calls.
models.curated[].supports_reasoningbooleanempty.true, false.Whether the model supports reasoning effort.
models.curated[].reasoning_effortsarrayempty.Subset of none, minimal, low, medium, high, xhigh, max, ultra. Blanks rejected.Allowed reasoning levels for this model.
models.curated[].default_reasoning_effortstringempty.Member of reasoning_efforts when both are set.Per-model default reasoning level. Must appear in reasoning_efforts when both are set.
models.curated[].default_speedstringempty.normal, fast.Per-model speed used when an agent omits speed.
models.curated[].cost_input_per_millionnumberempty.Non-negative finite number.Input rate used for catalog-based usage estimates when the agent reports no actual cost.
models.curated[].cost_output_per_millionnumberempty.Non-negative finite number.Output rate used for catalog-based usage estimates when the agent reports no actual cost.
models.curated[].cost_cache_read_per_millionnumberempty.Non-negative finite number.Cache-read rate used only for the cache-read token bucket.
models.curated[].cost_cache_write_per_millionnumberempty.Non-negative finite number.Cache-write rate used only for the cache-write token bucket.
models.curated[].cost_reasoning_per_millionnumberempty.Non-negative finite number.Reasoning-token rate used only for the reasoning token bucket.
models.curated[].deprecatedbooleanomitted; inherit lower source.true, false.Marks a row deprecated; curated view excludes it. Explicit false clears lower-source metadata.
models.curated[].hiddenbooleanomitted; inherit lower source.true, false.Operator curation flag; curated view excludes it. Explicit false clears a lower-source flag.
models.curated[].featuredbooleanomitted; inherit lower source.true, false.Adds a current row to curated candidates. Explicit false clears a lower-source flag.
models.curated[].release_datestringempty.YYYY-MM or YYYY-MM-DD.Optional release metadata used for curated ordering; unknown dates stay empty.
models.reasoning.applystringnone unless a built-in opts in.acp_option, none.Applies effort through an advertised ACP option or exposes no selectable strategy.
models.discovery.enabledbooleanfalse unless a built-in opts in.true, false.Enables model discovery for this provider. Claude, Codex, and Cursor have built-in paths.
models.discovery.commandstringadapter default.Shell-style command string.Discovery command. Cursor defaults to cursor-agent models; ACP adapters use the provider command.
models.discovery.endpointstringadapter default.Absolute HTTP(S) URL.Discovery endpoint URL. Cursor accepts command discovery only.
models.discovery.timeoutstringmodel catalog source timeout.Positive duration (10s, 45s, 2m).Per-discovery timeout.

Rate changes apply to subsequent usage updates and do not reprice persisted token statistics. Agent-reported actual cost remains authoritative over catalog estimation. Estimation requires a finite, non-negative rate for every nonzero token bucket; missing cache-read, cache-write, or reasoning rates do not fall back to input or output rates.

Discovery adapters use the resolved provider auth, env, and home policy. Claude and Codex use a short-lived ACP inspection. Cursor runs cursor-agent models, groups its aliases into logical models, and stores the valid Reasoning, Fast, and Thinking combinations plus private launch bindings. If discovery is unavailable or fails, the catalog records source status and keeps the last successful rows as stale data. A blank Cursor model lets Cursor choose its native default. The built-in Cursor adapter uses the operator home; catalog reads schedule a non-blocking refresh after the five-minute live-source TTL, and the daemon also refreshes periodically. Writes under providers.<id>.models.discovery apply live. A changed discovery source refreshes immediately; provider model metadata changes do not rerun discovery.

Pi and custom providers need an explicit discovery path or an extension model.source when they should contribute live rows.

Top-level [model_catalog.sources.models_dev] controls the cross-provider models.dev enrichment source documented in the next section.

models.reasoning.apply = "none" means CompozyOS does not apply a reasoning level for the provider; it is different from explicit effort none. For a custom provider with provider-controlled reasoning:

[providers.custom.models.reasoning]
apply = "none"

Selectable effort lists come only from config/catalog/live truth. CompozyOS does not infer them from GPT or Claude name prefixes, and it never substitutes the provider default for an unknown model ID.

env_policy = "filtered" preserves ordinary operator context such as PATH, HOME, and locale while stripping secret-shaped daemon variables before launch. env_policy = "isolated" starts from a small operational allowlist and then adds only CompozyOS session metadata, provider-home variables, and explicit bound_secret slots. home_policy = "operator" lets native CLIs reuse existing login state. home_policy = "isolated" creates $COMPOZY_HOME/providers/<provider> with private permissions, sets PROVIDER_HOME, XDG directories, and known provider-specific home variables such as CLAUDE_CONFIG_DIR, CODEX_HOME, and OPENCODE_CONFIG_DIR.

Credential slots use this shape:

[providers.openrouter]
auth_mode = "bound_secret"

[[providers.openrouter.credential_slots]]
name = "api_key"
target_env = "OPENROUTER_API_KEY"
secret_ref = "vault:providers/openrouter/api-key"
kind = "api_key"
required = true

secret_ref supports env:NAME for operator-managed environment variables and vault:providers/<provider>/<slot> for CompozyOS-managed encrypted provider credentials written through the settings API, compozy vault, or web provider editor. The vault: form accepts lowercase provider/slot identifiers only, with letters, numbers, hyphens, underscores, or dots; arbitrary vault paths are not valid provider credential refs.

The slot required flag is enforced at session start for both env: and vault: refs. credential_slots are valid only when auth_mode = "bound_secret". Direct ACP built-ins and the direct pi provider use native_cli by default and do not preflight provider API keys. Pi-backed API-key wrappers such as OpenRouter, z.ai, Moonshot/Kimi, Vercel AI Gateway, and xAI default to CompozyOS-managed bound_secret slots while CompozyOS runs Pi under the hood.

Built-inHarnessRuntime providermodels.defaultAuth modeCredential target
claudeacpclaudeclaude-sonnet-5native_cliprovider login
codexacpcodexgpt-5.6-solnative_cliprovider login
geminiacpgeminigemini-3.1-pro-previewnative_cliprovider login
opencodeacpopencodenonenative_cliprovider login
copilotacpcopilotnonenative_cliprovider login
cursoracpcursornonenative_cliprovider login
kimi-cliacpkimi-clinonenative_cliprovider login
qwen-codeacpqwen-codeqwen3.6-plusnative_cliprovider login
pipi_acpanthropicclaude-opus-4-7native_cliPi /login
openrouterpi_acpopenrouteropenai/gpt-5.4bound_secretOPENROUTER_API_KEY
zaipi_acpzaiglm-4.6bound_secretZAI_API_KEY
moonshotpi_acpkimi-codingkimi-k2-thinkingbound_secretKIMI_API_KEY
vercel-ai-gatewaypi_acpvercel-ai-gatewayanthropic/claude-opus-4-7bound_secretAI_GATEWAY_API_KEY
xaipi_acpxaigrok-4-fast-non-reasoningbound_secretXAI_API_KEY

[model_catalog.sources.models_dev]

The models.dev enrichment source feeds catalog metadata such as token windows, tool support, and reasoning hints. It is cross-provider, daemon-owned, and never proves account-level availability.

FieldTypeDefaultValid valuesDescription
enabledbooleantruetrue, falseToggles the models.dev source. When false, the source still appears in status but performs no outbound calls.
endpointstringhttps://models.dev/api.jsonAbsolute HTTP(S) URL.Endpoint queried for the cross-provider model index. Must be HTTP(S) when set.
ttlduration24hPositive Go duration.Cache lifetime before catalog rows are flagged stale and a refresh is scheduled.
timeoutduration10sPositive Go duration.Per-call HTTP timeout for the source. The HTTP client always uses an explicit deadline (no shared http.DefaultClient).
[model_catalog.sources.models_dev]
enabled = true
endpoint = "https://models.dev/api.json"
ttl = "24h"
timeout = "10s"

models.dev rows write provider-scoped status: a single models.dev refresh records one (source_id="models_dev", provider_id) status row per CompozyOS provider mapped, never a global empty-provider sentinel. Disabled sources still expose status but skip outbound calls. Refresh lifetime is daemon-owned: requests trigger refreshes but the daemon detaches the work, applies an explicit deadline, and joins outstanding refresh workers during shutdown.

Live provider discovery is configured per provider through [providers.<id>.models.discovery]. Discovery refresh is serialized per provider_id before any subprocess or provider-home work, and concurrent refresh requests for the same provider coalesce behind the in-flight refresh. Cursor's discovery command defaults to cursor-agent models; its session launch command remains cursor-agent acp. A configured discovery command must emit the same model-list format.

[provider_usage]

Provider usage is experimental: these keys may change without the usual deprecation window. The section is user/machine config only. A workspace config.toml that sets it fails to load with provider_usage settings are global-only, and profile overlays reject it with profile_config_key_denied. Every path applies live. Operators can use Settings → Provider usage; agents and scripts can use compozy config set provider_usage.<key> <value>. See Provider usage for the feature.

FieldTypeDefaultValidationDescription
enabledbooleantruetrue or falseMaster switch. false removes the topbar meter, stops all usage collection, and returns empty usage reads.
refresh_intervalduration5m1m to 1hBackground refresh cadence per provider. Popover open and explicit refreshes are not bound by it.
providers.<id>.enabledbooleantrueKnown canonical provider IDPer-provider opt-out. A turned-off provider is not collected and leaves the usage views.
providers.claude.keychain_accessbooleanfalseOnly valid under claudemacOS consent to read Claude Code's Keychain login, only on an explicit local refresh. Ignored on other systems.
[provider_usage]
enabled = true
refresh_interval = "5m"

[provider_usage.providers.cursor]
enabled = false

[provider_usage.providers.claude]
keychain_access = true

Validation errors name the path:

$ compozy config set provider_usage.refresh_interval 10s
error: validate config write for "global-config": provider_usage.refresh_interval must be between 1m and 1h

A provider ID that is neither built in nor configured under [providers.<id>] is rejected with unknown canonical provider ID. In config.toml and Settings, keychain_access under any provider other than claude is rejected with is only valid for claude, and compozy config set accepts it only under claude. Agents' native config tools cannot write keychain_access at all; consent is an operator action. The settings response reports keychain_supported, which is true only when the daemon runs on macOS. Turning keychain_access off clears the Claude login the daemon loaded from the Keychain and its usage snapshot.

[marketplace.catalog]

CompozyOS reads the extension catalog from <base_url>/v3/extensions.json and presets from <base_url>/v3/marketplaces.json. HTTP(S) and file mirrors must provide the v3 family. Missing or invalid v3 documents report a source failure. The daemon never reads root feeds as a fallback and never rewrites the configured base URL. Failed refreshes retain the last valid projection and mark it stale.

This section is global-only because the projection and refresh service are global. Put it in $COMPOZY_HOME/config.toml; workspace overlays and workspace-scoped CLI/native writes are rejected.

KeyTypeDefaultValidationEffect
base_urlURLhttps://raw.githubusercontent.com/compozy/compozy/main/catalogAbsolute http, https, or local file URL.Base directory for the v3 feed family; applies live.
ttlduration1hPositive Go duration.Projection freshness for HTTP(S) and file catalogs; browse schedules stale refreshes.
timeoutduration10sPositive Go duration.Deadline for one feed request; applies live.
[marketplace.catalog]
base_url = "https://catalog.example.com/compozy"
ttl = "30m"
timeout = "5s"

Repository development uses the checkout catalog without changing production defaults. make dev and make dev-daemon set COMPOZY_MARKETPLACE_CATALOG_BASE_URL=file://<checkout>/catalog for their daemon process. This runtime-only value takes precedence over config.toml, appears in effective config reads, and is never persisted. Normal daemon launches keep the main catalog default.

These paths are live in the generated lifecycle matrix: the next feed fetch reads the applied values. Reconcile and inspect the active generation after a write:

compozy config set marketplace.catalog.ttl 30m -o json
compozy config reload -o json
compozy config apply-history --limit 5 -o json

Agent-side config mutation is narrower than the operator CLI. compozy__config_set accepts marketplace.catalog.ttl and marketplace.catalog.timeout with the same duration validation and live lifecycle. It rejects marketplace.catalog.base_url with config_trust_root_forbidden; changing the feed root remains an operator action through compozy config set. Both surfaces require global scope for every marketplace.catalog.* write.

[[marketplace.plugin_sources]]

Register plugin marketplaces in the global config.toml. Source identity is the normalized repository or folder reference; the display name does not identify installed packages.

[[marketplace.plugin_sources]]
name = "team-plugins"
source = "github:my-team/plugins"
enabled = true

[[marketplace.plugin_sources]]
name = "local-plugins"
source = "file:///absolute/path/to/plugins"
enabled = false
KeyTypeDefaultValidation
namestringRequiredUnique, 1–64 lowercase letters, digits, dots, underscores or hyphens. compozy, compozy-catalog, . and .. are reserved.
sourcestringRequiredgithub:owner/repo, git+https://host/repository, or an absolute local file URL. Credentials, query strings and fragments are rejected.
enabledbooleanPreset default, or true for a custom sourceExplicit false disables the source.

Distinct names may register the same acquisition reference. Equivalent GitHub spellings resolve to one origin, so those catalog items share installed state. Preset choices match source, so an upstream preset rename keeps the operator's enabled choice and uses the preset's current name. If several rows share that source, the current preset name selects its override; otherwise the first matching row does. Other rows remain custom registrations. The catalog order is the CompozyOS feed, presets in publication order, then custom sources in configuration order. A custom name that collides with a resolved preset is rejected.

An overlay that omits plugin_sources preserves the current rows; an explicit [marketplace] plugin_sources = [] clears the operator rows. Clearing an override restores the preset's default, so use enabled = false to disable a preset. Workspace and profile overlays cannot register global marketplaces. Plugin marketplace packages remain unverified; a digest identifies the listed bytes and does not establish publisher trust.

The daemon restores the last valid preset document from its local cache on startup. Feed refreshes read the current presets and apply operator choices by source reference. Catalog reads use stored projections while refreshes run; disabling a source excludes its entries and cancels its refresh.

[observability]

FieldTypeDefaultValid valuesDescription
enabledbooleantruetrue or falseEnables global event summary and health observation paths.
retention_daysinteger7Zero or positive.Retention window for global event summaries, token stats, and permission logs. 0 keeps history.
max_global_bytesinteger1073741824Positive integer.Size cap for global observability storage.

[observability.transcripts]

FieldTypeDefaultValid valuesDescription
enabledbooleantruetrue or falseEnables transcript capture from persisted session events.
segment_bytesinteger1048576Positive integer.Segment size for transcript retention.
max_bytes_per_sessioninteger268435456Positive integer.Per-session transcript byte cap.

[log]

The daemon writes structured logs to $COMPOZY_HOME/logs/compozy.log. Rotation is owned by the daemon's LogSink, so operators do not need an external logrotate rule for the default file.

Changing any [log] key is restart-required in v1. The daemon applies the new rotation writer on the next start.

At info, HTTP mutations, rejected requests, server failures, and background repairs remain visible. Successful HTTP reads, successful hook dispatches, and background sweeps that change nothing use debug. Enable debug when investigating those paths. Hook dispatch records carry the available session, workspace, turn, Task, run, and Loop run identifiers for correlation. Rotation retains whichever limit is reached first: a busy daemon can exhaust five backups long before the 30-day age limit. These file limits do not remove persisted session events.

FieldTypeDefaultValid valuesDescription
levelstringinfodebug, info, warn, errorStructured daemon log level.
max_size_mbinteger10Positive integer.Rotate compozy.log when the active file would exceed this size.
max_backupsinteger5Zero or positive integer.Maximum rotated backups to keep. 0 disables backup count retention.
max_age_daysinteger30Zero or positive integer.Delete backups older than this many days. 0 disables age retention.
compress_backupsbooleanfalsetrue or falseCompress rotated backups with gzip after rotation.

[redact]

FieldTypeDefaultValid valuesDescription
enabledbooleantruetrue or falseEnables additive heuristics for likely credentials in agent-visible content and runtime logs.

The daemon snapshots this value during boot, so changing redact.enabled is restart-required. When enabled, CompozyOS redacts matching content before appending it to the global event ledger or a session's events.db; SSE, history, and replay consume that stored redacted form. Structured correlation fields such as session and run IDs, hashes, digests, and fingerprints remain intact.

Disabling the heuristic does not disable exact protections for claim tokens, secret references, or secrets registered by the Vault and other runtime subsystems. Those protections remain authoritative. The heuristic is an additional defense for unregistered provider keys and similar credentials.

Manage the setting through General Settings, compozy config set redact.enabled <true|false> -o json, or the live compozy__config_set descriptor. These writes return lifecycle="restart-required" and next_action="restart-daemon"; they do not change the running process.

[skills]

If enabled = false, local skill discovery and polling are disabled.

FieldTypeDefaultValid valuesDescription
enabledbooleantruetrue or falseEnables skill catalog discovery and polling.
sourcesstring array["agents"]agents, claude.Folder conventions scanned besides CompozyOS's own. Applies live. An unknown name is rejected.
custom_sourcesstring arrayemptyAbsolute or ~/ paths; workspace-relative paths too.Extra directories scanned as skill roots. Applies live. Workspace-relative needs workspace scope.
disabled_skillsstring arrayemptySkill names.Replaces the disabled-skill list in overlays.
poll_intervalduration3sPositive Go duration.Skill metadata check interval; unchanged trees reuse discovery instead of being walked again.
allowed_marketplace_hooksstring arrayemptyMarketplace slug, registry:slug, or verified hash.Trust allowlist for hooks declared by marketplace skills.

sources and custom_sources pass through all four config layers — user, personal profile, workspace, workspace named profile. Each layer decides each key on its own: leave the key out and the layer inherits it, write a list and that list replaces the inherited one, write an empty list and the layer scans no configured folders for that key. CompozyOS's own skill folders are always scanned and never appear in sources. Both keys apply live: the daemon rescans and the next session picks up the change without a restart.

sources accepts only the names in the preset table CompozyOS ships — agents (.agents/skills and ~/.agents/skills) and claude (.claude/skills and ~/.claude/skills). A name outside that table is rejected with unknown_skill_source, which lists the valid names and the closest match. A custom_sources path that resolves to a directory another source already owns is rejected with duplicate_skill_source. A workspace-relative path written at user or profile scope is rejected with invalid_source_path.

Walkthrough with expected output: Use skills from your other tools.

Removed skill acquisition settings

skills.marketplace.registry and skills.marketplace.base_url are no longer active settings. On load, CompozyOS removes those keys and archives their values as inactive TOML comments in the same file. The archive and active configuration share one atomic write; repeated loads leave the migrated file unchanged. Unrelated comments, settings, installed skills and extension configuration are preserved. Settings and compozy config set reject new writes to the removed acquisition keys.

The installed-skill hook allowlist above governs existing hook declarations. Embedded MCP declarations stay blocked; the archived skills.allowed_marketplace_mcp setting cannot enable them or remote skill acquisition.

[extensions.trust]

Operator policy for installs that are not curated-and-tiered. Enabling it permits the install path; each install must still carry --allow-unverified (plus --yes for structured output). Curated installs always verify the catalog-pinned archive digest before extraction, and that check cannot be bypassed. Policy changes apply live.

FieldTypeDefaultValid valuesDescription
allow_unverifiedbooleantruetrue or falseAllows individually confirmed curated (unverified), github, git, and local_path installs.

A policy-blocked install reports extension_unverified_policy_blocked and points to /settings/extensions. Policy on without request consent reports extension_checksum_unverified. Verify the applied result through compozy config reload -o json and compozy config apply-history -o json.

[extensions.sources.github]

FieldTypeDefaultValid valuesDescription
enabledbooleantruetrue or falseEnables github: installs and the GitHub arm of source-union search.
base_urlstring URLhttps://api.github.comhttp or https URL with host. Required when enabled.GitHub API base URL. http is accepted but logs a warning.

The GitHub client reads GITHUB_TOKEN from the daemon environment for authenticated release access and higher rate limits. An anonymous rate limit degrades search to cached-or-omitted results with a sources_degraded marker instead of failing the query.

[extensions.sources.git]

FieldTypeDefaultValid valuesDescription
enabledbooleantruetrue or falseEnables git: installs through a shallow clone at the requested ref.

Git installs require Git 2.37 or newer on the daemon host. A missing binary reports extension_git_unavailable; an older version reports extension_git_version_unsupported. Git source URLs must use HTTPS and resolve only to public addresses. Git sources do not support search.

[extensions.dev]

FieldTypeDefaultValid valuesDescription
watch_intervalduration2sPositive Go duration.Poll cadence for compozy extension dev --watch. The watcher is CLI-side; the daemon never watches author directories.

[extensions.resources]

Operator policy for resources an extension publishes through resources/snapshot. Every field is optional; empty means "do not narrow". The effective grant is the narrowest of the manifest's [resources.publish] request, the extension source tier, this policy, and the runtime session scope.

FieldTypeDefaultValid valuesDescription
allowed_kindsstring arrayemptyRegistered resource kinds, such as window_layout.Restricts which resource kinds any extension may publish.
max_scopestringemptyglobal or workspace.Caps the widest publication scope.

[extensions.resources.snapshot_rate_limit] and [extensions.resources.operator_write_rate_limit]

Both buckets share one shape and are disabled when all three fields are zero. Setting any field requires positive requests and window, and a non-negative queue.

FieldTypeDefaultValid valuesDescription
requestsinteger0Positive integer.Requests allowed per window.
windowduration0sPositive Go duration.Rolling window.
queueinteger0Zero or positive.Requests allowed to wait when the bucket is empty.

[automation]

FieldTypeDefaultValid valuesDescription
enabledbooleantruetrue or falseEnables config-defined automation jobs and triggers.
timezonestringHost zone for new configs; UTC fallbackIANA timezone loadable by Go, such as UTC or America/Sao_Paulo.Schedule timezone for automation.
max_concurrent_jobsinteger5Positive integer.Global automation concurrency cap.
default_fire_limitobject{ max = 12, window = "1h" }max positive, window positive Go duration.Default rolling fire limit copied into jobs and triggers that omit their own limit.

New user configs persist the detected IANA time zone of the machine running CompozyOS. If detection fails, they persist UTC. Existing configs without automation.timezone continue using UTC; upgrading or running the installer again does not shift existing schedules.

Choose Settings → Automation → Schedule time zone, or run compozy config set automation.timezone America/Sao_Paulo with your local IANA zone. The Settings shortcut uses the machine running CompozyOS, including when you connect remotely; it does not use the browser's zone. The shortcut is absent when host detection is unavailable. Detection checks TZ, the system zone name, the /etc/localtime zoneinfo link, and /etc/timezone; unrecognized names fall back to UTC for new configs.

Write cron in that zone's wall-clock time. Do not manually convert local schedules to a fixed UTC offset: daylight saving time can change that offset. Changing the shared zone affects cron schedules; review existing expressions before saving. Restart CompozyOS when prompted to apply the change. Existing cron cursors then recalculate in the new zone; intervals and absolute timestamps keep their timing. A fire already deferred for capacity is honored once, followed by the next cron time in the new zone. Upgrading alone preserves legacy cursors and catch-up work.

[automation.suggestions]

FieldTypeDefaultValid valuesDescription
pending_capinteger5Positive integer.Maximum unresolved suggestions stored for one workspace.

Changes under automation.suggestions.* require a daemon restart. Agents can write the desired value with compozy config set automation.suggestions.pending_cap <value> and inspect the resulting restart action through structured config apply history.

[[automation.jobs]]

Jobs are appended by overlays; they do not replace earlier jobs with the same name.

FieldTypeDefaultValid valuesDescription
scopestringrequiredglobal or workspaceBinding scope.
namestringrequiredNon-empty.Job name.
agentstringrequired for an Agent target without task; empty for a Loop targetAfter trimming, ^[a-z][a-z0-9_-]{0,105}$ (maximum 106 characters).Agent that handles the scheduled prompt.
workspacestringrequired when scope = "workspace", empty when scope = "global"Workspace ID or root accepted by the automation resolver.Workspace binding.
promptstringrequired unless task is configuredNon-empty.Prompt submitted when the job fires.
schedule.modestringrequiredcron, every, atSchedule mode.
schedule.exprstringemptyRequired only for cron; standard five-field cron.Cron expression.
schedule.intervalstringemptyRequired only for every; positive Go duration.Repeating interval.
schedule.timestringemptyRequired only for at; RFC3339 timestamp.One-shot fire time.
task.titlestringemptyAny string.Task title when the job materializes a task instead of starting an agent session.
task.descriptionstringemptyAny string.Task description.
task.ownerobjectemptyTask ownership object.Owner metadata validated by the task package.
enabledbooleantruetrue or falseEnables this job.
retry.strategystringnonenone or backoffRetry behavior. Must be none when task is configured.
retry.max_retriesinteger00 for none; positive for backoff.Maximum retry count.
retry.base_delaystringemptyEmpty for none; positive Go duration for backoff.Base backoff delay.
fire_limitobject[automation].default_fire_limitmax positive, window positive Go duration.Per-job fire limit.

[[automation.triggers]]

Triggers are appended by overlays; they do not replace earlier triggers with the same name.

FieldTypeDefaultValid valuesDescription
scopestringrequiredglobal or workspaceBinding scope.
namestringrequiredNon-empty.Trigger name.
agentstringrequired for an Agent target; empty for a Loop targetAfter trimming, ^[a-z][a-z0-9_-]{0,105}$ (maximum 106 characters).Agent that handles the trigger prompt.
workspacestringrequired when scope = "workspace", empty when scope = "global"Workspace ID or root accepted by the automation resolver.Workspace binding.
promptstringrequiredValid Go template over the activation envelope.Prompt submitted when the trigger fires.
eventstringrequiredsession.created, session.stopped, hook.<hook_name>.completed, webhook, or ext.*.Producer-backed event name; surrounding whitespace and unknown names are rejected.
filterstring mapemptyKeys kind, scope, workspace_id, source, or data.<field>; values non-empty.Activation-envelope filter.
enabledbooleantruetrue or falseEnables this trigger.
retry.strategystringnonenone or backoffRetry behavior.
retry.max_retriesinteger00 for none; positive for backoff.Maximum retry count.
retry.base_delaystringemptyEmpty for none; positive Go duration for backoff.Base backoff delay.
fire_limitobject[automation].default_fire_limitmax positive, window positive Go duration.Per-trigger fire limit.
endpoint_slugstringemptyRequired when event = "webhook"; empty otherwise.Webhook endpoint slug.
webhook_secret_refstringemptyRequired when event = "webhook"; must be env:NAME or vault:automation/...; empty otherwise.Secret ref used to validate webhook requests.

See Vault for the CLI, HTTP, UDS, web, and session-scoped surfaces that store and inspect encrypted CompozyOS-managed secret refs.

[loops]

reconcile_interval defaults to "1m" and must be a positive Go duration. It controls the daemon-owned sweep that settles task and task-run records whose Loop is already terminal or missing. The startup sweep always runs before task recovery, regardless of the configured cadence. Changes are restart-required and can be made through compozy config set loops.reconcile_interval <duration> or the matching native config tools.

[loops.defaults.*]

[loops.defaults.delivery] and [loops.defaults.watch] are global or workspace seed defaults for new loop runs. They are a config plane, not the per-loop loop_config store: effective loop config is resolved as definition defaults, then [loops.defaults.*], then loop_config, then per-run overrides. Existing loop runs keep the config values materialized at start.

Compile-time ceilings stay in code for no-progress and gate revisions. fan_out_width has no fixed daemon cap; it controls the active lane window while each node's positive max_fan_out bounds logical width.

FieldDeliveryWatchValid valuesDescription
iteration_cap500Zero or positive integer.Maximum iterations. 0 means unbounded.
no_progress.window320 through 30.Generation-hash and blocker-signature window.
gates.max_revisions1000 through 64.Gate revision seed; 0 supplies no seed.
budget.tokens00Zero or positive integer.Token budget; 0 means unlimited.
budget.wall_clock_sec00Zero or positive integer.Active-work wall-clock budget; parked time is suspended.
budget.on_exceededhalthalthalt or escalate.End exhausted or park for approval when a set budget is exceeded.
fan_out_width42Zero or positive integer.Active fan-out window seed; logical width is bounded by each node's max_fan_out.
retry.max_attempts330 through 10.Total attempts for mechanical families; 0 disables automatic retry.
retry.backoff_base1s1sPositive Go duration.Initial retry delay; must not exceed backoff_max.
retry.backoff_max30s30sPositive Go duration.Maximum retry delay.
liveness.silence_window30m30mZero or positive Go duration.Raises silence attention; 0 disables evaluation.
resume.death_streak_limit33Positive integer.Confirmed-death resumptions allowed without post-resume evidence.
predicates.cost_limit1000010000Positive integer.CEL runtime cost limit; evaluation warns at 80%.
waits.admission_attempts33Positive integer.Bounded attempts to admit a wait resume.
waits.admission_retry_interval60s60sPositive Go duration.Delay between admission attempts.
requests.expire_afteremptyemptyEmpty or positive Go duration.Seeds an expiry only when an authored request omits expires; authored expiry wins.
admission.tombstone_horizon168h168hPositive Go duration.Minimum watch-admission deduplication retention.
runtime_defaults.{worker,judge}.{provider,model,reasoning}emptyemptyValid provider/model/reasoning fields.Worker and judge runtime defaults.
runtime_rules[]nonenoneExactly id, type, complexity, or type + complexity.Field-merged worker override; conjunction is AND, specificity is id > type + complexity > type > complexity, and later equal-specificity rules win per non-empty field.

Only a definition containing watch-source selects the watch defaults. A definition with watch-events but no watch-source remains on delivery defaults, including iteration_cap = 50, unless its definition or run override sets another value.

[[loops.defaults.delivery.autopause]] and [[loops.defaults.watch.autopause]] append ordered rules. Each rule has a CEL match over failure facts and action = "pause"; the first match wins. These arrays are operator-owned and are not available through scalar agent config mutation.

These paths are mutable through compozy config set and the native config tools. Writes return lifecycle="restart-required" because the daemon does not rewrite active runs after defaults change. New starts read the resolved defaults after the daemon reloads its config state; live runs keep the effective config materialized at start.

For workers, runtime fields resolve from per-run rules, imported task frontmatter, configured rules, node params.runtime, runtime defaults, then the agent definition. Judges use judge defaults plus criterion runtime; task rules never apply. model_defaults, scalar params.model, and scalar criterion model are rejected instead of translated. Static loop validate checks authored definition values; dry-run and submission check the effective workspace/run values before any ACP process starts.

[loops.breaker]

The target breaker is one reload-scoped policy because health is shared per workspace, target family, and target rather than per Loop kind. Only transport failures count toward opening it; semantic or handled failures record target success.

FieldDefaultValid valuesDescription
threshold5Positive integer.Consecutive transport failures before the target opens.
probe_interval60sPositive Go duration.Delay before a half-open probe may test the target again.

Both paths are available through compozy config get|set|unset. Breaker changes apply when the daemon reloads config and are not pinned per run; the per-kind lifecycle values above remain pinned when work is admitted.

[loops.inputs.<loop-name>]

Each table supplies global or workspace defaults for inputs declared by one dynamically enrolled Loop. Values resolve independently as per-run input, workspace default, global default, then definition default. Presence is tracked before config overlays merge, so false, 0, and valid empty strings do not collapse into “unset.” Dry-run exposes every effective value and its run|workspace|global|definition origin.

Config loading accepts dynamic Loop names and scalar values without consulting the current catalog. Static loop validate validates only defaults authored in the definition. Configured defaults and per-run values are validated together during effective dry-run or submission; unknown keys and type mismatches return typed input_default {loop,key,reason} diagnostics before a run or ACP process is created.

Manage one value through the validated config writer:

compozy config set loops.inputs.implement-tasks.auto_commit false \
  --scope workspace --workspace /absolute/project/root
compozy config get loops.inputs.implement-tasks.auto_commit \
  --workspace /absolute/project/root
compozy config unset loops.inputs.implement-tasks.auto_commit \
  --scope workspace --workspace /absolute/project/root

For implement-tasks, mode accepts per-task or orchestrated. Its optional orchestrator_runtime, backend_runtime, frontend_runtime, and default_runtime inputs use the same partial {provider, model, reasoning, speed} object as a Loop runtime input. A task file's own frontmatter runtime still overrides the category input field by field.

HTTP and UDS provide exact-scope inspect, replace, set, and delete operations at /api/workspaces/{workspace_id}/loops/{name}/input-defaults. These paths are also available through native config tools. Changes are restart-required; existing runs retain their materialized inputs.

[goals]

[goals] defines global or workspace defaults for newly started Goals. Its global configuration also controls the daemon-wide durable Goal session-event relay. All four paths are mutable through compozy config set and the native config tools. Writes return lifecycle="restart-required"; restarting activates Goal defaults for later starts and applies global relay settings without rewriting active Runs or their checkpoints. Workspace overlays affect Goal defaults only because one relay serves every workspace in the daemon.

FieldTypeDefaultValid valuesDescription
max_turnsinteger20Positive integer.Turn limit written into a new synthetic Goal definition.
context_nudge_rationumber0.80.0 through 1.0Context threshold pinned to the Run and copied into every Goal checkpoint.
outbox_batch_sizeinteger501 through 200Global maximum pending projections and cleanup obligations claimed per pass.
outbox_poll_intervalduration2sPositive Go duration.Global fallback sweep; committed enqueues wake the relay immediately.

context_nudge_ratio = 0.0 is explicit and disables context nudging. A config change cannot alter the ratio of an existing Run, including after daemon restart or a later generation.

When the Goal's last known context use reaches the ratio of the context size, the Goal compacts through the agent's own compaction command, or reseeds when the agent advertises none. While the context is unknown, for example after a compaction until the agent reports both usage and a positive size again, no nudge fires. See Goal compaction.

Goal defaults follow the normal config precedence: built-in defaults, then global $COMPOZY_HOME/config.toml, then the resolved workspace's .compozy/config.toml. The Run start resolves that effective config once and persists the selected values. Later config generations apply only to later starts.

The CLI and native-tool mutation allowlists contain exactly these Goal paths:

  • goals.max_turns — integer
  • goals.context_nudge_ratio — number
  • goals.outbox_batch_size — integer
  • goals.outbox_poll_interval — duration
# Global desired state
compozy config set goals.max_turns 30 -o json

# Workspace desired state
compozy config set goals.context_nudge_ratio 0.75 \
  --scope workspace \
  --workspace /absolute/path/to/workspace \
  -o json

The equivalent agent path is compozy__config_set after resolving its live descriptor. Both surfaces validate the same allowlist and bounds, write desired state at global or workspace scope, and return lifecycle="restart-required" with next_action="restart-daemon". They do not mutate a live Goal.

[autonomy]

Top-level [autonomy] holds task-kernel autonomy defaults that are not scoped to the coordinator or scheduler subtables. The default below is loaded from internal/config/autonomy.go.

FieldTypeDefaultValid valuesDescription
block_recurrence_limitinteger20 or a positive intSame-kind re-block count that escalates a task to needs_attention. 0 disables the unblock-loop breaker.

[task.orchestration]

[task.orchestration] controls bounded task orchestration behavior: active workspace-run capacity, action deadlines, run summaries, task context bundles, scheduler health telemetry, the spawn-failure circuit breaker, and the per-task max-runtime default. The defaults shown below are loaded from internal/config/task_orchestration.go and apply when neither the global file nor a workspace overlay sets a value.

FieldTypeDefaultValid valuesDescription
summary_max_bytesinteger4096Positive integer.Maximum byte size for stored task-run summary fields used by context, web, CLI, and review surfaces.
context_body_max_bytesinteger8192Positive integer.Maximum byte size for the rendered task context bundle attached to /agent/context.
context_prior_attemptsinteger5Zero or positive integer.Maximum prior-attempt summaries included in the task context bundle.
context_recent_eventsinteger50Zero or positive integer.Maximum recent task events included in the task context bundle.
spawn_failure_limitinteger5Positive integer.Spawn-failure threshold before the per-task spawn circuit breaker opens.
scheduler_bad_tick_thresholdinteger6Positive integer.Bad-tick count before the scheduler health watchdog flags a degraded scheduler tick.
scheduler_bad_tick_cooldownduration5mPositive Go duration with whole-second precision.Cooldown between scheduler bad-tick health emissions.
default_max_runtimeduration0sZero or positive Go duration up to 24h with whole-second precision.Default per-task max-runtime watchdog. Zero disables the default; tasks may still set a per-task override.
designated_run_maxinteger5Positive integer up to 5.Maximum sibling runs a single designated fan-out request may enqueue.
max_active_runs_per_workspaceinteger16Zero or positive integer.Maximum claimed, starting, or running worker and coordinator runs with live leases per workspace. Zero disables the limit; global runs are exempt.
action_run_timeoutduration30mPositive Go duration up to 24h with whole-second precision.Wall-clock deadline inherited by action nodes without an explicit timeout. An explicit node timeout remains authoritative.

[task.orchestration] keys are agent-mutable through the existing config tool surface (compozy config set and the compozy__config_* native tools). Workspace overlays apply through the existing config loader without any special handling.

Task orchestration writes are restart-required because the task runtime snapshots these bounds at daemon boot. Structured config mutation responses report applied=false, lifecycle="restart-required", and next_action="restart-daemon" until the daemon restarts.

Config and hook mutation tools return lifecycle truth in their structured payloads. Writes under hooks.* and extensions.* are persisted as desired state with applied=false, lifecycle="restart-required", and next_action="restart-daemon"; agents should not treat those writes as active runtime truth until the daemon restarts.

[task.orchestration.profile]

Defaults and gates that apply when a task does not store its own execution profile, plus the gates that allow or reject task-level provider overrides. See Task Execution Profiles for the runtime behavior.

FieldTypeDefaultValid valuesDescription
default_coordinator_modestringinheritinherit, guidedCoordinator behavior when a task profile does not set coordinator.mode.
default_worker_modestringinheritinheritWorker selection mode when a task profile does not set worker.mode. select is allowed only on the per-task profile.
default_worktree_modestringinheritinherit, none, per_runWorktree policy resolved when a task profile keeps worktree.mode = "inherit"; bare inherit resolves to workspace root.
allow_task_provider_overridebooleantruetrue or falseWhen false, task profiles may not set provider/model on coordinator, worker, or review blocks.

Profile validation runs in task.Service when a profile is created or updated; the normalized profile is then stored. Session start loads the persisted profile (or the default inherit profile when none exists) without re-running validation. Updates and deletes are rejected while the task has an active run (tasks.current_run_id is set); resolve or release the run before retrying.

Per-task profiles store runtime.mode. evidence adds guidance for runtime validation while keeping the configured session permission policy.

[task.orchestration.review]

Defaults and bounds for the post-terminal review gate. These values shape the task.review_policy defaults and the bounded reviewer evidence enforced by task.Service.RecordRunReview.

FieldTypeDefaultValid valuesDescription
default_policystringnonenone, on_success, on_failure, alwaysDefault review trigger policy when a task does not set its own.
max_roundsinteger3Positive integer.Maximum review rounds before the failure policy applies.
max_review_attemptsinteger2Positive integer.Maximum reviewer attempts within a single review round before the round counts as exhausted.
timeoutduration20mPositive Go duration with whole-second precision.Maximum reviewer think time before the review counts as a timeout.
rapid_terminal_windowduration2mPositive Go duration with whole-second precision.Window used by the review circuit breaker to detect rapid shallow attempts.
rapid_terminal_limitinteger3Positive integer.Maximum terminal events within rapid_terminal_window before the review circuit opens.
missing_work_max_itemsinteger20Positive integer.Maximum missing_work items recorded per rejected verdict.
missing_work_item_max_bytesinteger512Positive integer.Maximum byte size per missing_work item.
reason_max_bytesinteger2048Positive integer.Maximum byte size for the reviewer reason field.
review_text_max_bytesinteger12000Positive integer.Maximum byte size for reviewer free-form review text.
next_round_guidance_max_bytesinteger4096Positive integer.Maximum byte size for the typed next-round guidance attached to continuation runs.
failure_policystringblock_taskblock_task, fail_taskWhat happens when review attempts/rounds are exhausted: block the task awaiting operator action, or terminally fail it.

Workspace overlays may tighten or relax these values per workspace through <workspace>/.compozy/config.toml. Unknown keys still fail validation.

[task.recovery]

[task.recovery] controls who may call the task-run recovery verbs documented in Task Runs and Leases. The setting does not change normal token-fenced claim, heartbeat, complete, fail, or release tools.

FieldTypeDefaultValid valuesDescription
allow_agent_forcebooleantruetrue or falseWhen false, agent identities cannot force release, force fail, retry, or bulk-recover runs.

Set allow_agent_force = false when recovery should require a non-agent operator identity. The CLI/API/UDS surfaces then return a deterministic authorization diagnostic for agent callers and leave the run unchanged.

[[hooks.declarations]]

Config hooks merge by name; a later overlay replaces the whole hook declaration with the same name.

FieldTypeDefaultValid valuesDescription
namestringrequiredNon-empty.Hook declaration name.
eventstringrequiredCurrent dot-form hook event.Event that triggers the hook.
modestringasyncsync or async; sync only for sync-eligible events.Dispatch mode.
requiredbooleanfalsetrue or false; required hooks must be sync.Whether a hook failure blocks the source operation.
priorityintegerConfig source default 500 when omitted.Integer.Ordering priority.
timeoutduration0s; subprocess executor uses 5 seconds when zero.Non-negative Go duration.Hook timeout.
matcherobjectemptySupported matcher fields listed below.Narrows hook eligibility.
commandstringemptyRequired for subprocess hooks unless nested executor fields are used.Subprocess command.
argsstring arrayemptyStrings.Subprocess arguments.
envstring mapemptyString keys and values.Explicit hook environment values.
executor.kindstringsubprocess when a command is present.subprocess or wasm for user config. native is internal only.Executor kind.
executor.commandstringemptySame as command.Nested command form. Cannot be mixed with top-level command/args/env.
executor.argsstring arrayemptyStrings.Nested args form.
executor.envstring mapemptyString keys and values.Nested env form.

Supported matcher fields are agent_name, agent_type, workspace_id, workspace_root, session_type, input_class, acp_event_type, turn_id, tool_id, tool_name, tool_read_only, decision_class, message_role, message_delta_type, and compaction_trigger. Each event family accepts only some of them. compaction_trigger is requested or agent and narrows context.pre_compact and context.post_compact, which observe real agent compactions; compaction_reason and compaction_strategy are retired and archived on load. See Hook declarations.

Current hook events are:

session.pre_create
session.post_create
session.pre_resume
session.post_resume
session.pre_stop
session.post_stop
input.pre_submit
prompt.post_assemble
event.pre_record
event.post_record
automation.job.pre_fire
automation.job.post_fire
automation.trigger.pre_fire
automation.trigger.post_fire
automation.run.completed
automation.run.failed
agent.pre_start
agent.spawned
agent.crashed
agent.stopped
turn.start
turn.end
message.start
message.delta
message.end
tool.pre_call
tool.post_call
tool.post_error
permission.request
permission.resolved
permission.denied
context.pre_compact
context.post_compact

[gateway]

Gateway config sets a local safety ceiling and bounded runtime controls. Exposure intent is stored in the Global DB; no config key can enable a provider, tier, or surface. Setting enabled = false disables all gateway tiers during live reload.

FieldTypeDefaultValidationLifecycle
enabledbooleanfalsetrue or falseLive
private_portinteger00 or 1 through 65535Restart required
public_portinteger00 or 1 through 65535Restart required

Port 0 asks the operating system to assign an available loopback port. Gateway tier listeners do not bind non-loopback addresses.

FieldTypeDefaultValidationLifecycle
pairing.ttlduration5mPositiveLive
pairing.max_pendinginteger8PositiveLive
stream_ticket.ttlduration30sPositiveLive
auth.rate_limit.windowduration60sPositiveLive
auth.rate_limit.max_failsinteger10PositiveLive
verify.timeoutduration10sPositiveLive
verify.public_dns_resolverstring1.1.1.1:853Public IP and non-zero portLive

Public endpoint verification uses authenticated DNS-over-TLS through verify.public_dns_resolver instead of the host resolver. This keeps a Tailscale MagicDNS answer from satisfying a public Funnel proof through the private tailnet. Change it when the default resolver is unavailable from the daemon host.

verify.timeout bounds one endpoint probe. Provider activation runs under its own fixed one-minute bound, and a failed probe keeps the listener staged while the daemon retries with backoff — raising verify.timeout is rarely the fix for a slow first activation.

gateway.public_ui.enabled does not exist. Public operator UI reachability is an explicit, consented database transition, not a config value.

Client connection profiles

active_connection and [[gateway.connections]] describe how this machine reaches a remote daemon. They are written by compozy connect on the client side and are not read by a daemon that is serving a gateway.

FieldTypeDefaultValidationLifecycle
active_connectionstringemptyName of a declared connectionLive
connections[].namestring—Unique, non-emptyLive
connections[].schemestring—https or sshLive
connections[].hoststring—Non-empty hostLive
connections[].portinteger—1 through 65535Live
connections[].credential_filestringempty<name>.cred for an https connectionLive
connections[].default_workspacestringemptyWorkspace idLive
connections[].remote_homestringemptyRemote COMPOZY_HOME; ssh connections onlyLive

A connection entry holds no secret: the device credential lives in the referenced file with owner-only permissions. Copying these entries to another machine does not transfer identity — use compozy connect export and compozy connect import.

See the Gateway section for setup, tutorials, and operations, and Gateway security for what each tier exposes.

  • mcp.json documents JSON sidecars and whole-object replacement.
  • AGENT.md documents agent-local mcp_servers and hooks.
  • Providers covers [providers.<id>.models] shape and models.discovery defaults.
  • Provider Model Catalog explains the catalog projection, OpenAI-compatible endpoint, and extension model.source.
  • File Locations lists the exact global and workspace paths.

Follow-up behavior and steering capability

session.busy_input.default_mode defaults to steer; choose queue to defer busy follow-ups. Changes through General Settings apply to subsequent admissions without a daemon restart. The historical interrupt default remains accepted with a warning through v0.4 and is scheduled for removal in v0.5.0. Interrupt remains an explicit per-send action.

Agent message budget and reply watches

session.messaging.rate_limit_messages (default 30, 1–10000, or -1 to disable the budget) and session.messaging.rate_limit_window (default 10m, 1s–24h) bound how many messages one agent session may send to the same target session. The count comes from durable admissions, so retries of an admitted message are not counted twice. A send past the budget is refused with message_rate_limited (HTTP 429), and the error details carry limit, window, and retry_after_seconds. The refusal affects only that send; neither session is stopped or canceled. session.messaging.reply_watch_ttl (default 24h; 0s never expires a watch; otherwise 1m–30d): it releases a notify_on_complete reply watch that never fired, waking the sender once with the expired outcome so the watch stops keeping the sender awake. session.messaging.queue_promote_after (default 2m, 0s or 10s–24h) delivers a queue message from another session into a long-running target: once the target has been in the same turn this long and its provider supports injected steering, the oldest waiting agent message is injected into the live turn with its original message id, so its reply watch still follows it. Promotion only injects: it never interrupts or cancels the active turn, a refused injection waits for the turn to end, and it never runs while operator input is waiting. 0s disables it. These values apply when the daemon starts.

providers.<name>.steer_capability accepts steer_ext, concurrent_prompt, or none. An advertised steering extension takes precedence over this override. Otherwise the daemon uses the override, then a binary-verified provider table entry, then none. Unverified providers have no assumed injection support. This setting applies when a provider process initializes.

[providers.custom]
command = "my-acp-agent"
steer_capability = "none"

See Session control plane for delivery outcomes and strict fences.

[user_scope]

[user_scope]
work_dir = "~"

The User scope resolves its execution directory from this setting. A personal profile overlay can replace it. After ~ expansion the value must name an existing absolute directory; invalid values fail resolution with user_scope_work_dir_invalid. Validation happens when work starts, so a missing directory does not prevent existing work from being listed. Changes apply to later starts.

The execution directory does not contribute workspace or workspace-profile configuration. User resolution has no workspace registration. Neither the operator home nor COMPOZY_HOME can be registered as a project (workspace_home_forbidden); new project names cannot be user, global, or start with @ (workspace_name_reserved).

@user is the explicit User reference; the CLI forwards it without a workspace lookup. A route that requires a project returns user_scope_unsupported, or user_scope_worktree_unsupported for worktrees. See User scope for the walkthrough.

On this page

Quick ReferenceLoad And Merge OrderArchived retired settingsComplete Annotated Example[daemon][daemon.load][http][app][attention][terminal][shell.sessions]scope deprecation[shell.appearance][cmd_palette][window_manager][window_manager.gaps][window_manager.snap][window_manager.bindings] and shortcuts[window_manager.global_shortcuts][defaults][roles.<role>]Session-backed role fields[roles.coordinator][limits][subagents][session.restart][session.limits][session.attachments]Manage attachment settings[session.supervision][session.derive][subagents][agents.soul][agents.heartbeat][permissions][worktrees][tools][tools.artifacts][tools.hosted_mcp][tools.clarify][tools.policy][mcp.oauth][[mcp_servers]][providers.<name>][providers.<id>.models][model_catalog.sources.models_dev][provider_usage][marketplace.catalog][[marketplace.plugin_sources]][observability][observability.transcripts][log][redact][skills]Removed skill acquisition settings[extensions.trust][extensions.sources.github][extensions.sources.git][extensions.dev][extensions.resources][extensions.resources.snapshot_rate_limit] and [extensions.resources.operator_write_rate_limit][automation][automation.suggestions][[automation.jobs]][[automation.triggers]][loops][loops.defaults.*][loops.breaker][loops.inputs.<loop-name>][goals][autonomy][task.orchestration][task.orchestration.profile][task.orchestration.review][task.recovery][[hooks.declarations]][gateway]Client connection profilesRelated PagesFollow-up behavior and steering capabilityAgent message budget and reply watches[user_scope]