English static mirror for SEO/GEO · AI-assisted translation · Read Chinese original

Deep Dive into the Kimi Code CLI Wire Protocol: The Communication Bridge Between Soul and UI

Forum topic · 小凯 · 2026-02-21

Summary

This article presents an in-depth, source-level analysis of the Wire protocol in Kimi Code CLI, the internal messaging bus connecting the agent's core (Soul) with its user interfaces. The protocol employs a layered architecture that decouples reasoning from presentation, supports real-time streaming via Pydantic-typed events, and offers optional JSON Lines persistence (wire.jsonl) for debugging and replay. Key components include the Wire class with raw and merged queues under a Single-Producer Multi-Consumer model, a Soul-side sender with in-place text merging to reduce UI re-renders, and a UI-side asynchronous receiver. Messages fall into two categories: Events such as TurnBegin, StepBegin, ToolCall, and StatusUpdate for one-way notifications, and Requests like ApprovalRequest and ToolCallRequest that use asyncio.Future for asynchronous waiting. The article contrasts Wire with MCP and LSP, notes its native support for broadcasting, backpressure, and audit-friendly recording, and provides concrete examples for extending the protocol.

Key points

  • Architecture and rationale: Wire is the decoupling layer between Kimi Code CLI's Soul (agent core: KimiSoul, Toolset, Context) and its UIs (Shell TUI, ACP Server, Print). Design goals are decoupling, real-time streaming, persistence for replay/debug, and multi-cast to several UIs simultaneously.
  • Core components:
  • Wire class: communication bus holding a raw queue, a merged queue, a WireSoulSide, and an optional _WireRecorder for file logging.
  • WireSoulSide.send: publishes to raw queue, then routes mergeable messages into _merge_buffer via merge_in_place; non-mergeable messages trigger flush().
  • WireUISide: async receiver with backpressure support.
  • Message taxonomy:
  • Events (one-way): TurnBegin, TurnEnd, StepBegin, StepInterrupted, CompactionBegin, CompactionEnd, StatusUpdate (carries context_usage, token_usage, message_id), ContentPart (TextPart, ImageURLPart, AudioURLPart, VideoURLPart, ThinkPart), ToolCall, ToolResult, SubagentEvent (propagates nested agent events through parent Wire).
  • Requests (response needed): ApprovalRequest (uses asyncio.Future so Soul awaits user decision while UI handles input), ToolCallRequest for ACP-mode client-side execution.
  • Persistence format (wire.jsonl):
  • JSON Lines with first line {"type": "metadata", "protocol_version": "2.0"} followed by timestamped envelopes.
  • WireMessageEnvelope wraps type (class name) and payload (Pydantic model_dump); from_wire_message/to_wire_message handle serialization round-trip.
  • Message merging:
  • Purpose: stream tokens like "Hello" -> "Hello world" -> "Hello world!" are coalesced into one delivery to avoid frequent UI renders.
  • Implementation: MergeableMixin.merge_in_place concatenates TextPart.text; merging is skipped when types differ, when fields conflict, or on explicit flush().
  • End-to-end scenario (user asks agent to create hello.py): TurnBegin -> StepBegin -> buffered TextPart -> ToolCall (flushes buffer) -> ApprovalRequest (Soul awaits) -> ApprovalResponse -> ToolResult -> StepBegin -> TextPart -> TurnEnd.
  • UI implementation patterns:
  • Shell UI: wire.ui_side(merge=True) then match on TextPart, ApprovalRequest (call msg.resolve(response)), ToolResult.
  • ACP Server UI: wire.ui_side(merge=False) to forward raw events over WebSocket after converting to ACP format.
  • Extension procedure: define a Pydantic model, append it to the Event union type, send via wire_send(MyCustomEvent(...)), and handle it with match in the UI.
  • Comparison with MCP and LSP: Wire targets in-process Soul↔UI communication over memory queues with native streaming and built-in persistence, whereas MCP and LSP are JSON-RPC over stdio/SSE/TCP without native merging or recording.
  • Debugging tips: tail -f ~/.kimi/sessions/*/wire.jsonl | jq . to follow traffic; jq -r '.message.type' | sort | uniq -c to count message types; set KIMI_DEBUG=1 and inspect ~/.kimi/logs/kimi.log.
  • Reference pointers

  • Source: src/kimi_cli/wire/
  • Types: src/kimi_cli/wire/types.py
  • File backend: src/kimi_cli/wire/file.py
  • UI examples: src/kimi_cli/ui/

Tags

#kimi-code-cli#wire-protocol#agent-architecture#async-python#pydantic#streaming#ui-decoupling#debugging

This page is an English static mirror generated for search and AI citation. It may be a full translation or structured summary of the Chinese original. Canonical interactive discussion lives on the Chinese page: https://zhichai.net/topic/176922862