Skip to main content
Version: 2.9.0

config.toml

Path: <repo-root>/.kasmos/config.toml

This is the project-local configuration file. It is the authoritative source for agent profiles, lifecycle phase mappings, UI behavior, orchestration tuning, and webhook hooks.

kasmos generates this file on first boot via kas setup. You can safely edit it by hand — it is re-read on each startup.

top-level fields

fieldtypedefaultdescription
default_programstringcodex when generated; auto-detected fallback (codexopencodeclaude)fallback agent executable when a role has no profile
auto_yesbooltruewhen true, the daemon automatically accepts all agent prompts
daemon_poll_intervalint (ms)1000how often the daemon checks session state (milliseconds)
branch_prefixstring<username>/prefix prepended to git branch names created by kasmos
notifications_enabledbool?truedesktop notifications; null defaults to enabled
claude_no_flickerbool?falseset CLAUDE_CODE_NO_FLICKER=1 on spawned claude agents; reduces TUI flicker but may affect prompt detection
database_urlstringremote task store URL (e.g. http://host:7433); local SQLite used when empty

[phases] — lifecycle phase-to-role mapping

Maps lifecycle phase names to agent role names. kasmos uses this to look up which [agents.<role>] profile to use for each phase.

[phases]
planning = "planner"
elaborating = "architect"
implementing = "coder"
spec_review = "reviewer"
quality_review = "reviewer"
fixer = "fixer"
readiness_review = "master" # also accepted as master_review (backward-compat alias)

The phase names on the left are fixed lifecycle identifiers. The role names on the right must match keys in the [agents] table.

readiness_review vs master_review: readiness_review is the canonical phase key — it maps to the master agent role that runs during the verifying FSM status. The old key master_review is accepted as a backward-compatible alias and is resolved to readiness_review at profile resolution time (not at load time). New configs should use readiness_review.

[agents.<role>] — agent profiles

Each role is configured in its own [agents.<role>] subsection. See the agent profiles reference for all available fields.

[agents.coder]
enabled = true
program = "codex"
model = "gpt-5.5"
execution_mode = "tmux"
tier = "fast"
effort = "low"

[agents.planner_opus]
enabled = true
program = "claude"
model = "claude-opus-4-8"
execution_mode = "tmux"
effort = "xhigh"
flags = ["--permission-mode bypassPermissions"]

permission_default can be set per profile to "prompt" or "bypass", or left unset to inherit the spawn-source default. "inherit" is accepted as an alias for unset, but normalized configs usually omit it; see agent profiles: permission_default for resolution details.

[ui] — UI behavior

fieldtypedefaultdescription
animate_bannerboolfalseenable idle banner animation
auto_advancebool?trueskip the planner→architect confirmation dialog; start elaboration automatically after planning finishes
auto_advance_wavesbool?trueskip confirmation dialog after a clean wave
auto_review_fixbool?trueautomatically start the review→fix→re-review loop
max_review_fix_cyclesint?0 (unlimited)cap the review-fix loop iterations; 0 means no cap
auto_readiness_reviewbool?truewhen true, reviewer approval transitions the task to verifying status and spawns the master agent for a holistic readiness check before done. When false, review_approved flows directly to done and no master agent is spawned.
readiness_self_fix_max_linesint?80maximum number of net lines the master agent may change in a single self-fix attempt; findings that would require more are escalated via verify_failed instead
readiness_max_verify_cyclesint?2maximum number of verify-round attempts before the loop is force-promoted to verify_approved. Must be a positive integer — values ≤ 0 are rejected at load time and fall back to the default. To disable the readiness gate entirely, set auto_readiness_review = false instead.
theme_sourcestring"static"color palette source: empty/"static" uses the built-in Rose Pine Moon palette; "system" resolves a Linux system-derived palette
system_theme_providerstring"auto"provider used when theme_source = "system": empty/"auto", "file", "caelestia", "freedesktop", or "gnome"
theme_palette_filestringpalette JSON file used by the "file" and "caelestia" providers; ~ is expanded, and relative paths resolve from .kasmos/config.toml's directory
[ui]
animate_banner = false
auto_advance = true
auto_advance_waves = true
auto_review_fix = true
max_review_fix_cycles = 3
auto_readiness_review = true # set to false to skip verifying status and go directly to done
readiness_self_fix_max_lines = 80 # master agent self-fix ceiling (net lines changed)
readiness_max_verify_cycles = 2 # force-promote after this many verify rounds

system theme example:

[ui]
theme_source = "system"
system_theme_provider = "file"
theme_palette_file = "~/.config/caelestia/kasmos-theme.json"

Omitting all three theme keys keeps the built-in Rose Pine Moon palette. Setting theme_source = "" or theme_source = "static" also disables system theming. When theme_source = "system" and system_theme_provider is empty or "auto", kasmos uses the Linux desktop color-scheme preference when it can and safely falls back to the built-in palette when it cannot. System theme providers are Linux-only; unsupported platforms, unreadable files, unknown providers, and invalid palette JSON all fall back to Rose Pine Moon.

The "file" and "caelestia" providers read a JSON palette with kasmos semantic role names. A top-level object is supported, and so is a nested colors or colours object. Missing roles inherit the default palette.

{
"base": "#232136",
"surface": "#2a273f",
"overlay": "#393552",
"text": "#e0def4",
"muted": "#6e6a86",
"subtle": "#908caa",
"iris": "#c4a7e7",
"foam": "#9ccfd8",
"gold": "#f6c177"
}

For Caelestia, configure Caelestia to generate a kasmos-specific palette/template target at the file path you set in theme_palette_file; the path above is only an example. Relative paths are resolved from the directory containing .kasmos/config.toml, so theme_palette_file = "themes/kasmos-theme.json" points at .kasmos/themes/kasmos-theme.json. Use system_theme_provider = "file" for a plain generated JSON file, or system_theme_provider = "caelestia" when you want the config to document that Caelestia owns the generated target. kasmos resolves the palette on startup only, so restart kasmos after regenerating the file.

[sdk] — SDK transcript retention

Controls the in-process memory limits for SDK-backed agent sessions (those with execution_mode = "sdk"). Only the in-process renderer is affected — the on-disk log file and tmux scrollback are not.

fieldtypedefaultdescription
transcript_max_bytesint644194304 (4 MiB)byte cap across flat lines and structured turns combined
transcript_max_turnsint642000completed-turn cap; the active in-progress turn is never evicted

value semantics:

  • key omitted — use the built-in default.
  • 0 — disable that dimension's limit (unlimited).
  • negative — normalized to 0 (unlimited).
[sdk]
transcript_max_bytes = 4194304 # 4 MiB (default; may be omitted)
transcript_max_turns = 2000 # (default; may be omitted)

To disable retention limits entirely (unlimited growth):

[sdk]
transcript_max_bytes = 0
transcript_max_turns = 0

When the byte limit is exceeded the renderer evicts the oldest completed turns first (FIFO), then trims the oldest flat lines, and finally truncates rows from the active turn as a last resort. Eviction markers appear in rendered output: earlier turns evicted: N in the structured presentation and [earlier lines evicted: N] in flat CapturePaneContent output.

See sdk backend: transcript retention for the full eviction model.

[telemetry] — crash reporting

fieldtypedefaultdescription
enabledbool?trueSentry crash reporting; null defaults to enabled
[telemetry]
enabled = false

[resources] — agent resource controls

Controls the CPU and I/O priority of kasmos-managed agent processes. The block is entirely optional — omitting it leaves default scheduling unchanged.

fieldtypedefaultdescription
profilestring"normal"preset to apply: normal (no-op), interactive, or custom
niceint(profile preset)process niceness value: 0–19 (higher = lower priority). Negative values are rejected.
ionice_classstring(profile preset)Linux I/O scheduling class: none, best-effort, or idle. realtime is rejected.
ionice_levelint(profile preset)I/O priority level 0–7; only valid for ionice_class = "best-effort"
build_jobsint(profile preset)-j parallelism hint injected into build tool environment; 0 = unset
go_package_parallelismint(profile preset)sets GOFLAGS=-p=<n> for go build/go test; 0 = unset
gomaxprocsint(profile preset)overrides GOMAXPROCS for agent processes; 0 = unset
max_parallel_wave_tasksint(profile preset)caps concurrent wave tasks; 0 = unset/unlimited
[resources.env]tableextra environment variables injected into every agent process; keys must be valid shell names and must not overwrite kasmos control variables

profiles:

  • normal (default) — no wrapper, no environment injection, no wave cap. Identical to omitting the block.
  • interactive — preset for latency-sensitive desktop workloads: nice = 10, ionice_class = "best-effort", ionice_level = 7, build_jobs = 1, go_package_parallelism = 1, gomaxprocs = 2, max_parallel_wave_tasks = 1. Individual keys override the preset.
  • custom — starts from normal and enables only the keys you supply explicitly. At least one control key must be set.

minimal example (interactive preset):

[resources]
profile = "interactive"
# optional overrides
build_jobs = 1
go_package_parallelism = 1
max_parallel_wave_tasks = 1

custom example:

[resources]
profile = "custom"
nice = 15
max_parallel_wave_tasks = 2

See the interactive resource controls guide for a step-by-step walkthrough including how to confirm the profile is active and how pending wave tasks behave.

[orchestration] — wave orchestration tuning

fieldtypedefaultdescription
blueprint_skip_thresholdint?2when a plan has ≤ this many tasks, kasmos skips elaboration and wave orchestration and runs a single-agent "blueprint" mode instead
planners[]string?["planner_opus", "planner_gpt"] in generated configsordered [agents.<profile>] names to run as parallel planners during plan_start; unset or [] keeps the legacy single-planner path
[orchestration]
blueprint_skip_threshold = 2
planners = ["planner_opus", "planner_gpt"]

Setting this to 0 forces multi-agent wave orchestration for every plan, including single-task ones. Setting it higher causes more plans to run in single-agent mode.

To run multiple planners, list the agent profiles in the order they should run:

[orchestration]
planners = ["planner_opus", "planner_gpt"]

[agents.planner_opus]
enabled = true
program = "claude"
model = "claude-opus-4-8"

[agents.planner_gpt]
enabled = true
program = "codex"
model = "gpt-5.5"

When planners is unset or [], kasmos keeps the legacy path: the configured planning profile writes the task store and emits planner_finished.

When planners contains profile names, the runtime sequence is:

  1. plan_start clears stale .kasmos/cache/<planSlug>-planner-<profile>.md files
  2. kasmos spawns one planner per listed profile in draft mode
  3. the first listed planner also updates the task store as a preview
  4. each draft-mode planner writes its cache file and emits gateway-only planner_draft_finished with {"planner_id":"<profile>"}
  5. the processor aggregates draft signals and internally advances planning when every expected profile has reported
  6. the final architect reads all planner draft caches, derives its own implementation baseline inline, writes the final task-store plan, and records consumed drafts in decision_audit.planner_drafts

parallel_planner_architect is a legacy key. If it remains in an older config, kasmos ignores it and logs a warning; use [orchestration].planners for current multi-planner behavior.

Blueprint skip still applies after the planner draft is available. A small planner draft can skip the final architect pass, so planner draft caches may be produced but unused.

[linear.receipts] — Linear receipt comments

Posts compact one-way comments from kasmos to explicitly linked Linear issues. See the linear receipts guide for setup and examples.

[linear.receipts]
enabled = true
events = ["implement_start", "implement_finished", "review_approved"]
pr = true
merge = true
cancel = true

[linear.receipts.state_map]
implementing = "lin_state_in_progress"
reviewing = "lin_state_in_review"
done = "lin_state_done"
cancelled = "lin_state_cancelled"
fieldtypedefaultdescription
enabledboolfalseenable Linear receipt comments for tasks with explicit Linear links
events[]string["plan_start", "implement_start", "implement_finished", "review_approved", "review_changes_requested", "verify_approved", "verify_failed", "mark_done", "cancel", "reopen"]lifecycle FSM events that post status receipts; omit or set empty to use the default allowlist
prbool?truepost a PR receipt after a PR URL is persisted
mergebool?truepost a merge receipt when kasmos merges a task branch
cancelbool?truepost a cancellation receipt when kasmos cancels a task
[linear.receipts.state_map]tableoptional map from kasmos status (ready, planning, implementing, reviewing, verifying, done, cancelled) to Linear workflow state id

receipt failures never block kasmos lifecycle transitions. missing Linear API credentials are silent; other Linear errors are recorded as EventTaskLinearReceiptFailed audit rows.

[linear.triggers] — guarded Linear-side triggers

Polls Linear for strict /kasmos <verb> comments and configured trigger labels, then routes accepted work through kasmos link/create/FSM paths. See the linear triggers guide for operator setup and troubleshooting.

[linear.triggers]
enabled = true
poll_interval = "1m"
lookback = "15m"
max_issues_per_poll = 100
verbs = ["help", "status", "link", "create", "plan", "start"]
ack_comment_body = "kasmos trigger ack"

[[linear.triggers.routes]]
team_id = "6f24259a-1f9f-4c5b-9f14-9dff1f6b2b21"
project_id = "a82f4c97-3e15-47ef-9a7a-2b04f4e2a3dd"
require_labels = ["4d1a5ec7-3e1d-4f2a-9a66-8a5e64f0d721"]
topic = "linear"
branch_prefix = "linear/"

[linear.triggers.labels]
create = "0a5bf2d7-b581-4183-9e28-3fef63d47a18"
plan = "c9170d93-68cf-4370-a7ea-6503a1dd30be"
start = "89c8c2bf-d118-42e0-8f3d-85eeb7d775db"
ack = "a8a63a51-5042-4697-a5dd-6a3e342fffd7"

[linear.triggers.actor]
allowed_user_ids = ["a7c74ca9-8a18-4f62-a784-76ec43a5cb87"]
allowed_user_emails = ["ops@example.com"]
allow_public_status = true

[linear.triggers.start_guard]
require_start_label = true
allow_label_start = false

[linear.triggers.webhook]
enabled = true
secret_env = "KASMOS_LINEAR_WEBHOOK_SECRET"
timestamp_tolerance = "5m"
max_body_bytes = 1048576
fieldtypedefaultvalidation
enabledboolfalsewhen false, the zero-value trigger config is used and no trigger routes are required
poll_intervalduration"1m"values below "15s" are clamped to "15s"; zero or omitted uses the default
lookbackduration"15m"zero or negative values use the default first-poll comment lookback
max_issues_per_pollint100values <= 0 use the default
verbs[]string["help", "status", "link", "create", "plan", "start"]each value must be one of help, status, link, create, plan, or start; mutating verbs require an actor allowlist
ack_comment_bodystring"kasmos trigger ack"empty uses the default acknowledgement comment body
[[linear.triggers.routes]]table arrayat least one route is required when enabled; duplicate routes by team_id, project_id, and sorted require_labels are rejected
team_idstringrequired Linear team UUID for each route
project_idstring""optional Linear project UUID route qualifier
require_labels[]string[]optional Linear label UUIDs; all listed labels must be present on the issue
topicstringrequired kasmos topic for tasks created by the route
branch_prefixstring""optional branch prefix appended before the generated task filename
[linear.triggers.labels]tableall emptyoptional UUID map for label-source triggers and acknowledgements
createstring""Linear label UUID that enqueues create intents
planstring""Linear label UUID that enqueues create-and-plan intents
startstring""Linear label UUID used by start guards and optional label-only start
ackstring""optional Linear label UUID applied after successful create or plan label triggers
[linear.triggers.actor]tableempty allowlistrequired for any enabled mutating verb
allowed_user_ids[]string[]canonical Linear User.id allowlist for mutating comment commands
allowed_user_emails[]string[]friendly aliases resolved to Linear users on first hit and cached
allow_public_statusboolfalsewhen true, /kasmos status and /kasmos help can bypass the actor allowlist
[linear.triggers.start_guard]tableboth falseoptional guard settings for start triggers
require_start_labelboolfalsewhen true, /kasmos start requires the issue to carry labels.start
allow_label_startboolfalsewhen true, label-only start is allowed; validation requires labels.start
[linear.triggers.webhook]tabledisabledoptional inbound linear webhook ingestion settings
enabledboolfalsewhen true, kas serve accepts signed linear webhook deliveries for the project endpoint
secret_envstringKASMOS_LINEAR_WEBHOOK_SECRETenvironment variable that contains the linear webhook signing secret
timestamp_toleranceduration"5m"accepted delivery timestamp skew; clamped from "1m" to "15m"
max_body_bytesint641048576maximum raw request body size before the webhook returns body_too_large

linear webhook delivery ids are part of the ingestion contract, not optional metadata. requests missing Linear-Delivery are rejected with missing_delivery before kasmos records a delivery row or evaluates duplicate state, so a proxy/header issue cannot collapse unrelated deliveries onto an empty dedupe key. if a delivery was recorded but processing stopped in received or failed, a retry with the same delivery id resumes normalization and enqueue instead of being treated as a terminal duplicate. after accepted deliveries enqueue trigger rows, kas serve drains the queue until no rows remain or a retryable error stops the pass.

[[hooks]] — FSM transition hooks

Hooks fire when a task transitions between lifecycle states. They are defined as an array of tables.

webhook hook

[[hooks]]
type = "webhook"
url = "https://your-server.example.com/webhook"
events = ["implement_start", "implement_finished", "review_approved"]

[hooks.headers]
Authorization = "Bearer your-secret-token"
X-Source = "kasmos"

command hook

[[hooks]]
type = "command"
command = "/usr/local/bin/notify-team.sh"
events = ["review_approved"]

hook fields

fieldtypedescription
typestring"webhook" or "command"
urlstring(webhook) full URL to POST to
headerstable(webhook) additional HTTP headers
commandstring(command) shell command to execute
events[]stringFSM event names that trigger this hook

Valid event names: plan_start, planner_finished, implement_start, implement_finished, request_review, review_approved, review_changes_requested, start_over, reimplement, cancel, reopen.

full example

# Generated by kas setup

default_program = "codex"
auto_yes = true
daemon_poll_interval = 1000
branch_prefix = "alice/"
notifications_enabled = true
claude_no_flicker = false

[phases]
planning = "planner"
elaborating = "architect"
implementing = "coder"
spec_review = "reviewer"
quality_review = "reviewer"
fixer = "fixer"
readiness_review = "master"

[agents.planner_opus]
enabled = true
program = "claude"
model = "claude-opus-4-8"
temperature = 0.3
execution_mode = "tmux"
tier = "fast"
effort = "xhigh"
flags = ["--permission-mode bypassPermissions"]

[agents.planner_gpt]
enabled = true
program = "codex"
model = "gpt-5.5"
temperature = 0.3
execution_mode = "tmux"
tier = "fast"
effort = "xhigh"

[agents.architect]
enabled = true
program = "claude"
model = "claude-opus-4-8"
temperature = 0.2
execution_mode = "tmux"
tier = "fast"
effort = "xhigh"
flags = ["--permission-mode bypassPermissions"]

[agents.coder]
enabled = true
program = "codex"
model = "gpt-5.5"
temperature = 0.1
execution_mode = "tmux"
tier = "fast"
effort = "low"

[agents.reviewer]
enabled = true
program = "claude"
model = "claude-sonnet-4-6"
temperature = 0.2
execution_mode = "tmux"
tier = "fast"
effort = "medium"
flags = ["--permission-mode bypassPermissions"]

[agents.master]
enabled = true
program = "codex"
model = "gpt-5.5"
temperature = 0.2
execution_mode = "tmux"
tier = "fast"
effort = "xhigh"

[agents.fixer]
enabled = true
program = "codex"
model = "gpt-5.5"
temperature = 0.1
execution_mode = "tmux"
tier = "fast"
effort = "high"

[ui]
animate_banner = false
auto_advance = true
auto_advance_waves = true
auto_review_fix = true
max_review_fix_cycles = 3
auto_readiness_review = true
readiness_self_fix_max_lines = 80
readiness_max_verify_cycles = 2
theme_source = "system"
system_theme_provider = "file"
theme_palette_file = "~/.config/caelestia/kasmos-theme.json"

[telemetry]
enabled = true

[orchestration]
blueprint_skip_threshold = 2
planners = ["planner_opus", "planner_gpt"]

[sdk]
transcript_max_bytes = 4194304 # 4 MiB per sdk session renderer
transcript_max_turns = 2000 # completed structured turns retained

[[hooks]]
type = "webhook"
url = "https://hooks.example.com/kasmos"
events = ["implement_finished", "review_approved"]

troubleshooting

If kasmos does not seem to pick up your config changes, run:

kas debug

This prints the resolved config path and the fully-parsed configuration, so you can confirm the right file is being read.