/** * KimiCodeAgentRunner — AgentRunner impl backed by the Kimi Code CLI. * * Architectural difference from ClaudeAgentRunner: * - Claude's `query()` runs the whole agent loop IN-PROCESS (tools execute * inside the same Node process as the daemon). That's why ClaudeAgentRunner * can register org tools (org_send, ask_human, …) directly via * createSdkMcpServer. * - Kimi Code has no embeddable SDK. The CLI is driven as a subprocess: * `kimi --print --output-format stream-json` with the prompt on stdin * runs one non-interactive turn and emits JSONL events on stdout * (event shapes verified against kimi 0.29.2: * {"role":"assistant","content":...} per reply, then a * {"role":"meta","type":"session.resume_hint",session_id} event). * Session continuity comes from `--session ` on later turns. * The prompt is NOT passed as `-p ` — see streamTurn for why. * * Streaming / liveness — WHY INCREMENTAL: * Kimi turns routinely run 10-20+ minutes when the model chains many * internal tool calls (observed: 45+ steps in one turn). session.ts races * the FIRST pull from this runner against a 4-minute silent-stream * watchdog, so buffering stdout until process exit (the original design) * meant any turn longer than 4 minutes yielded zero messages in time — * abort, retry, kill, circuit breaker, stalled org. This runner therefore * parses stdout LINE BY LINE as data arrives: a liveness `tool_use` * message is yielded the moment the subprocess spawns (deterministically * winning the first-pull race regardless of model-thinking latency), * assistant text is yielded as each event lands, and kimi's own tool * calls (an assistant message's `tool_calls`) and results * ({"role":"tool","tool_call_id",...}) are forwarded as rich * `tool_use`/`tool_result` pairs matched by kimi's call id, so the * StateDetector/idle watchdog see a working agent throughout the turn and * agent exec gets full-fidelity tool_activity. Tool_call fences are still collected from * the raw texts and parsed at end of turn (fence parsing needs the * complete text). * * Org tools (org_send, knowledge_search, ask_human, …) — FENCE PROTOCOL: * kimi's tool surface can only be extended via MCP servers or plugins, both * loaded by the CLI itself, not by an external caller per-turn. Instead the * tools are rendered INTO the role's system prompt: the model emits * ```tool_call fenced JSON blocks, this runner parses them out of the * assistant text, executes the real OrgToolDef handlers in-process (the same * handlers ClaudeAgentRunner registers with the SDK), and feeds the results * back as the next prompt IN THE SAME kimi session. Loop repeats until a * turn produces no tool calls (cap: AgentRunArgs.maxToolRounds, default MAX_TOOL_ROUNDS). Tool-call fences are * stripped from the text yielded to session.ts so the bus only sees prose. * * Usage accounting — WIRE FILE: * kimi's stream-json has no usage/result event, but every session writes * usage.record entries to $KIMI_CODE_HOME/sessions/// * agents/main/wire.jsonl. After each CLI turn this runner reads the new * entries written since each round started (timestamp-filtered, so a * resumed session's historical entries are never double-counted) and * attaches the summed tokens to the synthesized result message session.ts * needs for budget checks. * * Non-disturbance guarantees (mirrors the opencode integration): * - No new package dependency: the runner shells out to the `kimi` binary * via node:child_process; nothing is imported at module load time. * - The runner is only constructed when MONOMIND_RUNTIME=kimicode is set * (daemon.ts runner resolution). Without the env var, or without a `kimi` * binary on PATH, the Claude path is byte-for-byte unchanged and run() * rejects with a clear actionable error instead of crashing at import. * * File-size sweep: the stream-json parser + fatal-error classification live * in kimicode-runner-parse.ts, and the subprocess turn runner (streamTurn), * usage-file reader (readUsageDelta) and turn-error builder (turnError) live * in kimicode-runner-stream.ts — both re-exported below where other modules * import them from here. */ import type { AgentMessage, AgentRunArgs, AgentRunner } from './agent-runner.js'; export type { FatalErrorInfo, KimiStreamEvent } from './kimicode-runner-parse.js'; export { classifyStderr, parseStreamJsonLine, parseStreamJsonLines, } from './kimicode-runner-parse.js'; export declare class KimiCodeAgentRunner implements AgentRunner { private kimiBin?; private emptySkillsDir; constructor(kimiBin?: string | undefined); run(args: AgentRunArgs): AsyncIterable; } //# sourceMappingURL=kimicode-runner.d.ts.map