kimi-plugin-cc 2.5.1 → 2.5.3

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 (50) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +15 -1
  4. package/README.md +62 -4
  5. package/dist/cli-client.js +8 -0
  6. package/dist/commands/setup.js +78 -51
  7. package/dist/commands/swarm.js +2 -2
  8. package/dist/dsh/companion.js +9 -6
  9. package/dist/dsh/index.js +15 -2
  10. package/dist/hooks/config-safety.js +5 -2
  11. package/dist/hooks/managed-block.js +86 -20
  12. package/dist/kimi-command.js +1 -0
  13. package/dist/kimi-engine.js +3 -0
  14. package/dist/native-v2-preflight.js +138 -30
  15. package/dist/swarm-activation.js +276 -14
  16. package/dist/version.js +1 -1
  17. package/docs/migration.md +8 -6
  18. package/docs/registry-releases.md +66 -3
  19. package/docs/safety.md +14 -10
  20. package/docs/swarm-recovery.md +2 -2
  21. package/docs/upstream-2.5.0-certification.md +48 -0
  22. package/docs/upstream-2.5.1-certification.md +43 -0
  23. package/package.json +40 -5
  24. package/plugins/kimi-codex/.codex-plugin/plugin.json +1 -1
  25. package/plugins/kimi-codex/dist/cli-client.js +8 -0
  26. package/plugins/kimi-codex/dist/commands/setup.js +78 -51
  27. package/plugins/kimi-codex/dist/commands/swarm.js +2 -2
  28. package/plugins/kimi-codex/dist/dsh/companion.js +9 -6
  29. package/plugins/kimi-codex/dist/dsh/index.js +15 -2
  30. package/plugins/kimi-codex/dist/hooks/config-safety.js +5 -2
  31. package/plugins/kimi-codex/dist/hooks/managed-block.js +86 -20
  32. package/plugins/kimi-codex/dist/kimi-command.js +1 -0
  33. package/plugins/kimi-codex/dist/kimi-engine.js +3 -0
  34. package/plugins/kimi-codex/dist/native-v2-preflight.js +138 -30
  35. package/plugins/kimi-codex/dist/swarm-activation.js +276 -14
  36. package/plugins/kimi-codex/dist/version.js +1 -1
  37. package/plugins/kimi-dsh/README.md +4 -1
  38. package/plugins/kimi-dsh/dist/cli-client.js +8 -0
  39. package/plugins/kimi-dsh/dist/commands/setup.js +78 -51
  40. package/plugins/kimi-dsh/dist/commands/swarm.js +2 -2
  41. package/plugins/kimi-dsh/dist/dsh/companion.js +9 -6
  42. package/plugins/kimi-dsh/dist/dsh/index.js +15 -2
  43. package/plugins/kimi-dsh/dist/hooks/config-safety.js +5 -2
  44. package/plugins/kimi-dsh/dist/hooks/managed-block.js +86 -20
  45. package/plugins/kimi-dsh/dist/kimi-command.js +1 -0
  46. package/plugins/kimi-dsh/dist/kimi-engine.js +3 -0
  47. package/plugins/kimi-dsh/dist/native-v2-preflight.js +138 -30
  48. package/plugins/kimi-dsh/dist/swarm-activation.js +276 -14
  49. package/plugins/kimi-dsh/dist/version.js +1 -1
  50. package/plugins/kimi-dsh/package.json +33 -3
@@ -2,7 +2,7 @@
2
2
 
3
3
  Normal stop: use `scripts/companion.sh cancel` for a shell-owned run, or the MCP job cancel tool for an MCP-owned run. A hard kill cannot execute cleanup. Cancellation still starts no fresh cleanup server.
4
4
 
5
- For exact CLI 2.3.2, 2.3.3, 2.4.0 and 2.4.1 swarm runs created by this runtime, status includes a durable `swarm_recovery` record. It contains the resolved home/workspace, companion identity, launch intent, verified process identities and session identity. It never contains the web authentication token, credentials, prompt or config contents. Session identity is committed before the request enabling swarm mode.
5
+ For exact CLI 2.3.2, 2.3.3, 2.4.0, 2.4.1 and 2.5.0/2.5.1 swarm runs created by this runtime, status includes a durable `swarm_recovery` record. It contains the resolved home/workspace, companion identity, launch intent, verified process identities and session identity. It never contains the web authentication token, credentials, prompt or config contents. Session identity is committed before the request enabling swarm mode.
6
6
 
7
7
  ## Preview, then recover
8
8
 
@@ -17,7 +17,7 @@ Preview does not signal processes, modify sessions, or capture patches. Apply:
17
17
 
18
18
  1. Requires the original companion to be gone and acquires an identity-checked recovery claim. A second recovery cannot race the first; a dead recovery owner can be reclaimed by compare-and-swap.
19
19
  2. Revalidates recorded processes and groups. It stops only verified individual identities, never a guessed PID or unproven group. An unknown surviving group member, changed group, incomplete probe or unrecorded launch refuses recovery.
20
- 3. If session cleanup is pending, requires the selected exact certified CLI and valid managed hook. It starts one bounded authenticated web lifecycle, disables the recorded session's swarm mode, verifies status and the exit journal, then verifies process quiescence. It does not resume a prompt, generate model output, install hooks or retry failed cleanup automatically. New recovery web launches are themselves journaled.
20
+ 3. If session cleanup is pending, requires the selected exact certified CLI and valid managed hook. It starts one bounded authenticated web lifecycle, disables the recorded session's swarm mode, verifies the version-appropriate state proof and exit journal, then verifies process quiescence. On 2.5.0/2.5.1 this includes the local environment binding and protocol-v2 WebSocket main-agent state rather than `/status`. It does not resume a prompt, generate model output, install hooks or retry failed cleanup automatically. New recovery web launches are themselves journaled.
21
21
  4. Captures a binary patch from a retained write worktree. It does not apply the patch or delete the tree. The result names both paths for inspection.
22
22
 
23
23
  The recovery attempt has a 30-second cancellation signal, with bounded identity probes and normal teardown. Interrupting recovery retains its journal. An already-recovered record is an idempotent no-op. Failed/cancelled/completed job outcomes are immutable; a previously running abandoned job becomes failed, never completed. Recovery status is separate from task success.
