feat(agents): wire Kaiten Agents into columns - #275
Merged
Conversation
A `spawn_cli` trigger now carries `agent_id`. When set, the agent supplies the CLI, instructions, tools and its preferred model, and the column may override model only — the rule the Kaiten Agents spec locked in, enforced in `pipeline::spawn::resolve()` and nowhere else. Precedence reads task > trigger > agent > workspace > global > default. A leftover `cli` token on the column is logged and ignored rather than obeyed: the roster tile has to tell the truth about what runs. The UI says the same thing by removing controls it can't honour — the CLI token disappears from the automation sentence once an agent is attached, and the Advanced editor swaps its dropdown for "Set by <agent>". Instructions ship as `.agent.md`, not a system-prompt flag. No flag spans the three runtimes. codex has none. claude's `--append-system-prompt` is last-wins rather than cumulative — verified against 2.1.239, two flags and only the second applied — and interactive mode already spends it on the done-sentinel, appending it after user args. So routing instructions through it would silently drop them in exactly the columns most likely to use an agent. Scripts have no prompt concept at all. So instructions and skills render to a file in the working dir and the prompt gains a newline-free pointer to it, the convention `.task.md` already established. One mechanism across three runtimes and three runtime modes, no flag-existence assumptions, and switching a column between headless and interactive cannot change what the agent was told — mode is a rendering and billing choice and has no business altering behaviour. Verified end-to-end against a real `claude -p`. A stale `.agent.md` is removed when a column has no agent, since worktrees are reused and it would otherwise keep instructing the next column. Script agents run too, through the same tmux transport, so they get a live terminal, exit-code completion and an agent card. They bypass `validate_agent_cli_path`: that allow-list guards hand-editable trigger JSON, and a script agent's command comes from an `agents` row authored in the Roster UI — the same trust level `run_script` already grants user-authored scripts. They are forced to `terminal` mode, since managed parses an LLM event stream and interactive drives a TUI, and they receive the prompt via `$TRIGGER_PROMPT` rather than argv — the generic command builder appends the prompt as a positional argument, which would break `./render.sh --preset high` on the extra word. `get_agent_usage` sweeps every workspace for columns running an agent; agents are global, so scoping it to the open board would miss most of them. The dossier lists them and the delete confirmation names them. Deleting is still allowed — blocking it would leave no way to remove an agent attached to a workspace you no longer have open — and a fired trigger for a missing agent fails loudly by name instead of quietly falling back to a bare CLI. The `Runtime` trait still has no `execute`. `roster::plan::plan_for()` builds spawn parameters; resolve applies precedence; neither launches anything. 44 new tests (13 Rust, 10 frontend, plus the plan/precedence coverage). Renames the action clause from "run an AI agent" to "run an agent", since it now covers script agents too.
This was referenced Aug 22, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The Roster shipped agents you could craft but never run. Now a column can run one.
A
spawn_clitrigger carriesagent_id. When set the agent supplies the CLI, instructions, tools and its preferred model, and the column may override model only — the rule the Kaiten Agents spec locked in, enforced inpipeline::spawn::resolve()and nowhere else.A leftover
clitoken on the column is logged and ignored rather than obeyed: the roster tile has to tell the truth about what runs. The UI says the same thing by removing controls it can't honour — the CLI token disappears from the automation sentence once an agent is attached, and Advanced swaps its dropdown for "Set by <agent>".The one real design decision: instructions ship as a file
No flag spans the three runtimes, so I checked each rather than assuming:
--append-system-promptis last-wins, not cumulative — verified against 2.1.239: two flags, only the second appliedThat second one is the trap. Interactive mode already spends
--append-system-prompton the done-sentinel and appends it after user args, so routing an agent's instructions through it would have silently dropped them in exactly the columns most likely to use an agent — the same class of bug as the codex flag that killed every sentinel-carrying column before.So instructions and skills render to
.agent.mdin the working dir and the prompt gains a newline-free pointer to it — the convention.task.mdalready established. One mechanism across 3 runtimes × 3 runtime modes, no flag-existence assumptions, and switching a column between headless and interactive cannot change what the agent was told. Mode is a rendering and billing choice; it has no business altering behaviour.Verified end-to-end against a real
claude -p: given an.agent.mdsaying "begin every reply with BENTO-OK", the reply began with BENTO-OK.A stale
.agent.mdis removed when a column has no agent — worktrees are reused across columns, so leaving it would let one column's agent keep instructing the next.Script agents run too
Same tmux transport, so they get a live terminal, exit-code completion and an agent card. Two things they need that LLM agents don't:
validate_agent_cli_path. That allow-list guards hand-editable trigger JSON; a script agent's command comes from anagentsrow authored in the Roster UI — the same trust levelrun_scriptalready grants user-authored scripts. Flagging explicitly because it is me widening a security boundary.$TRIGGER_PROMPT, never argv. The generic command builder appends the prompt as a positional argument, which would break./render.sh --preset highon the extra word. They're also forced toterminalmode, sincemanagedparses an LLM event stream andinteractivedrives a TUI.Deleting an agent that columns use
get_agent_usagesweeps every workspace — agents are global, so scoping to the open board would miss most attachments. The dossier lists the columns; the delete confirmation names them.Deleting is still allowed, not blocked: forbidding it would leave no way to remove an agent attached to a workspace you no longer have open. A fired trigger for a missing agent fails loudly by name instead of quietly falling back to a bare CLI, which would look like it still worked.
The seam stayed shut
Runtimestill has noexecute.roster::plan::plan_for()builds spawn parameters,resolve()applies precedence, neither launches anything — so there is still exactly one copy of the cli/model/cwd/prompt rules.Not in this slice
MCP +
/api/*tools for agents, RAG, typed I/O, Orchestrator-as-section, per-task agent override. Listed under "Still v2" in the spec.Checks
cargo clippy -D warnings·cargo test557 (+13) ·tsc·eslint·test:ipc·test:type-scale·vitest444 (+10) ·vite build·pnpm audit.Visually verified in browser-mock: picker lists agents plus a bare-CLI escape hatch, CLI token vanishes on attach, script agents show neither CLI nor model.