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.
sdk-fast is not a config value
execution_mode accepts exactly two stable values: "tmux" and "sdk" (plus the legacy alias "headless"). there is no "sdk-fast" config value.
the fast speed tier for codex is a per-spawn, per-session option selected at spawn time through the S execution-mode picker. it is stored as SDKSpeedTier = "fast" on the instance record and forwarded by the codex transport as serviceTier: "fast" on thread/start. it does not appear in config.toml and cannot be set as a profile default. to get a fast-tier codex session you must choose sdk-fast in the S picker each time you spawn.
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.
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.