kimi-plugin-cc 2.2.1 → 2.3.1
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.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +2 -2
- package/CHANGELOG.md +22 -0
- package/README.md +5 -5
- package/agents/kimi-challenge.md +3 -3
- package/agents/kimi-pursue.md +1 -1
- package/agents/kimi-rescue.md +2 -2
- package/agents/kimi-review.md +3 -3
- package/commands/README.md +2 -2
- package/commands/challenge.md +1 -1
- package/commands/pursue.md +1 -1
- package/commands/review.md +1 -1
- package/dist/background-spawn.js +9 -17
- package/dist/cli-client.js +22 -62
- package/dist/commands/ask.js +24 -29
- package/dist/commands/cancel.js +7 -19
- package/dist/commands/cli-helpers.js +9 -17
- package/dist/commands/pursue.js +21 -31
- package/dist/commands/registry.js +6 -19
- package/dist/commands/rescue.js +20 -20
- package/dist/commands/review-gate.js +16 -24
- package/dist/commands/review.js +37 -43
- package/dist/commands/setup.js +85 -171
- package/dist/commands/swarm.js +103 -88
- package/dist/commands/worker.js +0 -6
- package/dist/git.js +14 -7
- package/dist/hooks/approval-hook.js +9 -20
- package/dist/hooks/approval-policy.js +36 -69
- package/dist/hooks/config-safety.js +7 -4
- package/dist/hooks/install-paths.js +62 -138
- package/dist/hooks/install.js +37 -108
- package/dist/hooks/managed-block.js +88 -214
- package/dist/job-store.js +6 -28
- package/dist/kimi-command.js +1 -3
- package/dist/kimi-engine.js +2 -1
- package/dist/kimi-timeouts.js +9 -21
- package/dist/kimi-version-probe.js +16 -1210
- package/dist/native-v2-preflight.js +1 -1
- package/dist/parsing.js +4 -18
- package/dist/rescue-approval.js +78 -164
- package/dist/session-recovery.js +7 -10
- package/dist/stream-json.js +22 -174
- package/dist/version.js +1 -1
- package/docs/migration.md +2 -6
- package/docs/registry-releases.md +25 -5
- package/docs/safety.md +16 -12
- package/package.json +2 -2
- package/plugins/kimi-codex/.codex-plugin/plugin.json +4 -4
- package/plugins/kimi-codex/dist/background-spawn.js +9 -17
- package/plugins/kimi-codex/dist/cli-client.js +22 -62
- package/plugins/kimi-codex/dist/commands/ask.js +24 -29
- package/plugins/kimi-codex/dist/commands/cancel.js +7 -19
- package/plugins/kimi-codex/dist/commands/cli-helpers.js +9 -17
- package/plugins/kimi-codex/dist/commands/pursue.js +21 -31
- package/plugins/kimi-codex/dist/commands/registry.js +6 -19
- package/plugins/kimi-codex/dist/commands/rescue.js +20 -20
- package/plugins/kimi-codex/dist/commands/review-gate.js +16 -24
- package/plugins/kimi-codex/dist/commands/review.js +37 -43
- package/plugins/kimi-codex/dist/commands/setup.js +85 -171
- package/plugins/kimi-codex/dist/commands/swarm.js +103 -88
- package/plugins/kimi-codex/dist/commands/worker.js +0 -6
- package/plugins/kimi-codex/dist/git.js +14 -7
- package/plugins/kimi-codex/dist/hooks/approval-hook.js +9 -20
- package/plugins/kimi-codex/dist/hooks/approval-policy.js +36 -69
- package/plugins/kimi-codex/dist/hooks/config-safety.js +7 -4
- package/plugins/kimi-codex/dist/hooks/install-paths.js +62 -138
- package/plugins/kimi-codex/dist/hooks/install.js +37 -108
- package/plugins/kimi-codex/dist/hooks/managed-block.js +88 -214
- package/plugins/kimi-codex/dist/job-store.js +6 -28
- package/plugins/kimi-codex/dist/kimi-command.js +1 -3
- package/plugins/kimi-codex/dist/kimi-engine.js +2 -1
- package/plugins/kimi-codex/dist/kimi-timeouts.js +9 -21
- package/plugins/kimi-codex/dist/kimi-version-probe.js +16 -1210
- package/plugins/kimi-codex/dist/native-v2-preflight.js +1 -1
- package/plugins/kimi-codex/dist/parsing.js +4 -18
- package/plugins/kimi-codex/dist/rescue-approval.js +78 -164
- package/plugins/kimi-codex/dist/session-recovery.js +7 -10
- package/plugins/kimi-codex/dist/stream-json.js +22 -174
- package/plugins/kimi-codex/dist/version.js +1 -1
- package/plugins/kimi-codex/skills/kimi-ask/SKILL.md +2 -2
- package/plugins/kimi-codex/skills/kimi-cancel/SKILL.md +1 -1
- package/plugins/kimi-codex/skills/kimi-challenge/SKILL.md +1 -1
- package/plugins/kimi-codex/skills/kimi-pursue/SKILL.md +1 -1
- package/plugins/kimi-codex/skills/kimi-replay/SKILL.md +1 -1
- package/plugins/kimi-codex/skills/kimi-rescue/SKILL.md +1 -1
- package/plugins/kimi-codex/skills/kimi-result/SKILL.md +1 -1
- package/plugins/kimi-codex/skills/kimi-review/SKILL.md +3 -3
- package/plugins/kimi-codex/skills/kimi-setup/SKILL.md +1 -1
- package/plugins/kimi-codex/skills/kimi-status/SKILL.md +1 -1
- package/plugins/kimi-codex/skills/kimi-swarm/SKILL.md +1 -1
- package/plugins/kimi-codex/skills/kimi-swarm-write/SKILL.md +1 -1
package/dist/stream-json.js
CHANGED
|
@@ -10,162 +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"} //
|
|
13
|
+
// {"role":"meta","type":"system.version","version":"0.24.2"} // native-v2 marker; required first on native-v2 plans and filtered after capture
|
|
14
|
+
//
|
|
15
|
+
// {"type":"error","code":"session.occupied","message":"...","details":{...}} // 2.3.0; stdout-only refusal, exit 4
|
|
14
16
|
//
|
|
15
17
|
// Notes on what does and does not appear here:
|
|
16
|
-
// -
|
|
17
|
-
//
|
|
18
|
-
// `PromptJsonResumeMetaMessage` / `writeResumeHint` / `PromptJsonWriter` —
|
|
19
|
-
// sits at apps/kimi-code/src/cli/run-prompt.ts:567-696 as of 0.9.0 and is
|
|
20
|
-
// verified compatible through 0.24.2. 2026-05-27 audit covered 0.4.0,
|
|
21
|
-
// 2026-05-28 audit covered 0.5.0 (run-prompt.ts zero-byte diff across
|
|
22
|
-
// both), 2026-05-31 audit covered 0.6.0, 2026-06-03 audit covered
|
|
23
|
-
// 0.7.0/0.8.0/0.9.0 in one cumulative pass, 2026-06-09 audit covered
|
|
24
|
-
// 0.10.0/0.11.0/0.12.0 in one cumulative 0.9.0→0.12.0 pass, 2026-06-12 audit
|
|
25
|
-
// 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
|
|
26
|
-
// pass (records/ dir empty diff; the 0.13/0.14 packages/protocol/ REST+WS
|
|
27
|
-
// control API is a separate transport — run-prompt.ts imports none of it,
|
|
28
|
-
// `-p` stdout stays the direct PromptJsonWriter), 2026-06-13 audit covered
|
|
29
|
-
// the 0.14.2 patch (records/ + the PromptJsonWriter/writeResumeHint region
|
|
30
|
-
// both 0-byte; the patch is a repo-wide `.md`→`.md?raw` bundler-import
|
|
31
|
-
// migration plus a Bash-tool stdout/stderr streaming callback, neither
|
|
32
|
-
// touching the writer), and 2026-06-14 covered the 0.14.3 patch (all four
|
|
33
|
-
// scoped diffs 0-byte; the entire patch is a TUI model-picker
|
|
34
|
-
// provider-refresh change — PR #713 — off the `-p` path), and 2026-06-16
|
|
35
|
-
// covered the 0.15.0 minor (permission/hooks/run-prompt/cli all 0-byte by
|
|
36
|
-
// independent `git diff` byte-count; the only scoped change is records/ +
|
|
37
|
-
// session/ persistence churn — PR #786 drops app_version/resumed from the
|
|
38
|
-
// .records/ metadata artifact, a SessionSkillRegistry rename, a static
|
|
39
|
-
// model-capability lookup — none of it on the `-p` stdout stream; plus an
|
|
40
|
-
// additive `transport:'sse'` MCP config variant the plugin never writes),
|
|
41
|
-
// and 2026-06-17 covered the 0.16.0 minor (permission/hooks/run-prompt all
|
|
42
|
-
// 0-byte; the only argv change registers a new `kimi vis` subcommand off
|
|
43
|
-
// the `-p` path, and the records/replay/compaction/logging churn is
|
|
44
|
-
// internal — not on the stdout stream; GREEN smoke 7/0 on the 0.16.0
|
|
45
|
-
// binary), 2026-06-19 covered the 0.16.0→0.18.0 jump (0.17.0/0.17.1/0.18.0;
|
|
46
|
-
// 03-hooks.diff 0-byte, run-prompt.ts only a telemetry refactor outside the
|
|
47
|
-
// writer, the sole permission change a NON-auto-mode GoalStartReviewAsk that
|
|
48
|
-
// is dead on `-p`), and 2026-06-23 covered the 0.18.0→0.19.1 minor (the
|
|
49
|
-
// writer + records/ 0-byte; the bumping feature #812 `--add-dir` touches
|
|
50
|
-
// only an approve policy at the queue tail, structurally dead below the
|
|
51
|
-
// `-p` auto-mode approve and still pre-empted by our index-0 hook even when
|
|
52
|
-
// project-local `.kimi-code/local.toml` makes additionalDirs non-empty;
|
|
53
|
-
// #963's new `turn.ended` reason:'filtered' is a failure REASON not a new
|
|
54
|
-
// stdout record SHAPE;
|
|
55
|
-
// GREEN smoke 9/0 on the operator's 0.19.1 binary —
|
|
56
|
-
// .claude/kimi-code-research/reports/85-upstream-0191-surface.md), and
|
|
57
|
-
// 2026-06-26 covered the 0.19.1→0.20.0 minor (run-prompt.ts + options.ts +
|
|
58
|
-
// records/ all 0-byte; #1040's new `warning` agent event is swallowed on `-p`
|
|
59
|
-
// at run-prompt.ts:495 `case 'warning': return;`, so it never reaches this
|
|
60
|
-
// parser; #1062's tool-result budget adds a `truncated` flag to tool-result
|
|
61
|
-
// CONTENT, not the record SHAPE; the new runShellCommand RPC + kimi server/web
|
|
62
|
-
// stack are off the `-p` path — report 88. Smoke NOT run this cert: the 0.20.0
|
|
63
|
-
// `bun run smoke:real` was quota-blocked, 403 usage-limit, records:[] — an
|
|
64
|
-
// operator-billing false alarm, not a compat break; certified on source audit;
|
|
65
|
-
// the later 0.20.2 smoke ran GREEN 9/0 on 2026-06-29), and 2026-07-01
|
|
66
|
-
// covered the 0.20.3→0.21.1 minor+patch (report 93; agent/records/ 0-byte,
|
|
67
|
-
// run-prompt.ts writer/resume_hint/goal.summary unchanged, #1204 plugin
|
|
68
|
-
// slash commands off the `-p` path, #1233 cleanup timeout after the writer
|
|
69
|
-
// flush; GREEN smoke 9/0 on the operator's 0.21.1 binary), and 2026-07-05
|
|
70
|
-
// covered the 0.21.1→0.22.3 minor+patch (reports 94/95; hook engine
|
|
71
|
-
// unchanged, policy order still hook-first, writer shape compatible; 0.22.3's
|
|
72
|
-
// prompt-mode background-drain happens after assistant-output flush and is
|
|
73
|
-
// bounded by plugin budgets; shell-output caps and session-owned
|
|
74
|
-
// media-originals do not alter the NDJSON record schema; GREEN smoke 9/0 on
|
|
75
|
-
// a temp-installed 0.22.3 binary), and 2026-07-07 covered the
|
|
76
|
-
// 0.22.3→0.23.1 minor (report 96; hook engine unchanged, policy order still
|
|
77
|
-
// hook-first, PromptJsonWriter-compatible scoped `run-prompt.ts` delta only
|
|
78
|
-
// adds telemetry sessionId; `select_tools` is denied by our hook unless
|
|
79
|
-
// deliberately allowlisted; observability records are persisted agent-log
|
|
80
|
-
// records, not new stdout NDJSON shapes; GREEN smoke 9/0 on a temp-installed
|
|
81
|
-
// 0.23.1 binary), and 2026-07-08 covered the 0.23.2 patch (daily checkup;
|
|
82
|
-
// no writer/resume_hint/goal.summary drift, `02-permission.diff` and
|
|
83
|
-
// `05-session-bootstrap.diff` 0-byte; the only scoped `run-prompt.ts`
|
|
84
|
-
// change keeps the cleanup timeout ref'd so failed headless `-p` turns exit
|
|
85
|
-
// non-zero reliably, and the only hook-engine-adjacent change adds
|
|
86
|
-
// `windowsHide:true` to hook child-process spawn options on Windows; GREEN
|
|
87
|
-
// smoke 9/0 on a temp-installed 0.23.2 binary), and 2026-07-09 covered the
|
|
88
|
-
// 0.23.3/0.23.4 patches (daily checkup; permission + both hook-tree diffs
|
|
89
|
-
// 0-byte; #1516 keeps prompt-mode goals alive across continuation turns
|
|
90
|
-
// until terminal without changing PromptJsonWriter, session.resume_hint,
|
|
91
|
-
// or goal.summary shapes; the wire/session delta is internal provider-auth
|
|
92
|
-
// and media-projection plumbing, while the bootstrap delta adds `[image]`
|
|
93
|
-
// limits without changing hook or permission construction; GREEN smoke 9/0
|
|
94
|
-
// on a temp-installed 0.23.4 binary, including two-minute multi-turn goal
|
|
95
|
-
// confinement and both write-swarm worktree-boundary assertions), and
|
|
96
|
-
// 2026-07-11 covered the 0.23.5 patch at tag commit `352a4492`
|
|
97
|
-
// (permission, both hook trees, and session bootstrap all 0-byte; the wire
|
|
98
|
-
// diff only adds the internal `media-stripped` projection type). The one
|
|
99
|
-
// live stdout change is this file's `turn.step.retrying` meta record: it is
|
|
100
|
-
// modeled exactly and filtered from consumer records while malformed shapes
|
|
101
|
-
// remain diagnostic. The post-change exact-binary smoke was GREEN 9/0 in
|
|
102
|
-
// 229.56s, including the full goal budget and both write-swarm boundaries),
|
|
103
|
-
// and 2026-07-12 covered the 0.23.6 patch at tag commit `b5c236d0`
|
|
104
|
-
// (permission and both hook trees 0-byte; no new PromptJsonWriter record;
|
|
105
|
-
// `run-prompt.ts` now keeps print mode alive across active goals, pending
|
|
106
|
-
// cron fires, and opt-in background steering, all still bounded externally
|
|
107
|
-
// by plugin budgets; subagent timeout config and the capability rename to
|
|
108
|
-
// dynamically_loaded_tools do not alter stdout shape). Exact-binary smoke
|
|
109
|
-
// was GREEN 9/0 in 274.55s, including the full goal budget and both
|
|
110
|
-
// write-swarm boundaries), and 2026-07-15 covered the 0.23.6→0.24.2
|
|
111
|
-
// minor (Phase-1 reports 97-100; permission and the live session/hooks
|
|
112
|
-
// surfaces 0-byte; default-v1 writer/resume/retry/goal shapes compatible;
|
|
113
|
-
// GREEN exact-binary v1 smoke 9/0, 39 assertions in 261.33s). A truthy
|
|
114
|
-
// native v2 shares the writer shapes and terminal session.resume_hint but
|
|
115
|
-
// prepends one new
|
|
116
|
-
// role:"meta", type:"system.version" record. Unknown meta is intentionally
|
|
117
|
-
// non-fatal: it becomes diagnostic-only, never enters records[]/onRecord,
|
|
118
|
-
// and cannot affect prose or session pinning. A targeted exact-v2 review
|
|
119
|
-
// smoke denied the forced write (1 pass / 8 skip / 0 fail).
|
|
120
|
-
// 2026-07-16 covered 0.25.0 + 0.26.0 (the 0.25.0 step added only 943 B
|
|
121
|
-
// of permission telemetry; all three primary safety surfaces were 0-byte
|
|
122
|
-
// on the 0.26.0 step; no new PromptJsonWriter record on either),
|
|
123
|
-
// 2026-07-18 covered 0.27.0, 2026-07-21 covered 0.28.1,
|
|
124
|
-
// 2026-07-23/24 covered 0.29.0 + 0.29.1, and 2026-07-27 covered 0.29.2
|
|
125
|
-
// (permission/hooks stayed 0-byte across every step; the 0.29.x
|
|
126
|
-
// wire/bootstrap changes did not alter PromptJsonWriter output; GREEN
|
|
127
|
-
// exact-binary smoke 10/0, 43 assertions, on each of exact 0.27.0,
|
|
128
|
-
// 0.28.1, 0.29.0, 0.29.1, and 0.29.2; the 0.29.1 veto-event refactor
|
|
129
|
-
// and 0.29.2 automatic scope-activation refactor preserved the modeled
|
|
130
|
-
// system.version signal and terminal session pinning), 2026-07-29 covered
|
|
131
|
-
// 0.30.0, 2026-07-31 covered 0.31.0, 2026-08-01 covered 0.31.1, and
|
|
132
|
-
// 2026-08-08 covered 0.32.0 through 0.34.0, 2026-08-12 covered 0.35.0,
|
|
133
|
-
// 2026-08-13 covered 0.36.0, 2026-08-24 covered 0.37.0 through
|
|
134
|
-
// 0.38.0, 2026-08-27 covered 0.39.0 (prompt-render.ts byte-identical;
|
|
135
|
-
// the MCP structured-content and error-code changes shift only prose
|
|
136
|
-
// inside opaque tool content strings), and 2026-09-02 covered 0.40.0
|
|
137
|
-
// (wire/session scope 0 bytes; run-prompt.ts absent from the diff). The
|
|
138
|
-
// 2026-09-06 covered 0.41.0 (v1 core, prompt-render.ts, goal-prompt.ts,
|
|
139
|
-
// and prompt-session.ts unchanged; exact-binary smoke 12/0, 55 assertions).
|
|
140
|
-
// The legacy-v1 writer remains compatible through 0.41.0;
|
|
141
|
-
// custom profiles can change content/tools but
|
|
142
|
-
// not the NDJSON envelope. In 0.33.0 native v2 became the unflagged default,
|
|
143
|
-
// so v1.9.6 forces KIMI_CODE_LEGACY_FLAG=1 in every accepted child while
|
|
144
|
-
// retaining the old experimental-selector refusal. This parser continues
|
|
145
|
-
// to model v2's system.version record for diagnostics and a future safe
|
|
146
|
-
// re-enable; absence of that record is now a legacy-pin smoke assertion.
|
|
147
|
-
// NB: from 0.6.0 run-prompt.ts
|
|
148
|
-
// is no longer a whole-file zero-byte diff — at 0.6.0 it gained a
|
|
149
|
-
// resume-session workDir guard, and at 0.8.0 it gained headless goal
|
|
150
|
-
// mode (`runHeadlessGoal`) — but both changes are OUTSIDE the
|
|
151
|
-
// stream-json writer, which every audit confirmed byte-identical by
|
|
152
|
-
// content/blob SHA. Goal mode also writes a `{"type":"goal.summary",...}`
|
|
153
|
-
// line (no `role` field) before the resume hint when the prompt is
|
|
154
|
-
// `/goal`-prefixed (on 0.8–0.11 the `goal-command` experimental flag must
|
|
155
|
-
// also be on; 0.12.0 removed that gate — PR #569 — so the `/goal` prefix
|
|
156
|
-
// alone triggers it). Read-only commands never trigger it (their trimmed
|
|
157
|
-
// prompt never starts with `/goal`); the v1.1 /kimi:pursue command is
|
|
158
|
-
// the intentional consumer — it is recognized as a first-class record on
|
|
159
|
-
// the dedicated `StreamJsonOutcome.goalSummary` channel (see
|
|
160
|
-
// GoalSummaryRecord below), NOT routed to the malformed channel. See
|
|
161
|
-
// reports 47-65, 72-76).
|
|
162
|
-
// The hint is emitted once per prompt run at session END
|
|
163
|
-
// (after runPromptTurn settles), not at session start. In 0.1.x the
|
|
164
|
-
// resume hint went to stderr only; 0.2.0+ emits a structured
|
|
165
|
-
// stream-json record on stdout when `--output-format stream-json` is
|
|
166
|
-
// selected (text-output mode still writes the stderr line). Our
|
|
167
|
-
// cli-client consumes the meta record's `session_id` for resume/replay
|
|
168
|
-
// 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.
|
|
169
20
|
// - hook.result events arrive as role:"assistant" with the block rendered as
|
|
170
21
|
// plain text (see formatHookResultPlain in run-prompt.ts). Our
|
|
171
22
|
// PreToolUse hook's deny reason therefore surfaces in the assistant
|
|
@@ -183,10 +34,8 @@ export const MAX_STREAM_JSON_LINE_BYTES = 1_048_576;
|
|
|
183
34
|
* Lifecycle:
|
|
184
35
|
* - `push(chunk)` — consume bytes; returns outcomes for any complete lines
|
|
185
36
|
* that finished within this chunk.
|
|
186
|
-
* - `flush()` — at end of stream,
|
|
187
|
-
*
|
|
188
|
-
* a non-empty flush remainder indicates an unfinished record (likely an
|
|
189
|
-
* 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.
|
|
190
39
|
*
|
|
191
40
|
* Outcomes are non-throwing — malformed lines surface via `malformedLine` /
|
|
192
41
|
* `malformedReason` so callers can route them to a diagnostics log without
|
|
@@ -223,8 +72,6 @@ export class StreamJsonParser {
|
|
|
223
72
|
while (newlineIndex !== -1) {
|
|
224
73
|
const line = this.buffer.slice(0, newlineIndex);
|
|
225
74
|
this.buffer = this.buffer.slice(newlineIndex + 1);
|
|
226
|
-
// Skip blank lines (the writer doesn't emit them, but tolerate
|
|
227
|
-
// CRLF-doubled or trailing whitespace defensively).
|
|
228
75
|
if (line.replace(/\r/g, "").length > 0) {
|
|
229
76
|
out.push(parseLine(line));
|
|
230
77
|
}
|
|
@@ -258,21 +105,26 @@ function parseLine(line) {
|
|
|
258
105
|
if (role === "meta") {
|
|
259
106
|
return validateMeta(parsed, line);
|
|
260
107
|
}
|
|
261
|
-
// Role-less goal-mode summary (kimi-code 0.8.0+). Keyed by `type`, not
|
|
262
|
-
// `role`, so it's surfaced on the dedicated goalSummary outcome channel.
|
|
263
108
|
if (role === undefined && parsed["type"] === "goal.summary") {
|
|
264
109
|
return validateGoalSummary(parsed, line);
|
|
265
110
|
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
111
|
+
if (role === undefined && parsed["type"] === "error") {
|
|
112
|
+
if (typeof parsed["code"] !== "string" || parsed["code"].length === 0 ||
|
|
113
|
+
typeof parsed["message"] !== "string" || parsed["message"].length === 0 ||
|
|
114
|
+
(parsed["details"] !== undefined && !isRecord(parsed["details"]))) {
|
|
115
|
+
return { malformedLine: line, malformedReason: "error record field has unexpected type" };
|
|
116
|
+
}
|
|
117
|
+
// Keep bounded diagnostic fields only; holder metadata is not needed and
|
|
118
|
+
// must not become a session-id source or be copied into persisted errors.
|
|
119
|
+
return { cliError: {
|
|
120
|
+
type: "error",
|
|
121
|
+
code: parsed["code"].slice(0, 200),
|
|
122
|
+
message: parsed["message"].slice(0, 4000),
|
|
123
|
+
} };
|
|
124
|
+
}
|
|
271
125
|
if (typeof role === "string" && role.length > 0) {
|
|
272
126
|
return { unknownRecord: { role, raw: parsed } };
|
|
273
127
|
}
|
|
274
|
-
// Genuinely malformed: no `role` at all (and not a recognized role-less typed
|
|
275
|
-
// record like goal.summary), or a non-string role. Surface as malformed.
|
|
276
128
|
return {
|
|
277
129
|
malformedLine: line,
|
|
278
130
|
malformedReason: `unknown role: ${JSON.stringify(role)}`,
|
|
@@ -384,9 +236,7 @@ function validateMeta(parsed, line) {
|
|
|
384
236
|
};
|
|
385
237
|
return { record };
|
|
386
238
|
}
|
|
387
|
-
// Unknown meta types are
|
|
388
|
-
// malformed channel for diagnostics, but don't crash. kimi-code may add
|
|
389
|
-
// new meta types in 0.3.x and our wrapper should keep working.
|
|
239
|
+
// Unknown meta types are diagnostic-only and nonfatal.
|
|
390
240
|
return {
|
|
391
241
|
malformedLine: line,
|
|
392
242
|
malformedReason: `unknown meta.type: ${JSON.stringify(type)}`,
|
|
@@ -487,8 +337,6 @@ export function extractSessionIdFromStderr(stderr) {
|
|
|
487
337
|
// hostile stderr line could pin `session_--------` (8 dashes) or any
|
|
488
338
|
// 8+ char hex-dash token as the captured session id, weakening the
|
|
489
339
|
// "anchored full-UUID regex" safety invariant documented in AGENTS.md.
|
|
490
|
-
// Review-smoke (kimi 0.2.0 on the alpha.5 candidate) flagged the loose
|
|
491
|
-
// form; this is the post-review tightening.
|
|
492
340
|
const uuid = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}";
|
|
493
341
|
const pattern = new RegExp(`^To resume this session:\\s+kimi\\s+-r\\s+(session_${uuid}|${uuid})\\s*$`, "im");
|
|
494
342
|
return stderr.match(pattern)?.[1];
|
package/dist/version.js
CHANGED
package/docs/migration.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Compatibility and upgrades
|
|
2
2
|
|
|
3
|
-
For plugin 2.
|
|
3
|
+
For plugin 2.3.1, 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.
|
|
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.3.1 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,10 +6,30 @@ remain available. The package has no install scripts and no separate CLI.
|
|
|
6
6
|
|
|
7
7
|
## Publishing status
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
The current release candidate is plugin **2.3.1**, certifying exact CLI **2.3.1**; publication is pending final repository and CI gates. 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
|
+
|
|
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
|
|
28
|
+
the release-event run of `publish.yml` (run 38720071707) through GitHub Actions OIDC with a signed
|
|
29
|
+
provenance statement (sigstore log index 3263398504; tarball shasum
|
|
30
|
+
`03907c66d440155763a860ab52b5107c0233938b`, registry propagation took a few minutes this time).
|
|
31
|
+
2.2.0 (same day, run 38658918855, log index 3256863983) was the first release where the tag-triggered
|
|
32
|
+
path worked end to end without a manual dispatch; 2.0.8 (same day, run 38650044033) was the first
|
|
13
33
|
OIDC publication; 2.0.7 (September 26) was the hand-published bootstrap.
|
|
14
34
|
The first upload used the validated GitHub Actions artifact and interactive npm
|
|
15
35
|
authentication. Its public tarball matched that artifact byte-for-byte, and a
|
|
@@ -19,7 +39,7 @@ The trusted publisher is configured for `linxule/kimi-plugin-cc`, workflow
|
|
|
19
39
|
`publish.yml`, environment `npm`, with direct publishing allowed.
|
|
20
40
|
`NPM_TRUSTED_PUBLISHING=true` is enabled in GitHub Actions. Future GitHub releases
|
|
21
41
|
use short-lived OIDC credentials, without a stored npm token or interactive 2FA.
|
|
22
|
-
|
|
42
|
+
OIDC publication is established; never republish an existing version.
|
|
23
43
|
|
|
24
44
|
**2026-10-15 status.** The `v2.0.8` release triggered the first OIDC attempt. It
|
|
25
45
|
failed twice on the publish step for reasons unrelated to the package: current
|
package/docs/safety.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# Safety
|
|
2
2
|
|
|
3
|
-
This guide describes the
|
|
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`, and `2.
|
|
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,6 +156,10 @@ 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 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.
|
|
162
|
+
|
|
159
163
|
## The rescue allowlist
|
|
160
164
|
|
|
161
165
|
For `/kimi:rescue`, the hook delegates to [`evaluateRescueHookRequest`](../runtime/rescue-approval.ts). The allowlist has been extended since the original Wire-transport implementation. Highlights:
|
|
@@ -167,7 +171,7 @@ For `/kimi:rescue`, the hook delegates to [`evaluateRescueHookRequest`](../runti
|
|
|
167
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).
|
|
168
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.
|
|
169
173
|
- **Read-safe commands** (`ls`, `cat`, `head`, `tail`, `rg`, `pwd`, `wc`, `git status`, `git diff`, ...): allowed.
|
|
170
|
-
- **`.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
|
|
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.
|
|
171
175
|
|
|
172
176
|
### Effective working directory (`Bash.cwd`, v1.10.0)
|
|
173
177
|
|
|
@@ -187,7 +191,7 @@ Treat these files as **trusted model instructions** before using write-capable r
|
|
|
187
191
|
|
|
188
192
|
## Autonomous goal mode (`/kimi:pursue`, experimental)
|
|
189
193
|
|
|
190
|
-
`/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
|
|
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.
|
|
191
195
|
|
|
192
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.
|
|
193
197
|
|
|
@@ -196,10 +200,10 @@ If another installed host still points to an older hook, that hook may veto thes
|
|
|
196
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.
|
|
197
201
|
|
|
198
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.
|
|
199
|
-
- **The only new risk is unboundedness, and the plugin bounds it.** Headless goal create sets no token/turn budget
|
|
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.
|
|
200
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.
|
|
201
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.
|
|
202
|
-
- **A model-invocable `kimi-pursue` agent (v1.3) wraps this command.** The main
|
|
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`.
|
|
203
207
|
|
|
204
208
|
## Read-only swarm (`/kimi:swarm`)
|
|
205
209
|
|
|
@@ -207,10 +211,10 @@ If another installed host still points to an older hook, that hook may veto thes
|
|
|
207
211
|
|
|
208
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.
|
|
209
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.
|
|
210
|
-
- **
|
|
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.
|
|
211
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.
|
|
212
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.
|
|
213
|
-
- **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
|
|
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`.
|
|
214
218
|
|
|
215
219
|
## Write-capable swarm (`/kimi:swarm --write`)
|
|
216
220
|
|
|
@@ -218,12 +222,12 @@ If another installed host still points to an older hook, that hook may veto thes
|
|
|
218
222
|
|
|
219
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.
|
|
220
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.
|
|
221
|
-
- **`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).
|
|
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.
|
|
222
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.
|
|
223
|
-
- **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.
|
|
224
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`.
|
|
225
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.
|
|
226
|
-
- **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
|
|
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.
|
|
227
231
|
|
|
228
232
|
## Response budgets and partial output (2.2.0)
|
|
229
233
|
|
|
@@ -368,7 +372,7 @@ If you find a way to coax /kimi:review, /kimi:challenge, /kimi:review_gate, or /
|
|
|
368
372
|
|
|
369
373
|
**Hook-escape monitor** (§1): the cli-client replays the pure hook policy on every streamed `tool_calls` entry; a call the policy denies whose tool result is not the hook's denial (byte-exact, or with upstream's repeat-breaker `<system-reminder>` decoration / handoff text) is recorded as `mismatch`, and a denied call still without a result at settlement as `unresolved`. Findings are **detection evidence, never a failure**: logged (`hook_escape`), persisted on the job row (`hook_escapes`, JSON), exposed by `status`/`result --json`, printed as a stderr `safety hook notice`, and carried in timeout details; the run continues, the job's outcome is unchanged, and the review gate stays fail-open and only annotates. The stream has no outcome field, so a mismatch is equally an upstream pre-hook rejection (unknown tool, disabled tool, invalid args, sensitive path, per-tool resolve errors), a coexisting hook or permission policy blocking first, or hook drift — failing the job needs positive execution evidence this stream does not provide. Streamed calls under read-only labels are covered (swarm subagents are in-process, not streamed); write labels only for the pure denials (plan/goal tools).
|
|
370
374
|
|
|
371
|
-
A denied attempt alone is not a finding: the hook's denial text in the corresponding tool result (byte-exact, or carrying upstream's repeat-breaker decoration) proves enforcement and clears it. Anything else is recorded as `mismatch` with a 200-character excerpt; a denied call with no result at settlement as `unresolved`. **What a finding means:** the model received something other than the managed denial. On certified 2.2.0 that is produced just as readily by calls upstream rejects before the hook runs (`Tool "LS" not found`, `Invalid args for tool …`, the sensitive-path `PathSecurityError` for `.env`, per-tool resolve errors, `Tool skipped because a previous tool call stopped the turn.`), by a coexisting PreToolUse hook ahead of the managed block or an upstream permission policy blocking first, or by a cancel/budget abort landing before the result — none of which executed anything — as by a hook that failed open. Because the stream carries no outcome field, the plugin cannot tell these apart, so it reports and does not fail: the job completes or fails on its own merits, `status`/`result --json` carry `hook_escapes`, stderr prints a `safety hook notice`, and the review gate annotates `systemMessage` without changing its decision. Treat a finding as a prompt to run `/kimi:setup --check` and read the excerpt, not as proof of a breach; treat a *recurring* `mismatch` whose excerpt looks like tool output (`Wrote N bytes to …`) as a drifted hook. Coverage is streamed calls under read-only labels and pure denials under write labels — not filesystem-dependent rescue allowlist decisions, not swarm subagent calls (in-process, never streamed). Non-vacuous live control (CLI 2.2.0, 2026-10-15): with the hook command pointed at a nonexistent script upstream failed open, `Write` created the file, and the finding was `mismatch` with excerpt `Wrote 7 bytes to ESCAPE.txt`; with the hook intact the same prompt produced the byte-exact denial and no finding. Enforcement (stopping the run, failing the job) is deliberately not shipped: it needs positive execution evidence. The
|
|
375
|
+
A denied attempt alone is not a finding: the hook's denial text in the corresponding tool result (byte-exact, or carrying upstream's repeat-breaker decoration) proves enforcement and clears it. Anything else is recorded as `mismatch` with a 200-character excerpt; a denied call with no result at settlement as `unresolved`. **What a finding means:** the model received something other than the managed denial. On certified 2.2.0 that is produced just as readily by calls upstream rejects before the hook runs (`Tool "LS" not found`, `Invalid args for tool …`, the sensitive-path `PathSecurityError` for `.env`, per-tool resolve errors, `Tool skipped because a previous tool call stopped the turn.`), by a coexisting PreToolUse hook ahead of the managed block or an upstream permission policy blocking first, or by a cancel/budget abort landing before the result — none of which executed anything — as by a hook that failed open. Because the stream carries no outcome field, the plugin cannot tell these apart, so it reports and does not fail: the job completes or fails on its own merits, `status`/`result --json` carry `hook_escapes`, stderr prints a `safety hook notice`, and the review gate annotates `systemMessage` without changing its decision. Treat a finding as a prompt to run `/kimi:setup --check` and read the excerpt, not as proof of a breach; treat a *recurring* `mismatch` whose excerpt looks like tool output (`Wrote N bytes to …`) as a drifted hook. Coverage is streamed calls under read-only labels and pure denials under write labels — not filesystem-dependent rescue allowlist decisions, not swarm subagent calls (in-process, never streamed). Non-vacuous live control (CLI 2.2.0, 2026-10-15): with the hook command pointed at a nonexistent script upstream failed open, `Write` created the file, and the finding was `mismatch` with excerpt `Wrote 7 bytes to ESCAPE.txt`; with the hook intact the same prompt produced the byte-exact denial and no finding. Enforcement (stopping the run, failing the job) is deliberately not shipped: it needs positive execution evidence. The upstream ask, filed as MoonshotAI/kimi-code#4241, is an `outcome: executed|vetoed|rejected` field on print-mode tool records; a plugin-side alternative is a per-job trace the hook writes before it exits.
|
|
372
376
|
|
|
373
377
|
## Review-gate thinking request
|
|
374
378
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kimi-plugin-cc",
|
|
3
|
-
"version": "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.3.1",
|
|
4
|
+
"description": "Kimi for Claude Code and Codex: code questions, independent reviews, bounded fixes, autonomous goals, and parallel tasks through the local Kimi Code CLI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": ">=22.5.0"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kimi",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "2.3.1",
|
|
4
|
+
"description": "Kimi for Codex: code questions, independent reviews, bounded fixes, autonomous goals, and parallel tasks through the local Kimi Code CLI.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "linxule"
|
|
7
7
|
},
|
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
"skills": "./skills/",
|
|
20
20
|
"interface": {
|
|
21
21
|
"displayName": "Kimi",
|
|
22
|
-
"shortDescription": "
|
|
23
|
-
"longDescription": "
|
|
22
|
+
"shortDescription": "Ask, review, and build with Kimi",
|
|
23
|
+
"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; no MCP server is required.",
|
|
24
24
|
"developerName": "linxule",
|
|
25
25
|
"category": "Developer Tools",
|
|
26
26
|
"capabilities": [
|
|
@@ -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
|
-
//
|
|
19
|
-
// the
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
//
|
|
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
|
-
//
|
|
28
|
-
//
|
|
29
|
-
//
|
|
30
|
-
//
|
|
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
|