kimi-plugin-cc 2.2.2 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +5 -3
  3. package/.mcp.json +13 -0
  4. package/CHANGELOG.md +18 -0
  5. package/README.md +57 -5
  6. package/agents/kimi-challenge.md +3 -3
  7. package/agents/kimi-pursue.md +1 -1
  8. package/agents/kimi-rescue.md +2 -2
  9. package/agents/kimi-review.md +3 -3
  10. package/commands/README.md +2 -2
  11. package/commands/challenge.md +1 -1
  12. package/commands/pursue.md +1 -1
  13. package/commands/review.md +1 -1
  14. package/dist/background-spawn.js +9 -17
  15. package/dist/cli-client.js +24 -63
  16. package/dist/commands/ask.js +28 -31
  17. package/dist/commands/cancel.js +50 -23
  18. package/dist/commands/cli-helpers.js +7 -16
  19. package/dist/commands/pursue.js +24 -33
  20. package/dist/commands/registry.js +6 -19
  21. package/dist/commands/rescue.js +23 -22
  22. package/dist/commands/review-gate.js +18 -28
  23. package/dist/commands/review.js +41 -45
  24. package/dist/commands/setup.js +85 -171
  25. package/dist/commands/swarm.js +107 -91
  26. package/dist/commands/worker.js +0 -6
  27. package/dist/companion.js +13 -40
  28. package/dist/git.js +14 -7
  29. package/dist/help.js +2 -0
  30. package/dist/hooks/approval-hook.js +9 -20
  31. package/dist/hooks/approval-policy.js +36 -69
  32. package/dist/hooks/config-safety.js +6 -4
  33. package/dist/hooks/install-paths.js +62 -138
  34. package/dist/hooks/install.js +37 -108
  35. package/dist/hooks/managed-block.js +88 -214
  36. package/dist/job-id.js +12 -0
  37. package/dist/job-store.js +65 -28
  38. package/dist/jobs.js +9 -1
  39. package/dist/kimi-command.js +1 -3
  40. package/dist/kimi-engine.js +1 -1
  41. package/dist/kimi-timeouts.js +9 -21
  42. package/dist/kimi-version-probe.js +16 -1210
  43. package/dist/mcp/binding.js +58 -0
  44. package/dist/mcp/job-control.js +345 -0
  45. package/dist/mcp/launcher.js +257 -0
  46. package/dist/mcp/operations.js +301 -0
  47. package/dist/mcp/protocol.js +275 -0
  48. package/dist/mcp/server.js +8 -0
  49. package/dist/mcp/stdio.js +78 -0
  50. package/dist/mcp/supervised-child.js +15 -0
  51. package/dist/mcp/tool-result.js +7 -0
  52. package/dist/mcp/workspace.js +34 -0
  53. package/dist/model-command.js +32 -0
  54. package/dist/native-v2-preflight.js +1 -1
  55. package/dist/parsing.js +4 -18
  56. package/dist/rescue-approval.js +78 -164
  57. package/dist/session-recovery.js +7 -10
  58. package/dist/stream-json.js +6 -174
  59. package/dist/version.js +1 -1
  60. package/docs/migration.md +2 -6
  61. package/docs/registry-releases.md +23 -7
  62. package/docs/safety.md +30 -14
  63. package/package.json +7 -3
  64. package/plugins/kimi-codex/.codex-plugin/plugin.json +8 -5
  65. package/plugins/kimi-codex/.mcp.json +28 -0
  66. package/plugins/kimi-codex/dist/background-spawn.js +9 -17
  67. package/plugins/kimi-codex/dist/cli-client.js +24 -63
  68. package/plugins/kimi-codex/dist/commands/ask.js +28 -31
  69. package/plugins/kimi-codex/dist/commands/cancel.js +50 -23
  70. package/plugins/kimi-codex/dist/commands/cli-helpers.js +7 -16
  71. package/plugins/kimi-codex/dist/commands/pursue.js +24 -33
  72. package/plugins/kimi-codex/dist/commands/registry.js +6 -19
  73. package/plugins/kimi-codex/dist/commands/rescue.js +23 -22
  74. package/plugins/kimi-codex/dist/commands/review-gate.js +18 -28
  75. package/plugins/kimi-codex/dist/commands/review.js +41 -45
  76. package/plugins/kimi-codex/dist/commands/setup.js +85 -171
  77. package/plugins/kimi-codex/dist/commands/swarm.js +107 -91
  78. package/plugins/kimi-codex/dist/commands/worker.js +0 -6
  79. package/plugins/kimi-codex/dist/companion.js +13 -40
  80. package/plugins/kimi-codex/dist/git.js +14 -7
  81. package/plugins/kimi-codex/dist/help.js +2 -0
  82. package/plugins/kimi-codex/dist/hooks/approval-hook.js +9 -20
  83. package/plugins/kimi-codex/dist/hooks/approval-policy.js +36 -69
  84. package/plugins/kimi-codex/dist/hooks/config-safety.js +6 -4
  85. package/plugins/kimi-codex/dist/hooks/install-paths.js +62 -138
  86. package/plugins/kimi-codex/dist/hooks/install.js +37 -108
  87. package/plugins/kimi-codex/dist/hooks/managed-block.js +88 -214
  88. package/plugins/kimi-codex/dist/job-id.js +12 -0
  89. package/plugins/kimi-codex/dist/job-store.js +65 -28
  90. package/plugins/kimi-codex/dist/jobs.js +9 -1
  91. package/plugins/kimi-codex/dist/kimi-command.js +1 -3
  92. package/plugins/kimi-codex/dist/kimi-engine.js +1 -1
  93. package/plugins/kimi-codex/dist/kimi-timeouts.js +9 -21
  94. package/plugins/kimi-codex/dist/kimi-version-probe.js +16 -1210
  95. package/plugins/kimi-codex/dist/mcp/binding.js +58 -0
  96. package/plugins/kimi-codex/dist/mcp/job-control.js +345 -0
  97. package/plugins/kimi-codex/dist/mcp/launcher.js +257 -0
  98. package/plugins/kimi-codex/dist/mcp/operations.js +301 -0
  99. package/plugins/kimi-codex/dist/mcp/protocol.js +275 -0
  100. package/plugins/kimi-codex/dist/mcp/server.js +8 -0
  101. package/plugins/kimi-codex/dist/mcp/stdio.js +78 -0
  102. package/plugins/kimi-codex/dist/mcp/supervised-child.js +15 -0
  103. package/plugins/kimi-codex/dist/mcp/tool-result.js +7 -0
  104. package/plugins/kimi-codex/dist/mcp/workspace.js +34 -0
  105. package/plugins/kimi-codex/dist/model-command.js +32 -0
  106. package/plugins/kimi-codex/dist/native-v2-preflight.js +1 -1
  107. package/plugins/kimi-codex/dist/parsing.js +4 -18
  108. package/plugins/kimi-codex/dist/rescue-approval.js +78 -164
  109. package/plugins/kimi-codex/dist/session-recovery.js +7 -10
  110. package/plugins/kimi-codex/dist/stream-json.js +6 -174
  111. package/plugins/kimi-codex/dist/version.js +1 -1
  112. package/plugins/kimi-codex/skills/kimi-ask/SKILL.md +2 -2
  113. package/plugins/kimi-codex/skills/kimi-cancel/SKILL.md +1 -1
  114. package/plugins/kimi-codex/skills/kimi-challenge/SKILL.md +1 -1
  115. package/plugins/kimi-codex/skills/kimi-pursue/SKILL.md +1 -1
  116. package/plugins/kimi-codex/skills/kimi-replay/SKILL.md +1 -1
  117. package/plugins/kimi-codex/skills/kimi-rescue/SKILL.md +1 -1
  118. package/plugins/kimi-codex/skills/kimi-result/SKILL.md +1 -1
  119. package/plugins/kimi-codex/skills/kimi-review/SKILL.md +3 -3
  120. package/plugins/kimi-codex/skills/kimi-setup/SKILL.md +1 -1
  121. package/plugins/kimi-codex/skills/kimi-status/SKILL.md +1 -1
  122. package/plugins/kimi-codex/skills/kimi-swarm/SKILL.md +1 -1
  123. package/plugins/kimi-codex/skills/kimi-swarm-write/SKILL.md +1 -1
@@ -10,164 +10,13 @@
10
10
  // {"role":"tool","tool_call_id":"...","content":"..."}
11
11
  // {"role":"meta","type":"session.resume_hint","session_id":"session_<uuid>","command":"kimi -r session_<uuid>","content":"To resume this session: kimi -r session_<uuid>"}
12
12
  // {"role":"meta","type":"turn.step.retrying","failed_attempt":1,"next_attempt":2,"max_attempts":3,"delay_ms":300,"error_name":"APIProviderRateLimitError","error_message":"...","status_code":429}
13
- // {"role":"meta","type":"system.version","version":"0.24.2"} // experimental v2 only; modeled since v1.8.5 (SystemVersionRecord), filtered like other meta
13
+ // {"role":"meta","type":"system.version","version":"0.24.2"} // native-v2 marker; required first on native-v2 plans and filtered after capture
14
14
  //
