configuring the sdk backend
the sdk backend is configured per agent role in .kasmos/config.toml using the execution_mode field.
the execution_mode field
each [agents.<role>] block in config.toml accepts an optional execution_mode key:
[agents.coder]
enabled = true
program = "claude"
flags = ["--permission-mode bypassPermissions"]
execution_mode = "sdk"
accepted values:
| value | behavior |
|---|---|
"sdk" | drives the agent via its app-server JSON-RPC protocol |
"headless" | legacy alias for "sdk" — accepted but "sdk" is preferred |
"tmux" | (default) runs the agent inside a tmux session |
"" (empty) | falls back to "tmux" |
| any other string | falls back to "tmux" |
the fallback behavior is implemented by config.NormalizeExecutionMode at the config layer:
// config/profile.go
func NormalizeExecutionMode(mode string) string {
switch strings.TrimSpace(mode) {
case ExecutionModeTmux:
return ExecutionModeTmux
case ExecutionModeSDK, "headless":
return ExecutionModeSDK
default:
return ExecutionModeTmux
}
}
and by session.NormalizeExecutionMode at the session layer:
// session/execution.go
func NormalizeExecutionMode(mode ExecutionMode) ExecutionMode {
switch ExecutionMode(strings.TrimSpace(string(mode))) {
case ExecutionModeSDK, "headless":
return ExecutionModeSDK
case ExecutionModeTmux:
return ExecutionModeTmux
default:
return ExecutionModeTmux
}
}
unknown values — including typos like "SDK" or "Headless" — silently fall back to "tmux". the comparison is case-sensitive.
program compatibility
setting execution_mode = "sdk" only takes effect if the program has an sdk transport. currently only claude and codex are supported. for any other program, session.ResolveExecutionMode silently downgrades the mode to tmux:
// session/execution.go
func ResolveExecutionMode(requested ExecutionMode, program string) ExecutionMode {
normalised := NormalizeExecutionMode(requested)
if normalised == ExecutionModeSDK && !sdk.SupportsProgram(program) {
return ExecutionModeTmux
}
return normalised
}
consequence: if you set execution_mode = "sdk" for opencode, gemini, amp, or a custom program, kasmos will silently run it in tmux instead. kas debug output will still show "sdk" from the profile (the normalized config value), but the runtime session is a tmux session.
full agent block example
[agents.coder]
enabled = true
program = "claude"
model = "claude-opus-4-5"
flags = ["--permission-mode bypassPermissions"]
execution_mode = "sdk"
[agents.reviewer]
enabled = true
program = "claude"
execution_mode = "tmux"
this configuration runs the coder agent via the claude app-server protocol while keeping the reviewer in tmux for interactive attach capability.
permission defaults
permission_default controls the resolved SkipPermissions bool passed to the session runtime before the SDK transport starts:
[agents.coder]
enabled = true
program = "claude"
execution_mode = "sdk"
permission_default = "bypass"
Accepted values are "inherit" or omitted, "prompt", and "bypass"; see agent profiles: permission_default for the normalization table and spawn-source defaults. Claude SDK and Codex SDK both receive the same final SkipPermissions bool. The SDK backends do not expose a finer-grained permission policy today.
Raw harness flags are still appended to the command where applicable, so flags = ["--permission-mode", "bypassPermissions"] can still force Claude's bypass mode even if permission_default = "prompt".
speed tier: profile default + per-spawn override
execution_mode accepts exactly two stable values: "tmux" and "sdk" (plus the legacy alias "headless"). there is no "sdk-fast" value for execution_mode — the speed tier is a separate concern from the execution mode.
profile default: [agents.<role>].tier sets the per-role Codex SDK speed tier for any session spawned by orchestration into that role. see agent profiles: tier for the full normalization table and gating conditions.
per-spawn override: the S execution-mode picker offers an sdk-fast choice that overrides the profile default for that one spawn. it is stored as SDKSpeedTier = "fast" on the instance record and forwarded by the codex transport as serviceTier: "fast" on thread/start. the choice is not written back to config.toml.
daemon api: external callers can supply the same per-spawn tier via the sdk_speed_tier field on POST /instances.
standalone spawn modes
three TUI entry points (N, s, S) also respect execution_mode when spawning standalone (ad-hoc) agents.
-
squick launch reads the fixer profile. addexecution_mode = "sdk"there to makessessions use the sdk backend:[agents.fixer]enabled = trueprogram = "claude"execution_mode = "sdk" -
Sspawn agent andNnew with prompt read the master (and default/chat) profiles respectively. the execution mode resolved from those profiles is used as the default for the spawned session:[agents.master]enabled = trueprogram = "claude"execution_mode = "sdk"
unsupported programs always use tmux. if fixer or master programs are set to opencode, gemini, amp, or any custom binary, session.ResolveExecutionMode downgrades to tmux regardless of the execution_mode setting — sdk.SupportsProgram in session/sdk/registry.go determines which programs have a transport.
per-phase role mapping
the [phases] table maps lifecycle phases to agent roles. the execution mode is resolved by looking up the role's profile:
[phases]
implementing = "coder"
quality_review = "reviewer"
when kasmos starts an implementing session, it reads agents.coder.execution_mode, normalizes it, and then calls ResolveExecutionMode with the program to determine the actual backend. the resolution path is:
phases.implementing→"coder"agents.coder.execution_mode→"sdk"config.NormalizeExecutionMode("sdk")→"sdk"session.ResolveExecutionMode(ExecutionModeSDK, "claude")→ExecutionModeSDK(claude is supported)session.NewExecutionSession(ExecutionModeSDK, ...)→sdk.New(...)
if the program were opencode instead, step 4 would return ExecutionModeTmux and step 5 would create a tmux session.
for a codex coder with [agents.coder].tier = "fast", the parallel tier resolution path is:
[agents.coder].tier→"fast"config.NormalizeTier("fast")→"fast"sdkSpeedTierForAgent(repoDir, AgentTypeCoder)indaemon/daemon.go→"fast"session.Instance.SDKSpeedTier="fast"sdk.Transport.Start(..., SpeedTier: "fast")→ codex transportcodexServiceTier("fast")→thread/startpayloadserviceTier: "fast"
the default_program fallback
if a phase has no role mapping, or the role's profile is disabled or has an empty program, kasmos falls back to default_program with tmux mode:
// config/profile.go
func (c *Config) ResolveProfile(phase string, defaultProgram string) AgentProfile {
// ...
if profile.Program == "" || !profile.Enabled {
return AgentProfile{Program: defaultProgram, ExecutionMode: ExecutionModeTmux}
}
profile.ExecutionMode = NormalizeExecutionMode(profile.ExecutionMode)
return profile
}
the fallback always uses ExecutionModeTmux — there is no way to make the default fallback use sdk mode without an explicit [agents.*] block.
inspecting resolved configuration
kas debug
prints the config path and the fully resolved Config struct as JSON, including the normalized execution_mode for each agent profile. use this to confirm that "headless" aliases were resolved to "sdk". the runtime tmux fallback for unsupported programs is not reflected here — as noted above, kas debug shows the profile's configured value ("sdk"), not the actual backend used at session start.