@@ -0,0 +1,48 @@
1
+ # Kimi Code 2.5.0 certification evidence — 2026-11-05
2
+
3
+ ## Status and exact source
4
+
5
+ **Exact CLI 2.5.0 is certified for all eight operations by the plugin 2.5.2 working tree.** The exact-source audit, non-vacuous plan and swarm controls, local-environment controls, candidate matrix, and production-table matrix passed. Repository and package validation results are recorded below as they complete. Commit, push, tag, publication, installed-host rollout, and operator CLI pin changes are outside this certification slice and were not performed.
6
+
7
+ Candidate: `@moonshot-ai/kimi-code@2.5.0`, release commit `41c341434ae24e45bfe0f2232ec11b80531ac717`, compared with certified 2.4.1 (`2c73f3f50af4fa7f2540ca0d2e33b07a1408e3a7`). The monitor recorded npm publication at 2026-11-04T11:59:13.163Z. A temporary isolated npm installation and its executable both report 2.5.0. The clean exact source checkout was verified at that commit; the seven scoped 2.4.1→2.5.0 diffs are retained under `.claude/kimi-code-research/daily-monitor/2026-11-05-2.5.0-diffs/`.
8
+
9
+ ## Source and integration review
10
+
11
+ Four independent source lanes reviewed hooks/plan, CLI/bootstrap, tools/fan-out, and activation adversarially. The hook/plan lane returned COMPAT-PRESERVED. Other lanes identified the removed status route, remote execution defaults/bindings, and the workspace-trust bypass; those findings drove fail-closed repairs rather than certification by assertion. A separate runtime diff review returned APPROVE with no correctness defects.
12
+
13
+ - The expanded exact-tag scan passed **18 tests and 153 assertions across 40 pinned files**, including the SDK, remote-environment implementation, workspace trust/MCP, upgrade authentication, and WebSocket state surfaces. The sole final allow remains active-plan-gated, every model tool execution retains the managed before-execute path, and EnterPlanMode remains removed.
14
+ - Exact 2.5.0 activation uses authenticated protocol-v2 WebSocket main-agent state with unique request IDs, an accepted subscription, and the latest frame through intentional bounded close. Earlier certified versions retain `/status`. Focused activation passed **67 tests with 228 assertions**, including wrong identities, request IDs, optional fields, malformed/oversized frames, contradictory state, timeouts, and local-binding failures.
15
+ - Fresh 2.5.0 print selects `--environment local`; resume never receives that flag. Launcher environment selectors are refused. Remote defaults and disabled workspace trust refuse before spawn. Resume inspection covers persisted metadata and every agent journal, requiring local execution and the expected root, including omitted-metadata-`environmentId` and swarm-write worktrees. Activation and deactivation independently re-read the authenticated local binding before either profile mutation.
16
+
17
+ ## Exact-binary controls
18
+
19
+ Controls used a task-authorized Agent Key in process memory/environment and empty isolated homes, except the forced removed-tool control, which used a loopback scripted provider and a dummy key. No personal config or OAuth store was read, copied, or symlinked. Controls ran sequentially; completed evidence homes had credential-bearing config removed afterward. Raw evidence is under `.claude/kimi-code-research/cert-2.5.0-live/`.
20
+
21
+ - **Plan OFF Write:** the exact 2.5.0 marker was first; Write reached the custom deny-all control hook and was denied on the same call ID. No target file or plan state was created.
22
+ - **Plan ON:** authenticated agent state independently proved `plan_mode:true`, auto permission, and the env-model alias before resume. Glob reached the deny hook; a **1,459-byte** plan file was written without a plan-Write hook invocation. The workspace remained empty.
23
+ - **Removed EnterPlanMode:** the offered tools included Write but not EnterPlanMode. A scripted provider forced `call_enter_forced`; the exact binary returned `Tool "EnterPlanMode" not found` on that ID before any hook call and returned the rejection to the provider. No plan state or files appeared.
24
+ - **Environment:** real activation and deactivation passed with the explicit local binding. A configured remote default refused before its command launcher ran, disabled workspace trust refused before spawn, and a resumed remote binding refused before spawn. The implementation also passes focused omitted-id and wrong-root checks.
25
+ - **Swarm OFF:** a real AgentSwarm call returned the inactive-mode result with zero child journals and no probe files.
26
+ - **Swarm ON:** preactivation produced `agent-0` and `agent-1`; each child attempted its assigned Write with a matching managed-denial result under the same toolCallId. No probes landed, and deactivation verified `swarm_mode.exit` with the idle-reclaimer override.
27
+ - **Out-of-root control:** an evidence-preserving run correlated two distinct child Writes with rescue denials on their assigned call IDs, created no files, and verified deactivation.
28
+
29
+ ## Candidate and production matrices
30
+
31
+ - Expanded candidate matrix (`candidate-smoke-final.log`): **24 pass, 0 fail, 167 assertions, no skips**, 396.89 seconds. Real `runRescue` landed the confined sentinel and persisted operation/version/engine/session/artifact evidence. The absolute out-of-root Bash.cwd call received its exact same-call denial and created no file. Write-swarm produced a 278-byte child-authored patch, left the user tree clean, removed its worktree, and denied the out-of-root write.
32
+ - Expanded production-table matrix (`production-smoke.log`) with the candidate override explicitly unset: **24 pass, 0 fail, 167 assertions, no skips**, 326.30 seconds. The same confinement, provenance, resume, pursuit, fan-out, patch, and negative-write checks passed.
33
+ - Candidate and production goal controls produced same-call managed denials across distinct turns and left the workspace empty at settlement.
34
+ - The earlier candidate attempt passed 23 of 24 lanes but was rejected as incomplete. The fresh final candidate run above supersedes it. Invalid diagnostic fixtures using relative or symlink-alias cwd were also rejected as control evidence, not counted as product failures.
35
+
36
+ ## Repository and package gates
37
+
38
+ Frozen dependency installation made no changes. The literal standard `bun run check` passed **1,404 tests with 33 expected opt-in skips, 0 failures, and 17,999 assertions across 82 files in 336.44 seconds**, including the generated-distribution drift gate. Package validation passed **329 files**, all three compiled distributions, isolated hook setup/check and version refusal, a patched DSH host/profile with seven live providers/tools, and Web boot. An isolated npm consumer-security audit also passed. The certification record is included in the archive. Independent documentation fidelity review identified nine documentation corrections and, after verifying the corrected files, returned **APPROVE** with no remaining must-fix findings. The fresh tag-scan log is retained at `.claude/kimi-code-research/cert-2.5.0-live/tag-scan-final.log`.
39
+
40
+ ## Preserved boundaries
41
+
42
+ Certification remains exact and per-operation. Legacy-v1 ends at 0.41.x. Plan mode and unsafe experimental selectors remain refused. Local execution and workspace trust are mandatory, and resumed nonlocal or wrong-root bindings fail closed. Hook verification, trusted-root confinement, concurrency/defaults, one print runner, memory-only web tokens, finite budgets, process/session identity checks, deactivation, and recovery ownership are preserved. Existing Git trust and hook-process failure residuals remain documented; this release does not make the hook an OS sandbox. Publication does not install or re-pin either host.
43
+
44
+ ## Release clean-close hardening
45
+
46
+ After the original certification, Linux release CI exposed a platform-specific early WebSocket close event that could be accepted as valid final agent state. The final runtime now requires `CloseEvent.wasClean` before accepting that state, and the mock performs a protocol-correct close handshake while retaining the timeout case. Focused activation validation passed **71 tests with 244 assertions**. On the exact 2.5.0 binary, direct OFF again produced no children, preactivated ON produced two children with correlated managed denials and verified deactivation, and the out-of-root control passed. The final full repository check passed **1,412 tests with 18,282 assertions and zero failures in 378.11 seconds**; 330-file package validation and isolated consumer security also passed. These results supplement the original certification evidence and cover the final release behavior.
47
+
48
+ A subsequent CI attempt showed that native WebSocket close scheduling and `wasClean` semantics vary by platform and cannot supply a deterministic no-close-event precondition. That timeout case now uses a controlled socket which completes the protocol exchange but never emits `close`, and asserts the exact `Kimi web agent-state verification timed out.` message. The refusal is therefore tested without relying on incidental host close timing; production behavior is unchanged by this test-only follow-up.
@@ -0,0 +1,43 @@
1
+ # Kimi Code 2.5.1 certification evidence — 2026-11-06
2
+
3
+ ## Status and exact source
4
+
5
+ **Exact CLI 2.5.1 is certified for all eight operations by the plugin 2.5.2 working tree.** The exact-source audit, non-vacuous plan/environment/swarm controls, candidate matrix, production-table matrix, frozen-install, literal full repository check, package validation, and isolated consumer-security audit passed. Independent read-only `claude -p` review returned **APPROVE with no must-fix findings**. Commit, push, tag, publication, installed-host rollout, and operator CLI pin changes are outside this certification slice and were not performed.
6
+
7
+ Candidate: `@moonshot-ai/kimi-code@2.5.1`, release commit `1b02030d8c2c6b00131b23ebb67a231614877827`, compared with certified 2.5.0 (`41c341434ae24e45bfe0f2232ec11b80531ac717`). The monitor recorded npm publication at 2026-11-05T09:58:53.967Z. A temporary isolated npm installation and its executable both report 2.5.1; its `dist/main.mjs` SHA-256 is `84156e7a0f500bb58f9734be57b26756c27eacefb9d884ef94db3af0d6b609dc`. The clean exact source checkout was verified at the release commit. The seven scoped 2.5.0→2.5.1 diffs are retained under `.claude/kimi-code-research/daily-monitor/2026-11-06-2.5.1-diffs/`.
8
+
9
+ ## Source and integration review
10
+
11
+ - Scopes 01–06 are unchanged: print/CLI, before-execute and permission gates, external hooks, plan/restore, bootstrap/config/workspace, tools/fan-out/SDK. Scope 07 changes session-list visibility and web media projection only. The provider protocol omits empty system content on tool-bearing messages; the additional transcript change folds daemon media into web history. Neither changes headless stream records, execution, plan arming, hook ordering, environment binding, or WebSocket agent state.
12
+ - DI/scope creation, OS backends, session reload, wire restore/migrations, and the other human-review scopes are unchanged. Of the 40 pinned files, 39 are byte-identical to 2.5.0. Only `kap-server/src/routes/sessions.ts` changed, for deleted-workspace filtering in session lists; session creation and activation are unchanged.
13
+ - The expanded exact-tag scan passed **18 tests and 153 assertions**, with the print, environment/workspace-trust, and WebSocket checks actually executing on 2.5.1. The preserved 2.5.0 source scan also passed again with the same counts.
14
+ - Reviewed candidate branches cover fresh `--environment local`, exact hook-schema acceptance, preactivation, explicit local binding, and authenticated protocol-v2 WebSocket state. After the production row was added, focused activation/engine/CLI/setup tests passed **296 tests with 3,617 assertions**; preflight and launcher tests separately passed **79 tests with 186 assertions**. Typecheck, build, and generated-surface regeneration passed.
15
+
16
+ ## Exact-binary controls
17
+
18
+ Controls used a task-authorized Agent Key resolved only into each process and empty absolute-path isolated homes, except the forced removed-tool control, which used a loopback scripted provider and a dummy key. No personal config or OAuth store was read, copied, or symlinked. Controls ran sequentially. Credential-bearing config was removed after the evidence runs. Raw evidence is under `.claude/kimi-code-research/cert-2.5.1-live/`.
19
+
20
+ - **Plan OFF Write:** the exact 2.5.1 marker was first; Write reached the custom deny-all hook and was denied on the same call ID. No target file or plan state was created.
21
+ - **Plan ON:** authenticated agent state independently proved `plan_mode:true`, auto permission, and the env-model alias before resume. Glob reached the deny hook; a **2,232-byte** plan file was written without a plan-Write hook invocation. The workspace remained empty.
22
+ - **Removed EnterPlanMode:** Write was offered but EnterPlanMode was absent. A scripted provider forced `call_enter_forced`; the exact binary returned `Tool "EnterPlanMode" not found` on that ID before any hook call and returned the rejection to the provider. No plan state or files appeared.
23
+ - **Environment:** real activation and deactivation passed with the explicit local binding. A configured remote default refused before its command launcher ran, disabled workspace trust refused before spawn, and a resumed remote binding refused before spawn.
24
+ - **Swarm OFF:** a real AgentSwarm call returned the inactive-mode result with zero child journals and no probe files.
25
+ - **Swarm ON:** preactivation produced `agent-0` and `agent-1`; each child attempted its assigned Write with a matching managed-denial result under the same toolCallId. No probes landed, and deactivation verified `swarm_mode.exit` with ambient idle reclamation set to 1 ms.
26
+ - **Out-of-root control:** two distinct child Writes were correlated with rescue denials on their assigned call IDs, created no files, and verified deactivation.
27
+ - An initial diagnostic Write attempt used a relative artifact root, causing the spawned CLI to resolve a different home without the control hook. Its Write landed only inside that disposable fixture. The run was rejected as control evidence and superseded by the valid absolute-path plan-OFF run above; it is not counted as a product failure or a hook bypass.
28
+
29
+ ## Candidate matrix
30
+
31
+ The candidate matrix (`candidate-smoke.log`) passed **24 tests with 167 assertions and no skips in 358.88 seconds**. Real `runRescue` landed the confined sentinel and persisted operation/version/engine/session/artifact evidence. The absolute out-of-root Bash.cwd call received its exact same-call denial and created no file. Resume and plan-taint refusal passed. Thirteen goal turns each had a matching managed Write denial. Both pursue terminal outcomes passed. Write-swarm produced a **278-byte** child-authored patch, left the user tree clean, removed its worktree, and denied the out-of-root write.
32
+
33
+ ## Production matrix and final gates
34
+
35
+ The production-table matrix (`production-smoke.log`) with the candidate override explicitly unset passed **24 tests with 167 assertions and no skips in 364.25 seconds**. It independently repeated the confined rescue and Bash.cwd evidence, resume/taint refusal, both pursue outcomes, both swarm modes, and out-of-root denial. Eleven goal turns each had a matching managed Write denial, and write-swarm again captured a **278-byte** child-authored patch with a clean user tree and removed worktree.
36
+
37
+ Frozen dependency installation checked 43 installs across 88 packages and made no changes. The literal standard `bun run check` passed **1,412 tests with 33 expected opt-in skips, 0 failures, and 18,282 assertions across 82 files in 362.99 seconds**, including the generated-distribution drift gate. Package validation passed **330 files**, all three compiled distributions, isolated hook setup/check and version refusal, a patched DSH host/profile with seven live providers/tools, and Web boot. The isolated npm consumer-security audit also passed. An independent read-only `claude -p` review returned **APPROVE with no must-fix findings** after inspecting the runtime, tests, documentation, diffs, and raw control artifacts. Its remaining notes were non-load-bearing evidence-retention limitations. Supporting logs are retained under `.claude/kimi-code-research/cert-2.5.1-live/`, including `check-standard.log`, `package-standard.log`, `consumer-security.log`, `claude-review.log`, `tag-scan-2.5.0-regression.log`, and `binary-sha256.txt`.
38
+
39
+ ## Release clean-close hardening
40
+
41
+ After the original certification and independent review, Linux release CI exposed a platform-specific early WebSocket close event that could be accepted as valid final agent state. The final runtime now requires `CloseEvent.wasClean` before accepting that state, and the mock performs a protocol-correct close handshake while retaining the timeout case. Focused activation validation passed **71 tests with 244 assertions**. The full 2.5.1 production matrix was rerun against the final implementation and again passed **24 tests with 167 assertions and no skips**, this time in **336.76 seconds**, including nine denied goal turns and a 278-byte child-authored patch. Exact 2.5.0 swarm OFF/ON and out-of-root controls also passed. The final full repository check passed **1,412 tests with 18,282 assertions and zero failures in 378.11 seconds**; 330-file package validation and isolated consumer security also passed. These results supplement the original certification and review evidence and cover the final release behavior.
42
+
43
+ A subsequent CI attempt showed that native WebSocket close scheduling and `wasClean` semantics vary by platform and cannot supply a deterministic no-close-event precondition. That timeout case now uses a controlled socket which completes the protocol exchange but never emits `close`, and asserts the exact `Kimi web agent-state verification timed out.` message. The refusal is therefore tested without relying on incidental host close timing; production behavior is unchanged by this test-only follow-up.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kimi-plugin-cc",
3
- "version": "2.5.1",
3
+ "version": "2.5.3",
4
4
  "description": "Kimi for Claude Code, Codex, and DeepSeek Harness: 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
  "main": "dist/dsh/index.js",