15
15
  // {"type":"error","code":"session.occupied","message":"...","details":{...}} // 2.3.0; stdout-only refusal, exit 4
16
16
  //
17
17
  // Notes on what does and does not appear here:
18
- // - The session.resume_hint meta record is NEW in kimi-code 0.2.0
19
- // (introduced at run-prompt.ts:477-505 in 0.2.0; the writer region —
20
- // `PromptJsonResumeMetaMessage` / `writeResumeHint` / `PromptJsonWriter` —
21
- // sits at apps/kimi-code/src/cli/run-prompt.ts:567-696 as of 0.9.0 and is
22
- // verified compatible through 0.24.2. 2026-05-27 audit covered 0.4.0,
23
- // 2026-05-28 audit covered 0.5.0 (run-prompt.ts zero-byte diff across
24
- // both), 2026-05-31 audit covered 0.6.0, 2026-06-03 audit covered
25
- // 0.7.0/0.8.0/0.9.0 in one cumulative pass, 2026-06-09 audit covered
26
- // 0.10.0/0.11.0/0.12.0 in one cumulative 0.9.0→0.12.0 pass, 2026-06-12 audit
27
- // covered 0.12.1/0.13.0/0.13.1/0.14.0/0.14.1 in one cumulative 0.12.0→0.14.1
28
- // pass (records/ dir empty diff; the 0.13/0.14 packages/protocol/ REST+WS
29
- // control API is a separate transport — run-prompt.ts imports none of it,
30
- // `-p` stdout stays the direct PromptJsonWriter), 2026-06-13 audit covered
31
- // the 0.14.2 patch (records/ + the PromptJsonWriter/writeResumeHint region
32
- // both 0-byte; the patch is a repo-wide `.md`→`.md?raw` bundler-import
33
- // migration plus a Bash-tool stdout/stderr streaming callback, neither
34
- // touching the writer), and 2026-06-14 covered the 0.14.3 patch (all four
35
- // scoped diffs 0-byte; the entire patch is a TUI model-picker
36
- // provider-refresh change — PR #713 — off the `-p` path), and 2026-06-16
37
- // covered the 0.15.0 minor (permission/hooks/run-prompt/cli all 0-byte by
38
- // independent `git diff` byte-count; the only scoped change is records/ +
39
- // session/ persistence churn — PR #786 drops app_version/resumed from the
40
- // .records/ metadata artifact, a SessionSkillRegistry rename, a static
41
- // model-capability lookup — none of it on the `-p` stdout stream; plus an
42
- // additive `transport:'sse'` MCP config variant the plugin never writes),
43
- // and 2026-06-17 covered the 0.16.0 minor (permission/hooks/run-prompt all
44
- // 0-byte; the only argv change registers a new `kimi vis` subcommand off
45
- // the `-p` path, and the records/replay/compaction/logging churn is
46
- // internal — not on the stdout stream; GREEN smoke 7/0 on the 0.16.0
47
- // binary), 2026-06-19 covered the 0.16.0→0.18.0 jump (0.17.0/0.17.1/0.18.0;
48
- // 03-hooks.diff 0-byte, run-prompt.ts only a telemetry refactor outside the
49
- // writer, the sole permission change a NON-auto-mode GoalStartReviewAsk that
50
- // is dead on `-p`), and 2026-06-23 covered the 0.18.0→0.19.1 minor (the
51
- // writer + records/ 0-byte; the bumping feature #812 `--add-dir` touches
52
- // only an approve policy at the queue tail, structurally dead below the
53
- // `-p` auto-mode approve and still pre-empted by our index-0 hook even when
54
- // project-local `.kimi-code/local.toml` makes additionalDirs non-empty;
55
- // #963's new `turn.ended` reason:'filtered' is a failure REASON not a new
56
- // stdout record SHAPE;
57
- // GREEN smoke 9/0 on the operator's 0.19.1 binary —
58
- // .claude/kimi-code-research/reports/85-upstream-0191-surface.md), and
59
- // 2026-06-26 covered the 0.19.1→0.20.0 minor (run-prompt.ts + options.ts +
60
- // records/ all 0-byte; #1040's new `warning` agent event is swallowed on `-p`
61
- // at run-prompt.ts:495 `case 'warning': return;`, so it never reaches this
62
- // parser; #1062's tool-result budget adds a `truncated` flag to tool-result
63
- // CONTENT, not the record SHAPE; the new runShellCommand RPC + kimi server/web
64
- // stack are off the `-p` path — report 88. Smoke NOT run this cert: the 0.20.0
65
- // `bun run smoke:real` was quota-blocked, 403 usage-limit, records:[] — an
66
- // operator-billing false alarm, not a compat break; certified on source audit;
67
- // the later 0.20.2 smoke ran GREEN 9/0 on 2026-06-29), and 2026-07-01
68
- // covered the 0.20.3→0.21.1 minor+patch (report 93; agent/records/ 0-byte,
69
- // run-prompt.ts writer/resume_hint/goal.summary unchanged, #1204 plugin
70
- // slash commands off the `-p` path, #1233 cleanup timeout after the writer
71
- // flush; GREEN smoke 9/0 on the operator's 0.21.1 binary), and 2026-07-05
72
- // covered the 0.21.1→0.22.3 minor+patch (reports 94/95; hook engine
73
- // unchanged, policy order still hook-first, writer shape compatible; 0.22.3's
74
- // prompt-mode background-drain happens after assistant-output flush and is
75
- // bounded by plugin budgets; shell-output caps and session-owned
76
- // media-originals do not alter the NDJSON record schema; GREEN smoke 9/0 on
77
- // a temp-installed 0.22.3 binary), and 2026-07-07 covered the
78
- // 0.22.3→0.23.1 minor (report 96; hook engine unchanged, policy order still
79
- // hook-first, PromptJsonWriter-compatible scoped `run-prompt.ts` delta only
80
- // adds telemetry sessionId; `select_tools` is denied by our hook unless
81
- // deliberately allowlisted; observability records are persisted agent-log
82
- // records, not new stdout NDJSON shapes; GREEN smoke 9/0 on a temp-installed
83
- // 0.23.1 binary), and 2026-07-08 covered the 0.23.2 patch (daily checkup;
84
- // no writer/resume_hint/goal.summary drift, `02-permission.diff` and
85
- // `05-session-bootstrap.diff` 0-byte; the only scoped `run-prompt.ts`
86
- // change keeps the cleanup timeout ref'd so failed headless `-p` turns exit
87
- // non-zero reliably, and the only hook-engine-adjacent change adds
88
- // `windowsHide:true` to hook child-process spawn options on Windows; GREEN
89
- // smoke 9/0 on a temp-installed 0.23.2 binary), and 2026-07-09 covered the
90
- // 0.23.3/0.23.4 patches (daily checkup; permission + both hook-tree diffs
91
- // 0-byte; #1516 keeps prompt-mode goals alive across continuation turns
92
- // until terminal without changing PromptJsonWriter, session.resume_hint,
93
- // or goal.summary shapes; the wire/session delta is internal provider-auth
94
- // and media-projection plumbing, while the bootstrap delta adds `[image]`
95
- // limits without changing hook or permission construction; GREEN smoke 9/0
96
- // on a temp-installed 0.23.4 binary, including two-minute multi-turn goal
97
- // confinement and both write-swarm worktree-boundary assertions), and
98
- // 2026-07-11 covered the 0.23.5 patch at tag commit `352a4492`
99
- // (permission, both hook trees, and session bootstrap all 0-byte; the wire
100
- // diff only adds the internal `media-stripped` projection type). The one
101
- // live stdout change is this file's `turn.step.retrying` meta record: it is
102
- // modeled exactly and filtered from consumer records while malformed shapes
103
- // remain diagnostic. The post-change exact-binary smoke was GREEN 9/0 in
104
- // 229.56s, including the full goal budget and both write-swarm boundaries),
105
- // and 2026-07-12 covered the 0.23.6 patch at tag commit `b5c236d0`
106
- // (permission and both hook trees 0-byte; no new PromptJsonWriter record;
107
- // `run-prompt.ts` now keeps print mode alive across active goals, pending
108
- // cron fires, and opt-in background steering, all still bounded externally
109
- // by plugin budgets; subagent timeout config and the capability rename to
110
- // dynamically_loaded_tools do not alter stdout shape). Exact-binary smoke
111
- // was GREEN 9/0 in 274.55s, including the full goal budget and both
112
- // write-swarm boundaries), and 2026-07-15 covered the 0.23.6→0.24.2
113
- // minor (Phase-1 reports 97-100; permission and the live session/hooks
114
- // surfaces 0-byte; default-v1 writer/resume/retry/goal shapes compatible;
115
- // GREEN exact-binary v1 smoke 9/0, 39 assertions in 261.33s). A truthy
116
- // native v2 shares the writer shapes and terminal session.resume_hint but
117
- // prepends one new
118
- // role:"meta", type:"system.version" record. Unknown meta is intentionally
119
- // non-fatal: it becomes diagnostic-only, never enters records[]/onRecord,
120
- // and cannot affect prose or session pinning. A targeted exact-v2 review
121
- // smoke denied the forced write (1 pass / 8 skip / 0 fail).
122
- // 2026-07-16 covered 0.25.0 + 0.26.0 (the 0.25.0 step added only 943 B
123
- // of permission telemetry; all three primary safety surfaces were 0-byte
124
- // on the 0.26.0 step; no new PromptJsonWriter record on either),
125
- // 2026-07-18 covered 0.27.0, 2026-07-21 covered 0.28.1,
126
- // 2026-07-23/24 covered 0.29.0 + 0.29.1, and 2026-07-27 covered 0.29.2
127
- // (permission/hooks stayed 0-byte across every step; the 0.29.x
128
- // wire/bootstrap changes did not alter PromptJsonWriter output; GREEN
129
- // exact-binary smoke 10/0, 43 assertions, on each of exact 0.27.0,
130
- // 0.28.1, 0.29.0, 0.29.1, and 0.29.2; the 0.29.1 veto-event refactor
131
- // and 0.29.2 automatic scope-activation refactor preserved the modeled
132
- // system.version signal and terminal session pinning), 2026-07-29 covered
133
- // 0.30.0, 2026-07-31 covered 0.31.0, 2026-08-01 covered 0.31.1, and
134
- // 2026-08-08 covered 0.32.0 through 0.34.0, 2026-08-12 covered 0.35.0,
135
- // 2026-08-13 covered 0.36.0, 2026-08-24 covered 0.37.0 through
136
- // 0.38.0, 2026-08-27 covered 0.39.0 (prompt-render.ts byte-identical;
137
- // the MCP structured-content and error-code changes shift only prose
138
- // inside opaque tool content strings), and 2026-09-02 covered 0.40.0
139
- // (wire/session scope 0 bytes; run-prompt.ts absent from the diff). The
140
- // 2026-09-06 covered 0.41.0 (v1 core, prompt-render.ts, goal-prompt.ts,
141
- // and prompt-session.ts unchanged; exact-binary smoke 12/0, 55 assertions).
142
- // The legacy-v1 writer remains compatible through 0.41.0;
143
- // custom profiles can change content/tools but
144
- // not the NDJSON envelope. In 0.33.0 native v2 became the unflagged default,
145
- // so v1.9.6 forces KIMI_CODE_LEGACY_FLAG=1 in every accepted child while
146
- // retaining the old experimental-selector refusal. This parser continues
147
- // to model v2's system.version record for diagnostics and a future safe
148
- // re-enable; absence of that record is now a legacy-pin smoke assertion.
149
- // NB: from 0.6.0 run-prompt.ts
150
- // is no longer a whole-file zero-byte diff — at 0.6.0 it gained a
151
- // resume-session workDir guard, and at 0.8.0 it gained headless goal
152
- // mode (`runHeadlessGoal`) — but both changes are OUTSIDE the
153
- // stream-json writer, which every audit confirmed byte-identical by
154
- // content/blob SHA. Goal mode also writes a `{"type":"goal.summary",...}`
155
- // line (no `role` field) before the resume hint when the prompt is
156
- // `/goal`-prefixed (on 0.8–0.11 the `goal-command` experimental flag must
157
- // also be on; 0.12.0 removed that gate — PR #569 — so the `/goal` prefix
158
- // alone triggers it). Read-only commands never trigger it (their trimmed
159
- // prompt never starts with `/goal`); the v1.1 /kimi:pursue command is
160
- // the intentional consumer — it is recognized as a first-class record on
161
- // the dedicated `StreamJsonOutcome.goalSummary` channel (see
162
- // GoalSummaryRecord below), NOT routed to the malformed channel. See
163
- // reports 47-65, 72-76).
164
- // The hint is emitted once per prompt run at session END
165
- // (after runPromptTurn settles), not at session start. In 0.1.x the
166
- // resume hint went to stderr only; 0.2.0+ emits a structured
167
- // stream-json record on stdout when `--output-format stream-json` is
168
- // selected (text-output mode still writes the stderr line). Our
169
- // cli-client consumes the meta record's `session_id` for resume/replay
170
- // routing.
18
+ // - session.resume_hint is emitted once at session end. Legacy 0.1.x sent
19
+ // the resume hint to stderr; stream-json uses this stdout meta record.
171
20
  // - hook.result events arrive as role:"assistant" with the block rendered as
