Skip to content

feat(hub): map kimi 0.31 wire events — cancel, compaction, plan/swarm/permission modes - #486

Merged
physercoe merged 1 commit into
physercoe:mainfrom
agentfleets:agent/agentfleet/kimi-wire-0.31
Jul 31, 2026
Merged

feat(hub): map kimi 0.31 wire events — cancel, compaction, plan/swarm/permission modes#486
physercoe merged 1 commit into
physercoe:mainfrom
agentfleets:agent/agentfleet/kimi-wire-0.31

Conversation

@agentfleets

Copy link
Copy Markdown
Collaborator

Summary

Updates the kimi-code M4 wire-tail mapper (hub/internal/drivers/local_log_tail/kimi_code) for the wire event vocabulary added in kimi-code 0.31.0. Verified against live 0.31.0 wire.jsonl captures on a dev machine (~/.kimi-code/sessions/wd_*/session_*/agents/main/wire.jsonl, 21991-line busy session).

1.4 → 1.5 is vocabulary growth, not a flag day. 0.31.0 still writes protocol_version: "1.5" and every 0.28.x event shape we already parse is byte-identical (content.part, tool.call args, tool.result result.output, step.end finishReason, usage.record, todo store). The SupportedProtocolVersion major-version gate (any 1.x) is unchanged, as is the launch gate in launch_m4_kimi.go. What's new is 10 event types that today fall into the mapper's default: arm and each become a noisy system{subtype:unknown_type} event (hundreds per busy session — 133 turn.steer alone in the sampled wire) while missing real signal — most importantly the cancel terminal.

Mapping table implemented

wire type maps to rationale
turn.cancel turn.result {status:"cancelled", stop_reason:"cancelled"} (main agent only) The correctness fix. kimi writes no step.end end_turn for an interrupted turn (verified: cancel → next line is the next turn's prompt), so a cancelled turn left the session stuck busy forever. The payload is exactly the ACP driver's eager-cancel vocabulary (driver_acp.go Input("cancel")): mobile's busy-walker terminates on the kind, the hub digest reads stop_reason (cancellednormalTurnStopReasons — an intentional end, not an integrity finding), and status matches what every ACP engine posts on cancel, so digest + mobile failure accounting treat both engines identically. Subagent guard mirrors mapStepEnd (#374).
turn.steer (any origin) explicit drop background_task (129/133 in capture) is the engine's own task-notification envelope — CLI-internal noise. user steers: same duplication rationale as turn.prompt — hub-originated input arrives via tmux send-keys and was already posted as input.text by the hub, so mapping would double the bubble; pane-typed steers are the accepted loss, same as pane-typed prompts. Unknown future origins drop quietly. Value over default: = no unknown_type noise + a stated decision. No plan-chain side effects (steers redirect the in-flight turn; verified turnId unchanged).
turn.prompt origin.kind="task" explicit drop (still arms plan chain) Goal-mode/cron engine tasks + background-task notices. They open a real engine turn, so the per-turn plan chain still re-arms; the body is an engine-generated notification envelope, not user prose.
context.apply_compaction ONE system {subtype:"compaction", summary(≤500 chars), tokens_before, tokens_after, compacted_count, text} Clients render it as a system card today (the text line lands on the default renderer); the fields keep structure for a future fold marker. Mirrors claude mapper's compact_boundary arm (single producer=system notice, off the busy-inference path). Summary is rune-bounded so one event can't dominate the transcript/event store.
full_compaction.begin / full_compaction.complete drop They bracket the apply event that carries the signal — one card per compaction, not three (15 trios in the sampled wire).
plan_mode.enter/cancel/exit system {subtype:"plan_mode", phase, id?, text} No dedicated mode event kind exists hub-side — the ACP driver forwards current_mode_update as a verbatim producer=system frame — so a system subtype is the reuse-consistent shape. exit handled though not yet observed (enter/cancel verified live).
permission.set_mode system {subtype:"permission_mode", mode, text} Mid-session complement to the launch-time permission_mode on session.init. Modes observed live: yolo / auto / manual.
swarm_mode.enter/exit system {subtype:"swarm_mode", phase, trigger?, text} Enter carries trigger ("tool" observed).
mcp.tools_discovered drop Tool catalog noise, same class as tools.set_active_tools / llm.tools_snapshot.
anything else system {subtype:unknown_type} Drift policy unchanged for genuinely unknown future types.

Evidence (local 0.31.0 wire samples)

  • Type histogram of the 21991-line main wire: turn.steer ×133 (129 background_task + 4 user), turn.prompt ×111 (109 user + 2 task), full_compaction.* ×15 trios, permission.set_mode ×5, swarm_mode.* ×4 pairs, plan_mode.* ×1 pair, turn.cancel ×2, mcp.tools_discovered ×1, metadata.protocol_version="1.5".
  • Cancel context: llm.request (turn 19.11)turn.cancel → next line is turn.prompt (turn 20) — no end_turn for the interrupted turn, confirming the stuck-busy mechanism.
  • New testdata/wire_main_0_31.jsonl holds redacted real lines for every new type (user text replaced with placeholders); wire_main.jsonl stays as the 0.28.1 pin.

Tests

  • mapper_test.go extended per type: exact payload shapes (esp. the cancel → turn.result vocabulary), explicit drops emit NOTHING (incl. no unknown_type), plan chain (turnSeq/planMsgID) provably unaffected by every new type, subagent guard on turn.cancel, task-origin prompt still re-arms, 500-rune summary truncation, and a 0.31 fixture replay pinning the kind histogram.
  • go test ./internal/drivers/local_log_tail/... ./internal/hostrunner/... — green except 2 hostrunner failures that reproduce identically on pristine origin/main on this machine (TestWriteKimiTSMCPConfig_FreshWorkdir — local TermiPod.app browser bridge injection; TestDatasetExportRRD_EndToEndWithAStubExporter — macOS /var/private/var symlink). Pre-existing, environment-induced, unrelated to this diff.
  • go vet clean on the touched packages.
  • Full go test ./... result noted in the PR comments if it differs (the 8 TestLintGovernedActions_* failures on macOS bash 3.2 are pre-existing and green in CI).

…/permission modes

kimi-code 0.31.0 keeps wire protocol 1.5 (gate unchanged) but adds 10
event types that fell into the mapper's default arm as unknown_type
noise. Map them:

- turn.cancel → turn.result {status:cancelled, stop_reason:cancelled}
  (main only, same guard as mapStepEnd). Fixes a cancelled turn
  leaving the session stuck busy — kimi writes no end_turn for the
  interrupted turn, and mobile's busy-walker had no terminal to find.
  Payload mirrors the ACP driver's eager-cancel vocabulary verbatim.
- turn.steer (all origins) + turn.prompt origin=task → explicit,
  documented drops (engine-internal notices / input.text duplication).
- context.apply_compaction → one system{subtype:compaction} card with
  bounded summary + token delta; begin/complete dropped as bookends.
- plan_mode.*, permission.set_mode, swarm_mode.* → producer=system
  cards (no dedicated mode kind exists hub-side; ACP forwards
  current_mode_update the same way).
- mcp.tools_discovered → drop (catalog noise).

default: unknown_type drift policy unchanged. Pinned by a redacted
0.31.0 fixture replay (testdata/wire_main_0_31.jsonl); wire_main.jsonl
stays the 0.28.1 pin.
@physercoe
physercoe merged commit 4f5d86c into physercoe:main Jul 31, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants