Architecture
ka is a workspace of small crates with a strictly one-way dependency direction: ka-harness → ka-term / ka-protocol / ka-dialect / ka-strand → ka-engine. The engine never imports a surface.
ka-harness ──► ka-term (ratatui TUI) │ ► ka-protocol (Command / Event enums) │ ► ka-dialect (catalog + 3 wires + discovery) │ ► ka-strand (append-only sessions) └────────────► ka-engine (engine: turn machine, Hands, gate, digests) ka-index (SQLite FTS, optional) ka-sandbox (landlock/…)The binary crate and the future Ka Agent
Section titled “The binary crate and the future Ka Agent”The binary crate is published as ka-harness (renamed from ka-agent in v0.3.0; the old name is reserved for Ka Agent, a planned separate product: an optional persistent companion with long-term memory, messaging channels, and a web UI). Ka Agent will build on ka strictly from the outside, through the shipped surfaces: ka run --print ndjson, ka serve, ka acp, the strand format, or the binary as a subprocess. Resident-agent concerns (channels, schedulers, plugin runtimes, memory beyond strands) never enter these crates, and CI gates the binary’s dependency graph (cargo xtask deps) to keep it that way. Decision record: research/ka-harness/agent-boundary.md.
The protocol (ka-protocol)
Section titled “The protocol (ka-protocol)”Two queues connect any surface to the engine, both plain serde enums, both NDJSON-shaped:
- Command (surface → engine):
Prompt,Steer(mid-turn steering; wire taginterject),Queue(held until the turn settles; wire tagdefer),Abort,SetModel,SetEffort,SetMode,AlwaysAllow,Resume,Compact,Answer… - Event (engine → surface):
TurnStarted(with a context meter),Delta(text, thought, or tool call),CallStarted/CallFinished,Ask,TurnFinished(stop kind, usage, cost, cache-hit),DigestStarted/DigestFinished,ModeChanged,Error…
Because the protocol is NDJSON-first, ka run --print ndjson is literally the engine’s Event side serialized. The TUI, the HTTP server, and the ACP adapter are all just different consumers of the same stream.
The turn machine (ka-engine)
Section titled “The turn machine (ka-engine)”Every prompt runs through four states:
- Receiving: the prompt lands, guards and trust are checked.
- Speaking: the active
Speakerstreams the model’s response. Steers are checked between deltas;Escaborts, keeping the partial transcript. - Acting: tool calls whose clearance is granted run in parallel (
FuturesUnordered), with results re-ordered to call order and per-tool timeouts. Exec-tier calls wait for your approval when in guarded mode. Filesystem work (read/grep/glob and the per-turn git snapshot) runs on blocking threads, and grep scans large candidate sets on parallel workers with byte-identical output, so a big repo walk never stalls streaming, redraws, or interrupts. - Settling: post-turn maintenance. Ledger sync, digest threshold checks, queue drain, harness checks, strand flush.
Speaking and Acting alternate until the model stops calling tools or the step cap (max_steps, default 20) forces a text reply.
Engine purity
Section titled “Engine purity”The engine crate knows nothing about terminals, TOML files, or any vendor’s HTTP quirks:
Speakeris the provider trait (“the model speaks through it”). The three implementations (Anthropic messages, OpenAI chat, OpenAI Responses) live inka-dialect, vendored on reqwest + rustls with hand-rolled SSE. No SDK crates.Handsis the tool registry trait (“the engine acts through them”). Every tool is a struct behind the same seam, which is where MCP servers,pathfinder, anddelegateplug in.- A strand is the state: replaying its records to the tip reproduces everything: model, effort, mode, queued items. No other persistence exists in core.
Context survival
Section titled “Context survival”Context pressure is handled by a ladder, cheapest first:
- Caps + spill: oversized tool output is parked on disk and referenced as
spill://<id>(default 32 KB tail / 8 KB head) before the model ever sees it. - Prune: blank tool results older than the protected window are elided with markers.
- Digest: a same-model summary that never cuts between a tool call and its result; triggers at a reserve threshold.
- Promotion: on overflow, ka can re-dispatch to a bigger-context sibling model (
[context] promote, on by default).
Verification culture
Section titled “Verification culture”- Footprint contract in CI: binary size, cold-start, and idle RSS asserted against committed
baselines.json. - Wire fixtures: recorded SSE transcripts per wire (including malformed chunks and partial-JSON repair cases) replayed in CI; never a live key.
- Property tests: strand replay determinism, ledger refusal on stale writes, bash decomposition tables, hardstop matrix including wrapper obfuscation.
Read the original design document in the repository: research/ka-harness/architecture.md.