Chat pipeline
How krad-chat drives each harness without a terminal and turns its stream into the same chat events.
A chat drives its harness without a terminal and renders its stream natively. Each harness speaks its own protocol. krad-chat has one adapter per harness, and all adapters produce the same live events, so the rest of the app (the chat view, asks, diffs, skills, the agents bus) handles every harness the same way.
One adapter per harness
| Harness | Process | Protocol |
|---|---|---|
| Claude Code | claude -p --input-format stream-json --output-format stream-json --verbose, one live process per conversation | stream-json over stdin and stdout, with the control protocol the Agent SDK speaks |
| Codex | codex app-server | JSON-RPC requests and notifications |
| opencode | opencode serve --stdio --port 0, a shared server | The server's events |
| Gateway | None; the app's own engine | Anthropic Messages, OpenAI Responses or Chat Completions over HTTP |
The live events cover chat events (text, tool calls, diffs and states), asks, the model catalogue once the harness is ready, plan usage, notices, the result of a rewind, and a refused resume.
Claude Code
The chat starts one process per conversation and writes to its stdin. A message written while a turn is still running is not lost: it queues inside that process and Claude Code picks it up when it can.
- Claude Code stores the conversation itself. The app passes
--session-idthe first time and--resumewhen it starts the process again later. - Tool approvals and the harness's questions come as the control protocol's
can_use_toolrequests, which the chat shows as asks. - The model switches with
set_model, and a turn stops withinterrupt. - Rewind restarts the process with
--resume-session-atand--resume-drops-turn. Files are restored through Claude Code's file checkpointing, which the app turns on for its chats. - Terminal view switches in place, over the same conversation. A copy of the conversation can continue in a new tab with
--resume <id> --fork-session.
Codex and opencode
Codex runs its app server. Rewind calls thread/revert, which keeps files, so only the conversation is rewound.
opencode runs as a shared server. Rewind stages and commits a revert, which also restores files. opencode Workers open as chats driven by that server, with no terminal; their brief is their first message.
For both, terminal view reopens the conversation in the other surface.
The Gateway engine
Gateway chats run on a loop inside krad-chat. The model's reply streams in, the engine runs the tools it asked for, returns their output to the model and goes round again. A message you send mid-loop is added at the next round, and old turns are summarised once the context is full. The protocol is picked per model from the owner the Gateway's /v1/models reports.
- Tools follow Codex's: read, write, edit, find, grep and shell, plus web fetch and search, questions, plans, checklists, subagents and MCP tools.
- The chat's permission mode decides each call. Commands known to only read run freely, risky commands always ask, and approval for an edit outside the group's folder is asked each time rather than remembered.
- The engine's MCP client speaks initialize,
tools/listandtools/callover stdio and streamable HTTP. - It runs synchronously on threads of its own and writes each chat's session to a JSONL file in the config folder.
History that survives restarts
Besides the harness's own record, the app saves the parsed events of every chat, keyed by its resume id, through krad-store. That is why a chat shows its past messages straight away on reopen, and still shows them if the harness can no longer continue the conversation. When a harness rejects the id, the chat starts a new conversation and says so.
Why no async runtime
krad-chat does not use an async runtime. The Gateway engine uses plain threads, ureq for HTTP and the ripgrep crates for search. The Codex JSON-RPC client and the MCP client are small hand-written ones built on threads and channels, rather than the official async SDKs.
Boundaries
- Closing a chat or the app stops its process. There is no daemon.
- A harness process that hangs is stopped the same way a hung terminal is.
- Terminal view stays disabled until the harness has reported the conversation's resume id.
See Agents bus for how a lead's chat starts and follows Workers.