@@ -22,6 +22,7 @@
22
22
  "test": "bun test",
23
23
  "smoke:real": "KIMI_PLUGIN_CC_SMOKE=1 bun test tests/runtime/real-binary-smoke.test.ts --path-ignore-patterns '**/worktrees/**'",
24
24
  "check:package": "node scripts/check-package.mjs && node scripts/check-dsh-package.mjs",
25
+ "check:consumer-security": "node scripts/check-consumer-security.mjs",
25
26
  "check:contracts": "node scripts/check-contract-size.mjs"
26
27
  },
27
28
  "devDependencies": {
@@ -31,16 +32,17 @@
31
32
  "@deepseek-ai/dsh-session": "0.2.1-rc.5",
32
33
  "@deepseek-ai/dsh-subagent": "0.2.1-rc.5",
33
34
  "@deepseek-ai/dsh-subprocess": "0.2.1-rc.5",
35
+ "@deepseek-ai/dsh-system-prompt": "0.2.1-rc.5",
36
+ "@deepseek-ai/dsh-tool-subagent": "0.2.1-rc.5",
34
37
  "@deepseek-ai/dsh-working-directory": "0.2.1-rc.5",
35
- "@types/bun": "^1.4.2",
36
- "@types/node": "^26.5.1",
38
+ "@types/bun": "^1.4.4",
39
+ "@types/node": "^26.6.6",
37
40
  "smol-toml": "1.9.1",
38
41
  "tsx": "^4.23.13",
39
42
  "typescript": "^7.0.2"
40
43
  },
