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
@@ -5,7 +5,7 @@
5
5
  },
6
6
  "metadata": {
7
7
  "description": "Kimi for Claude Code: code questions, reviews, fixes, parallel tasks, and MCP tasks through the local Kimi Code CLI.",
8
- "version": "2.5.1"
8
+ "version": "2.5.3"
9
9
  },
10
10
  "plugins": [
11
11
  {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "kimi",
3
3
  "description": "Kimi for Claude Code: code questions, independent reviews, bounded fixes, autonomous goals, parallel tasks, and MCP tasks through the local Kimi Code CLI, with PreToolUse safety hooks.",
4
- "version": "2.5.1",
4
+ "version": "2.5.3",
5
5
  "homepage": "https://github.com/linxule/kimi-plugin-cc",
6
6
  "repository": "https://github.com/linxule/kimi-plugin-cc",
7
7
  "license": "Apache-2.0",
package/CHANGELOG.md CHANGED
@@ -2,11 +2,25 @@
2
2
 
3
3
  > **Post-1.0 release history (v1.0.1 -> present) lives in [ROADMAP-TO-GA.md § Post-GA audit log](./ROADMAP-TO-GA.md#post-ga-audit-log)** and the "Version" / "Upstream compat" lines of [AGENTS.md](./AGENTS.md). Docs-only kimi-code compat checkups that don't bump the plugin version (e.g. the 0.14.2 / 0.14.3 patches) are recorded there, not here. Notable releases are summarized below; the GA entry and full pre-GA detail follow.
4
4
 
5
+ ## 2.5.3 — 2026-11-06
6
+
7
+ - Certify exact kimi-code 2.5.0 and 2.5.1 for all eight native-v2 operations. Preserve the no-plan construction, local execution and workspace-trust enforcement, authenticated WebSocket swarm state, bounded lifecycle cleanup, and the legacy-v1 0.41.x boundary. See the [2.5.0](docs/upstream-2.5.0-certification.md) and [2.5.1](docs/upstream-2.5.1-certification.md) evidence.
8
+ - Review every exact-version branch for fresh `--environment local`, hook schemas, preactivation, local environment/cwd binding, and protocol-v2 agent state. Require a clean WebSocket close before accepting final agent state, preserving timeout and abnormal-close refusals across Bun platforms. The 2.5.1 source scan passed 18 tests with 153 assertions; candidate and production matrices each passed 24 tests with 167 assertions and no skips, including real rescue, Bash.cwd confinement, resume/taint refusal, goal-turn denials, and child-authored patches.
9
+ - Pass the full repository gate with 1,412 tests and 18,282 assertions, 330-file package checks across all three distributions, isolated consumer security, and independent read-only `claude -p` review with no must-fix findings. Update the manual smoke default to exact 2.5.1 and the current compatibility documentation; publication does not update installed hosts or operator pins.
10
+
11
+ ## 2.5.2 — 2026-11-02
12
+
13
+ - Preserve TOML multiline values, line endings, and foreign hook fields during setup; repair mixed-host orphan marker comments without altering foreign hooks.
14
+ - Keep the DSH disposal grace at least 6000ms, pin a stable Node path, and expose each Kimi tool's write boundary to the model.
15
+ - Treat DSH services and its tool adapter as host-provided peers so ordinary npm consumers do not install the harness. Require patched DSH host/profile dependencies and audit both installations before the seven-tool composition and Web-boot smoke.
16
+ - Add an installed-consumer security gate to CI and publication; reject failed or incomplete audits as well as high/critical advisories. Upstream DSH pins remain vulnerable, so existing installations require the documented explicit overrides.
17
+ - Validate exact Kimi Code 2.4.1 with 24 real-binary smoke tests (167 assertions, no skips) and the 16-test exact-source audit; certification remains unchanged.
18
+
5
19
  ## 2.5.1 — 2026-11-01
6
20
 
7
21
  - Consolidate DSH distribution into the existing `kimi-plugin-cc` npm package and its established OIDC publication path. DSH users install the same root artifact with `dsh plugin --profile <name> add kimi-plugin-cc`; no second npm package or Trusted Publishing binding is required.
8
22
  - Declare the root DSH bundle entry, runtime dependencies, and peer compatibility directly in `package.json`, and validate root-tarball installation, seven-tool composition, and Web boot in an isolated DSH profile.
9
- - Pin safe transitive `zod` and `undici` versions, remove the unnecessary full DSH CLI development dependency, install pnpm explicitly in CI, and exclude dependency directories from installed-script fixtures.
23
+ - Pin transitive `zod` and `undici` versions in the checkout lockfile, remove the unnecessary full DSH CLI development dependency, install pnpm explicitly in CI, and exclude dependency directories from installed-script fixtures. Downstream npm consumers do not inherit those overrides; see the consumer-security release gate.
10
24
 
11
25
  ## 2.5.0 — 2026-11-01
12
26
 
package/README.md CHANGED
@@ -99,6 +99,8 @@ If you use multiple hosts, run setup in each. Each app has its own hook in the s
99
99
 
100
100
  ## Install in DeepSeek Harness
101
101
 
102
+ DSH requires patched host and profile dependencies (`zod >=4.6.6`, `undici >=8.12.2`). Follow the [host installation and audit instructions](./docs/registry-releases.md#patched-dsh-host-and-profiles) before installing this plugin.
103
+
102
104
  The DSH bundle provides the same seven operations as the other hosts: `kimi_ask`, `kimi_review`, `kimi_challenge`, `kimi_rescue`, `kimi_pursue`, `kimi_swarm`, and `kimi_swarm_write`. They use the same companion runtime, safety hooks, jobs, budgets, and artifacts. Rescue and pursue edit the selected workspace; swarm-write returns a patch without applying or committing it.
103
105
 
104
106
  From this repository checkout, run:
@@ -109,6 +111,8 @@ dsh plugin --profile web add ./plugins/kimi-dsh
109
111
 
110
112
  For the published npm bundle, run `dsh plugin --profile web add kimi-plugin-cc` instead.
111
113
 
114
+ For DSH Discover, enable **Custom plugin indexes** and add `https://raw.githubusercontent.com/linxule/kimi-plugin-cc/main/dsh/index.json` as an index source.
115
+
112
116
  Restart DSH when requested, then run `/kimi-setup` and `/kimi-setup --check`. Ask DSH to use one of the seven tools with a self-contained task. Job controls are `/kimi-status`, `/kimi-result`, and `/kimi-cancel`. `/kimi-setup --models` lists configured models, and `/kimi-setup --uninstall` removes only the DSH hook.
113
117
 
114
118
  DSH requires Node.js 24.9 or newer and DSH `>=0.2.1-rc.5 <0.3.0`. See the [DSH bundle guide](./plugins/kimi-dsh/README.md) for profile-level model, thinking, budget, review-base, and other configuration.
@@ -337,9 +341,27 @@ codex plugin add kimi@kimi-marketplace
337
341
 
338
342
  如果同时使用 Claude Code 和 Codex,请分别运行 setup。两个应用各有自己的钩子,共用一份 Kimi 配置。
339
343
 
344
+ ## 在 DeepSeek Harness(DSH)中安装
345
+
346
+ DSH 宿主及各配置所用的依赖需要修补(`zod >=4.6.6`、`undici >=8.12.2`)。安装插件前,请按[宿主安装与审计说明](./docs/registry-releases.md#patched-dsh-host-and-profiles)操作。
347
+
348
+ DSH 与其他宿主一样提供七类操作:`kimi_ask`、`kimi_review`、`kimi_challenge`、`kimi_rescue`、`kimi_pursue`、`kimi_swarm` 和 `kimi_swarm_write`。它们使用同一套 companion 运行时、安全钩子、任务记录、预算和产物。Rescue 和 pursue 会修改所选工作区;swarm-write 只返回补丁,不会应用或提交它。
349
+
350
+ 从此仓库检出安装:
351
+
352
+ ```sh
353
+ dsh plugin --profile web add ./plugins/kimi-dsh
354
+ ```
355
+
356
+ 从已发布的根包安装时,改为运行 `dsh plugin --profile web add kimi-plugin-cc`。
357
+
358
+ 按提示重启 DSH,然后运行 `/kimi-setup` 和 `/kimi-setup --check`。让 DSH 使用上述任一工具,并提供独立完整的任务。任务控制命令为 `/kimi-status`、`/kimi-result` 和 `/kimi-cancel`。`/kimi-setup --models` 列出已配置模型,`/kimi-setup --uninstall` 只移除 DSH 自己的钩子。
359
+
360
+ DSH 需要 Node.js 24.9 或更高版本,以及 DSH `>=0.2.1-rc.5 <0.3.0`。配置模型、思考强度、预算、review base 等选项,请参阅 [DSH 指南](./plugins/kimi-dsh/README.md)。
361
+
340
362
  ## npm 分发(可选)
341
363
 
342
- 完整插件也已发布到 [npm,包名为 `kimi-plugin-cc`](https://www.npmjs.com/package/kimi-plugin-cc),包含 Claude Code 和 Codex 的预编译运行时,无需 Bun 或构建步骤。默认仍推荐上方的插件市场安装方式。需要通过 npm 安装本地 Claude Code 插件时,请参照 [npm 安装步骤](#npm-distribution-optional)。每个应用只加载一份插件;npm 不提供 `npx kimi-plugin-cc` 命令。Codex 用户继续使用上方的 GitHub 插件市场命令。
364
+ 完整插件也已发布到 [npm,包名为 `kimi-plugin-cc`](https://www.npmjs.com/package/kimi-plugin-cc),包含 Claude Code、Codex 和 DSH 的预编译运行时,无需 Bun 或构建步骤。默认仍推荐上方的插件市场安装方式。需要通过 npm 安装本地 Claude Code 插件时,请参照 [npm 安装步骤](#npm-distribution-optional)。每个应用只加载一份插件;npm 不提供 `npx kimi-plugin-cc` 命令。Codex 用户继续使用上方的 GitHub 插件市场命令。
343
365
 
344
366
  ## 选择任务
345
367
 
@@ -470,7 +492,9 @@ Demandez à Kimi d'expliquer du code, de faire une revue de code, de remettre en
470
492
 
471
493
  Le plugin exécute Kimi Code CLI sur votre ordinateur. Il utilise votre abonnement Kimi ou un fournisseur configuré dans Kimi. Les appels consomment votre quota d'abonnement ou sont facturés sur votre compte API. La connexion à l'application hôte ne change pas. Le fournisseur choisi reçoit les prompts et le code nécessaires à la tâche.
472
494
 
473
- Commencez par une question ou une revue de code. Ces modes autorisent la lecture des fichiers, mais interdisent leur modification et l'exécution de commandes shell. D'autres modes peuvent modifier des fichiers ; DSH propose les sept mêmes opérations avec les mêmes limites d'écriture. Consultez les [limites de sécurité](./docs/safety.md) avant de demander des modifications à Kimi. Voir le [guide DSH en anglais](#install-in-deepseek-harness) pour l'installation.
495
+ Commencez par une question ou une revue de code. Ces modes autorisent la lecture des fichiers, mais interdisent leur modification et l'exécution de commandes shell. D'autres modes peuvent modifier des fichiers ; DSH nécessite des dépendances corrigées dans son installation et ses profils (`zod >=4.6.6`, `undici >=8.12.2`). Suivez les [instructions d’installation et d’audit](./docs/registry-releases.md#patched-dsh-host-and-profiles) avant d’installer ce plugin.
496
+
497
+ DSH propose les sept mêmes opérations avec les mêmes limites d'écriture. Consultez les [limites de sécurité](./docs/safety.md) avant de demander des modifications à Kimi. Voir le [guide DSH en anglais](#install-in-deepseek-harness) pour l'installation.
474
498
 
475
499
  ## Préparer l'installation
476
500
 
@@ -536,9 +560,25 @@ Utilise $kimi-ask pour expliquer le fonctionnement du point d'entrée principal
536
560
 
537
561
  Si vous utilisez Claude Code et Codex, lancez setup dans chaque application. Chacune possède son propre hook dans la configuration Kimi commune.
538
562
 
563
+ ## Installer dans DeepSeek Harness (DSH)
564
+
565
+ DSH propose les sept mêmes opérations que les autres hôtes : `kimi_ask`, `kimi_review`, `kimi_challenge`, `kimi_rescue`, `kimi_pursue`, `kimi_swarm` et `kimi_swarm_write`. Elles utilisent le même runtime companion, les mêmes hooks de sécurité, jobs, budgets et artefacts. Rescue et pursue modifient l'espace de travail sélectionné ; swarm-write renvoie un patch sans l'appliquer ni le committer.
566
+
567
+ Depuis une copie de ce dépôt, exécutez :
568
+
569
+ ```sh
570
+ dsh plugin --profile web add ./plugins/kimi-dsh
571
+ ```
572
+
573
+ Pour installer le paquet racine publié, exécutez plutôt `dsh plugin --profile web add kimi-plugin-cc`.
574
+
575
+ Redémarrez DSH si demandé, puis lancez `/kimi-setup` et `/kimi-setup --check`. Demandez à DSH d'utiliser l'un des sept outils avec une tâche autonome. Les contrôles sont `/kimi-status`, `/kimi-result` et `/kimi-cancel`. `/kimi-setup --models` liste les modèles configurés, et `/kimi-setup --uninstall` supprime uniquement le hook DSH.
576
+
577
+ DSH requiert Node.js 24.9 ou supérieur et DSH `>=0.2.1-rc.5 <0.3.0`. Consultez le [guide DSH](./plugins/kimi-dsh/README.md) pour configurer le modèle, l'effort de réflexion, le budget, la base de revue et les autres options.
578
+
539
579
  ## Distribution npm (facultative)
540
580
 
541
- Le plugin complet est aussi disponible sur [npm sous le nom `kimi-plugin-cc`](https://www.npmjs.com/package/kimi-plugin-cc), avec les runtimes précompilés pour Claude Code et Codex, sans Bun ni compilation. Les marketplaces ci-dessus restent le mode d'installation par défaut. Pour installer une copie locale dans Claude Code via npm, suivez les [étapes npm](#npm-distribution-optional) (en anglais). Ne chargez qu'une copie du plugin par application ; le paquet ne fournit pas de commande `npx kimi-plugin-cc`. Pour Codex, continuez à utiliser la marketplace GitHub ci-dessus.
581
+ Le plugin complet est aussi disponible sur [npm sous le nom `kimi-plugin-cc`](https://www.npmjs.com/package/kimi-plugin-cc), avec les runtimes précompilés pour Claude Code, Codex et DSH, sans Bun ni compilation. Les marketplaces ci-dessus restent le mode d'installation par défaut. Pour installer une copie locale dans Claude Code via npm, suivez les [étapes npm](#npm-distribution-optional) (en anglais). Ne chargez qu'une copie du plugin par application ; le paquet ne fournit pas de commande `npx kimi-plugin-cc`. Pour Codex, continuez à utiliser la marketplace GitHub ci-dessus.
542
582
 
543
583
  ## Choisir une tâche
544
584
 
@@ -735,9 +775,27 @@ $kimi-ask を使って、このプロジェクトのメインのエントリー
735
775
 
736
776
  Claude Code と Codex の両方を使う場合は、それぞれで setup を実行します。共通の Kimi 設定内に、各アプリ専用のフックが作られます。
737
777
 
778
+ ## DeepSeek Harness(DSH)にインストールする
779
+
780
+ DSH 本体とプロファイルの依存パッケージには修正版(`zod >=4.6.6`、`undici >=8.12.2`)が必要です。プラグインのインストール前に[本体のインストールと監査の手順](./docs/registry-releases.md#patched-dsh-host-and-profiles)を確認してください。
781
+
782
+ DSH でも、ほかのホストと同じ 7 種類の操作を提供します:`kimi_ask`、`kimi_review`、`kimi_challenge`、`kimi_rescue`、`kimi_pursue`、`kimi_swarm`、`kimi_swarm_write`。同じ companion ランタイム、安全フック、ジョブ、予算、成果物を使用します。Rescue と pursue は選択したワークスペースを変更し、swarm-write はパッチを返すだけで、適用やコミットは行いません。
783
+
784
+ このリポジトリのチェックアウトからインストールする場合:
785
+
786
+ ```sh
787
+ dsh plugin --profile web add ./plugins/kimi-dsh
788
+ ```
789
+
790
+ 公開されているルートパッケージからインストールする場合は、代わりに `dsh plugin --profile web add kimi-plugin-cc` を実行します。
791
+
792
+ 案内に従って DSH を再起動したら、`/kimi-setup` と `/kimi-setup --check` を実行します。独立した内容のタスクを指定して、上記のいずれかのツールを使ってください。ジョブ制御は `/kimi-status`、`/kimi-result`、`/kimi-cancel` です。`/kimi-setup --models` で設定済みモデルを一覧でき、`/kimi-setup --uninstall` は DSH のフックだけを削除します。
793
+
794
+ DSH には Node.js 24.9 以降と、DSH `>=0.2.1-rc.5 <0.3.0` が必要です。モデル、思考強度、予算、review base などの設定は [DSH ガイド](./plugins/kimi-dsh/README.md)を参照してください。
795
+
738
796
  ## npm 配布(任意)
739
797
 
740
- 完全なプラグインは [npm の `kimi-plugin-cc`](https://www.npmjs.com/package/kimi-plugin-cc) としても公開されています。Claude Code と Codex 向けのコンパイル済みランタイムを含み、Bun やビルド作業は不要です。通常は上記のマーケットプレイス経由でインストールしてください。npm から Claude Code 用のローカルコピーを入れる場合は、[npm のインストール手順](#npm-distribution-optional)(英語)を参照してください。各アプリで読み込むプラグインは一つにし、重複させないでください。`npx kimi-plugin-cc` コマンドはありません。Codex では引き続き上記の GitHub マーケットプレイスを使います。
798
+ 完全なプラグインは [npm の `kimi-plugin-cc`](https://www.npmjs.com/package/kimi-plugin-cc) としても公開されています。Claude Code、Codex、DSH 向けのコンパイル済みランタイムを含み、Bun やビルド作業は不要です。通常は上記のマーケットプレイス経由でインストールしてください。npm から Claude Code 用のローカルコピーを入れる場合は、[npm のインストール手順](#npm-distribution-optional)(英語)を参照してください。各アプリで読み込むプラグインは一つにし、重複させないでください。`npx kimi-plugin-cc` コマンドはありません。Codex では引き続き上記の GitHub マーケットプレイスを使います。
741
799
 
742
800
  ## タスクを選ぶ
743
801
 
@@ -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;
package/dist/dsh/index.js CHANGED
@@ -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");
@@ -595,7 +595,8 @@ function validateVersionSensitiveEvents(events, version) {
595
595
  // Exact schemas reviewed at 2.0.0 (1b89e4b0), 2.0.1 (caf7d4e2),
596
596
  // 2.0.2 (9d07f634), 2.1.0 (52437299), 2.1.1 (f67e6398), 2.2.0 (472192c3),
597
597
  // 2.3.0 (b5e28fd6), 2.3.1 (684bae9e), 2.3.2 (b8dd3a13), 2.3.3 (ec495f0c),
598
- // 2.4.0 (f26e4d91), and 2.4.1 (2c73f3f5): the additive events and HookDefSchema are unchanged.
598
+ // 2.4.0 (f26e4d91), 2.4.1 (2c73f3f5), 2.5.0 (41c34143), and 2.5.1 (1b02030d):
599
+ // the additive events and HookDefSchema are unchanged.
599
600
  // Require the full version string AND matching components; no inferred 2.x compatibility.
600
601
  if (version.major === 2 && ((version.minor === 0 && ((version.version === "2.0.0" && version.patch === 0) ||
601
602
  (version.version === "2.0.1" && version.patch === 1) ||
@@ -608,7 +609,9 @@ function validateVersionSensitiveEvents(events, version) {
608
609
  (version.version === "2.3.2" && version.patch === 2) ||
609
610
  (version.version === "2.3.3" && version.patch === 3))) ||
610
611
  (version.minor === 4 && ((version.version === "2.4.0" && version.patch === 0) ||
611
- (version.version === "2.4.1" && version.patch === 1))))) {
612
+ (version.version === "2.4.1" && version.patch === 1))) ||
613
+ (version.minor === 5 && ((version.version === "2.5.0" && version.patch === 0) ||
614
+ (version.version === "2.5.1" && version.patch === 1))))) {
612
615
  return { valid: true };
613
616
  }
614
617
  if (version.major !== 0) {