config.toml
Complete CompozyOS config.toml schema, defaults, valid values, merge behavior, and annotated examples.
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.
| File | Scope | Loaded when |
|---|---|---|
$COMPOZY_HOME/config.toml | Global | Every config load. Defaults to ~/.compozy/config.toml. |
$COMPOZY_HOME/mcp.json | Global MCP sidecar | After global TOML. |
<workspace>/.compozy/config.toml | Workspace overlay | Only when a workspace root is resolved for the session or command. |
<workspace>/.compozy/mcp.json | Workspace MCP sidecar | After workspace TOML. |
Unknown TOML keys are errors. Sandbox profiles are implemented under [sandboxes.<name>]. Use only
[sandboxes.<name>] for session execution boundaries.
Quick Reference
| Section | Purpose | Default |
|---|---|---|
[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, MCP, and bridge subsystem applies. | providers 5s, MCP 10s, bridges 30s |
[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 |
[shell.sessions] | Live operator preferences for the session list. | last-activity order, recent sessions |
[window_manager] | Live virtual-desktop, focus, snap, layout, transition, and shortcut defaults. | floating windows, adaptive stacks, 50-entry history, slide transition |
[defaults] | Default agent, provider, and sandbox resolution. | agent = "general", provider = "", sandbox = "" |
[roles.coordinator] | Coordinator routing plus coordinator-owned session limits. | disabled, builtin coordinator, TTL 2 hours, 5 children, 5 active sessions/workspace |
[roles.dream] | Dream-session routing. | enabled, builtin dreaming-curator |
[roles.checkpoint_summary] | Checkpoint-summary session routing. | enabled, builtin dreaming-curator |
[roles.memory_extractor] | Memory-extractor session routing. | enabled, inherits the invoking agent |
[roles.auto_title] | Automatic-title session routing. | enabled, inherits the invoking agent |
[roles.memory_controller] | In-process memory-controller model routing and call bounds. | enabled, pi + anthropic/claude-haiku-4, 250 ms, top-K 5, prompt v1, 256 output tokens |
[limits] | Daemon-level concurrently running agent cap. | max_concurrent_agents = 20 |
[session.limits] | Session-scoped wall-clock timeout. | timeout = "0s" |
[session.supervision] | Runtime activity heartbeat, progress, warning, and inactivity timeout controls. | heartbeat 30 seconds, progress 10 minutes, warning 15 minutes, timeout 30 minutes |
[session.compaction] | Pressure-triggered checkpoint coverage and persisted replay archiving. | enabled, 85% pressure, 1 attempt per turn, 10-minute failure cooldown |
[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. | 5 minutes |
[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 |
[marketplace.catalog] | Curated marketplace feed location and fetch timing. | CompozyOS main catalog, 1 h TTL, 10 s timeout |
[sandboxes.<name>] | Local or provider-backed execution sandbox profiles. | local backend when no profile is selected |
[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 |
[memory] | Persistent memory runtime and global memory directory. | enabled, $COMPOZY_HOME/memory |
[memory.controller] | Hybrid write controller mode, latency, and fallback op. | hybrid, 300 ms, noop |
[memory.controller.policy] | Content/rate caps and allowed write origins. | 4096 chars, 60 writes/min, all canonical origins |
[memory.recall] | Deterministic recall: top-K, weights, freshness, signal queue. | top-K 5, raw 50, weighted fusion |
[memory.decisions] | Decision WAL retention and per-row body cap. | 90 days, audit summary on, 64 KiB body cap |
[memory.extractor] | Post-message extractor policy and bounded queue. | post_message mode, capacity 1, coalesce 16 |
[memory.dream] | Dreaming gates, cadence, and scoring policy. | 24 h, 3 sessions, 30 min ticker |
[memory.session] | Forensic session ledger materialization, archive, and unbound partition. | jsonl, $COMPOZY_HOME/sessions, 24 h grace, 30-day cold archive, _unbound partition |
[memory.daily] | Daily-log retention and rotation. | 1 MiB, 5000 lines, 7-day window, 30-day cold archive, sweep at 03:00 |
[memory.file] | Curated memory body and prompt-index limits. | 200 lines, 25 KiB |
[memory.provider] | Active memory provider selection and circuit breaker. | bundled local, 2 s timeout, 5 failures, 30 s cooldown |
[memory.workspace] | Workspace identity file location and auto-creation. | <workspace>/.compozy/workspace.toml, auto-create on first touch |
[skills] | Skill discovery, polling, disable list, and marketplace trust gates. | enabled, poll every 3 seconds |
[skills.marketplace] | Skill registry override. | unset |
[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, UTC, 5 concurrent jobs |
[[automation.jobs]] | Scheduled automation jobs. | empty list |
[[automation.triggers]] | Event-driven automation triggers. | empty list |
[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/sandbox; provider override + sandbox none 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 |
[network] | Network availability, protocol safety, and bounded Live defaults/limits. | enabled; Local remains the execution default |
[gateway] | Remote gateway ceiling, listener ports, and bounded security tunables. | disabled; both tier ports assigned by the operating system |
Load And Merge Order
| Order | Source | Merge behavior |
|---|---|---|
| 1 | Built-in defaults | Base config. |
| 2 | $COMPOZY_HOME/config.toml | Scalar fields replace defaults. TOML MCP and provider MCP entries merge by name. |
| 3 | $COMPOZY_HOME/mcp.json | Same-name MCP servers replace the whole server object. |
| 4 | <workspace>/.compozy/config.toml | Workspace overlay applies over global config. |
| 5 | <workspace>/.compozy/mcp.json | Same-name MCP servers replace the whole server object after workspace TOML. |
| 6 | Path normalization and validation | ~ is expanded for daemon.socket and memory.global_dir; 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.
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"
# Bridge runtime reload budget. Valid range: 1s to 300s.
bridges = "30s"
[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 = "6h"
[attention]
toasts = true
sound = true
system = false
muted_workspaces = []
[shell.sessions]
# Session ordering: "last_activity" or "attention".
sort = "last_activity"
# Session breadth: "recent", "all", or "all-workspaces".
scope = "recent"
[window_manager]
new_window_policy = "floating"
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 = 8
top = 8
right = 10
bottom = 8
left = 10
[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 = "reserved"
[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"
# Sandbox profile used when a workspace does not set sandbox_ref.
sandbox = "local"
[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.dream]
# Route dreaming work to a custom authored curator instead of the builtin dreaming-curator.
enabled = true
agent = "memory-curator"
[roles.checkpoint_summary]
# Empty agent selects the builtin dreaming-curator.
enabled = true
[roles.memory_extractor]
# Empty agent inherits the invoking context's agent at runtime.
enabled = true
[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 = "codex"
model = "gpt-5.4-mini"
reasoning_effort = "low"
[roles.memory_controller]
# This in-process role has no agent field.
enabled = true
provider = "pi"
model = "anthropic/claude-haiku-4"
timeout = "250ms"
top_k = 5
prompt_version = "v1"
max_tokens_out = 256
[limits]
# Positive daemon-wide agent concurrency bound.
max_concurrent_agents = 20
[session.limits]
# 0 means no configured wall-clock timeout. Use Go durations such as "30m" or "2h".
timeout = "0s"
[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"
# 0 disables inactivity warnings.
inactivity_warning_after = "15m"
# 0 disables inactivity timeout.
inactivity_timeout = "30m"
# Grace period after cooperative prompt cancel before CompozyOS stops the session as timeout.
timeout_cancel_grace = "30s"
[session.compaction]
# Disable all pressure-triggered compaction work when false.
enabled = true
# Context-used/context-size ratio that admits compaction. 0 also disables admission.
pressure_threshold = 0.85
# Maximum failed or successful compaction starts admitted for one triggering turn.
max_attempts_per_turn = 1
# Delay before a failed session compaction may be attempted again.
failure_cooldown = "10m"
[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"
[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"
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
# Default result cap for tool descriptors that do not set a smaller cap.
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.
bind_nonce_ttl_seconds = 30
[tools.clarify]
# Maximum wait for an operator answer before the caller receives the fallback sentinel.
timeout = "5m"
[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"
[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" }
[sandboxes.local]
backend = "local"
sync_mode = "none"
persistence = "reuse"
runtime_root = "~"
[sandboxes.daytona-dev]
backend = "daytona"
sync_mode = "session-bidirectional"
persistence = "reuse"
runtime_root = "/home/daytona/workspace"
[sandboxes.daytona-dev.env]
NODE_ENV = "development"
[sandboxes.daytona-dev.network]
allow_public_ingress = false
allow_outbound = true
allow_list = ["api.example.test"]
deny_list = ["metadata.google.internal"]
[sandboxes.daytona-dev.daytona]
api_url = "https://app.daytona.io/api"
target = "team-default"
snapshot = "snap-agent-base"
image = "ubuntu:24.04"
class = "cpu-2"
auto_stop = "30m"
auto_archive = "24h"
[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
[memory]
enabled = true
global_dir = "~/.compozy/memory"
[memory.controller]
mode = "hybrid" # hybrid | rules | llm
max_latency = "300ms"
default_op_on_fail = "noop"
[memory.controller.policy]
max_content_chars = 4096
max_writes_per_min = 60
allow_origins = ["cli", "http", "uds", "tool", "extractor", "dreaming", "file", "provider"]
[memory.recall]
top_k = 5
raw_candidates = 50
fusion = "weighted"
include_already_surfaced = false
include_system = false
[memory.recall.weights]
bm25_unicode = 0.55
bm25_trigram = 0.20
recency = 0.15
recall_signal = 0.10
[memory.recall.freshness]
banner_after_days = 1
[memory.recall.signals]
queue_capacity = 256
worker_retry_max = 3
[memory.decisions]
prune_after_applied_days = 90
keep_audit_summary = true
max_post_content_bytes = 65536
[memory.extractor]
mode = "post_message"
throttle_turns = 1
deadline = "60s"
sandbox_inbox_only = true
inbox_path = "$COMPOZY_HOME/memory/_inbox"
dlq_path = "$COMPOZY_HOME/memory/_system/extractor/failures"
[memory.extractor.queue]
capacity = 1
coalesce_max = 16
[memory.dream]
min_hours = 24
min_sessions = 3
debounce = "10m"
prompt_version = "v1"
check_interval = "30m"
[memory.dream.gates]
min_unpromoted = 5
min_recall_count = 2
min_score = 0.75
[memory.dream.scoring]
recency_half_life_days = 14
[memory.dream.scoring.weights]
frequency = 0.30
relevance = 0.35
recency = 0.20
freshness = 0.15
[memory.session]
ledger_format = "jsonl"
ledger_root = "$COMPOZY_HOME/sessions"
events_purge_grace = "24h"
cold_archive_days = 30
hard_delete_days = 0
max_archive_bytes = 10737418240
unbound_partition = "_unbound"
[memory.daily]
max_bytes = 1048576
max_lines = 5000
rotate_format = "{date}.{seq}.md"
dreaming_window = 7
cold_archive_days = 30
hard_delete_days = 0
max_archive_bytes = 1073741824
sweep_hour = 3
archive_path = "_system/archive"
[memory.file]
max_lines = 200
max_bytes = 25600
[memory.provider]
name = "" # empty = bundled local provider
timeout = "2s"
failure_threshold = 5
cooldown = "30s"
[memory.workspace]
toml_path = "<workspace>/.compozy/workspace.toml" # informational; not configurable
auto_create = true
[skills]
enabled = true
disabled_skills = ["experimental-skill"]
poll_interval = "3s"
allowed_marketplace_mcp = ["clawhub:@team/search-skill"]
allowed_marketplace_hooks = ["clawhub:@team/format-hook"]
[skills.marketplace]
registry = "clawhub"
base_url = "https://registry.example.com"
[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 = "UTC"
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.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.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"
[loops.defaults.delivery.runtime_defaults.judge]
provider = "claude"
model = "opus"
[[loops.defaults.delivery.runtime_rules]]
[loops.defaults.delivery.runtime_rules.match]
type = "frontend"
[loops.defaults.delivery.runtime_rules.runtime]
model = "gpt-5.5-codex"
[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.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
[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 = "100ms"
[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"
bridge_notification_timeout = "10s"
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"
network_status_queue_size = 64
network_status_timeout = "5s"
[task.orchestration.profile]
# Defaults that apply when a task does not persist its own execution profile.
default_coordinator_mode = "inherit"
default_worker_mode = "inherit"
default_sandbox_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
# Gate task-level sandbox = "none" selections.
allow_task_sandbox_none = 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"
[network]
enabled = true
max_replay_age = 300
[network.live.defaults]
max_wakes = 8
max_wake_wall_time = "5m"
max_total_wall_time = "30m"
max_input_tokens = 200000
max_output_tokens = 50000
max_wake_depth = 3
coalesce_window = "500ms"
[network.live.limits]
max_wakes = 64
max_wake_wall_time = "15m"
max_total_wall_time = "2h"
max_input_tokens = 1000000
max_output_tokens = 200000
max_wake_depth = 5
min_coalesce_window = "100ms"
max_coalesce_window = "5s"
[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]
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
socket | string path | $COMPOZY_HOME/daemon.sock | Non-empty path. ~ is expanded. | Unix domain socket used by CLI IPC and the UDS API server. |
memory_report_interval | duration | 5m | 0s to disable, or any positive duration. | Cadence for daemon process-memory snapshots in logs and runtime.memory. |
subprocess_health_escalation_threshold | integer | 3 | 0 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.
[http]
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
host | string | localhost | Non-empty host or bind address. | HTTP/SSE server host. |
port | integer | 2123 | 1 through 65535 | HTTP/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.
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
update_check | boolean | true | true, false | Enables daemon-scheduled host update checks. |
update_check_interval | duration | 6h | 15m to 168h | Time between daemon-scheduled checks. |
[attention]
Attention settings are global and live-applied as one validated candidate. Operators can use the
Settings attention controls; agents can use compozy config get|set attention.<key> or the typed
GET/PATCH /api/settings/attention surface. The title count is always on and has no config key.
| Field | Type | Default | Validation | Description |
|---|---|---|---|---|
toasts | boolean | true | true or false | Allows in-app notification toasts. |
sound | boolean | true | true or false | Allows the built-in notification sound. |
system | boolean | false | true or false | Allows supported clients to use system notifications while not in view. |
muted_workspaces | string array | [] | Unique registration IDs returned by workspace list surfaces. | Suppresses delivery for listed workspaces without hiding rows or counters. |
All four fields have the live lifecycle. Removing a workspace also removes its ID from
muted_workspaces; unmuting never replays notifications that were suppressed.
[shell.sessions]
Shell session preferences are global and live-applied as one validated candidate. The session list
updates them through the typed Settings API; operators and agents can also use
compozy config get|set shell.sessions.<key> or GET/PATCH /api/settings/shell.
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
sort | string | last_activity | last_activity, attention | Orders sessions by recent activity or attention urgency. |
scope | string | recent | recent, all, all-workspaces | Chooses the breadth of sessions shown in the shell. |
Both 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.
[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.
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
new_window_policy | string | floating | floating, beside_focus | Initial structural placement. |
small_viewport_policy | string | stack | stack, reject | Projection behavior when minimum sizes cannot fit. |
focus_policy | string | click_directional | click_directional, directional | Enabled focus inputs. |
focus_wrap | boolean | false | true or false | Whether directional focus wraps at a desktop boundary. |
focus_follows_pointer | boolean | false | true or false | Whether pointer entry changes client-local focus. |
raise_on_focus | boolean | true | true or false | Whether focusing a floating window raises it. |
drag_away_policy | string | window | window, group | Default detach scope for a tiled drag. |
group_move_modifier | string | alt | alt, control, meta, shift, none | Modifier that temporarily moves the complete group. |
swap_modifier | string | shift | alt, control, meta, shift, none | Modifier that turns an occupied drop into a window swap. |
history_limit | integer | 50 | 1 through 500 | Maximum workspace-scoped layout undo entries. |
nav_stack_limit | integer | 50 | 1 through 200 | Write-time cap for each window's prior-route stack. |
closed_entry_limit | integer | 20 | 1 through 100 | Write-time cap for retained closed-window entries. |
desktop_transition | string | slide | slide, crossfade, instant | Desktop 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.
Clients apply them to the measured work area; normalized durable geometry never stores pixels.
[window_manager.snap]
| Field | Type | Default | Validation |
|---|---|---|---|
edge_band | integer | 32 | 4 through 128 pixels. |
corner_reach | integer | 150 | 16 through 512 pixels. |
exit_slack | integer | 16 | 0 through 64 pixels. |
repeat_ratios | number 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. reserved blocks that
edge-center snap; the default bottom_center = "reserved" keeps the approach strip above the Dock
clear (the Dock band itself is already excluded from the measured work area). 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.
[defaults]
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
agent | string | general | Empty, 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. |
provider | string | empty | Built-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. |
[roles.<role>]
[roles] is the closed routing roster for daemon-owned background work. It is not an extension
registry: only the six sections below are accepted. Global $COMPOZY_HOME/config.toml values apply to
every workspace unless the selected workspace's .compozy/config.toml overlays them.
| Role | Default | Empty agent resolution | Extra policy owned here |
|---|---|---|---|
coordinator | off | builtin coordinator | ttl, child cap, workspace active-session cap |
dream | on | builtin dreaming-curator | none |
checkpoint_summary | on | builtin dreaming-curator | none |
memory_extractor | on | invoking context | none |
auto_title | on | invoking context | none |
memory_controller | on | not applicable | call timeout, candidate count, prompt version, output cap |
coordinator and dreaming-curator are CompozyOS-owned virtual identities. They resolve without an
authored AGENT.md, do not appear in public agent catalogs, and are reserved from create, update,
duplicate, and extension agent publication. Their embedded prompts are fixed; selecting a custom
agent changes routing, not the builtin prompt.
Session-backed role fields
These fields apply to coordinator, dream, checkpoint_summary, memory_extractor, and
auto_title.
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
enabled | boolean | role-specific | true or false | Admits new work for the role. Disabling a role does not stop an already accepted session. |
agent | string | empty | Empty, 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. |
provider | string | empty | Empty or a configured provider key | Optional provider override applied after agent resolution. |
model | string | empty | Empty or an exact provider model ID | Optional model override. |
reasoning_effort | string | empty | none, minimal, low, medium, high, xhigh, max | Optional reasoning override. |
fallback_chain | array of route tables | empty | Each entry requires provider and model | Ordered fallback routes; each entry may also set reasoning_effort. |
Fallback chains replace as a whole when a workspace overlay declares them. A session-backed
consumer may advance only before ACP accepts a session; CompozyOS never silently reroutes work after
acceptance. Immediately before each fallback attempt, CompozyOS emits role.fallback.used with the role,
the fallback's one-based attempt, provider, and model. The event records an attempt, not success.
[[roles.auto_title.fallback_chain]]
provider = "codex"
model = "gpt-5.4-mini"
reasoning_effort = "low"[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.
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
ttl | duration | 2h | 1m through 24h | Managed coordinator session lifetime. |
max_children | integer | 5 | 1 through 5 | Maximum concurrently held safe-spawn children. |
max_active_sessions_per_workspace | integer | 5 | Positive integer | Active coordinator and spawned-session cap for one workspace. |
[roles.memory_controller]
The memory controller is an in-process model call, so it has no agent field. It accepts
enabled, provider, model, reasoning_effort, and fallback_chain with the same routing
semantics above, plus these call-policy fields:
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
provider | string | pi | Configured provider name | ACP transport used by the transient model call. |
model | string | anthropic/claude-haiku-4 | Non-empty when enabled | Model used by the controller tiebreaker. |
timeout | duration | 250ms | Positive Go duration | Deadline for one controller model call. |
top_k | integer | 5 | Positive integer | Candidate count passed to the tiebreaker. |
prompt_version | string | v1 | Non-empty pinned version | Fixed prompt version recorded for replay fidelity. |
max_tokens_out | integer | 256 | Positive integer | Output-token cap for one call. |
timeout is valid only for roles.memory_controller; the strict decoder rejects it on any
session-backed role. 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 jsonHTTP 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]
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
max_concurrent_agents | integer | 20 | Positive integer. | Daemon-wide cap for concurrently running agent subprocesses. |
[session.limits]
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
timeout | duration | 0s | Zero or positive Go duration. | Session-level wall-clock timeout. Zero means no configured wall-clock timeout. |
[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"| Field | Type | Default | Description |
|---|---|---|---|
max_file_bytes | integer | 10485760 | Maximum stored bytes for one attachment. |
max_files_per_prompt | integer | 10 | Maximum attachment references on one prompt. |
allowed_mime | array of string | six v1 types | Exact detected MIME types accepted by the store. |
retention.max_count | integer | 200 | Global retained attachment count ceiling. |
retention.max_bytes | integer | 1073741824 | Global retained byte ceiling. |
retention.max_age | duration | 720h | Maximum 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 jsonThe 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 keeps long-running prompts observable without turning every heartbeat into a stored event. CompozyOS updates session metadata on short heartbeats and only writes durable events for lower-frequency progress, warnings, and timeout classification.
This timeout is based on inactivity, not total runtime. A prompt can run for hours as long as CompozyOS continues to observe runtime activity or controlled waiting heartbeats.
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
activity_heartbeat_interval | duration | 30s | Positive Go duration. | Cadence for metadata-only activity heartbeats while a prompt is waiting on ACP/provider work. |
progress_notify_interval | duration | 10m | Zero or positive Go duration. | Cadence for persisted runtime_progress events. Zero disables periodic progress events. |
inactivity_warning_after | duration | 15m | Zero or positive Go duration. | Idle age before CompozyOS emits one runtime_warning. Zero disables the warning. |
inactivity_timeout | duration | 30m | Zero or positive Go duration. | Idle age before CompozyOS cancels the prompt cooperatively. Zero disables inactivity timeout. |
timeout_cancel_grace | duration | 30s | Positive Go duration. | Grace period after timeout cancel before CompozyOS stops the session with stop reason timeout. |
[session.compaction]
[session.compaction] bounds daemon-owned compaction of completed persisted turns when an ACP
usage update reports context pressure. CompozyOS first updates the workspace checkpoint with durable
sequence coverage, then marks that exact event range archived. Archived rows remain queryable in
session events and history; only degraded transcript replay excludes them. The triggering turn is
never included in its own compaction span.
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
enabled | boolean | true | true or false | Enables pressure-triggered compaction. |
pressure_threshold | number | 0.85 | 0.0 through 1.0 | Admission ratio. 0.0 disables admission even when enabled. |
max_attempts_per_turn | integer | 1 | Positive integer. | Per-trigger-turn attempt cap, including failed attempts. |
failure_cooldown | duration | 10m | Zero or positive value. | Per-session delay after a failed summary, archive, or hook transition. |
These paths are global or workspace desired state and are available through compozy config set and
the compozy__config_* native tools. Structured writes return lifecycle="restart-required" and
next_action="restart-daemon"; the running daemon keeps its bound compaction policy until restart.
Use compozy config apply-history -o json to inspect the blocked apply record.
[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.
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
enabled | boolean | true | true or false | When false, CompozyOS stops injecting Soul into prompts and /agent/context but keeps inspect/validate/write/delete/history/rollback available. |
max_body_bytes | integer | 32768 | Positive integer. | Maximum normalized SOUL.md body size. Files beyond this fail validation with oversized_body. |
context_projection_bytes | integer | 2048 | Positive 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.
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
enabled | boolean | true | true or false | When false, CompozyOS disables wake decisions but keeps managed authoring available so operators can repair a broken policy. |
max_body_bytes | integer | 32768 | Positive integer; capped at the runtime hard limit. | Maximum normalized HEARTBEAT.md body size; oversized files fail validation. |
context_projection_bytes | integer | 4096 | Positive integer; must be <= max_body_bytes. | Compact projection budget for status responses. |
min_interval | duration | 5m | Positive Go duration; must be <= default_interval. | Hard floor for authored preferences.min_interval. Files below this are clamped with heartbeat_preference_clamped. |
default_interval | duration | 30m | Positive Go duration. | Default cadence baseline used when authored files omit min_interval. |
wake_cooldown | duration | 1m | Positive Go duration. | Cooldown between wakes targeting the same session. |
max_wakes_per_cycle | integer | 25 | Positive integer. | Daemon-wide cap on wakes per scheduler cycle. Excess wakes return heartbeat_rate_limited. |
active_session_only | boolean | true | true or false | When true, only sessions reported as active and attachable are wake-eligible. |
allow_active_hours_preferences | boolean | true | true or false | When false, authored active_hours/quiet_windows are ignored with heartbeat_preference_ignored. |
wake_event_retention | duration | 168h | Positive Go duration; minimum 1h. | Retention for agent_heartbeat_wake_events audit rows. Cleanup runs at boot and during scheduler maintenance cycles. |
session_health_stale_after | duration | 2m | Positive Go duration. | Idle age before metadata-only presence becomes stale for wake eligibility. |
session_health_hook_min_interval | duration | 1m | Positive 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 Compozy Network [network]
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]
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
mode | string | approve-all | deny-all, approve-reads, approve-all | Default permission policy used when an agent definition does not override permissions. |
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.
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
root | string | $COMPOZY_HOME/worktrees | Empty or absolute path. | Central parent for new managed Worktrees, grouped by workspace and name. |
run_branch_namespace | string | run/ | Lowercase slash-terminated namespace. | Prefix for per-run branches. |
copy_list | string array | empty | Non-empty relative Git pathspecs. | Matching ignored, untracked files copied without overwriting files. |
setup_command | string | empty | Shell command or empty. | Bootstrap command run inside a new managed Worktree. |
setup_timeout | duration | 10m | Positive Go duration. | Deadline for setup_command. |
discovery_cache_ttl | duration | 30s | Positive 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.
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
enabled | boolean | true | true or false. | When false, disables CompozyOS-owned registry dispatch and hosted MCP exposure. |
hosted_mcp_enabled | boolean | true | true or false. | Allows CompozyOS to expose the local hosted MCP proxy for session-visible tools. |
default_max_result_bytes | integer | 262144 | 0 through 16777216. | Result cap used when a descriptor does not specify a smaller limit. |
[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.
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
max_count | integer | 200 | Positive integer. | Maximum retained oversized tool-result artifacts across the daemon. |
max_bytes | integer | 1073741824 | Positive integer. | Maximum combined retained bytes across the daemon. |
max_age | duration | 720h | Positive Go duration. | Maximum artifact age before deterministic retention cleanup removes it. |
[tools.hosted_mcp]
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
bind_nonce_ttl_seconds | integer | 30 | 1 through 300 seconds. | Lifetime for the non-secret hosted MCP bind nonce before UDS peer binding. |
[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.
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
timeout | duration | 5m | 1s through 24h | Maximum wait before CompozyOS returns {choice:null,text:"",fallback:true}. |
[tools.policy]
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
external_default | string | disabled | disabled, ask, enabled. | Default executable policy for extension, MCP, and dynamic tools before explicit tool/source grants. |
approval_timeout_seconds | integer | 120 | 1 through 600 seconds. | Maximum wait for daemon-mediated approval flows. |
trusted_sources | string 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.
| Field | Type | Default | Description |
|---|---|---|---|
client_metadata_url | string URL | https://compozy.com/.well-known/mcp-client.json | Public client metadata document used for CIMD. |
redirect_uri | string URL | http://127.0.0.1:2123/api/mcp/oauth/callback | Optional 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.
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
name | string | required | Non-empty after trimming. | Stable server key used for merge and replacement. |
transport | string | inferred | stdio, http. | Transport kind. Command servers default to stdio; URL servers default to http. |
command | string | required for stdio | Non-empty command. | Executable command for a local stdio MCP server. Invalid for remote transports. |
url | string URL | required for remote | Absolute URL. | Remote MCP endpoint for http. Invalid for stdio. |
auth | object | empty | OAuth 2.1 PKCE settings. | Remote auth metadata and client settings. Invalid for stdio. |
args | string array | empty | Strings. | Process arguments passed to the command. |
env | string map | empty | String keys and values. | Literal environment values passed to the MCP server. CompozyOS does not perform shell expansion. |
secret_env | string map | empty | env: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 field | Type | Required | Description |
|---|---|---|---|
registration | string | no | auto (default) or pre_registered. |
issuer_url | string URL | conditional | Required for pre_registered; must match discovery. |
client_id | string | conditional | Required for pre_registered. |
client_secret_ref | string | no | Scope-qualified vault:mcp/**/oauth/client-secret ref for pre_registered. |
scopes | string array | no | Unique 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, kiro, blackbox, cline,
goose, hermes, junie, kimi-cli, openclaw, openhands, qoder, qwen-code, pi,
openrouter, zai, moonshot, vercel-ai-gateway, xai, minimax, mistral, and groq.
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
command | string | Built-in command or empty for custom providers. | Required after built-in plus override resolution. | ACP launch command for this provider. |
display_name | string | Built-in label or empty. | Any string. | Operator-facing label shown in settings and provider pickers. |
models | table | Built-in defaults or empty. | Nested model config block (see below). | Pre-session model defaults, curated metadata, and optional discovery wiring. |
harness | string | acp unless a built-in sets pi_acp. | acp, pi_acp. | Launch strategy. pi_acp routes the provider through Pi's ACP adapter. |
runtime_provider | string | Provider key. | Harness-specific provider id. | Downstream provider id used by Pi and other harnesses. |
transport | string | empty. | Harness-specific string. | Optional Pi models override transport/API family. |
base_url | string | empty. | URL string. | Optional Pi models override base URL for custom gateways. |
auth_mode | string | bound_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_security | string | local_transport. | local_transport, external_identity, public_readonly. | Safety rationale required for auth_mode = "none". |
env_policy | string | filtered. | filtered, isolated. | Controls which daemon environment variables the provider subprocess inherits. |
home_policy | string | operator. | operator, isolated. | Controls whether native CLI state comes from the operator home or a CompozyOS provider home. |
auth_status_command | string | empty. | Shell-style command string. | Optional status probe run by compozy provider auth status <provider>. |
auth_login_command | string | empty 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_mcp | boolean | true unless a provider disables it. | true, false. | Enables CompozyOS session MCP injection for providers that support it. |
credential_slots | array | empty. | See below. | Bound secret refs injected into provider subprocess environment variables at launch. |
mcp_servers | array of MCP server objects | empty. | 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.
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
models.default | string | Built-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.curated | array | omitted; no explicit set. | See entry fields below. | Curated model entries projected as config rows. Not an allowlist; manual model ids stay valid. |
models.curated[].id | string | required per entry. | Unique provider model id. | Provider model identifier sent to the runtime. |
models.curated[].display_name | string | empty. | Any string. | Optional human label. |
models.curated[].context_window | integer | empty. | Positive integer. | Context window in tokens. |
models.curated[].max_input_tokens | integer | empty. | Positive integer. | Maximum input tokens. |
models.curated[].max_output_tokens | integer | empty. | Positive integer. | Maximum output tokens. |
models.curated[].supports_tools | boolean | empty. | true, false. | Whether the model supports tool calls. |
models.curated[].supports_reasoning | boolean | empty. | true, false. | Whether the model supports reasoning effort. |
models.curated[].reasoning_efforts | array | empty. | Subset of none, minimal, low, medium, high, xhigh, max. Blanks rejected. | Allowed reasoning levels for this model. |
models.curated[].default_reasoning_effort | string | empty. | Member of reasoning_efforts when both are set. | Per-model default reasoning level. Must appear in reasoning_efforts when both are set. |
models.curated[].cost_input_per_million | number | empty. | Non-negative finite number. | Input rate used for catalog-based usage estimates when the agent reports no actual cost. |
models.curated[].cost_output_per_million | number | empty. | 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_million | number | empty. | Non-negative finite number. | Cache-read rate used only for the cache-read token bucket. |
models.curated[].cost_cache_write_per_million | number | empty. | Non-negative finite number. | Cache-write rate used only for the cache-write token bucket. |
models.curated[].cost_reasoning_per_million | number | empty. | Non-negative finite number. | Reasoning-token rate used only for the reasoning token bucket. |
models.curated[].deprecated | boolean | omitted; inherit lower source. | true, false. | Marks a row deprecated; curated view excludes it. Explicit false clears lower-source metadata. |
models.curated[].hidden | boolean | omitted; inherit lower source. | true, false. | Operator curation flag; curated view excludes it. Explicit false clears a lower-source flag. |
models.curated[].featured | boolean | omitted; inherit lower source. | true, false. | Adds a current row to curated candidates. Explicit false clears a lower-source flag. |
models.curated[].release_date | string | empty. | YYYY-MM or YYYY-MM-DD. | Optional release metadata used for curated ordering; unknown dates stay empty. |
models.reasoning.apply | string | none unless a built-in opts in. | acp_option, none. | Applies effort through an advertised ACP option or exposes no selectable strategy. |
models.discovery.enabled | boolean | false unless a built-in opts in. | true, false. | Enables model discovery for this provider. Cursor uses a short-lived ACP inspection. |
models.discovery.command | string | empty. | Shell-style command string. | Discovery command. For Cursor, this must start an ACP server such as cursor-agent acp. |
models.discovery.endpoint | string | empty. | Absolute HTTP(S) URL. | Discovery endpoint URL. Cursor does not support an endpoint because its model values come from ACP. |
models.discovery.timeout | string | model 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. Most discovery paths do not
create an ACP session. Cursor is the exception: it starts a short-lived cursor-agent acp inspection and
uses only the exact values advertised by its model option. If discovery is unavailable or fails, the
catalog records source status and falls back to stale or lower-priority rows. An explicit Cursor model
refreshes that ACP catalog before session creation and must be one of its fresh advertised values. A blank
Cursor model still lets Cursor choose its native default. The built-in Cursor adapter uses the operator
home and stores either the advertised ACP rows or a failed status; later reads use that stored state until
an explicit refresh runs.
Writes under providers.<id>.models.discovery apply live. A changed discovery source refreshes
immediately; provider model metadata changes do not rerun discovery.
OpenClaw, Hermes, and Pi only register a live provider source when models.discovery.enabled = true and either command or endpoint is set; with no discovery wiring, those providers stay
on builtin/config rows plus the optional models.dev enrichment.
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 = truesecret_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, xAI, MiniMax, Mistral,
and Groq default to CompozyOS-managed bound_secret slots while CompozyOS runs Pi under the hood.
| Built-in | Harness | Runtime provider | models.default | Auth mode | Credential target |
|---|---|---|---|---|---|
claude | acp | claude | claude-sonnet-5 | native_cli | provider login |
codex | acp | codex | gpt-5.6-sol | native_cli | provider login |
gemini | acp | gemini | gemini-3.1-pro-preview | native_cli | provider login |
opencode | acp | opencode | none | native_cli | provider login |
copilot | acp | copilot | none | native_cli | provider login |
cursor | acp | cursor | none | native_cli | provider login |
kiro | acp | kiro | none | native_cli | provider login |
blackbox | acp | blackbox | none | native_cli | provider login |
cline | acp | cline | none | native_cli | provider login |
goose | acp | goose | none | native_cli | provider login |
hermes | acp | hermes | none | native_cli | provider login |
junie | acp | junie | none | native_cli | provider login |
kimi-cli | acp | kimi-cli | none | native_cli | provider login |
openclaw | acp | openclaw | none | native_cli | provider login |
openhands | acp | openhands | none | native_cli | provider login |
qoder | acp | qoder | none | native_cli | provider login |
qwen-code | acp | qwen-code | qwen3.6-plus | native_cli | provider login |
pi | pi_acp | anthropic | claude-opus-4-7 | native_cli | Pi /login |
openrouter | pi_acp | openrouter | openai/gpt-5.4 | bound_secret | OPENROUTER_API_KEY |
zai | pi_acp | zai | glm-4.6 | bound_secret | ZAI_API_KEY |
moonshot | pi_acp | kimi-coding | kimi-k2-thinking | bound_secret | KIMI_API_KEY |
vercel-ai-gateway | pi_acp | vercel-ai-gateway | anthropic/claude-opus-4-7 | bound_secret | AI_GATEWAY_API_KEY |
xai | pi_acp | xai | grok-4-fast-non-reasoning | bound_secret | XAI_API_KEY |
minimax | pi_acp | minimax | MiniMax-M2.1 | bound_secret | MINIMAX_API_KEY |
mistral | pi_acp | mistral | devstral-medium-latest | bound_secret | MISTRAL_API_KEY |
groq | pi_acp | groq | openai/gpt-oss-120b | bound_secret | GROQ_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.
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
enabled | boolean | true | true, false | Toggles the models.dev source. When false, the source still appears in status but performs no outbound calls. |
endpoint | string | https://models.dev/api.json | Absolute HTTP(S) URL. | Endpoint queried for the cross-provider model index. Must be HTTP(S) when set. |
ttl | duration | 24h | Positive Go duration. | Cache lifetime before catalog rows are flagged stale and a refresh is scheduled. |
timeout | duration | 10s | Positive 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 default command is cursor-agent acp; setting command overrides that ACP discovery path.
[marketplace.catalog]
CompozyOS fetches the curated MCP server, extension, and skill documents from this base URL. Each kind is validated independently and projected into the global database. If a fetch fails after a successful projection, CompozyOS keeps serving the prior rows with stale status instead of replacing them with an invalid or incomplete document.
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.
| Key | Type | Default | Validation | Effect |
|---|---|---|---|---|
base_url | URL | https://raw.githubusercontent.com/compozy/compozy/main/catalog | Absolute http, https, or local file URL. | Base directory for mcp.json, extensions.json, and skills.json; applies live. |
ttl | duration | 1h | Positive Go duration. | HTTP(S) cache age before browse refreshes that kind; local files refresh on access. |
timeout | duration | 10s | Positive 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 jsonAgent-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.
[observability]
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
enabled | boolean | true | true or false | Enables global event summary and health observation paths. |
retention_days | integer | 7 | Zero or positive. | Retention window for global event summaries, token stats, and permission logs. 0 keeps history. |
max_global_bytes | integer | 1073741824 | Positive integer. | Size cap for global observability storage. |
[observability.transcripts]
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
enabled | boolean | true | true or false | Enables transcript capture from persisted session events. |
segment_bytes | integer | 1048576 | Positive integer. | Segment size for transcript retention. |
max_bytes_per_session | integer | 268435456 | Positive 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.
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
level | string | info | debug, info, warn, error | Structured daemon log level. |
max_size_mb | integer | 10 | Positive integer. | Rotate compozy.log when the active file would exceed this size. |
max_backups | integer | 5 | Zero or positive integer. | Maximum rotated backups to keep. 0 disables backup count retention. |
max_age_days | integer | 30 | Zero or positive integer. | Delete backups older than this many days. 0 disables age retention. |
compress_backups | boolean | false | true or false | Compress rotated backups with gzip after rotation. |
[redact]
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
enabled | boolean | true | true or false | Enables 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.
[memory]
The [memory] tree is the Memory v2 runtime configuration. Memory v2 is the only memory subsystem;
there is no [memory.v2] enabled flag and no parallel compatibility namespace. Disabling
[memory] enabled short-circuits prompt assembly, the controller, recall, dreaming, the extractor,
and the bundled local provider.
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
enabled | boolean | true | true or false | Master switch for the Memory v2 runtime. |
global_dir | string path | $COMPOZY_HOME/memory | Non-empty path when set. ~ is expanded. | Global memory directory. Blank overlays are ignored and keep the previous value. |
[memory.controller]
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
mode | string | hybrid | hybrid, rules, llm | Controller decision mode. hybrid runs the rule-first lexical/entity logic with an LLM tiebreaker. |
max_latency | duration | 300ms | Positive Go duration. | Maximum end-to-end controller latency before fall-back behavior triggers. |
default_op_on_fail | string | noop | noop, reject | Decision op used when the controller fails or its deadline is exceeded. |
[memory.controller.policy]
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
max_content_chars | integer | 4096 | Positive integer. | Hard cap on candidate body length. |
max_writes_per_min | integer | 60 | Positive integer. | Per-workspace write rate limit before the controller starts rejecting candidates. |
allow_origins | string array | ["cli","http","uds","tool","extractor","dreaming","file","provider"] | Subset of canonical origins. | Origins permitted to submit candidates. Empty rejects every write. |
[memory.recall]
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
top_k | integer | 5 | Positive integer. | Top-K curated entries packaged per recall response. |
raw_candidates | integer | 50 | Positive integer. | Raw candidate pool before scoring/shadowing. |
fusion | string | weighted | weighted, rrf | Score fusion strategy. rrf is reserved for slice 3. |
include_already_surfaced | boolean | false | true or false | Includes entries already surfaced this session. |
include_system | boolean | false | true or false | Includes _system/ artifacts in recall (operator-only). |
[memory.recall.weights]
Weights are blended into the recall score. Defaults sum to 1.0.
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
bm25_unicode | float | 0.55 | 0.0–1.0 | BM25 score from the unicode FTS5 index. |
bm25_trigram | float | 0.20 | 0.0–1.0 | BM25 score from the trigram FTS5 index. |
recency | float | 0.15 | 0.0–1.0 | Time-decay factor. |
recall_signal | float | 0.10 | 0.0–1.0 | Live recall-signal feedback factor. |
[memory.recall.freshness]
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
banner_after_days | integer | 1 | Positive integer. | Adds a "verify before asserting" banner to entries older than this. |
[memory.recall.signals]
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
queue_capacity | integer | 256 | Positive integer. | Bounded channel for recall-signal updates. Oldest drops on overflow. |
worker_retry_max | integer | 3 | Positive integer. | Per-update retries before emitting memory.recall.signal_update_failed. |
[memory.decisions]
WAL retention controls. Pruned rows are replaced by a redaction-safe summary so audit history survives.
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
prune_after_applied_days | integer | 90 | Positive integer. | Decisions with applied_at older than this are pruned after a memory.decisions.audit_summarized event. |
keep_audit_summary | boolean | true | true or false | Emits the audit-summary event before pruning. |
max_post_content_bytes | integer | 65536 | Positive integer. | Per-row body cap. Oversized rows store a content-hash reference instead. |
[memory.extractor]
The extractor consumes the session.message_persisted hook and writes structured proposals via the
controller. inbox_path and dlq_path are validation-locked to the canonical CompozyOS-managed
locations.
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
mode | string | post_message | post_message | Extraction mode. CompozyOS currently supports only post-message extraction. |
throttle_turns | integer | 1 | Positive integer. | Minimum same-session queued turns to coalesce before launching another extraction; drain flushes pending content. |
deadline | duration | 60s | Positive Go duration. | Per-extraction deadline before the runtime drops the candidate. |
sandbox_inbox_only | boolean | true | true or false | Restricts extractor writes to _inbox/ until the controller accepts them. |
inbox_path | string | $COMPOZY_HOME/memory/_inbox | Daemon-managed. | Read-only display field; the daemon manages the inbox path. |
dlq_path | string | $COMPOZY_HOME/memory/_system/extractor/failures | Daemon-managed. | Read-only display field; the daemon manages the DLQ path. |
[memory.extractor.queue]
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
capacity | integer | 1 | Positive integer. | Maximum concurrent provider-backed extractor child sessions; additional sessions wait without losing queued turns. |
coalesce_max | integer | 16 | Positive integer greater than or equal to throttle_turns. | Maximum coalesced turns when consecutive triggers arrive while the extractor is busy. |
[memory.dream]
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
min_hours | float | 24 | Positive number. | Time-gate threshold before another successful run. |
min_sessions | integer | 3 | Positive integer. | Sessions-gate threshold (completed sessions since last run). |
debounce | duration | 10m | Positive Go duration. | Debounce window for triggers fed by session.post_stop. |
prompt_version | string | v1 | Non-empty pinned version. | Prompt template version pinned to dreaming sessions for replay fidelity. |
check_interval | duration | 30m | Positive Go duration. | Background ticker interval. |
[memory.dream.gates]
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
min_unpromoted | integer | 5 | Positive integer. | Minimum unpromoted candidates with sufficient signal. |
min_recall_count | integer | 2 | Positive integer. | Minimum recall hits per candidate before it qualifies. |
min_score | float | 0.75 | 0.0–1.0 | Minimum scored value for a candidate to clear the signal gate. |
[memory.dream.scoring]
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
recency_half_life_days | integer | 14 | Positive integer. | Half-life applied to recall recency in the dreaming score. |
[memory.dream.scoring.weights]
Defaults sum to 1.0 across the four factors.
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
frequency | float | 0.30 | 0.0–1.0 | Weight of recall frequency. |
relevance | float | 0.35 | 0.0–1.0 | Weight of BM25 relevance. |
recency | float | 0.20 | 0.0–1.0 | Weight of recency decay. |
freshness | float | 0.15 | 0.0–1.0 | Penalty weight for stale, never-promoted entries. |
[memory.session]
Forensic ledger materialization. The materializer writes one read-only ledger.jsonl
under <ledger_root>/<workspace_id>/<session_id>/. Sessions without a workspace use
<unbound_partition> instead of a workspace_id segment.
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
ledger_format | string | jsonl | jsonl | Forensic ledger format. |
ledger_root | string | $COMPOZY_HOME/sessions | Daemon-managed. | Ledger root. Read-only; daemon-managed path. |
events_purge_grace | duration | 24h | Positive Go duration. | Time after materialization before live events.db rows are purged. |
cold_archive_days | integer | 30 | Zero or positive. | Cold-archive window for ledger.jsonl. 0 keeps ledgers in-place. |
hard_delete_days | integer | 0 | Zero or positive. | 0 keeps ledgers forever; explicit prune via CLI. |
max_archive_bytes | integer | 10737418240 | Positive integer. | Safety-valve cap on the cold-archive store. |
unbound_partition | string | _unbound | Daemon-managed. | Partition used for sessions without a workspace_id. |
[memory.daily]
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
max_bytes | integer | 1048576 | Positive integer. | Per-day daily-log byte cap before rotation. |
max_lines | integer | 5000 | Positive integer. | Per-day daily-log line cap before rotation. |
rotate_format | string | {date}.{seq}.md | Daemon-managed. | Rotation filename format. Read-only. |
dreaming_window | integer | 7 | Positive integer. | Days the dreaming curator reads from daily logs. |
cold_archive_days | integer | 30 | Zero or positive. | Cold-archive window for rotated daily logs. |
hard_delete_days | integer | 0 | Zero or positive. | 0 keeps daily logs forever; explicit prune via CLI. |
max_archive_bytes | integer | 1073741824 | Positive integer. | Safety-valve cap on the daily archive. |
sweep_hour | integer | 3 | 0–23 | Local hour for daily housekeeping sweep. |
archive_path | string | _system/archive | Daemon-managed. | Archive subdirectory under each scope's _system/. |
[memory.file]
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
max_lines | integer | 200 | Positive integer. | Maximum final body lines for controlled writes and MEMORY.md lines included in prompts. |
max_bytes | integer | 25600 | Positive integer. | Maximum final body bytes for controlled writes and MEMORY.md bytes included in prompts. |
[memory.provider]
name = "" selects the bundled local provider. Setting a configured provider name routes runtime
calls through the registered MemoryProvider while keeping the local provider as a circuit-breaker
fallback (see Extensions).
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
name | string | empty | Empty or registered name. | Active provider. Empty falls back to the bundled local provider. |
timeout | duration | 2s | Positive Go duration. | Per-method deadline before fail-open to the bundled local provider. |
failure_threshold | integer | 5 | Positive integer. | Consecutive failures before the circuit opens. |
cooldown | duration | 30s | Positive Go duration. | How long the circuit stays open before retry. |
[memory.workspace]
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
toml_path | string | <workspace>/.compozy/workspace.toml | Daemon-managed. | Read-only display field. Workspace identity always lives at this path. |
auto_create | boolean | true | true/false | Auto-create <workspace>/.compozy/workspace.toml on first daemon touch of a new workspace. |
[skills]
If enabled = false, skill polling and marketplace validation are skipped.
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
enabled | boolean | true | true or false | Enables skill catalog discovery and polling. |
disabled_skills | string array | empty | Skill names. | Replaces the disabled-skill list in overlays. |
poll_interval | duration | 3s | Positive Go duration. | Filesystem poll interval for skill catalog refresh. |
allowed_marketplace_mcp | string array | empty | Marketplace slug, registry:slug, or verified hash. | Trust allowlist for MCP servers declared by marketplace skills. |
allowed_marketplace_hooks | string array | empty | Marketplace slug, registry:slug, or verified hash. | Trust allowlist for hooks declared by marketplace skills. |
[skills.marketplace]
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
registry | string | empty | Empty or clawhub. Required when base_url is set. | Skill marketplace registry adapter. |
base_url | string URL | empty | Empty or http or https URL with host. | Registry base URL override. |
[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.
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
allow_unverified | boolean | true | true or false | Allows 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]
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
enabled | boolean | true | true or false | Enables github: installs and the GitHub arm of source-union search. |
base_url | string URL | https://api.github.com | http 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]
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
enabled | boolean | true | true or false | Enables 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]
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
watch_interval | duration | 2s | Positive 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.
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
allowed_kinds | string array | empty | Registered resource kinds, such as window_layout. | Restricts which resource kinds any extension may publish. |
max_scope | string | empty | global 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.
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
requests | integer | 0 | Positive integer. | Requests allowed per window. |
window | duration | 0s | Positive Go duration. | Rolling window. |
queue | integer | 0 | Zero or positive. | Requests allowed to wait when the bucket is empty. |
[automation]
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
enabled | boolean | true | true or false | Enables config-defined automation jobs and triggers. |
timezone | string | UTC | IANA timezone loadable by Go, such as UTC or America/Sao_Paulo. | Schedule timezone for automation. |
max_concurrent_jobs | integer | 5 | Positive integer. | Global automation concurrency cap. |
default_fire_limit | object | { max = 12, window = "1h" } | max positive, window positive Go duration. | Default rolling fire limit copied into jobs and triggers that omit their own limit. |
[automation.suggestions]
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
pending_cap | integer | 5 | Positive 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.
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
scope | string | required | global or workspace | Binding scope. |
name | string | required | Non-empty. | Job name. |
agent | string | required for an Agent target without task; empty for a Loop target | After trimming, ^[a-z][a-z0-9_-]{0,105}$ (maximum 106 characters). | Agent that handles the scheduled prompt. |
workspace | string | required when scope = "workspace", empty when scope = "global" | Workspace ID or root accepted by the automation resolver. | Workspace binding. |
prompt | string | required unless task is configured | Non-empty. | Prompt submitted when the job fires. |
schedule.mode | string | required | cron, every, at | Schedule mode. |
schedule.expr | string | empty | Required only for cron; standard five-field cron. | Cron expression. |
schedule.interval | string | empty | Required only for every; positive Go duration. | Repeating interval. |
schedule.time | string | empty | Required only for at; RFC3339 timestamp. | One-shot fire time. |
task.title | string | empty | Any string. | Task title when the job materializes a task instead of starting an agent session. |
task.description | string | empty | Any string. | Task description. |
task.owner | object | empty | Task ownership object. | Owner metadata validated by the task package. |
task.network_participation | object | empty | Local/Live participation request. | Optional participation request resolved once when the generated task run starts. |
enabled | boolean | true | true or false | Enables this job. |
retry.strategy | string | none | none or backoff | Retry behavior. Must be none when task is configured. |
retry.max_retries | integer | 0 | 0 for none; positive for backoff. | Maximum retry count. |
retry.base_delay | string | empty | Empty for none; positive Go duration for backoff. | Base backoff delay. |
fire_limit | object | [automation].default_fire_limit | max 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.
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
scope | string | required | global or workspace | Binding scope. |
name | string | required | Non-empty. | Trigger name. |
agent | string | required for an Agent target; empty for a Loop target | After trimming, ^[a-z][a-z0-9_-]{0,105}$ (maximum 106 characters). | Agent that handles the trigger prompt. |
workspace | string | required when scope = "workspace", empty when scope = "global" | Workspace ID or root accepted by the automation resolver. | Workspace binding. |
prompt | string | required | Valid Go template over the activation envelope. | Prompt submitted when the trigger fires. |
event | string | required | session.created, session.stopped, memory.consolidated, hook.<hook_name>.completed, webhook, or ext.*. | Producer-backed event name; surrounding whitespace and unknown names are rejected. |
filter | string map | empty | Keys kind, scope, workspace_id, source, or data.<field>; values non-empty. | Activation-envelope filter. |
enabled | boolean | true | true or false | Enables this trigger. |
retry.strategy | string | none | none or backoff | Retry behavior. |
retry.max_retries | integer | 0 | 0 for none; positive for backoff. | Maximum retry count. |
retry.base_delay | string | empty | Empty for none; positive Go duration for backoff. | Base backoff delay. |
fire_limit | object | [automation].default_fire_limit | max positive, window positive Go duration. | Per-trigger fire limit. |
endpoint_slug | string | empty | Required when event = "webhook"; empty otherwise. | Webhook endpoint slug. |
webhook_secret_ref | string | empty | Required 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.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. Operator config can tighten fan-out, no-progress, and gate revision defaults, but it cannot loosen them past the hard runtime ceilings.
| Field | Delivery | Watch | Valid values | Description |
|---|---|---|---|---|
iteration_cap | 50 | 0 | Zero or positive integer. | Maximum iterations. 0 means unbounded. |
no_progress.window | 3 | 2 | 0 through 30. | Generation-hash and blocker-signature window. |
gates.max_revisions | 10 | 0 | 0 through 64. | Gate revision seed; 0 supplies no seed. |
budget.tokens | 0 | 0 | Zero or positive integer. | Token budget; 0 means unlimited. |
budget.wall_clock_sec | 0 | 0 | Zero or positive integer. | Active-work wall-clock budget; parked time is suspended. |
budget.on_exceeded | halt | halt | halt or escalate. | End exhausted or park for approval when a set budget is exceeded. |
fan_out_width | 4 | 2 | 0 through 64. | Fan-out width seed. |
retry.max_attempts | 3 | 3 | 0 through 10. | Total attempts for mechanical families; 0 disables automatic retry. |
retry.backoff_base | 1s | 1s | Positive Go duration. | Initial retry delay; must not exceed backoff_max. |
retry.backoff_max | 30s | 30s | Positive Go duration. | Maximum retry delay. |
liveness.silence_window | 30m | 30m | Zero or positive Go duration. | Raises silence attention; 0 disables evaluation. |
resume.death_streak_limit | 3 | 3 | Positive integer. | Confirmed-death resumptions allowed without post-resume evidence. |
predicates.cost_limit | 10000 | 10000 | Positive integer. | CEL runtime cost limit; evaluation warns at 80%. |
waits.admission_attempts | 3 | 3 | Positive integer. | Bounded attempts to admit a wait resume. |
waits.admission_retry_interval | 60s | 60s | Positive Go duration. | Delay between admission attempts. |
admission.tombstone_horizon | 168h | 168h | Positive Go duration. | Minimum watch-admission deduplication retention. |
runtime_defaults.{worker,judge}.{provider,model,reasoning} | empty | empty | Valid provider/model/reasoning fields. | Worker and judge runtime defaults. |
runtime_rules[] | none | none | One id, type, or complexity match. | Field-merged worker override; id > type > complexity, later wins on equal specificity. |
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.
| Field | Default | Valid values | Description |
|---|---|---|---|
threshold | 5 | Positive integer. | Consecutive transport failures before the target opens. |
probe_interval | 60s | Positive 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/rootHTTP 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.
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
max_turns | integer | 20 | Positive integer. | Turn limit written into a new synthetic Goal definition. |
context_nudge_ratio | number | 0.8 | 0.0 through 1.0 | Context threshold pinned to the Run and copied into every Goal checkpoint. |
outbox_batch_size | integer | 50 | 1 through 200 | Global maximum pending projections and cleanup obligations claimed per pass. |
outbox_poll_interval | duration | 100ms | Positive Go duration. | Global delay between durable relay passes. |
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.
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— integergoals.context_nudge_ratio— numbergoals.outbox_batch_size— integergoals.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 jsonThe 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.
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
block_recurrence_limit | integer | 2 | 0 or a positive int | Same-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.
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
summary_max_bytes | integer | 4096 | Positive integer. | Maximum byte size for stored task-run summary fields used by context, web, CLI, and review surfaces. |
context_body_max_bytes | integer | 8192 | Positive integer. | Maximum byte size for the rendered task context bundle attached to /agent/context. |
context_prior_attempts | integer | 5 | Zero or positive integer. | Maximum prior-attempt summaries included in the task context bundle. |
context_recent_events | integer | 50 | Zero or positive integer. | Maximum recent task events included in the task context bundle. |
spawn_failure_limit | integer | 5 | Positive integer. | Spawn-failure threshold before the per-task spawn circuit breaker opens. |
scheduler_bad_tick_threshold | integer | 6 | Positive integer. | Bad-tick count before the scheduler health watchdog flags a degraded scheduler tick. |
scheduler_bad_tick_cooldown | duration | 5m | Positive Go duration with whole-second precision. | Cooldown between scheduler bad-tick health emissions. |
default_max_runtime | duration | 0s | Zero 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. |
bridge_notification_timeout | duration | 10s | Positive Go duration with whole-second precision. | Timeout budget for bridge task notification delivery. |
designated_run_max | integer | 5 | Positive integer up to 5. | Maximum sibling runs a single designated fan-out request may enqueue. |
max_active_runs_per_workspace | integer | 16 | Zero or positive integer. | Maximum claimed, starting, or running worker and coordinator runs with live leases per workspace. Zero disables the limit; global and Network wake runs are exempt. |
action_run_timeout | duration | 30m | Positive 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. |
network_status_queue_size | integer | 64 | Positive integer. | Queue depth for network-origin task status observer events. |
network_status_timeout | duration | 5s | Positive Go duration with whole-second precision. | Per-event timeout for summarizing task run status back to the Compozy Network origin thread. |
[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/sandbox overrides. See Task Execution Profiles for the runtime behavior.
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
default_coordinator_mode | string | inherit | inherit, guided | Coordinator behavior when a task profile does not set coordinator.mode. |
default_worker_mode | string | inherit | inherit | Worker selection mode when a task profile does not set worker.mode. select is allowed only on the per-task profile. |
default_sandbox_mode | string | inherit | inherit, none | Sandbox mode when a task profile does not set sandbox.mode. |
default_worktree_mode | string | inherit | inherit, none, per_run | Worktree policy resolved when a task profile keeps worktree.mode = "inherit"; bare inherit resolves to workspace root. |
allow_task_provider_override | boolean | true | true or false | When false, task profiles may not set provider/model on coordinator, worker, or review blocks. |
allow_task_sandbox_none | boolean | true | true or false | When false, task profiles may not set sandbox.mode = "none". Required to be true if default_sandbox_mode = "none". |
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 also store sandbox.mode and runtime.mode. sandbox.mode accepts inherit,
none, or ref; ref requires sandbox.sandbox_ref and resolves a named [sandboxes.<name>].
runtime.mode accepts default or evidence. Evidence mode adds runtime evidence guidance and
widens session permissions only when the same profile explicitly selects sandbox.mode = "ref";
inherited or disabled sandbox modes keep the configured 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.
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
default_policy | string | none | none, on_success, on_failure, always | Default review trigger policy when a task does not set its own. |
max_rounds | integer | 3 | Positive integer. | Maximum review rounds before the failure policy applies. |
max_review_attempts | integer | 2 | Positive integer. | Maximum reviewer attempts within a single review round before the round counts as exhausted. |
timeout | duration | 20m | Positive Go duration with whole-second precision. | Maximum reviewer think time before the review counts as a timeout. |
rapid_terminal_window | duration | 2m | Positive Go duration with whole-second precision. | Window used by the review circuit breaker to detect rapid shallow attempts. |
rapid_terminal_limit | integer | 3 | Positive integer. | Maximum terminal events within rapid_terminal_window before the review circuit opens. |
missing_work_max_items | integer | 20 | Positive integer. | Maximum missing_work items recorded per rejected verdict. |
missing_work_item_max_bytes | integer | 512 | Positive integer. | Maximum byte size per missing_work item. |
reason_max_bytes | integer | 2048 | Positive integer. | Maximum byte size for the reviewer reason field. |
review_text_max_bytes | integer | 12000 | Positive integer. | Maximum byte size for reviewer free-form review text. |
next_round_guidance_max_bytes | integer | 4096 | Positive integer. | Maximum byte size for the typed next-round guidance attached to continuation runs. |
failure_policy | string | block_task | block_task, fail_task | What 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.
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
allow_agent_force | boolean | true | true or false | When 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.
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
name | string | required | Non-empty. | Hook declaration name. |
event | string | required | Current dot-form hook event. | Event that triggers the hook. |
mode | string | async | sync or async; sync only for sync-eligible events. | Dispatch mode. |
required | boolean | false | true or false; required hooks must be sync. | Whether a hook failure blocks the source operation. |
priority | integer | Config source default 500 when omitted. | Integer. | Ordering priority. |
timeout | duration | 0s; subprocess executor uses 5 seconds when zero. | Non-negative Go duration. | Hook timeout. |
matcher | object | empty | Supported matcher fields listed below. | Narrows hook eligibility. |
command | string | empty | Required for subprocess hooks unless nested executor fields are used. | Subprocess command. |
args | string array | empty | Strings. | Subprocess arguments. |
env | string map | empty | String keys and values. | Explicit hook environment values. |
executor.kind | string | subprocess when a command is present. | subprocess or wasm for user config. native is internal only. | Executor kind. |
executor.command | string | empty | Same as command. | Nested command form. Cannot be mixed with top-level command/args/env. |
executor.args | string array | empty | Strings. | Nested args form. |
executor.env | string map | empty | String 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_name, tool_namespace,
tool_read_only, decision_class, message_role, message_delta_type, compaction_reason, and
compaction_strategy.
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[network]
Network config controls administrative availability, protocol-safety windows, and the defaults and ceilings used only after an execution explicitly selects Live. It never selects participation.
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
enabled | boolean | true | true or false | Live availability kill switch. Does not opt any execution in. |
max_replay_age | integer seconds | 300 | Positive integer seconds. | Replay/freshness window for accepted envelopes. |
[network.live.defaults]
Defaults fill omitted bounds only after Live is selected. They never convert Local into Live.
| Field | Type | Default | Validation |
|---|---|---|---|
max_wakes | integer | 8 | Positive and at or below its limit. |
max_wake_wall_time | duration | 5m | Positive and at or below its limit. |
max_total_wall_time | duration | 30m | Positive and at or below its limit. |
max_input_tokens | integer | 200000 | Positive and at or below its limit. |
max_output_tokens | integer | 50000 | Positive and at or below its limit. |
max_wake_depth | integer | 3 | Positive and at or below its limit. |
coalesce_window | duration | 500ms | Between the configured minimum and maximum. |
[network.live.limits]
Limits are inclusive administrative ceilings. A default outside its limit fails strict config validation.
| Field | Type | Default | Meaning |
|---|---|---|---|
max_wakes | integer | 64 | Maximum requested wake count. |
max_wake_wall_time | duration | 15m | Maximum deadline for one wake. |
max_total_wall_time | duration | 2h | Maximum aggregate wake wall time. |
max_input_tokens | integer | 1000000 | Maximum aggregate reported input tokens. |
max_output_tokens | integer | 200000 | Maximum aggregate reported output tokens. |
max_wake_depth | integer | 5 | Maximum causal wake depth. |
min_coalesce_window | duration | 100ms | Minimum requested coalescing window. |
max_coalesce_window | duration | 5s | Maximum requested coalescing window. |
Removed keys are rejected as unknown: network.default_channel, network.port,
network.max_payload, network.activation_top_k, network.digest_flush_interval,
network.digest_max_envelopes, network.response_guidance_max_bytes, and
network.delivery_structured_body_max_bytes.
Workspace coordination is not a TOML key. Manage its future-run default with
compozy network coordination status|enable|disable or the equivalent HTTP/UDS surface.
[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.
| Field | Type | Default | Validation | Lifecycle |
|---|---|---|---|---|
enabled | boolean | false | true or false | Live |
private_port | integer | 0 | 0 or 1 through 65535 | Restart required |
public_port | integer | 0 | 0 or 1 through 65535 | Restart required |
Port 0 asks the operating system to assign an available loopback port. Gateway tier listeners do
not bind non-loopback addresses.
| Field | Type | Default | Validation | Lifecycle |
|---|---|---|---|---|
pairing.ttl | duration | 5m | Positive | Live |
pairing.max_pending | integer | 8 | Positive | Live |
stream_ticket.ttl | duration | 30s | Positive | Live |
auth.rate_limit.window | duration | 60s | Positive | Live |
auth.rate_limit.max_fails | integer | 10 | Positive | Live |
verify.timeout | duration | 10s | Positive | Live |
verify.public_dns_resolver | string | 1.1.1.1:853 | Public IP and non-zero port | Live |
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.
| Field | Type | Default | Validation | Lifecycle |
|---|---|---|---|---|
active_connection | string | empty | Name of a declared connection | Live |
connections[].name | string | — | Unique, non-empty | Live |
connections[].scheme | string | — | https or ssh | Live |
connections[].host | string | — | Non-empty host | Live |
connections[].port | integer | — | 1 through 65535 | Live |
connections[].credential_file | string | empty | <name>.cred for an https connection | Live |
connections[].default_workspace | string | empty | Workspace id | Live |
connections[].remote_home | string | empty | Remote COMPOZY_HOME; ssh connections only | Live |
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.
Related Pages
- mcp.json documents JSON sidecars and whole-object replacement.
- AGENT.md documents agent-local
mcp_serversand hooks. - Providers covers
[providers.<id>.models]shape andmodels.discoverydefaults. - Provider Model Catalog explains the catalog projection, OpenAI-compatible endpoint, and extension
model.source. - File Locations lists the exact global and workspace paths.