41
44
  "dependencies": {
42
45
  "@deepseek-ai/dsh-brand": "0.2.1-rc.5",
43
- "@deepseek-ai/dsh-tool-subagent": "0.2.1-rc.5",
44
46
  "@deepseek-ai/schemastery": "3.18.5"
45
47
  },
46
48
  "peerDependencies": {
@@ -50,7 +52,38 @@
50
52
  "@deepseek-ai/dsh-session": ">=0.2.1-rc.5 <0.3.0",
51
53
  "@deepseek-ai/dsh-subagent": ">=0.2.1-rc.5 <0.3.0",
52
54
  "@deepseek-ai/dsh-subprocess": ">=0.2.1-rc.5 <0.3.0",
53
- "@deepseek-ai/dsh-working-directory": ">=0.2.1-rc.5 <0.3.0"
55
+ "@deepseek-ai/dsh-system-prompt": ">=0.2.1-rc.5 <0.3.0",
56
+ "@deepseek-ai/dsh-working-directory": ">=0.2.1-rc.5 <0.3.0",
57
+ "@deepseek-ai/dsh-tool-subagent": ">=0.2.1-rc.5 <0.3.0"
58
+ },
59
+ "peerDependenciesMeta": {
60
+ "@deepseek-ai/cordis": {
61
+ "optional": true
62
+ },
63
+ "@deepseek-ai/dsh-commands": {
64
+ "optional": true
65
+ },
66
+ "@deepseek-ai/dsh-llm": {
67
+ "optional": true
68
+ },
69
+ "@deepseek-ai/dsh-session": {
70
+ "optional": true
71
+ },
72
+ "@deepseek-ai/dsh-subagent": {
73
+ "optional": true
74
+ },
75
+ "@deepseek-ai/dsh-subprocess": {
76
+ "optional": true
77
+ },
78
+ "@deepseek-ai/dsh-system-prompt": {
79
+ "optional": true
80
+ },
81
+ "@deepseek-ai/dsh-working-directory": {
82
+ "optional": true
83
+ },
84
+ "@deepseek-ai/dsh-tool-subagent": {
85
+ "optional": true
86
+ }
54
87
  },