172
21
  // plain text (see formatHookResultPlain in run-prompt.ts). Our
173
22
  // PreToolUse hook's deny reason therefore surfaces in the assistant
@@ -185,10 +34,8 @@ export const MAX_STREAM_JSON_LINE_BYTES = 1_048_576;
185
34
  * Lifecycle:
186
35
  * - `push(chunk)` — consume bytes; returns outcomes for any complete lines
187
36
  * that finished within this chunk.
188
- * - `flush()` — at end of stream, force-parse the trailing buffer if any.
189
- * Stream-json emits a newline after every record (run-prompt.ts:573), so
190
- * a non-empty flush remainder indicates an unfinished record (likely an
191
- * interrupted spawn).
37
+ * - `flush()` — at end of stream, parse a valid trailing JSON record even
38
+ * without a final newline; malformed trailing input becomes diagnostic.
192
39
  *
193
40
  * Outcomes are non-throwing — malformed lines surface via `malformedLine` /
194
41
  * `malformedReason` so callers can route them to a diagnostics log without
@@ -225,8 +72,6 @@ export class StreamJsonParser {
225
72
  while (newlineIndex !== -1) {
226
73
  const line = this.buffer.slice(0, newlineIndex);
227
74
  this.buffer = this.buffer.slice(newlineIndex + 1);
228
- // Skip blank lines (the writer doesn't emit them, but tolerate
229
- // CRLF-doubled or trailing whitespace defensively).
230
75
  if (line.replace(/\r/g, "").length > 0) {
231
76
  out.push(parseLine(line));
232
77
  }
@@ -260,8 +105,6 @@ function parseLine(line) {
260
105
  if (role === "meta") {
261
106
  return validateMeta(parsed, line);
262
107
  }
263
- // Role-less goal-mode summary (kimi-code 0.8.0+). Keyed by `type`, not
264
- // `role`, so it's surfaced on the dedicated goalSummary outcome channel.
265
108
  if (role === undefined && parsed["type"] === "goal.summary") {
266
109
  return validateGoalSummary(parsed, line);
267
110
  }
@@ -279,16 +122,9 @@ function parseLine(line) {
279
122
  message: parsed["message"].slice(0, 4000),
280
123
  } };
281
124
  }
282
- // H3 forward-compat: a non-empty string role we don't model (a role a future
283
- // kimi-code adds). Surface it out-of-band on the unknownRecord channel —
284
- // tolerated, not malformed — so the parser keeps working and the diagnostic
285
- // log distinguishes "new upstream role" from a genuinely broken line. It
286
- // never enters records[] (see UnknownRoleRecord + the consumer sweep).
287
125
  if (typeof role === "string" && role.length > 0) {
288
126
  return { unknownRecord: { role, raw: parsed } };
289
127
  }
290
- // Genuinely malformed: no `role` at all (and not a recognized role-less typed
291
- // record like goal.summary), or a non-string role. Surface as malformed.
292
128
  return {
293
129
  malformedLine: line,
294
130
  malformedReason: `unknown role: ${JSON.stringify(role)}`,
@@ -400,9 +236,7 @@ function validateMeta(parsed, line) {
400
236
  };
401
237
  return { record };
402
238
  }
403
- // Unknown meta types are forward-compatible — surface them via the
404
- // malformed channel for diagnostics, but don't crash. kimi-code may add
405
- // new meta types in 0.3.x and our wrapper should keep working.
239
+ // Unknown meta types are diagnostic-only and nonfatal.
406
240
  return {
407
241
  malformedLine: line,
408
242
  malformedReason: `unknown meta.type: ${JSON.stringify(type)}`,
@@ -503,8 +337,6 @@ export function extractSessionIdFromStderr(stderr) {
503
337
  // hostile stderr line could pin `session_--------` (8 dashes) or any
504
338
  // 8+ char hex-dash token as the captured session id, weakening the
505
339
  // "anchored full-UUID regex" safety invariant documented in AGENTS.md.
506
- // Review-smoke (kimi 0.2.0 on the alpha.5 candidate) flagged the loose
507
- // form; this is the post-review tightening.
508
340
  const uuid = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}";
509
341
  const pattern = new RegExp(`^To resume this session:\\s+kimi\\s+-r\\s+(session_${uuid}|${uuid})\\s*$`, "im");
510
342
  return stderr.match(pattern)?.[1];
package/dist/version.js CHANGED
@@ -4,4 +4,4 @@
4
4
  // and AGENTS.md on every release. A future improvement would read this from
5
5
  // package.json at build time, but that adds a build-step dependency we
6
6
  // don't want yet.
7
- export const KIMI_PLUGIN_CC_VERSION = "2.2.2";
7
+ export const KIMI_PLUGIN_CC_VERSION = "2.4.0";
package/docs/migration.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Compatibility and upgrades
2
2
 
3
- For plugin 2.0.7, use Kimi Code CLI 2.1.1. The plugin certifies exact native-v2 versions `0.42.0`, `0.43.0`, `0.43.1`, `2.0.0`, `2.0.1`, `2.0.2`, `2.1.0`, and `2.1.1` for all eight operations. It does not automatically accept later patches or minor releases. The npm distribution adds an installation channel; it does not change this compatibility table.
3
+ For plugin 2.4.0, use Kimi Code CLI 2.3.1. The plugin certifies exact native-v2 versions `0.42.0`, `0.43.0`, `0.43.1`, `2.0.0`, `2.0.1`, `2.0.2`, `2.1.0`, `2.1.1`, `2.2.0`, `2.3.0`, and `2.3.1` for all eight operations. It does not automatically accept later patches or minor releases. The npm distribution adds an installation channel; it does not change this compatibility table.
4
4
 
5
5
  Explicitly pinned legacy-v1 binaries remain supported within `KIMI_TESTED_MINORS`, through 0.41.x, subject to each operation's minimum version. The [runtime table](../runtime/kimi-engine.ts) is the authority.
6
6
 
@@ -8,11 +8,7 @@ Use the [README installation and update steps](../README.md) for Claude Code or
8
8
 
9
9
  ## Plugin versions and upgrades
10
10
 
11
- Plugin versions advance independently using ordinary SemVer. Plugin 2.0.7 retains
12
- the exact CLI certifications from 2.0.6; the runtime table records that mapping. Each compatible
13
- maintenance or certification update takes the next unused plugin patch. Published
14
- versions and tags are never reused. Every upstream patch requires its own certification.
15
- The native-v2 no-plan profile, plugin/marketplace IDs and existing job store are preserved.
11
+ Plugin versions advance independently using ordinary SemVer and may deliberately align with a certified CLI release. Plugin 2.4.0 certifies CLI 2.3.1; the runtime table records that mapping. Published versions and tags are never reused. Every upstream patch requires its own certification. The native-v2 no-plan profile, plugin/marketplace IDs and existing job store are preserved.
16
12
 
17
13
  After updating the plugin, run Claude Code `/kimi:setup` or Codex `$kimi-setup`
18
14
  from that host's active install to re-pin its version-stamped hook path, then
@@ -6,6 +6,24 @@ remain available. The package has no install scripts and no separate CLI.
6
6
 
7
7
  ## Publishing status
8
8
 
9
+ Plugin **2.3.1** certifies exact CLI **2.3.1**. [Release v2.3.1](https://github.com/linxule/kimi-plugin-cc/releases/tag/v2.3.1) is anchored at `20011ff0554c611afe0a426b30a68c2473175568`; its [CI check](https://github.com/linxule/kimi-plugin-cc/actions/runs/39977037533) and [release-event publishing run](https://github.com/linxule/kimi-plugin-cc/actions/runs/39977818482) passed. The registry tarball has SHA-1 `e309448480a2ce841b49840cdaeeed37acc6c50b`, contains 196 files, and matches the CI artifact byte-for-byte. See the [certification evidence](./upstream-2.3.1-certification.md).
10
+ The previous verified publication was **2.2.2**:
11
+ [Release v2.2.2](https://github.com/linxule/kimi-plugin-cc/releases/tag/v2.2.2)
12
+ is anchored at `256e78ff7cbf302fb19e8e523d8e3f943f7cb366`; its
13
+ [CI check](https://github.com/linxule/kimi-plugin-cc/actions/runs/39755724024)
14
+ and [release-event publishing run](https://github.com/linxule/kimi-plugin-cc/actions/runs/39755982507)
15
+ passed. npm accepted the OIDC publication with signed provenance (sigstore log
16
+ index `3383319298`) and tarball shasum
17
+ `31aa9752bf05dc4e9c818d5eef5f99e604968613`. The registry tarball matches the
18
+ validated Actions artifact byte-for-byte. A fresh npm install passed isolated
19
+ setup/check for both hosts, and both runtime mirrors match the release checkout.
20
+ Metadata and tarball availability propagated separately; the tarball endpoint
21
+ briefly served a cached 404 (300-second TTL). No publication retry was needed.
22
+ Publication does not update installed
23
+ hosts, re-pin their hooks, or change the operator's CLI binary.
24
+
25
+ ### Previous publications
26
+
9
27
  As of October 15, 2026, [2.2.1 is live on npm](https://www.npmjs.com/package/kimi-plugin-cc/v/2.2.1), published by
10
28
  the release-event run of `publish.yml` (run 38720071707) through GitHub Actions OIDC with a signed
11
29
  provenance statement (sigstore log index 3263398504; tarball shasum
@@ -60,8 +78,9 @@ bun run check:package .tmp/npm
60
78
 
61
79
  The package check inspects the actual tarball, verifies runtime copies and
62
80
  vendored licenses, and installs/checks the safety hook in isolated homes for
63
- both hosts. It also exercises unsupported-version refusal using a fake CLI.
64
- It does not invoke a model or read the maintainer's Kimi configuration.
81
+ both hosts. It also exercises unsupported-version refusal using a fake CLI and
82
+ modern MCP discovery plus legacy initialization on both extracted compiled
83
+ distributions. It does not invoke a model or read the maintainer's Kimi configuration.
65
84
 
66
85
  ## First publication (completed bootstrap reference)
67
86
 
@@ -101,8 +120,5 @@ release. `.github/workflows/publish.yml` runs on `release.published`; it checks
101
120
  the tag against `package.json`, runs the full gate and package smoke, then
102
121
  publishes the validated tarball through OIDC with provenance. The publish job
103
122
  has no source checkout or build scripts. A manual dispatch with an existing
104
- release tag is available for a first OIDC release or recovery after a verified
105
- publication failure. Check npm before retrying; released versions are immutable.
106
-
107
- A locally bootstrapped first version is already published: do not dispatch it
108
- again. The next release is the first OIDC publication.
123
+ release tag is available for recovery after a verified publication failure.
124
+ Check npm before retrying; released versions are immutable.
package/docs/safety.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # Safety
2
2
 
3
- This guide describes the current checkout runtime; 2.3.0 certification is unreleased. Read-only tasks deny file writes and shell commands. Rescue and pursue can edit your workspace. Write-swarm returns a patch from a temporary Git worktree.
3
+ This guide describes the current checkout runtime. Read-only tasks deny file writes and shell commands. Rescue and pursue can edit your workspace. Write-swarm returns a patch from a temporary Git worktree.
4
4
 
5
- Native agent-core-v2 is certified at exact CLI versions `0.42.0`, `0.43.0`, `0.43.1`, `2.0.0`, `2.0.1`, `2.0.2`, `2.1.0`, `2.1.1`, `2.2.0`, and `2.3.0`, with plan mode disabled. The [runtime certification table](../runtime/kimi-engine.ts) controls routing. The hook is not an operating-system sandbox.
5
+ Native agent-core-v2 is certified at exact CLI versions `0.42.0`, `0.43.0`, `0.43.1`, `2.0.0`, `2.0.1`, `2.0.2`, `2.1.0`, `2.1.1`, `2.2.0`, `2.3.0`, and `2.3.1`, with plan mode disabled. The [runtime certification table](../runtime/kimi-engine.ts) controls routing. The hook is not an operating-system sandbox.
6
6
 
7
7
  ## Why the safety story is hook-based
8
8
 
@@ -156,7 +156,9 @@ Provenance: a native-v2 plan requires the `system.version` marker as the first s
156
156
 
157
157
  Residuals, unchanged from v1 and stated plainly: `config.toml` and the session journals are trusted operator state (an actor who can write them could already delete the `[[hooks]]` entry); the upstream hook runner fails open on its own internal errors; the plan-path comparison does not realpath (a symlink there would need a prior hook-allowed write). Native plan mode, tower, subagent fork, Remote Control and `--add-dir` are out of scope.
158
158
 
159
- In CLI 2.3.0, stale-session retry can reread those same journals. Do not concurrently operate on or edit a plugin-managed session from another client. The upstream session lock is not a security boundary; no parent-side rescan can make that reload atomic.
159
+ In CLI 2.3.0 and later, stale-session retry can reread those same journals. Do not concurrently operate on or edit a plugin-managed session from another client. The upstream session lock is not a security boundary; no parent-side rescan can make that reload atomic.
160
+
161
+ CLI 2.3.1 adds `KIMI_CODE_SESSION_LOCK=0` and explicit TUI/SDK/REST lock-takeover paths, which can disable or break mutual exclusion. Plugin children inherit an ambient lock-disable setting; print mode itself reports occupancy and does not take over. The prohibition on concurrent clients still applies. During POSIX lock contention, upstream can also execute bare `ps` with the child's inherited PATH and cwd outside PreToolUse; a PATH containing the workspace is therefore part of the internal-helper trust boundary. Legacy workspace-trust migration recognizes more old path spellings and can enable project MCP for those already trusted roots. Trust-store read failure can suppress the initial MCP load, an availability/diagnostics change rather than a trust fail-open.
160
162
 
161
163
  ## The rescue allowlist
162
164
 
@@ -169,7 +171,7 @@ For `/kimi:rescue`, the hook delegates to [`evaluateRescueHookRequest`](../runti
169
171
  - **Exec-delegating flags**: every shell command is rejected if its argv contains a flag whose *value* is run as a command or external tool — `--open-files-in-pager` (`git grep --open-files-in-pager=touch /tmp/x needle` makes git execute `touch`; same class as the pre-subcommand `git -c core.pager=` smuggle, just via a subcommand flag — also the `-O` short form under `git`), `-vettool` / `-toolexec` / `-exec` (`go vet -vettool=<bin>`, `go test -exec <bin>`). These were the CRITICAL/HIGH findings of the 2026-05-28 allowlist audit (report 43).
170
172
  - **Report-writing flags on type/test tools**: `mypy --junit-xml`/`--*-report` and `pytest --junitxml`/`--result-log`/`--report-log` write to arbitrary paths and are rejected.
171
173
  - **Read-safe commands** (`ls`, `cat`, `head`, `tail`, `rg`, `pwd`, `wc`, `git status`, `git diff`, ...): allowed.
172
- - **`.git/`**: every file-edit path check excludes anything inside `.git/`. Shell commands cannot include `git commit`, `git push`, `git reset --hard`, etc. Branch and commit ownership stays with the main Claude thread.
174
+ - **`.git/`**: every file-edit path check excludes anything inside `.git/`. Shell commands cannot include `git commit`, `git push`, `git reset --hard`, etc. Branch and commit ownership stays with the main thread.
173
175
 
174
176
  ### Effective working directory (`Bash.cwd`, v1.10.0)
175
177
 
@@ -189,7 +191,7 @@ Treat these files as **trusted model instructions** before using write-capable r
189
191
 
190
192
  ## Autonomous goal mode (`/kimi:pursue`, experimental)
191
193
 
192
- `/kimi:pursue` (v1.1 prototype) hands Kimi an objective to pursue across multiple continuation turns via kimi-code's headless goal mode (`kimi -p "/goal ..."`, kimi-code 0.8.0+). It is **write-capable and reuses the rescue trust boundary**: it runs with `KIMI_PLUGIN_CC_CMD=rescue`, so the PreToolUse hook applies the exact same workspace allowlist above, and it cannot mutate git state.
194
+ `/kimi:pursue` (v1.1 prototype) hands Kimi an objective to pursue across multiple continuation turns via kimi-code's headless goal mode (`kimi -p "/goal -- <objective>"`, kimi-code 0.8.0+). It is **write-capable and reuses the rescue trust boundary**: it runs with `KIMI_PLUGIN_CC_CMD=rescue`, so the PreToolUse hook applies the exact same workspace allowlist above, and it cannot mutate git state.
193
195
 
194
196
  Pursue also needs to settle its current goal. The child receives `KIMI_PLUGIN_CC_OPERATION` from its immutable execution plan, overwriting any inherited value. With the rescue label, operation `pursue`, and a trusted workspace root, the hook allows only `GetGoal {}` and `UpdateGoal {status: "complete" | "blocked"}`. These calls read or terminally update session goal metadata. Extra fields, goal creation, reactivation and budget changes are denied. `--turns` remains a soft instruction; the plugin's wall-clock budget is the hard bound.
195
197
 
@@ -198,10 +200,10 @@ If another installed host still points to an older hook, that hook may veto thes
198
200
  > **Goal-mode entry gate (changed in 0.12.0).** Headless goal mode is reached by a `/goal`-prefixed prompt. On kimi-code 0.8–0.11 it is *additionally* gated by the `goal-command` experimental flag, which `pursue.ts` sets per-spawn (`KIMI_CODE_EXPERIMENTAL_GOAL_COMMAND=1`). kimi-code 0.12.0 (PR #569) **removed that experimental-flag gate**, so the `/goal` prefix alone triggers goal mode. This does not weaken plugin safety: read-only commands (review/challenge/ask/review_gate) hard-prefix an English instruction line, so their trimmed prompt never starts with `/goal` and they cannot enter goal mode on any version; and the managed PreToolUse hook denies every write under a read-only label regardless (see § Native agent-core-v2 for why the hook cannot be pre-empted). The env var `pursue.ts` sets is now redundant on 0.12.0 but harmless, and is kept for 0.8–0.11 compat.
199
201
 
200
202
  - **The hook fires on every tool call in every continuation turn.** On native v2 every tool execution passes `prepareToolCall` → `fireBeforeExecute`, where the external-hooks listener vetoes ahead of any terminal allow (the auto-approve gate uses non-terminal `pass()`; the engine's only `allow()` is the plan-file guard, never armed in a plugin-managed session — see § Native agent-core-v2). (On legacy-v1 ≤ 0.41 the same guarantee came from `PreToolCallHookPermissionPolicy` sitting at policy index 0.) Goal continuation turns are ordinary turns — there is no autonomous-continuation path that bypasses per-tool approval (verified against kimi-code 0.9.0, audit report 58; re-verified through 0.12.0 by the goal-mode real-binary smoke, reports 61-65). A goal-mode run is exactly as write-gated as a single-turn rescue. This is locked by a real-binary smoke (`tests/runtime/real-binary-smoke.test.ts`, run via `KIMI_PLUGIN_CC_SMOKE=1 bun test ... -t "goal mode"`): it runs real headless goal mode under a read-only label and asserts that **no file lands across the entire multi-turn run** — a hook miss on any continuation turn would land one.
201
- - **The only new risk is unboundedness, and the plugin bounds it.** Headless goal create sets no token/turn budget; only the model (via `SetGoalBudget`) or an external signal stops the loop. `/kimi:pursue` therefore enforces a **mandatory finite wall-clock ceiling** via its AbortController (`--budget`, default 45m) — the SIGTERM→SIGKILL descendant-reaping cancellation reaps the whole goal process tree. `--turns` is a *soft* hint injected into the objective (the model may call `SetGoalBudget`); it is not enforceable headless. Treat `--budget` as the real bound.
203
+ - **The only new risk is unboundedness, and the plugin bounds it.** Headless goal create sets no token/turn budget, and the managed hook denies `SetGoalBudget`; the plugin's external wall-clock stop is therefore the guaranteed bound. `/kimi:pursue` enforces a **mandatory finite wall-clock ceiling** via its AbortController (`--budget`, default 45m) — the SIGTERM→SIGKILL descendant-reaping cancellation reaps the whole goal process tree. `--turns` is a *soft* instruction injected into the objective, never a request to call the denied `SetGoalBudget` tool; it is not enforceable headless, and stopping at that soft hint is not proof of completion. Treat `--budget` as the real bound.
202
204
  - **Loop runaway is now also capped upstream.** kimi-code 0.8.0 (#364) detects repeated identical tool calls and force-stops a turn at a repeat streak. This benefits `/kimi:rescue`, `/kimi:ask`, and `/kimi:pursue` alike: a force-stopped turn ends *normally* (exit 0, flows through the plugin's success path — not a failure), reducing the chance an autonomous run spins against the wall-clock budget. It is a complement to, not a replacement for, the wall-clock ceiling.
203
205
  - **Resume is intentionally not exposed yet.** In goal mode `goal.summary` emits a *goalId* distinct from the resume-hint *sessionId*; `kimi -r <sessionId>` re-enters the session but not necessarily the goal continuation. The plugin captures and surfaces the goalId but does not offer `--resume` for pursue until that split is reconciled upstream — exposing it now would be a silent-failure trap. **Re-verified against kimi-code 0.26.0 (2026-07-16):** the headless CLI still has no argv, prompt syntax, or session-resume path that calls `resumeGoal()`. `parseHeadlessGoalCreate` (`apps/kimi-code/src/cli/goal-prompt.ts`) accepts only the `/goal <objective>` *create* shape on both the v1 and experimental-v2 drivers, and upstream's own docs (`docs/en/guides/goals.md`, § "Use goal mode carefully") state that in non-interactive prompt mode *only goal creation is supported* — goal management is a TUI/kimi-web control. PR #552 makes a plain `-r <sessionId>` correctly *restore* the paused goal snapshot, but nothing in the headless path *acts* on it; the real `resumeGoal()` is wired only to the TUI (`/goal resume`) and kimi-web RPC (PR #1693, web-only). Implementing `--resume` would therefore require either prompt-injected model compliance (rejected — the plugin bounds autonomy with hard limits, not model goodwill) or abandoning the subprocess model for kimi-web's RPC. Re-check if upstream ever ships a headless `goal resume` verb.
204
- - **A model-invocable `kimi-pursue` agent (v1.3) wraps this command.** The main Claude thread can dispatch autonomous goal mode, not just the human-typed `/kimi:pursue` — the same command↔agent mirror as rescue/review/swarm. Auto-dispatch does NOT widen the blast radius: pursue runs under `commandLabel: "rescue"`, so the managed PreToolUse hook + workspace allowlist gate every tool call on every continuation turn (no git mutation), and the mandatory `--budget` wall-clock ceiling bounds the autonomy regardless of who launched it. Because it is write-capable AND autonomous — the plugin's highest blast radius — the agent's description requires an explicit user request for hands-off multi-turn pursuit, steers single bounded tasks to `kimi-rescue`, and never auto-promotes a fix or a question into a goal loop. Like the command, it refuses without the hook and offers no `--resume`.
206
+ - **A model-invocable `kimi-pursue` agent (v1.3) wraps this command.** The main thread can dispatch autonomous goal mode, not just the human-typed `/kimi:pursue` — the same command↔agent mirror as rescue/review/swarm. Auto-dispatch does NOT widen the blast radius: pursue runs under `commandLabel: "rescue"`, so the managed PreToolUse hook + workspace allowlist gate every tool call on every continuation turn (no git mutation), and the mandatory `--budget` wall-clock ceiling bounds the autonomy regardless of who launched it. Because it is write-capable AND autonomous — the plugin's highest blast radius — the agent's description requires an explicit user request for hands-off multi-turn pursuit, steers single bounded tasks to `kimi-rescue`, and never auto-promotes a fix or a question into a goal loop. Like the command, it refuses without the hook and offers no `--resume`.
205
207
 
206
208
  ## Read-only swarm (`/kimi:swarm`)
207
209
 
@@ -209,10 +211,10 @@ If another installed host still points to an older hook, that hook may veto thes
209
211
 
210
212
  - **The hook label is `swarm`, and it allows the read-only tool set PLUS `AgentSwarm`.** The coordinator must be allowed to call `AgentSwarm` (else the swarm never launches), but no write/edit/shell tool is allowed. The exact allowed tool name is `AgentSwarm` (verified at 0.42.0 against `packages/agent-core-v2/src/features/swarm/tools/agent-swarm/agentSwarmTool.ts`: `'AgentSwarm'`, strict schema). The singular `Agent` tool is deliberately *not* allowed — swarm is the fan-out surface, not arbitrary delegation.
211
213
  - **Every subagent inherits the `swarm` label and fires the same hook.** On native v2 (0.42.0) `AgentSwarm` children run in-process in a child DI scope with their OWN eager external-hooks service, so every child tool call passes the same before-execute channel and the same managed hook — there is no sub-vs-main branch and no deny-all shortcut on the swarm route. So a subagent's `Write`/`Edit`/`Bash` is denied exactly like a single-turn review's. The swarm listener in that channel is non-terminal and nothing auto-approves `AgentSwarm` ahead of the hook. (Legacy-v1 ≤ 0.41 reached the same result by placing the hook policy at index 0 of every agent's permission stack.) **This is locked by a real-binary smoke** (`tests/runtime/real-binary-smoke.test.ts`, run via `bun run smoke:real`): it spawns the real binary under the `swarm` label with an adversarial prompt that forces a spawned subagent to attempt a write, and asserts that **no file lands** and the hook deny marker appears — converting the subagent-denial claim from source-read-verified to runtime-test-verified.
212
- - **Defense-in-depth: read-only subagent profile.** The coordinator prompt steers the swarm to `subagent_type: "explore"` — kimi-code's read-only exploration profile, whose tool loadout has *no* `Write`/`Edit` tools at all. That is a second layer beneath the hook: even setting the hook aside, an explore subagent has no file-editing tool to call. (The hook remains the load-bearing enforcement; the profile is belt-and-suspenders.)
214
+ - **The managed hook is the read-only boundary; no `explore`-profile second layer is assumed.** Production deliberately omits `subagent_type` for read-only swarm rather than selecting kimi-code's `explore` profile. Every coordinator and subagent tool call is enforced by the same managed hook.
213
215
  - **Swarm refuses without the hook, like every model-spawning command.** A swarm makes the reason especially acute: it asks for up to `--cap` subagents, but this total-count hint is advisory, so a missing hook would remove enforcement from all of them at once. `KIMI_PLUGIN_CC_SKIP_HOOK_CHECK=1` remains an explicit tests/diagnostics escape hatch, not a recovery path.
214
216
  - **The only new risk is COST, and the plugin bounds it.** Read-only swarm writes nothing, so the new risk is runaway cost from N parallel model runs (and possible nested `AgentSwarm`). `/kimi:swarm` enforces a **mandatory finite wall-clock ceiling** via its AbortController (`--budget`, default 30m); the SIGTERM→SIGKILL descendant-reaping cancellation reaps the whole subagent process tree. Two **distinct** subagent bounds: `--cap N` is a *soft* prompt-injected hint on TOTAL subagent count (the hook is stateless and cannot enforce a count, so the model may exceed it), and `--max-concurrency N` is a *hard* ceiling on how many subagents kimi-code's `SubagentBatch` runs concurrently — exported as `KIMI_CODE_AGENT_SWARM_MAX_CONCURRENCY` (PR #888) on **kimi-code 0.18.0+** (older binaries ignore the unknown env var, so on < 0.18 only the soft hint applies). It **defaults to 4** when the user passes none (`resolveSwarmMaxConcurrency` in swarm.ts), so every swarm run — agent-dispatched or human-typed — carries an enforced peak-parallelism ceiling on 0.18.0+ rather than falling back to kimi-code's internal ramp; pass an explicit value to widen or throttle. **Never merge them into one flag:** concurrency ≠ total count — a hard concurrency cap of N still permits more than N subagents over the life of the run, just never more than N at once. The `--budget` bounds elapsed time, not a dollar amount or total token count; `--max-concurrency` bounds *peak* parallelism (the spike in simultaneous model spend), and `--cap` is only an advisory total-count nudge. Folding both onto one flag would create the "cap-as-total-count illusion" — a user reading `--cap N` would expect a total bound but receive only a peak-parallelism one.
215
- - **A model-invocable `kimi-swarm` agent (v1.3) wraps this command.** The `/kimi:swarm` slash command is human-only (`disable-model-invocation: true`, the blanket convention for *all* commands), but the `kimi-swarm` subagent lets the main Claude thread *dispatch* a read-only fan-out on its own judgement — the same human-command + model-invocable-agent split that `kimi-review`/`kimi-challenge`/`kimi-ask`/`kimi-rescue` already use. Swarm is a safe candidate for an agent precisely because it is **read-only (zero write surface)**: the only thing auto-dispatch adds is COST exposure, already bounded by the hard `--budget` and a hard `--max-concurrency` that the runtime now defaults to 4 for every swarm run (enforced on 0.18.0+) — so an auto-dispatched fan-out is bounded by construction, not by ignorable agent prose (the agent additionally restricts backgrounding to explicit user requests). `/kimi:pursue` ALSO gains a model-invocable agent (`kimi-pursue`, v1.3 — see the Autonomous goal mode section above): it is write-capable, but auto-dispatch widens no write surface (the rescue workspace allowlist + the managed PreToolUse hook gate every tool call on every continuation turn, and it cannot mutate git state) and removes no bound (the mandatory `--budget` ceiling holds), so its agent path is exactly as confined as its command path. Because it is the plugin's highest-autonomy surface, its agent description demands an explicit hands-off-autonomy request and steers single bounded tasks to `kimi-rescue`.
217
+ - **A model-invocable `kimi-swarm` agent (v1.3) wraps this command.** The `/kimi:swarm` slash command is human-only (`disable-model-invocation: true`, the blanket convention for *all* commands), but the `kimi-swarm` subagent lets the main thread *dispatch* a read-only fan-out on its own judgement — the same human-command + model-invocable-agent split that `kimi-review`/`kimi-challenge`/`kimi-ask`/`kimi-rescue` already use. Swarm is a safe candidate for an agent precisely because it is **read-only (zero write surface)**: the only thing auto-dispatch adds is COST exposure, already bounded by the hard `--budget` and a hard `--max-concurrency` that the runtime now defaults to 4 for every swarm run (enforced on 0.18.0+) — so an auto-dispatched fan-out is bounded by construction, not by ignorable agent prose (the agent additionally restricts backgrounding to explicit user requests). `/kimi:pursue` ALSO gains a model-invocable agent (`kimi-pursue`, v1.3 — see the Autonomous goal mode section above): it is write-capable, but auto-dispatch widens no write surface (the rescue workspace allowlist + the managed PreToolUse hook gate every tool call on every continuation turn, and it cannot mutate git state) and removes no bound (the mandatory `--budget` ceiling holds), so its agent path is exactly as confined as its command path. Because it is the plugin's highest-autonomy surface, its agent description demands an explicit hands-off-autonomy request and steers single bounded tasks to `kimi-rescue`.
216
218
 
217
219
  ## Write-capable swarm (`/kimi:swarm --write`)
218
220
 
@@ -220,12 +222,26 @@ If another installed host still points to an older hook, that hook may veto thes
220
222
 
221
223
  - **One ephemeral worktree for the whole swarm (Design A).** kimi-code runs `AgentSwarm` subagents **in-process**, all sharing the coordinator's session workspace (at 0.42.0 a child DI scope under `features/swarm/`; pre-0.42 `subagent-host.ts:362`, `cwd: parent.config.cwd`) — so "a worktree per subagent" is impossible without abandoning `AgentSwarm`. Instead the plugin creates ONE detached worktree off HEAD (under its own data dir, never inside the user repo) and spawns the coordinator `kimi -p` with cwd = that worktree. Every subagent inherits it. The worktree is disposable: a botched run is thrown away.
222
224
  - **Writes are confined by the hook to a forge-proof trusted root.** The `swarm-write` hook label routes write/edit/shell through the **rescue allowlist verbatim** (workspace check, symlink/realpath reject, mutating-flag + exec-delegating-flag detector, git-mutation deny), but scoped to `KIMI_PLUGIN_CC_WORKSPACE_ROOT` — the worktree path the plugin exports on the spawn — **NOT** the hook payload `cwd`. The model running inside kimi cannot set an env var on its already-spawned process, so confinement does not depend on how upstream derives the payload cwd for an in-process subagent. A missing trusted root **fails closed** (all writes denied). An out-of-worktree write (absolute path into the user's real tree) is rejected by the allowlist's `isWithin` check; a write to the worktree's `.git` pointer is rejected by the `.git`-segment check.
223
- - **`coder` subagents really can write, and the hook is the sole boundary.** Verified at kimi-code 0.42.0: swarm subagents get their own eager external-hooks service under the standard before-execute channel — there is no deny-all path on the swarm route (the `btw` side-question path is separate). So unlike read-only swarm (which has the `explore` profile's no-edit-tools as a second layer), write-swarm's `coder` subagents DO have edit tools, and the managed PreToolUse hook is the **only** write boundary — which is why the trusted-root confinement above is load-bearing and gated tightly.
225
+ - **`coder` subagents really can write, and the hook is the sole boundary.** Verified at kimi-code 0.42.0: swarm subagents get their own eager external-hooks service under the standard before-execute channel — there is no deny-all path on the swarm route (the `btw` side-question path is separate). Write-swarm's `coder` subagents DO have edit tools, and the managed PreToolUse hook is the **only** write boundary — which is why the trusted-root confinement above is load-bearing and gated tightly.
224
226
  - **The plugin never touches git; the main thread owns the merge.** Subagents cannot run git (allowlist denies mutation). The plugin captures the change set with `git add -N` (intent-to-add, so new files appear in the diff WITHOUT writing loose objects into the shared object DB) + `git diff --binary` (untruncated, applyable), writes it to a `swarm-write-<jobId>.patch` artifact, and prints the path. It does **not** `git apply`, commit, or move any ref — the user/main thread reviews the patch and applies it (`git apply <path>`). The worktree shares the main repo's object DB and refs, but no ref/HEAD is ever moved, so the user's repository state is untouched.
225
- - **The patch is captured on every terminal path, before cleanup.** Success, cancel, budget-expiry, and failure all capture the (possibly partial) patch BEFORE `git worktree remove --force` discards the tree, so a cancelled run never silently loses the work the subagents already did. That guarantee covers the plugin's own terminal paths, including `companion.sh cancel`. It does **not** cover a harness interrupt: Esc/TaskStop delivers SIGTERM then SIGKILL after ~1.35s (measured, process-group-wide), which is shorter than the 1500ms child escalation plus quiescence plus patch capture — so a hard interrupt can kill the companion mid-teardown and lose the patch. `companion.sh cancel` signals from a separate process not racing that deadline and is the work-preserving stop. A startup sweep reaps worktrees orphaned by a hard kill (SIGKILL of the plugin process); its reap TTL is derived as `MAX_DURATION_MS + 2h` so it always exceeds the longest a run can live (the 24h `--budget` cap) and can never force-remove a still-live worktree out from under a concurrent run — the sweep's only liveness signal is the worktree dir mtime, which a run editing only existing files leaves frozen at `git worktree add` time.
227
+ - **The patch is captured on every terminal path, before cleanup.** Success, cancel, budget-expiry, and failure all capture the (possibly partial) patch BEFORE `git worktree remove --force` discards the tree, so a cancelled run never silently loses the work the subagents already did. If capture itself fails, the job fails instead of completing and the worktree is retained with its path in the error details for recovery. That guarantee covers the plugin's own terminal paths, including `companion.sh cancel`. It does **not** cover a harness interrupt: Esc/TaskStop delivers SIGTERM then SIGKILL after ~1.35s (measured, process-group-wide), which is shorter than the 1500ms child escalation plus quiescence plus patch capture — so a hard interrupt can kill the companion mid-teardown and lose the patch. `companion.sh cancel` signals from a separate process not racing that deadline and is the work-preserving stop. A startup sweep reaps worktrees orphaned by a hard kill (SIGKILL of the plugin process); its reap TTL is derived as `MAX_DURATION_MS + 2h` so it always exceeds the longest a run can live (the 24h `--budget` cap) and can never force-remove a still-live worktree out from under a concurrent run — the sweep's only liveness signal is the worktree dir mtime, which a run editing only existing files leaves frozen at `git worktree add` time.
226
228
  - **Gated tightly.** Requires kimi-code **≥ 0.18.0** (below it `KIMI_CODE_AGENT_SWARM_MAX_CONCURRENCY` is ignored, so a write fan-out would have no hard peak bound — unacceptable for concurrent writers); because that cap cannot be guaranteed without a confirmed version, the write-mode version gate **fails closed** on an unconfirmable `kimi --version` probe (the shared execution-plan gate also refuses unverified or uncertified binaries for read-only swarm; older certified legacy versions may lack the hard concurrency setting). Also requires a git repo with a **committed HEAD**, and the PreToolUse hook (**refuses** without it, like read swarm). Bases the worktree on HEAD, so a **loud warning** fires when the working tree is dirty (uncommitted work is not visible to the swarm). Default `--max-concurrency` is **1** (serialize) since disjoint-target partitioning is prompt-only and unenforceable by the stateless hook; the coordinator prompt also forbids git mutation and nested `AgentSwarm`.
227
229
  - **Field-proven by a real-binary smoke.** `tests/runtime/real-binary-smoke.test.ts` runs the real `runSwarm --write` and asserts the POSITIVE end-to-end (a `coder` subagent's edits land in the worktree, captured as a patch; the user's real tree is byte-identical + git-clean; the worktree is cleaned up) plus the NEGATIVE (an absolute-path write outside the trusted root is hook-denied). It is the check that catches `tool_input` field-name drift — `Write`/`Edit` carry `path`, not `file_path`, and reading the wrong key denies every real write silently.
228
- - **Model-invocable agent (v1.5), patch-only by construction.** Following the field-proof above, `--write` now has a `kimi-swarm-write` subagent so the main Claude thread can dispatch a write fan-out on its own judgement — the same human-command + model-invocable-agent split the rest of the suite uses. Auto-dispatch is a safe extension here because it widens **no** write surface and removes **no** bound: the agent path runs the identical `runSwarm --write` code, so edits still land only in the throwaway worktree, the managed hook still gates every `coder` subagent, the patch is still handed back for the user to apply (**the plugin never applies or commits**), and `--budget` + default `--max-concurrency` 1 + the hook requirement all still hold. In fact write-swarm is a *less* dangerous agent than the `kimi-pursue` agent already shipped: pursue writes directly into the workspace, whereas write-swarm's output is a patch the human reviews before anything reaches a real tree. Because it is still a write surface, the agent description demands BOTH signals — many disjoint write targets AND explicit fan-out intent — and steers single edits to `kimi-rescue`, read-only fan-outs to `kimi-swarm`, and autonomous loops to `kimi-pursue`. Default `--max-concurrency` stays **1**; raising it (concurrent writers) remains a deliberate, human-or-explicitly-requested choice.
230
+ - **Model-invocable agent (v1.5), patch-only by construction.** Following the field-proof above, `--write` now has a `kimi-swarm-write` subagent so the main thread can dispatch a write fan-out on its own judgement — the same human-command + model-invocable-agent split the rest of the suite uses. Auto-dispatch is a safe extension here because it widens **no** write surface and removes **no** bound: the agent path runs the identical `runSwarm --write` code, so edits still land only in the throwaway worktree, the managed hook still gates every `coder` subagent, the patch is still handed back for the user to apply (**the plugin never applies or commits**), and `--budget` + default `--max-concurrency` 1 + the hook requirement all still hold. In fact write-swarm is a *less* dangerous agent than the `kimi-pursue` agent already shipped: pursue writes directly into the workspace, whereas write-swarm's output is a patch the human reviews before anything reaches a real tree. Because it is still a write surface, the agent description demands BOTH signals — many disjoint write targets AND explicit fan-out intent — and steers single edits to `kimi-rescue`, read-only fan-outs to `kimi-swarm`, and autonomous loops to `kimi-pursue`. Default `--max-concurrency` stays **1**; raising it (concurrent writers) remains a deliberate, human-or-explicitly-requested choice.
231
+
232
+ ## MCP transport and long-running jobs
233
+
234
+ The plugin also bundles an MCP stdio server for Claude Code and Codex. This is an additive transport, not a second execution engine or a way around the managed hook. The seven model tools run the same certified companion operations, exact-version checks, native-v2 preflight, workspace allowlists, finite budgets, and artifact rendering as the slash commands and skills. The review gate remains a separate Stop-hook feature.
235
+
236
+ Claude Code task-capable calls return durable MCP task IDs and use `tasks/get` and `tasks/cancel`. Codex currently consumes ordinary MCP tool results, not this task extension: after attachment, model tools return a job handle when the job is still running or its terminal result when already settled. `kimi_job_status`, `kimi_job_result`, and `kimi_job_cancel` provide polling and cancellation. Do not describe the Codex path as native MCP tasks.
237
+
238
+ Claude supplies its project directory as the default workspace. A configured host workspace is a containment boundary: an explicit workspace must be that directory or a descendant, not an arbitrary override. Codex calls normally supply an absolute `workspace` on the initial operation because its packaged stdio server runs from the plugin root; that explicit path is the operation's trust choice. Subsequent job control can use the persisted binding. Job controls manage only MCP-launched jobs, require the same canonical workspace and repository, and cannot inspect or cancel an arbitrary CLI job by ID.
239
+
240
+ Attachment waits for the operation to install cancellation handlers, with a 60-second bound. Verified teardown can close a stalled launch; missing identity or inconclusive termination evidence keeps it starting and recoverable by ID. `kimi_pid` is recorded at actual spawn rather than inferred from a later process-list match.
241
+
242
+ The MCP task/job ID is preassigned and bound to the job's real `operation_kind`, not merely its lineage. `mcp_tasks` stores launch and ownership metadata; the normal `jobs` table remains the outcome authority. Failed jobs, including budget timeouts, produce completed tasks with `isError:true` and the failed-job artifact, so partial output is not mistaken for a final answer. Supervisor and protocol failures are distinct from ordinary Kimi failures.
243
+
244
+ Closing an MCP connection does not cancel work. A later server reopens the same store and resumes status/result handling by ID. Generic stale sweeps skip MCP-owned rows, and direct CLI cancellation refuses them; use MCP job controls for those jobs. Companion and observed Kimi-descendant identities include start tokens and process groups; MCP cancellation and stale cleanup revalidate them rather than signalling a recycled PID. Cancellation remains cooperative and does not roll back edits. Pursue teardown and write-swarm patch capture still run through the existing companion path; write-swarm captures its patch on every terminal path before removing the worktree.
229
245
 
230
246
  ## Response budgets and partial output (2.2.0)
231
247
 
@@ -263,9 +279,9 @@ at it (the runtime resolves that path instead of `kimi` on `PATH`; every spawn
263
279
  still exports `KIMI_CODE_NO_AUTO_UPDATE=1`, so the pinned copy never drifts).
264
280
 
265
281
  ```sh
266
- PIN=~/.local/share/kimi-plugin-cc/kimi-bin/2.2.0
282
+ PIN=~/.local/share/kimi-plugin-cc/kimi-bin/2.3.1
267
283
  mkdir -p "$PIN" && cd "$PIN" && printf '{"name":"kimi-plugin-cc-pin","private":true}\n' > package.json
268
- bun add @moonshot-ai/kimi-code@2.2.0
284
+ bun add @moonshot-ai/kimi-code@2.3.1
269
285
  export KIMI_PLUGIN_CC_KIMI_BIN="$PIN/node_modules/.bin/kimi" # e.g. in ~/.zshenv
270
286
  ```
271
287
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "kimi-plugin-cc",
3
- "version": "2.2.2",
4
- "description": "Kimi for Claude Code and Codex: code questions, reviews, fixes, and parallel tasks through the local Kimi Code CLI.",
3
+ "version": "2.4.0",
4
+ "description": "Kimi for Claude Code and Codex: code questions, independent reviews, bounded fixes, autonomous goals, parallel tasks, and MCP tools through the local Kimi Code CLI.",
5
5
  "type": "module",
6
6
  "engines": {
7
7
  "node": ">=22.5.0"
@@ -38,14 +38,18 @@
38
38
  "claude-code",
39
39
  "codex",
40
40
  "kimi",
41
+ "kimi-code",
41
42
  "plugin",
42
- "code-review"
43
+ "code-review",
44
+ "mcp",
45
+ "model-context-protocol"
43
46
  ],
44
47
  "publishConfig": {
45
48
  "access": "public",
46
49
  "registry": "https://registry.npmjs.org/"
47
50
  },
48
51
  "files": [
52
+ ".mcp.json",
49
53
  ".claude-plugin/",
50
54
  ".agents/plugins/marketplace.json",
51
55
  "agents/",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "kimi",
3
- "version": "2.2.2",
4
- "description": "Codex plugin that delegates review, challenge, ask, rescue, pursue, and swarm workflows to the local kimi-code CLI through the kimi-plugin-cc companion runtime.",
3
+ "version": "2.4.0",
4
+ "description": "Kimi for Codex: code questions, independent reviews, bounded fixes, autonomous goals, parallel tasks, and MCP job controls through the local Kimi Code CLI.",
5
5
  "author": {
6
6
  "name": "linxule"
7
7
  },
@@ -14,13 +14,16 @@
14
14
  "review",
15
15
  "code-review",
16
16
  "delegation",
17
- "multi-model"
17
+ "multi-model",
18
+ "mcp",
19
+ "model-context-protocol"
18
20
  ],
19
21
  "skills": "./skills/",
22
+ "mcpServers": "./.mcp.json",
20
23
  "interface": {
21
24
  "displayName": "Kimi",
22
- "shortDescription": "Delegate repo work to local kimi-code",
23
- "longDescription": "Shell-only Codex packaging for kimi-plugin-cc. It exposes Codex skills that call the existing companion runtime and local kimi-code subprocess, while preserving the Claude Code plugin surface.",
25
+ "shortDescription": "Ask, review, and build with Kimi",
26
+ "longDescription": "Kimi Code workflows for Codex: read-only explanations and reviews, bounded workspace fixes, autonomous goals, and parallel tasks. Codex skills call a self-contained shell runtime, with MCP tools provided by the bundled server.",
24
27
  "developerName": "linxule",
25
28
  "category": "Developer Tools",
26
29
  "capabilities": [
@@ -0,0 +1,28 @@
1
+ {
2
+ "mcpServers": {
3
+ "kimi": {
4
+ "type": "stdio",
5
+ "command": "./scripts/companion.sh",
6
+ "args": [
7
+ "mcp"
8
+ ],
9
+ "cwd": ".",
10
+ "env_vars": [
11
+ "KIMI_PLUGIN_CC_DATA",
12
+ "CLAUDE_PLUGIN_DATA",
13
+ "PLUGIN_DATA",
14
+ "KIMI_CODE_HOME",
15
+ "KIMI_PLUGIN_CC_KIMI_BIN",
16
+ "KIMI_PLUGIN_CC_KIMI_PREFIX_ARGS",
17
+ "KIMI_PLUGIN_CC_NODE_BIN",
18
+ "KIMI_PLUGIN_CC_HOOK_SCRIPT",
19
+ "KIMI_PLUGIN_CC_HOST_ID",
20
+ "KIMI_MODEL_NAME",
21
+ "KIMI_MODEL_API_KEY",
22
+ "KIMI_MODEL_PROVIDER_TYPE",
23
+ "KIMI_MODEL_BASE_URL",
24
+ "KIMI_MODEL_THINKING_EFFORT"
25
+ ]
26
+ }
27
+ }
28
+ }
@@ -15,24 +15,16 @@ const companionEntrypoint = fileURLToPath(new URL(isCompiledRuntime ? "./compani
15
15
  const companionProjectRoot = path.resolve(path.dirname(companionEntrypoint), "..");
16
16
  const FAILURE_PERSIST_RETRY_DELAYS_MS = [100, 200, 400];
17
17
  export async function startBackgroundJob(job, prompt, context, paths, options) {
18
- // MUST be the same resolution the hook verifier uses. The worker re-verifies
19
- // the hook (rescue.ts / ask.ts, deliberately, so a background job cannot
20
- // outlive or bypass the check), and the verifier rebuilds the canonical
21
- // command from ITS OWN `process.argv0`. Spawning with a different spelling of
22
- // node than setup pinned makes the worker compute a different expected
23
- // command than the config holds → byte-exact mismatch → every background
24
- // rescue/ask refuses forever, with `/kimi:setup` unable to converge the two
25
- // sides (setup runs via companion.sh, whose argv0 is the stable symlink).
18
+ // Background spawn and hook verification must resolve the same stable Node
19
+ // path: the worker re-verifies the byte-exact hook command from its own
20
+ // runtime path, so a different spelling than setup pinned fails closed. For
21
+ // example, spawn(process.execPath) can resolve …/Cellar/node/<ver>/bin/node
22
+ // while setup pinned /opt/homebrew/bin/node.
26
23
  //
27
- // Concretely: `spawn(process.execPath)` gives the child an argv0 equal to the
28
- // fully symlink-resolved path (…/Cellar/node/<ver>/bin/node), whereas setup
29
- // pins /opt/homebrew/bin/node. Caught by Kimi's pre-release review of v1.9.0.
30
- // NOTE: this is deliberately NOT `resolveNodeBinary`, which additionally
31
- // REQUIRES an absolute override because the hook command runs under
32
- // `/bin/sh -c` with a possibly sanitized PATH. Spawning has no such
33
- // constraint — a bare name is resolved by spawn() via PATH — so the override
34
- // is honored verbatim here. Only the no-override default needs to change:
35
- // it must match what the verifier computes, hence `preferStableNodePath`.
24
+ // An explicit spawn override need not be absolute because spawn() resolves
25
+ // bare names through PATH. The hook command separately requires an absolute
26
+ // override because /bin/sh -c may have a sanitized PATH. Only the no-override
27
+ // default uses preferStableNodePath to match the verifier.
36
28
  const override = context.env.KIMI_PLUGIN_CC_NODE_BIN;
37
29
  const nodeBinary = override !== undefined && override.length > 0
38
30
  ? override