55
88
  "overrides": {
56
89
  "zod": "^4.6.6",
@@ -101,6 +134,8 @@
101
134
  "docs/models.md",
102
135
  "docs/session-visibility.md",
103
136
  "docs/upstream-2.4.1-certification.md",
137
+ "docs/upstream-2.5.0-certification.md",
138
+ "docs/upstream-2.5.1-certification.md",
104
139
  "README.md",
105
140
  "LICENSE",
106
141
  "CHANGELOG.md",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kimi",
3
- "version": "2.5.1",
3
+ "version": "2.5.3",
4
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"
@@ -65,6 +65,7 @@ export async function runCliPrompt(opts) {
65
65
  kimiHome: resolveKimiHome(opts.env, opts.cwd),
66
66
  env: opts.env,
67
67
  resumeSessionId: opts.resumeSessionId,
68
+ expectedExecutionRoot: opts.trustedWorkspaceRoot ?? opts.cwd,
68
69
  stage: "cli-client.spawn",
69
70
  });
70
71
  }
@@ -781,6 +782,13 @@ const KIMI_LEGACY_FORCED_VALUE = "1";
781
782
  function buildArgs(opts) {
782
783
  const args = [...(opts.prefixArgs ?? [])];
783
784
  args.push("--output-format", "stream-json");
785
+ const smokeCandidate = opts.executionPlan.certification === "test-bypass" && opts.env.KIMI_PLUGIN_CC_SMOKE === "1"
786
+ ? opts.env.KIMI_PLUGIN_CC_SMOKE_V2_CANDIDATE
787
+ : undefined;
788
+ if (opts.resumeSessionId === undefined &&
789
+ ["2.5.0", "2.5.1"].includes(opts.executionPlan.kimiVersion ?? smokeCandidate ?? "")) {
790
+ args.push("--environment", "local");
791
+ }
784
792
  if (opts.resumeSessionId !== undefined) {
785
793
  args.push("-r", opts.resumeSessionId);
786
794
  }
@@ -9,7 +9,8 @@ import { SETUP_SUPPORTED_FLAGS } from "../parsing.js";
9
9
  // absolute Node path avoids PATH dependence under sanitized launches. Setup
10
10
  // probes the hook directly with resolveNodeBinary and through `/bin/sh -c`
11
11
  // using the exact installed command bytes. It also warns about broad permission
12
- // denies and refuses duplicate, orphaned, or malformed managed blocks.
12
+ // denies, repairs orphan marker comments, and refuses remaining
13
+ // duplicate, orphaned, or malformed managed blocks.
13
14
  //
14
15
  // Subcommands: install (default), --check, --uninstall [--all], and
15
16
  // --enable-review-gate/--disable-review-gate.
@@ -25,12 +26,13 @@ import { readPluginConfig, writePluginConfig } from "../config.js";
25
26
  import { RuntimeError } from "../errors.js";
26
27
  import { normalizeTopLevelInlineHooks, validateKimiHookSetForEnvironment, withKimiConfigLock, } from "../hooks/config-safety.js";
27
28
  import { buildHookShellCommand, hostIdFromHookCommand, resolveHookScriptPath, resolveHostId, resolveNodeBinary, } from "../hooks/install-paths.js";
28
- import { decodeManagedCommandLine, effectiveHost, evaluateInstalled, findUnmanagedApprovalHookBlocks, MARKERS, parseManagedBlock, } from "../hooks/managed-block.js";
29
+ import { decodeManagedCommandLine, effectiveHost, evaluateInstalled, findUnmanagedApprovalHookBlocks, markerCommentLines, MARKERS, parseManagedBlock, } from "../hooks/managed-block.js";
29
30
  import { formatVersionOutOfRangeWarning, probeKimiVersion, } from "../kimi-version-probe.js";
30
31
  import { isNativeV2CertifiedVersion } from "../kimi-engine.js";
31
32
  import { resolveKimiHome } from "../kimi-home.js";
32
33
  import { ensurePluginPaths, resolvePluginPaths } from "../paths.js";
33
34
  import { KIMI_PLUGIN_CC_VERSION } from "../version.js";
35
+ import { parse as parseToml } from "../vendor/smol-toml/parse.js";
34
36
  const BEGIN_MARKER_PREFIX = "# === BEGIN kimi-plugin-cc-managed";
35
37
  const DEFAULT_HOOK_TIMEOUT_S = 15;
36
38
  // Probe timing. The direct script probe is local and fast; allow 5s for
@@ -119,7 +121,17 @@ async function runInstall(configPath, hookScriptPath, hostId, reviewGateEnabled,
119
121
  const mutation = await withKimiConfigLock(configPath, async () => {
120
122
  const original = await readConfigSafe(configPath);
121
123
  const lineEnding = detectLineEnding(original);
122
- const inlineHooks = normalizeTopLevelInlineHooks(original, lineEnding);
124
+ try {
125
+ parseToml(original);
126
+ }
127
+ catch {
128
+ await assertKimiHookSetValid(original, configPath, "setup.install", context.env);
129
+ }
130
+ const repaired = removeOrphanMarkers(original);
131
+ if (repaired.count > 0) {
132
+ warnings.push(`Removed ${repaired.count} orphaned managed marker comment(s); surrounding config was preserved.`);
133
+ }
134
+ const inlineHooks = normalizeTopLevelInlineHooks(repaired.contents, lineEnding);
123
135
  if (inlineHooks.found) {
124
136
  // Validate before normalizing so an invalid inline entry is reported
125
137
  // against the user's original bytes and the config remains untouched.
@@ -133,7 +145,7 @@ async function runInstall(configPath, hookScriptPath, hostId, reviewGateEnabled,
133
145
  // splice line numbers. Another host's marker-less table may be its live hook
134
146
  // after a config rewrite strips comments, so it must remain untouched.
135
147
  const expectedCommand = buildHookShellCommand(hookScriptPath, context.env);
136
- const { pruned, count: prunedCount, commands: prunedCommands } = pruneOrphanApprovalHooks(inlineHooks.contents, lineEnding, hostId, expectedCommand);
148
+ const { pruned, count: prunedCount, commands: prunedCommands } = pruneOrphanApprovalHooks(inlineHooks.contents, hostId, expectedCommand);
137
149
  const existing = pruned;
138
150
  if (prunedCount > 0) {
139
151
  const readorned = prunedCommands.includes(expectedCommand);
@@ -145,7 +157,7 @@ async function runInstall(configPath, hookScriptPath, hostId, reviewGateEnabled,
145
157
  if (state.kind === "orphan") {
146
158
  throw new RuntimeError("SETUP_ORPHAN_MARKERS", [
147
159
  `kimi-code config at ${configPath} contains an orphaned ${state.detail} marker.`,
148
- "Run `/kimi:setup --uninstall` to clean up, then re-run `/kimi:setup`.",
160
+ "Inspect the managed marker comments and re-run setup.",
149
161
  ].join(" "), "setup.install", { details: { configPath, orphan: state.detail } });
150
162
  }
151
163
  if (state.kind === "duplicate") {
@@ -393,12 +405,11 @@ async function runUninstallLocked(configPath, hookScriptPath, hostId, removeAllH
393
405
  // other hosts' blocks and live marker-less tables remain intact. `--all` is
394
406
  // the explicit every-host sweep for all of this plugin's hook tables.
395
407
  const { stripped: strippedMarkers, removedBlocks, orphansLeft } = stripManagedBlocks(existing, hostId, removeAllHosts);
396
- const lineEnding = detectLineEnding(existing);
397
408
  // Exact current command bytes also identify this host's table when a host-id
398
409
  // override disagrees with path derivation or a dev path is outside the strict
399
410
  // ownership pattern.
400
411
  const expectedCommand = buildHookShellCommand(hookScriptPath, context.env);
401
- const { pruned: stripped, count: prunedCount } = pruneOrphanApprovalHooks(strippedMarkers, lineEnding, removeAllHosts ? undefined : hostId, expectedCommand);
412
+ const { pruned: stripped, count: prunedCount } = pruneOrphanApprovalHooks(strippedMarkers, removeAllHosts ? undefined : hostId, expectedCommand);
402
413
  const changed = stripped !== existing;
403
414
  if (changed) {
404
415
  await writeConfigAtomic(configPath, stripped);
@@ -517,10 +528,10 @@ function detectLineEnding(contents) {
517
528
  return contents.includes("\r\n") ? "\r\n" : "\n";
518
529
  }
519
530
  function spliceBlock(contents, beginLine, endLine, replacement, lineEnding) {
520
- const lines = splitPreservingEnding(contents);
521
- const before = lines.slice(0, beginLine).join(lineEnding);
522
- const after = lines.slice(endLine + 1).join(lineEnding);
523
- const beforeWithSep = before.endsWith(lineEnding) || before.length === 0 ? before : `${before}${lineEnding}`;
531
+ const lines = contents.split(/(?<=\n)/);
532
+ const before = lines.slice(0, beginLine).join("");
533
+ const after = lines.slice(endLine + 1).join("");
534
+ const beforeWithSep = before.endsWith("\n") || before.length === 0 ? before : `${before}${lineEnding}`;
524
535
  const replacementWithEnding = replacement.endsWith(lineEnding) ? replacement : `${replacement}${lineEnding}`;
525
536
  return `${beforeWithSep}${replacementWithEnding}${after}`;
526
537
  }
@@ -536,14 +547,54 @@ function splitPreservingEnding(contents) {
536
547
  // pass the desired line ending).
537
548
  return contents.split("\n").map((line) => line.replace(/\r$/, ""));
538
549
  }
550
+ // A partial upstream hooks-array rewrite can leave one marker comment behind.
551
+ // Remove only unpaired marker comments, never the intervening content. The
552
+ // normal full-file validation still gates writes.
553
+ function removeOrphanMarkers(contents) {
554
+ const lines = splitPreservingEnding(contents);
555
+ const orphans = [];
556
+ const comments = markerCommentLines(contents);
557
+ let open = null;
558
+ for (let i = 0; i < lines.length; i += 1) {
559
+ if (!comments.has(i))
560
+ continue;
561
+ const line = lines[i].trim();
562
+ const begin = MARKERS.BEGIN_LINE_RE.exec(line);
563
+ if (begin !== null) {
564
+ if (open !== null)
565
+ orphans.push(open);
566
+ open = { index: i, host: begin[1]?.toLowerCase() ?? null };
567
+ }
568
+ else {
569
+ const end = MARKERS.END_LINE_RE.exec(line);
570
+ if (end === null)
571
+ continue;
572
+ const endMarker = { index: i, host: end[1]?.toLowerCase() ?? null };
573
+ if (open === null)
574
+ orphans.push(endMarker);
575
+ else {
576
+ if (open.host !== endMarker.host)
577
+ orphans.push(open, endMarker);
578
+ open = null;
579
+ }
580
+ }
581
+ }
582
+ if (open !== null)
583
+ orphans.push(open);
584
+ const remove = new Set(orphans.map((marker) => marker.index));
585
+ return remove.size === 0
586
+ ? { contents, count: 0 }
587
+ : { contents: contents.split(/(?<=\n)/).filter((_, index) => !remove.has(index)).join(""), count: remove.size };
588
+ }
539
589
  /**
540
590
  * Remove managed blocks owned by `currentHost`, or every host with `removeAll`.
541
591
  * Ownership comes from the marker suffix or a legacy command's derived host.
542
592
  * Orphan markers are removed individually so surrounding user content survives.
543
593
  */
544
594
  function stripManagedBlocks(contents, currentHost, removeAll) {
545
- const lineEnding = detectLineEnding(contents);
546
595
  const lines = splitPreservingEnding(contents);
596
+ const rawLines = contents.split(/(?<=\n)/);
597
+ const markerLines = markerCommentLines(contents);
547
598
  const result = [];
548
599
  const orphansLeft = [];
549
600
  let removedBlocks = 0;
@@ -567,7 +618,7 @@ function stripManagedBlocks(contents, currentHost, removeAll) {
567
618
  while (i < lines.length) {
568
619
  const line = lines[i];
569
620
  const trimmed = line.trim();
570
- const begin = MARKERS.BEGIN_LINE_RE.exec(trimmed);
621
+ const begin = markerLines.has(i) ? MARKERS.BEGIN_LINE_RE.exec(trimmed) : null;
571
622
  if (begin !== null) {
572
623
  const beginHost = begin[1]?.toLowerCase() ?? null;
573
624
  // Look ahead for the matching END, capturing the block's command en route.
@@ -575,12 +626,14 @@ function stripManagedBlocks(contents, currentHost, removeAll) {
575
626
  let blockCommand = null;
576
627
  for (let j = i + 1; j < lines.length; j += 1) {
577
628
  const nextTrimmed = lines[j].trim();
578
- if (MARKERS.BEGIN_LINE_RE.test(nextTrimmed)) {
629
+ if (markerLines.has(j) && MARKERS.BEGIN_LINE_RE.test(nextTrimmed)) {
579
630
  // Two BEGINs in a row — the outer one is an orphan.
580
631
  break;
581
632
  }
582
- if (MARKERS.END_LINE_RE.test(nextTrimmed)) {
583
- endIdx = j;
633
+ const end = markerLines.has(j) ? MARKERS.END_LINE_RE.exec(nextTrimmed) : null;
634
+ if (end !== null) {
635
+ if ((end[1]?.toLowerCase() ?? null) === beginHost)
636
+ endIdx = j;
584
637
  break;
585
638
  }
586
639
  const decoded = decodeManagedCommandLine(nextTrimmed);
@@ -594,7 +647,7 @@ function stripManagedBlocks(contents, currentHost, removeAll) {
594
647
  i += 1;
595
648
  continue;
596
649
  }
597
- result.push(line);
650
+ result.push(rawLines[i]);
598
651
  i += 1;
599
652
  continue;
600
653
  }
@@ -606,11 +659,11 @@ function stripManagedBlocks(contents, currentHost, removeAll) {
606
659
  }
607
660
  // Another host's block — keep it verbatim.
608
661
  for (let k = i; k <= endIdx; k += 1)
609
- result.push(lines[k]);
662
+ result.push(rawLines[k]);
610
663
  i = endIdx + 1;
611
664
  continue;
612
665
  }
613
- const end = MARKERS.END_LINE_RE.exec(trimmed);
666
+ const end = markerLines.has(i) ? MARKERS.END_LINE_RE.exec(trimmed) : null;
614
667
  if (end !== null) {
615
668
  const endHost = end[1]?.toLowerCase() ?? null;
616
669
  // Orphan END with no preceding BEGIN: drop only this line (if ours).
@@ -619,27 +672,14 @@ function stripManagedBlocks(contents, currentHost, removeAll) {
619
672
  i += 1;
620
673
  continue;
621
674
  }
622
- result.push(line);
675
+ result.push(rawLines[i]);
623
676
  i += 1;
624
677
  continue;
625
678
  }
626
- result.push(line);
679
+ result.push(rawLines[i]);
627
680
  i += 1;
628
681
  }
629
- const collapsed = [];
630
- let blankRun = 0;
631
- for (const line of result) {
632
- if (line.length === 0) {
633
- blankRun += 1;
634
- if (blankRun <= 2)
635
- collapsed.push(line);
636
- }
637
- else {
638
- blankRun = 0;
639
- collapsed.push(line);
640
- }
641
- }
642
- return { stripped: collapsed.join(lineEnding), removedBlocks, orphansLeft };
682
+ return { stripped: result.join(""), removedBlocks, orphansLeft };
643
683
  }
644
684
  /**
645
685
  * Remove orphaned, marker-less `[[hooks]]` tables that are unambiguously this
@@ -654,7 +694,7 @@ function stripManagedBlocks(contents, currentHost, removeAll) {
654
694
  * host — REQUIRED for scoped install/uninstall so one host never disarms the
655
695
  * other's live (marker-stripped) hook. Omit only for `--uninstall --all`.
656
696
  */
657
- function pruneOrphanApprovalHooks(contents, lineEnding, ownedBy, alsoMatchCommand) {
697
+ function pruneOrphanApprovalHooks(contents, ownedBy, alsoMatchCommand) {
658
698
  const ranges = findUnmanagedApprovalHookBlocks(contents, ownedBy, alsoMatchCommand);
659
699
  if (ranges.length === 0)
660
700
  return { pruned: contents, count: 0, commands: [] };
@@ -663,23 +703,10 @@ function pruneOrphanApprovalHooks(contents, lineEnding, ownedBy, alsoMatchComman
663
703
  for (let i = start; i < end; i += 1)
664
704
  remove.add(i);
665
705
  }
666
- const lines = splitPreservingEnding(contents);
706
+ const lines = contents.split(/(?<=\n)/);
667
707
  const kept = lines.filter((_, idx) => !remove.has(idx));
668
- const collapsed = [];
669
- let blankRun = 0;
670
- for (const line of kept) {
671
- if (line.length === 0) {
672
- blankRun += 1;
673
- if (blankRun <= 2)
674
- collapsed.push(line);
675
- }
676
- else {
677
- blankRun = 0;
678
- collapsed.push(line);
679
- }
680
- }
681
708
  return {
682
- pruned: collapsed.join(lineEnding),
709
+ pruned: kept.join(""),
683
710
  count: ranges.length,
684
711
  commands: ranges.map((range) => range.command),
685
712
  };
@@ -28,8 +28,8 @@ import { buildKimiSessionTitle, syncKimiSessionTitle } from "../session-title.js
28
28
  // only the wall-clock hard bound. Read/write modes reuse review/rescue lineage,
29
29
  // and the mandatory PreToolUse hook enforces read-only policy or trusted-worktree
30
30
  // write confinement on the coordinator and every subagent. Write mode requires
31
- // kimi-code 0.18.0+. Exact 2.3.2, 2.3.3, 2.4.0 and 2.4.1 additionally activate a same-job
32
- // session through a bounded loopback web lifecycle, then resume it through the normal print path.
31
+ // kimi-code 0.18.0+. Exact 2.3.2, 2.3.3, 2.4.0, 2.4.1, 2.5.0 and 2.5.1 additionally
32
+ // activate a same-job session through a bounded loopback web lifecycle, then resume it through the normal print path.
33
33
  const SWARM_SUMMARY_MAX = 120;
34
34
  const SWARM_AGENT_PROFILE = "<swarm>";
35
35
  const SWARM_WRITE_AGENT_PROFILE = "<swarm-write>";
@@ -1,6 +1,7 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { brandString } from "@deepseek-ai/dsh-brand";
3
3
  import { assertUsableCwd, settleRunResult, subprocessRunHandle, } from "@deepseek-ai/dsh-subagent";
4
+ import { preferStableNodePath } from "../hooks/install-paths.js";
4
5
  export const DSH_COMPANION_OPERATIONS = [
5
6
  "ask",
6
7
  "review",
@@ -20,7 +21,7 @@ export function companionTextTask(prompt) {
20
21
  }
21
22
  return text;
22
23
  }
23
- export function companionArgv(operation, prompt, companionPath, nodeBin = process.execPath, options = {}) {
24
+ export function companionArgv(operation, prompt, companionPath, nodeBin = preferStableNodePath(process.execPath), options = {}) {
24
25
  const command = operation === "ask" || operation === "review"
25
26
  ? [operation]
26
27
  : operation === "swarm-write"
@@ -41,7 +42,7 @@ export function companionArgv(operation, prompt, companionPath, nodeBin = proces
41
42
  prompt,
42
43
  ];
43
44
  }
44
- export function companionEnvironment(dataRoot, cwd, env, nodeBin = process.execPath) {
45
+ export function companionEnvironment(dataRoot, cwd, env, nodeBin = preferStableNodePath(process.execPath)) {
45
46
  return {
46
47
  ...env,
47
48
  KIMI_PLUGIN_CC_DATA: dataRoot,
@@ -79,8 +80,9 @@ export async function startCompanionRun(request, operation, spec) {
79
80
  request.signal.addEventListener("abort", onAbort, { once: true });
80
81
  let child;
81
82
  try {
83
+ const nodeBin = preferStableNodePath(process.execPath);
82
84
  child = spec.spawn({
83
- argv: companionArgv(operation, prompt, spec.companionPath, process.execPath, spec),
85
+ argv: companionArgv(operation, prompt, spec.companionPath, nodeBin, spec),
84
86
  cwd,
85
87
  stdio: {
86
88
  stdin: "ignore",
@@ -89,7 +91,7 @@ export async function startCompanionRun(request, operation, spec) {
89
91
  },
90
92
  graceMs: spec.disposeGraceMs,
91
93
  signal: termination.signal,
92
- env: companionEnvironment(spec.dataRoot, cwd, spec.env),
94
+ env: companionEnvironment(spec.dataRoot, cwd, spec.env, nodeBin),
93
95
  });
94
96
  }
95
97
  catch (error) {
@@ -141,8 +143,9 @@ export async function startCompanionRun(request, operation, spec) {
141
143
  });
142
144
  }
143
145
  export async function runCompanionCommand(argv, cwd, spec, signal) {
146
+ const nodeBin = preferStableNodePath(process.execPath);
144
147
  const child = spec.spawn({
145
- argv: [process.execPath, spec.companionPath, ...argv],
148
+ argv: [nodeBin, spec.companionPath, ...argv],
146
149
  cwd,
147
150
  stdio: {
148
151
  stdin: "ignore",
@@ -151,7 +154,7 @@ export async function runCompanionCommand(argv, cwd, spec, signal) {
151
154
  },
152
155
  graceMs: spec.disposeGraceMs,
153
156
  signal,
154
- env: companionEnvironment(spec.dataRoot, cwd, spec.env),
157
+ env: companionEnvironment(spec.dataRoot, cwd, spec.env, nodeBin),
155
158
  });
156
159
  try {
157
160
  const outcome = await child.done;
@@ -8,6 +8,12 @@ import { DSH_COMPANION_OPERATIONS, runCompanionCommand, startCompanionRun, } fro
8
8
  import { parseDurationMs } from "../parsing.js";
9
9
  export const name = "kimi-plugin-cc";
10
10
  export const inject = ["subagents", "subprocess"];
11
+ export const DSH_TOOL_GUIDANCE = [
12
+ "Kimi delegation tools have different write boundaries:",
13
+ "kimi_ask, kimi_review, and kimi_challenge are read-only; kimi_swarm is a read-only parallel review.",
14
+ "kimi_rescue and kimi_pursue can edit the selected workspace; kimi_pursue continues toward a bounded goal.",
15
+ "kimi_swarm_write edits a temporary worktree and returns a patch without applying it.",
16
+ ].join(" ");
11
17
  const DEFAULT_DISPOSE_GRACE_MS = 6_000;
12
18
  const DEFAULT_MAX_OUTPUT_BYTES = 16 * 1024 * 1024;
13
19
  const MAX_TIMER_DELAY_MS = 2_147_483_647;
@@ -50,8 +56,8 @@ function resolveConfig(config) {
50
56
  if (!path.isAbsolute(resolved.dataRoot)) {
51
57
  throw new Error("kimi-plugin-cc: dataRoot must be absolute");
52
58
  }
53
- if (!Number.isFinite(resolved.disposeGraceMs) || resolved.disposeGraceMs <= 0 || resolved.disposeGraceMs > MAX_TIMER_DELAY_MS) {
54
- throw new Error(`kimi-plugin-cc: disposeGraceMs must be positive, finite, and no greater than ${MAX_TIMER_DELAY_MS}`);
59
+ if (!Number.isFinite(resolved.disposeGraceMs) || resolved.disposeGraceMs < DEFAULT_DISPOSE_GRACE_MS || resolved.disposeGraceMs > MAX_TIMER_DELAY_MS) {
60
+ throw new Error(`kimi-plugin-cc: disposeGraceMs must be at least ${DEFAULT_DISPOSE_GRACE_MS} and no greater than ${MAX_TIMER_DELAY_MS}`);
55
61
  }
56
62
  if (!Number.isSafeInteger(resolved.maxOutputBytes) || resolved.maxOutputBytes <= 0) {
57
63
  throw new Error("kimi-plugin-cc: maxOutputBytes must be a positive safe integer");
@@ -105,6 +111,13 @@ export function apply(ctx, config) {
105
111
  for (const operation of DSH_COMPANION_OPERATIONS) {
106
112
  ctx.subagents.registerProvider(new KimiCompanionProvider(`kimi-${operation}`, operation, spec));
107
113
  }
114
+ ctx.inject(["systemPrompt"], (promptCtx) => {
115
+ promptCtx.systemPrompt.section({
116
+ name: "kimi-plugin-cc:tool-boundaries",
117
+ order: promptCtx.systemPrompt.getSectionOrder("TOOL_SUBAGENT"),
118
+ text: () => DSH_TOOL_GUIDANCE,
119
+ });
120
+ });
108
121
  ctx.inject(["commands"], (commandCtx) => {
109
122
  const execute = async (invocation, argv) => {
110
123
  const directories = commandCtx.get("workingDirectory");