kimi-plugin-cc 2.3.1 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +5 -3
- package/.mcp.json +13 -0
- package/CHANGELOG.md +10 -0
- package/README.md +57 -5
- package/dist/cli-client.js +11 -1
- package/dist/commands/ask.js +4 -2
- package/dist/commands/cancel.js +43 -4
- package/dist/commands/pursue.js +3 -2
- package/dist/commands/rescue.js +3 -2
- package/dist/commands/review-gate.js +2 -4
- package/dist/commands/review.js +4 -2
- package/dist/commands/swarm.js +4 -3
- package/dist/companion.js +13 -40
- package/dist/help.js +2 -0
- package/dist/job-id.js +12 -0
- package/dist/job-store.js +59 -0
- package/dist/jobs.js +9 -1
- package/dist/mcp/binding.js +58 -0
- package/dist/mcp/job-control.js +345 -0
- package/dist/mcp/launcher.js +257 -0
- package/dist/mcp/operations.js +301 -0
- package/dist/mcp/protocol.js +275 -0
- package/dist/mcp/server.js +8 -0
- package/dist/mcp/stdio.js +78 -0
- package/dist/mcp/supervised-child.js +15 -0
- package/dist/mcp/tool-result.js +7 -0
- package/dist/mcp/workspace.js +34 -0
- package/dist/model-command.js +32 -0
- package/dist/version.js +1 -1
- package/docs/migration.md +2 -2
- package/docs/registry-releases.md +6 -8
- package/docs/safety.md +16 -2
- package/package.json +7 -3
- package/plugins/kimi-codex/.codex-plugin/plugin.json +7 -4
- package/plugins/kimi-codex/.mcp.json +28 -0
- package/plugins/kimi-codex/dist/cli-client.js +11 -1
- package/plugins/kimi-codex/dist/commands/ask.js +4 -2
- package/plugins/kimi-codex/dist/commands/cancel.js +43 -4
- package/plugins/kimi-codex/dist/commands/pursue.js +3 -2
- package/plugins/kimi-codex/dist/commands/rescue.js +3 -2
- package/plugins/kimi-codex/dist/commands/review-gate.js +2 -4
- package/plugins/kimi-codex/dist/commands/review.js +4 -2
- package/plugins/kimi-codex/dist/commands/swarm.js +4 -3
- package/plugins/kimi-codex/dist/companion.js +13 -40
- package/plugins/kimi-codex/dist/help.js +2 -0
- package/plugins/kimi-codex/dist/job-id.js +12 -0
- package/plugins/kimi-codex/dist/job-store.js +59 -0
- package/plugins/kimi-codex/dist/jobs.js +9 -1
- package/plugins/kimi-codex/dist/mcp/binding.js +58 -0
- package/plugins/kimi-codex/dist/mcp/job-control.js +345 -0
- package/plugins/kimi-codex/dist/mcp/launcher.js +257 -0
- package/plugins/kimi-codex/dist/mcp/operations.js +301 -0
- package/plugins/kimi-codex/dist/mcp/protocol.js +275 -0
- package/plugins/kimi-codex/dist/mcp/server.js +8 -0
- package/plugins/kimi-codex/dist/mcp/stdio.js +78 -0
- package/plugins/kimi-codex/dist/mcp/supervised-child.js +15 -0
- package/plugins/kimi-codex/dist/mcp/tool-result.js +7 -0
- package/plugins/kimi-codex/dist/mcp/workspace.js +34 -0
- package/plugins/kimi-codex/dist/model-command.js +32 -0
- package/plugins/kimi-codex/dist/version.js +1 -1
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
"name": "linxule"
|
|
5
5
|
},
|
|
6
6
|
"metadata": {
|
|
7
|
-
"description": "Kimi for Claude Code: code questions, reviews, fixes, and
|
|
8
|
-
"version": "2.
|
|
7
|
+
"description": "Kimi for Claude Code: code questions, reviews, fixes, parallel tasks, and MCP tasks through the local Kimi Code CLI.",
|
|
8
|
+
"version": "2.4.0"
|
|
9
9
|
},
|
|
10
10
|
"plugins": [
|
|
11
11
|
{
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kimi",
|
|
3
|
-
"description": "Kimi for Claude Code:
|
|
4
|
-
"version": "2.
|
|
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.4.0",
|
|
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",
|
|
@@ -11,7 +11,9 @@
|
|
|
11
11
|
"review",
|
|
12
12
|
"code-review",
|
|
13
13
|
"delegation",
|
|
14
|
-
"multi-model"
|
|
14
|
+
"multi-model",
|
|
15
|
+
"mcp",
|
|
16
|
+
"model-context-protocol"
|
|
15
17
|
],
|
|
16
18
|
"author": {
|
|
17
19
|
"name": "linxule"
|
package/.mcp.json
ADDED
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
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.4.0 — 2026-10-24
|
|
6
|
+
|
|
7
|
+
- Add one dependency-free MCP stdio server for Claude Code and Codex, with typed schemas for seven model operations plus job status, result, and cancellation. The existing slash commands, skills, safety hooks, and review gate remain intact.
|
|
8
|
+
- Return native MCP task handles for Claude calls that negotiate the tasks extension per request. Codex receives ordinary launch handles and uses explicit job-control tools; this does not claim native task-extension support for Codex.
|
|
9
|
+
- Supervise each operation through a fixed task/job UUID, bounded attachment, durable reconnects, and PID/PGID/start-token identity checks. Preserve recoverable launches and their public IDs after inconclusive probes, reconcile stale dead companions, and keep cancellation from signalling a changed identity.
|
|
10
|
+
- Preserve exact CLI 2.3.1 certification, workspace containment, data-root isolation, hook verification, resume lineage, and trusted write roots. Terminal jobs retain their artifact semantics: failed jobs complete tasks with `isError:true`, and cancelled jobs map to `cancelled`.
|
|
11
|
+
- Document the transport, lifecycle, ownership, safety, comment-hygiene, and contributor rules. Align package and marketplace descriptions and keywords with the MCP surface.
|
|
12
|
+
|
|
13
|
+
Contributors: **Kimi** (design, implementation, documentation, and verification), **Claude** and **Codex** (adversarial review and re-review).
|
|
14
|
+
|
|
5
15
|
## 2.3.1 — 2026-10-23
|
|
6
16
|
|
|
7
17
|
- Certify exact kimi-code 2.3.1 for all eight native-v2 operations after the source scan, plan-ON/OFF controls and full 15-test candidate smoke. Preserve the no-plan construction and document the new session-lock, internal-`ps`/PATH and legacy-trust-migration residuals. See [evidence](docs/upstream-2.3.1-certification.md).
|
package/README.md
CHANGED
|
@@ -40,7 +40,7 @@ You need:
|
|
|
40
40
|
- macOS or Linux
|
|
41
41
|
- Claude Code or Codex with plugin support
|
|
42
42
|
- Node.js 22.5 or newer
|
|
43
|
-
- Kimi Code CLI 2.3.1 (2.3.0 also certified), the recommended version for plugin 2.
|
|
43
|
+
- Kimi Code CLI 2.3.1 (2.3.0 also certified), the recommended version for plugin 2.4.0
|
|
44
44
|
|
|
45
45
|
Windows is not currently supported. Check your versions in a terminal:
|
|
46
46
|
|
|
@@ -108,7 +108,7 @@ needed to use the package. Kimi Code CLI and Node are still prerequisites.
|
|
|
108
108
|
For an explicitly versioned local Claude Code plugin:
|
|
109
109
|
|
|
110
110
|
```sh
|
|
111
|
-
npm install --prefix ~/.local/share/kimi-plugin-cc kimi-plugin-cc@2.
|
|
111
|
+
npm install --prefix ~/.local/share/kimi-plugin-cc kimi-plugin-cc@2.4.0
|
|
112
112
|
claude --plugin-dir ~/.local/share/kimi-plugin-cc/node_modules/kimi-plugin-cc
|
|
113
113
|
```
|
|
114
114
|
|
|
@@ -180,6 +180,19 @@ Canceling a task does not undo changes it has already made.
|
|
|
180
180
|
|
|
181
181
|
Ask and rescue support resuming a session. For example, `/kimi:ask -r Explain the next step.` continues your latest ask session. In Codex, ask `$kimi-ask` to resume it. See the [ask options](./commands/ask.md) and [rescue options](./commands/rescue.md).
|
|
182
182
|
|
|
183
|
+
### Use the MCP server
|
|
184
|
+
|
|
185
|
+
The plugin also bundles one MCP stdio server for Claude Code and Codex, alongside the existing slash commands and skills. Those interfaces remain available, and the existing safety-hook setup is still mandatory.
|
|
186
|
+
|
|
187
|
+
The seven model tools are `kimi_ask`, `kimi_review`, `kimi_challenge`, `kimi_rescue`, `kimi_pursue`, `kimi_swarm`, and `kimi_swarm_write`. Job control uses `kimi_job_status`, `kimi_job_result`, and `kimi_job_cancel`.
|
|
188
|
+
|
|
189
|
+
- **Claude Code:** task-capable calls return durable MCP tasks. Poll them with `tasks/get` and cancel them with `tasks/cancel`.
|
|
190
|
+
- **Codex:** calls currently use ordinary MCP results, not native MCP tasks. After attachment, a running job returns a launch handle; an already settled job returns its terminal result. Use the three job-control tools to check, read, or cancel it.
|
|
191
|
+
|
|
192
|
+
Claude supplies the project directory as the default workspace. When the host has no default, as is normal for Codex, the initial operation requires an absolute `workspace` path. Later job-control calls can rely on the job's persisted workspace binding. An MCP server disconnect does not cancel jobs. The jobs table owns status and outcomes; the terminal artifact is the result authority. Failed jobs return `isError: true` with their artifacts.
|
|
193
|
+
|
|
194
|
+
For example: “Use `kimi_review` to check this diff. In Claude Code, poll the returned task with `tasks/get`; in Codex, use the returned handle with `kimi_job_status` and then `kimi_job_result`.”
|
|
195
|
+
|
|
183
196
|
## Update or remove the plugin
|
|
184
197
|
|
|
185
198
|
### Update in Claude Code
|
|
@@ -249,7 +262,7 @@ Contributors need Bun to run `bun run check`. The detailed engineering documents
|
|
|
249
262
|
- macOS 或 Linux
|
|
250
263
|
- 支持插件的 Claude Code 或 Codex
|
|
251
264
|
- Node.js 22.5 或更高版本
|
|
252
|
-
- Kimi Code CLI 2.3.1(2.3.0 亦已认证),这是插件 2.
|
|
265
|
+
- Kimi Code CLI 2.3.1(2.3.0 亦已认证),这是插件 2.4.0 的推荐版本
|
|
253
266
|
|
|
254
267
|
目前不支持 Windows。在终端检查版本:
|
|
255
268
|
|
|
@@ -364,6 +377,19 @@ Claude Code 还提供可选的[自动审查功能](./commands/setup.md),在 Cl
|
|
|
364
377
|
|
|
365
378
|
Ask 和 rescue 支持恢复会话。例如,`/kimi:ask -r 解释下一步。` 会继续最近一次 ask 会话。在 Codex 中,请 `$kimi-ask` 恢复该会话。参见 [ask 参数](./commands/ask.md)和 [rescue 参数](./commands/rescue.md)。
|
|
366
379
|
|
|
380
|
+
### 使用 MCP 服务器
|
|
381
|
+
|
|
382
|
+
插件现在还内置了一个同时供 Claude Code 和 Codex 使用的 MCP stdio 服务器,与现有斜杠命令和技能并存。这些界面仍然可用,现有的安全钩子配置也仍是必需步骤。
|
|
383
|
+
|
|
384
|
+
七个模型工具是 `kimi_ask`、`kimi_review`、`kimi_challenge`、`kimi_rescue`、`kimi_pursue`、`kimi_swarm` 和 `kimi_swarm_write`。任务控制使用 `kimi_job_status`、`kimi_job_result` 和 `kimi_job_cancel`。
|
|
385
|
+
|
|
386
|
+
- **Claude Code:** 支持任务式调用的工具会返回持久 MCP 任务,通过 `tasks/get` 轮询,通过 `tasks/cancel` 取消。
|
|
387
|
+
- **Codex:** 目前使用普通 MCP 结果,而不是原生 MCP 任务。附加完成后,仍在运行的任务返回启动句柄;已结束的任务直接返回最终结果。使用上述三个任务控制工具查看进度、读取结果或取消任务。
|
|
388
|
+
|
|
389
|
+
Claude 默认提供项目目录作为工作区。如果宿主没有默认工作区(Codex 通常如此),首次操作必须提供绝对的 `workspace` 路径。之后的任务控制调用可以使用为该任务保存的工作区绑定关系。MCP 服务器断开连接不会取消任务。`jobs` 表是任务状态与结局的依据,最终产物(artifact)是结果内容的依据。任务失败时会返回 `isError: true` 以及相应产物。
|
|
390
|
+
|
|
391
|
+
例如:“使用 `kimi_review` 检查这次差异。在 Claude Code 中,用 `tasks/get` 轮询返回的任务;在 Codex 中,用返回的句柄调用 `kimi_job_status`,然后调用 `kimi_job_result`。”
|
|
392
|
+
|
|
367
393
|
## 更新或移除插件
|
|
368
394
|
|
|
369
395
|
### 在 Claude Code 中更新
|
|
@@ -433,7 +459,7 @@ Il vous faut :
|
|
|
433
459
|
- macOS ou Linux
|
|
434
460
|
- une version de Claude Code ou de Codex prenant en charge les plugins
|
|
435
461
|
- Node.js 22.5 ou une version ultérieure
|
|
436
|
-
- Kimi Code CLI 2.3.1 (2.3.0 également certifiée), la version recommandée pour le plugin 2.
|
|
462
|
+
- Kimi Code CLI 2.3.1 (2.3.0 également certifiée), la version recommandée pour le plugin 2.4.0
|
|
437
463
|
|
|
438
464
|
Windows n'est pas pris en charge actuellement. Vérifiez les versions dans un terminal :
|
|
439
465
|
|
|
@@ -548,6 +574,19 @@ Annuler une tâche n'annule pas les modifications déjà effectuées.
|
|
|
548
574
|
|
|
549
575
|
Ask et rescue permettent de reprendre une session. Par exemple, `/kimi:ask -r Explique l'étape suivante.` reprend votre dernière session ask. Dans Codex, demandez à `$kimi-ask` de la reprendre. Consultez les [options d'ask](./commands/ask.md) et les [options de rescue](./commands/rescue.md).
|
|
550
576
|
|
|
577
|
+
### Utiliser le serveur MCP
|
|
578
|
+
|
|
579
|
+
Le plugin intègre aussi un serveur MCP stdio unique pour Claude Code et Codex, en plus des commandes slash et skills existants. Ces interfaces restent disponibles et la configuration du hook de sécurité reste obligatoire.
|
|
580
|
+
|
|
581
|
+
Les sept outils de modèle sont `kimi_ask`, `kimi_review`, `kimi_challenge`, `kimi_rescue`, `kimi_pursue`, `kimi_swarm` et `kimi_swarm_write`. Le contrôle des tâches utilise `kimi_job_status`, `kimi_job_result` et `kimi_job_cancel`.
|
|
582
|
+
|
|
583
|
+
- **Claude Code :** les appels compatibles avec les tâches renvoient des tâches MCP durables. Interrogez-les avec `tasks/get` et annulez-les avec `tasks/cancel`.
|
|
584
|
+
- **Codex :** les appels utilisent actuellement des résultats MCP ordinaires, et non des tâches MCP natives. Après l'attachement, un job encore actif renvoie un handle de lancement ; un job déjà terminé renvoie directement son résultat final. Utilisez les trois outils de contrôle pour suivre, lire ou annuler le job.
|
|
585
|
+
|
|
586
|
+
Claude fournit le répertoire du projet comme espace de travail par défaut. Sans espace de travail par défaut côté hôte, ce qui est normal pour Codex, l'opération initiale exige un chemin `workspace` absolu. Les contrôles ultérieurs peuvent utiliser l'association enregistrée entre la tâche et l'espace de travail. Une déconnexion du serveur MCP n'annule pas les tâches. La table jobs fait autorité pour l'état et l'issue des jobs ; l'artefact final fait autorité pour le contenu du résultat. Une tâche en échec renvoie `isError: true` avec ses artefacts.
|
|
587
|
+
|
|
588
|
+
Par exemple : « Utilise `kimi_review` pour vérifier ce diff. Dans Claude Code, interroge la tâche renvoyée avec `tasks/get` ; dans Codex, utilise le handle renvoyé avec `kimi_job_status`, puis `kimi_job_result`. »
|
|
589
|
+
|
|
551
590
|
## Mettre à jour ou supprimer le plugin
|
|
552
591
|
|
|
553
592
|
### Mettre à jour dans Claude Code
|
|
@@ -617,7 +656,7 @@ Les contributeurs ont besoin de Bun pour exécuter `bun run check`. La documenta
|
|
|
617
656
|
- macOS または Linux
|
|
618
657
|
- プラグインに対応した Claude Code または Codex
|
|
619
658
|
- Node.js 22.5 以降
|
|
620
|
-
- Kimi Code CLI 2.3.1(2.3.0 も認証済み。プラグイン 2.
|
|
659
|
+
- Kimi Code CLI 2.3.1(2.3.0 も認証済み。プラグイン 2.4.0 の推奨バージョン)
|
|
621
660
|
|
|
622
661
|
現在、Windows には対応していません。ターミナルでバージョンを確認します。
|
|
623
662
|
|
|
@@ -732,6 +771,19 @@ Codex では「$kimi-review を使い、設定済みの『……』というモ
|
|
|
732
771
|
|
|
733
772
|
Ask と rescue はセッションの再開に対応しています。たとえば `/kimi:ask -r 次の手順を説明してください。` は、直近の ask セッションを続けます。Codex では `$kimi-ask` に再開を依頼してください。[ask のオプション](./commands/ask.md)と [rescue のオプション](./commands/rescue.md)を参照できます。
|
|
734
773
|
|
|
774
|
+
### MCP サーバーを使う
|
|
775
|
+
|
|
776
|
+
プラグインには、既存のスラッシュコマンドとスキルに加え、Claude Code と Codex の両方で使える MCP stdio サーバーが 1 つ同梱されています。既存のコマンドとスキルも引き続き使え、既存の安全フックの設定も必須です。
|
|
777
|
+
|
|
778
|
+
7 つのモデルツールは `kimi_ask`、`kimi_review`、`kimi_challenge`、`kimi_rescue`、`kimi_pursue`、`kimi_swarm`、`kimi_swarm_write` です。ジョブ制御には `kimi_job_status`、`kimi_job_result`、`kimi_job_cancel` を使います。
|
|
779
|
+
|
|
780
|
+
- **Claude Code:** タスク対応の呼び出しは永続的な MCP タスクを返します。`tasks/get` でポーリングし、`tasks/cancel` でキャンセルします。
|
|
781
|
+
- **Codex:** 現在はネイティブの MCP タスクではなく、通常の MCP 結果を使います。アタッチ後、実行中のジョブには起動ハンドルを返し、すでに終了したジョブには最終結果を直接返します。進捗確認・結果取得・キャンセルには 3 つのジョブ制御ツールを使います。
|
|
782
|
+
|
|
783
|
+
Claude はプロジェクトディレクトリをデフォルトのワークスペースとして提供します。ホスト側にデフォルトがない場合(Codex ではこれが通常です)、最初の操作では絶対パスの `workspace` が必要です。後続のジョブ制御では、そのジョブに保存されたワークスペースの関連付けを利用できます。MCP サーバーとの接続が切れても、ジョブはキャンセルされません。`jobs` テーブルがジョブの状態と結果を管理し、最終成果物(artifact)が結果内容の正規の情報源となります。失敗したジョブも成果物とともに `isError: true` を返します。
|
|
784
|
+
|
|
785
|
+
例:「`kimi_review` でこの差分を確認してください。Claude Code では返されたタスクを `tasks/get` でポーリングし、Codex では返されたハンドルを使って `kimi_job_status`、`kimi_job_result` の順に呼び出してください。」
|
|
786
|
+
|
|
735
787
|
## プラグインを更新または削除する
|
|
736
788
|
|
|
737
789
|
### Claude Code で更新する
|
package/dist/cli-client.js
CHANGED
|
@@ -305,7 +305,7 @@ export async function runCliPrompt(opts) {
|
|
|
305
305
|
args,
|
|
306
306
|
command_label: opts.commandLabel ?? null,
|
|
307
307
|
});
|
|
308
|
-
return await new Promise((resolve, reject) => {
|
|
308
|
+
return await new Promise(async (resolve, reject) => {
|
|
309
309
|
let aborted = false;
|
|
310
310
|
let cancellationTeardown;
|
|
311
311
|
let settlementStarted = false;
|
|
@@ -536,6 +536,16 @@ export async function runCliPrompt(opts) {
|
|
|
536
536
|
if (opts.signal?.aborted === true) {
|
|
537
537
|
onAbort();
|
|
538
538
|
}
|
|
539
|
+
if (child.pid !== undefined && opts.onSpawn) {
|
|
540
|
+
try {
|
|
541
|
+
await opts.onSpawn(child.pid);
|
|
542
|
+
}
|
|
543
|
+
catch (error) {
|
|
544
|
+
onAbort();
|
|
545
|
+
settle("reject", new RuntimeError("CLI_SPAWN_CALLBACK_FAILED", `Failed to record the spawned kimi process: ${formatError(error)}`, "cli-client.spawn", error instanceof Error ? { cause: error, details: errorDetails() } : { details: errorDetails() }));
|
|
546
|
+
return;
|
|
547
|
+
}
|
|
548
|
+
}
|
|
539
549
|
earlyErrorHandled = true;
|
|
540
550
|
child.on("error", (err) => {
|
|
541
551
|
appendLogLine({ event: "process_error", message: formatError(err) });
|
package/dist/commands/ask.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { companionHelp } from "../help.js";
|
|
2
|
-
import {
|
|
2
|
+
import { allocateJobId } from "../job-id.js";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { createCliCancellationHandlers } from "../cli-cancellation.js";
|
|
5
5
|
import { runCliPromptWithBudget } from "../cli-client.js";
|
|
@@ -55,7 +55,7 @@ export async function runAsk(argv, context) {
|
|
|
55
55
|
const store = new JobStore(paths);
|
|
56
56
|
try {
|
|
57
57
|
await sweepStaleJobs(store, paths);
|
|
58
|
-
const jobId =
|
|
58
|
+
const jobId = allocateJobId(context);
|
|
59
59
|
const sessionResolution = await resolveAskSession(store, repoIdentity.repoId, parsed.fresh, parsed.resume, parsed.resumeTarget);
|
|
60
60
|
const askPrompt = buildAskPrompt(parsed.prompt, sessionResolution.reusedSession);
|
|
61
61
|
const executionPlan = await prepareKimiExecutionPlan({
|
|
@@ -152,6 +152,7 @@ export async function executeAskJob(jobId, prompt, context, options) {
|
|
|
152
152
|
}
|
|
153
153
|
handlers = createCliCancellationHandlers();
|
|
154
154
|
const executionPlan = executionPlanFromPersisted(job);
|
|
155
|
+
store.updateRunningJob(job.job_id, { phase: "turn-running" });
|
|
155
156
|
// Re-validate resume lineage from the store before spawning: a detached
|
|
156
157
|
// worker (or a forged queued row) reloads the persisted plan and would
|
|
157
158
|
// otherwise hand kimi_session_id to the spawn without the dispatch-time
|
|
@@ -174,6 +175,7 @@ export async function executeAskJob(jobId, prompt, context, options) {
|
|
|
174
175
|
thinkingEffort: job.thinking_effort ?? undefined,
|
|
175
176
|
resumeSessionId: job.kimi_session_id ?? undefined,
|
|
176
177
|
logPath: job.stream_log_path,
|
|
178
|
+
onSpawn: (pid) => { store.updateRunningJob(job.job_id, { kimi_pid: pid }); },
|
|
177
179
|
signal: handlers.signal,
|
|
178
180
|
}, job.budget_ms ?? KIMI_ASK_PROMPT_TIMEOUT_MS, "ask.prompt");
|
|
179
181
|
store.updateRunningJob(job.job_id, observedExecutionFields(result));
|
package/dist/commands/cancel.js
CHANGED
|
@@ -5,6 +5,7 @@ import { markJobCancelled, sweepStaleJobs } from "../jobs.js";
|
|
|
5
5
|
import { JobStore, withJobStore } from "../job-store.js";
|
|
6
6
|
import { ensurePluginPaths, resolvePluginPaths } from "../paths.js";
|
|
7
7
|
import { parseJobLookupArgs } from "../parsing.js";
|
|
8
|
+
import { revalidateProcessIdentities } from "../process-tree.js";
|
|
8
9
|
import { getManagedCommandConfig } from "./registry.js";
|
|
9
10
|
export async function runCancel(argv, context) {
|
|
10
11
|
const parsed = parseJobLookupArgs(argv);
|
|
@@ -31,6 +32,10 @@ export async function runCancel(argv, context) {
|
|
|
31
32
|
message: "cancel is a no-op for terminal jobs.",
|
|
32
33
|
}, null, 2)}\n`;
|
|
33
34
|
}
|
|
35
|
+
if (store.getMcpTask(job.job_id) &&
|
|
36
|
+
(context.mcpCompanionIdentity === undefined || context.mcpKimiIdentity === undefined)) {
|
|
37
|
+
throw new RuntimeError("MCP_CANCEL_REQUIRES_BINDING", `MCP job ${job.job_id} requires MCP job-control cancellation with recorded identities; use tasks/cancel or kimi_job_cancel.`, "mcp.cancel");
|
|
38
|
+
}
|
|
34
39
|
if (job.pid === null && job.kimi_pid === null) {
|
|
35
40
|
throw new RuntimeError("CANCEL_NOT_SUPPORTED", `Job ${job.job_id} does not have a recorded process id to cancel.`, "cancel.runtime");
|
|
36
41
|
}
|
|
@@ -53,6 +58,7 @@ export async function runCancel(argv, context) {
|
|
|
53
58
|
catch (error) {
|
|
54
59
|
preMarkError = error;
|
|
55
60
|
}
|
|
61
|
+
await assertMcpCompanionIdentity(job, context.mcpCompanionIdentity);
|
|
56
62
|
signalJobProcesses(job, "SIGTERM");
|
|
57
63
|
await sleep(1_000);
|
|
58
64
|
if (hasLiveRecordedProcess(job)) {
|
|
@@ -67,6 +73,8 @@ export async function runCancel(argv, context) {
|
|
|
67
73
|
message: "review_gate cancelled; SIGTERM→SIGKILL escalation completed.",
|
|
68
74
|
}, null, 2)}\n`;
|
|
69
75
|
}
|
|
76
|
+
await assertMcpCompanionIdentity(job, context.mcpCompanionIdentity);
|
|
77
|
+
await assertMcpKimiIdentity(job, context.mcpKimiIdentity);
|
|
70
78
|
signalJobProcesses(job, "SIGTERM");
|
|
71
79
|
const cancelled = await waitForCancellation(paths, job.job_id);
|
|
72
80
|
if (cancelled) {
|
|
@@ -76,7 +84,11 @@ export async function runCancel(argv, context) {
|
|
|
76
84
|
message: "Cancellation completed.",
|
|
77
85
|
}, null, 2)}\n`;
|
|
78
86
|
}
|
|
79
|
-
|
|
87
|
+
const companionMatches = await assertMcpCompanionIdentity(job, context.mcpCompanionIdentity, true);
|
|
88
|
+
const kimiMatches = await assertMcpKimiIdentity(job, context.mcpKimiIdentity, true);
|
|
89
|
+
if (companionMatches || kimiMatches) {
|
|
90
|
+
signalJobProcesses(job, "SIGKILL", { companion: companionMatches, kimi: kimiMatches });
|
|
91
|
+
}
|
|
80
92
|
await withJobStore(paths, async (forcedStore) => {
|
|
81
93
|
const current = forcedStore.getJob(job.job_id);
|
|
82
94
|
if (current?.status === "running") {
|
|
@@ -93,11 +105,38 @@ export async function runCancel(argv, context) {
|
|
|
93
105
|
store.close();
|
|
94
106
|
}
|
|
95
107
|
}
|
|
96
|
-
function
|
|
108
|
+
async function assertMcpCompanionIdentity(job, expected, allowMissing = false) {
|
|
109
|
+
return assertMcpProcessIdentity(job.pid, expected, "companion", allowMissing);
|
|
110
|
+
}
|
|
111
|
+
async function assertMcpKimiIdentity(job, expected, allowMissing = false) {
|
|
112
|
+
return assertMcpProcessIdentity(job.kimi_pid, expected, "Kimi", allowMissing);
|
|
113
|
+
}
|
|
114
|
+
async function assertMcpProcessIdentity(actualPid, expected, label, allowMissing) {
|
|
115
|
+
if (expected === undefined)
|
|
116
|
+
return true;
|
|
117
|
+
if (expected === null) {
|
|
118
|
+
if (actualPid === null)
|
|
119
|
+
return false;
|
|
120
|
+
throw new RuntimeError("CANCEL_NOT_SUPPORTED", `MCP job ${actualPid} has no verified ${label} identity; refusing PID-only cancellation.`, "mcp.cancel");
|
|
121
|
+
}
|
|
122
|
+
if (actualPid !== expected.pid) {
|
|
123
|
+
throw new RuntimeError("MCP_PROCESS_IDENTITY_MISMATCH", `The recorded ${label} process identity no longer matches; no signal was sent.`, "mcp.cancel");
|
|
124
|
+
}
|
|
125
|
+
const validation = await revalidateProcessIdentities([expected]);
|
|
126
|
+
if (!validation.complete) {
|
|
127
|
+
throw new RuntimeError("MCP_PROCESS_IDENTITY_UNAVAILABLE", `The ${label} identity probe was inconclusive; no further signal was sent and the job was not changed.`, "mcp.cancel");
|
|
128
|
+
}
|
|
129
|
+
const matches = validation.identities.length > 0;
|
|
130
|
+
if (!matches && !allowMissing) {
|
|
131
|
+
throw new RuntimeError("MCP_PROCESS_IDENTITY_MISMATCH", `The recorded ${label} process identity no longer matches; no signal was sent.`, "mcp.cancel");
|
|
132
|
+
}
|
|
133
|
+
return matches;
|
|
134
|
+
}
|
|
135
|
+
function signalJobProcesses(job, signal, include = { companion: true, kimi: true }) {
|
|
97
136
|
const pids = new Set();
|
|
98
|
-
if (job.pid !== null)
|
|
137
|
+
if (include.companion && job.pid !== null)
|
|
99
138
|
pids.add(job.pid);
|
|
100
|
-
if (job.kimi_pid !== null)
|
|
139
|
+
if (include.kimi && job.kimi_pid !== null)
|
|
101
140
|
pids.add(job.kimi_pid);
|
|
102
141
|
for (const pid of pids) {
|
|
103
142
|
try {
|
package/dist/commands/pursue.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { allocateJobId } from "../job-id.js";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { createCliCancellationHandlers } from "../cli-cancellation.js";
|
|
4
4
|
import { runCliPromptWithBudget } from "../cli-client.js";
|
|
@@ -122,7 +122,7 @@ export async function runPursue(argv, context) {
|
|
|
122
122
|
env: context.env,
|
|
123
123
|
});
|
|
124
124
|
const prompt = buildGoalPrompt(objective, parsed.turns);
|
|
125
|
-
const jobId =
|
|
125
|
+
const jobId = allocateJobId(context);
|
|
126
126
|
const logPath = path.join(paths.logsDir, `pursue-${jobId}.jsonl`);
|
|
127
127
|
const job = store.createJob({
|
|
128
128
|
job_id: jobId,
|
|
@@ -226,6 +226,7 @@ async function executePursueJob(jobId, prompt, objective, budgetMs, context) {
|
|
|
226
226
|
model: job.model ?? undefined,
|
|
227
227
|
thinkingEffort: job.thinking_effort ?? undefined,
|
|
228
228
|
logPath: job.stream_log_path,
|
|
229
|
+
onSpawn: (pid) => { store.updateRunningJob(job.job_id, { kimi_pid: pid }); },
|
|
229
230
|
signal: handlers.signal,
|
|
230
231
|
}, budgetMs, "pursue.prompt");
|
|
231
232
|
store.updateRunningJob(job.job_id, observedExecutionFields(result));
|
package/dist/commands/rescue.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { companionHelp } from "../help.js";
|
|
2
|
-
import {
|
|
2
|
+
import { allocateJobId } from "../job-id.js";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { createCliCancellationHandlers } from "../cli-cancellation.js";
|
|
5
5
|
import { runCliPromptWithBudget } from "../cli-client.js";
|
|
@@ -61,7 +61,7 @@ export async function runRescue(argv, context) {
|
|
|
61
61
|
resumeSessionId: sessionResolution.kimiSessionId ?? undefined,
|
|
62
62
|
resumeSource: sessionResolution.sourceJob,
|
|
63
63
|
});
|
|
64
|
-
const jobId =
|
|
64
|
+
const jobId = allocateJobId(context);
|
|
65
65
|
const logPath = path.join(paths.logsDir, `rescue-${jobId}.jsonl`);
|
|
66
66
|
const job = store.createJob({
|
|
67
67
|
job_id: jobId,
|
|
@@ -192,6 +192,7 @@ export async function executeRescueJob(jobId, prompt, context, options) {
|
|
|
192
192
|
thinkingEffort: job.thinking_effort ?? undefined,
|
|
193
193
|
resumeSessionId: job.kimi_session_id ?? undefined,
|
|
194
194
|
logPath: job.stream_log_path,
|
|
195
|
+
onSpawn: (pid) => { store.updateRunningJob(job.job_id, { kimi_pid: pid }); },
|
|
195
196
|
signal: handlers.signal,
|
|
196
197
|
}, job.budget_ms ?? KIMI_RESCUE_PROMPT_TIMEOUT_MS, "rescue.prompt");
|
|
197
198
|
store.updateRunningJob(job.job_id, observedExecutionFields(result));
|
|
@@ -166,10 +166,7 @@ async function executeReviewGate(payload, assistantMessage, context) {
|
|
|
166
166
|
try {
|
|
167
167
|
const activeStore = store;
|
|
168
168
|
// runCliPromptWithBudget ties the 8 s timeout to an AbortController
|
|
169
|
-
// that kills the kimi child on expiry
|
|
170
|
-
// Claude Code's Stop hook, so a runaway kimi after timeout would
|
|
171
|
-
// hold model tokens with no way for /kimi:cancel to reach it
|
|
172
|
-
// (the SQLite row's kimi_pid is null). See reports/17 and 18.
|
|
169
|
+
// that kills the kimi child on expiry and records its PID at spawn.
|
|
173
170
|
const result = await runCliPromptWithBudget({
|
|
174
171
|
cwd: payload.cwd,
|
|
175
172
|
env: context.env,
|
|
@@ -182,6 +179,7 @@ async function executeReviewGate(payload, assistantMessage, context) {
|
|
|
182
179
|
// Requested via env; provider/model may ignore it. The 8s budget still applies.
|
|
183
180
|
thinkingEffort,
|
|
184
181
|
logPath,
|
|
182
|
+
onSpawn: (pid) => { activeStore.updateRunningJob(job.job_id, { kimi_pid: pid }); },
|
|
185
183
|
}, KIMI_REVIEW_GATE_TIMEOUT_MS, "review_gate.runtime");
|
|
186
184
|
activeStore.updateRunningJob(job.job_id, observedExecutionFields(result));
|
|
187
185
|
assertCliResultSuccess(result, "review_gate.runtime");
|
package/dist/commands/review.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { allocateJobId } from "../job-id.js";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import process from "node:process";
|
|
4
4
|
import { createCliCancellationHandlers } from "../cli-cancellation.js";
|
|
@@ -38,7 +38,7 @@ export async function runReview(argv, context, commandType) {
|
|
|
38
38
|
env: context.env,
|
|
39
39
|
});
|
|
40
40
|
const store = new JobStore(paths);
|
|
41
|
-
const jobId =
|
|
41
|
+
const jobId = allocateJobId(context);
|
|
42
42
|
// Leave kimi_session_id NULL until kimi announces an actual ID; never invent
|
|
43
43
|
// one that no on-disk session matches.
|
|
44
44
|
const reviewSessionId = null;
|
|
@@ -75,6 +75,7 @@ export async function runReview(argv, context, commandType) {
|
|
|
75
75
|
const reviewConfig = getManagedCommandConfig(commandType);
|
|
76
76
|
const cancel = reviewConfig.cancellation;
|
|
77
77
|
const handlers = createCliCancellationHandlers();
|
|
78
|
+
store.updateRunningJob(job.job_id, { phase: "turn-running" });
|
|
78
79
|
try {
|
|
79
80
|
const result = await runCliPromptWithBudget({
|
|
80
81
|
cwd: context.cwd,
|
|
@@ -87,6 +88,7 @@ export async function runReview(argv, context, commandType) {
|
|
|
87
88
|
model: parsed.model,
|
|
88
89
|
thinkingEffort: parsed.thinkingEffort,
|
|
89
90
|
logPath,
|
|
91
|
+
onSpawn: (pid) => { store.updateRunningJob(job.job_id, { kimi_pid: pid }); },
|
|
90
92
|
signal: handlers.signal,
|
|
91
93
|
}, job.budget_ms ?? KIMI_REVIEW_PROMPT_TIMEOUT_MS, `${commandType}.prompt`);
|
|
92
94
|
store.updateRunningJob(job.job_id, observedExecutionFields(result));
|
package/dist/commands/swarm.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { allocateJobId } from "../job-id.js";
|
|
2
2
|
import { mkdir, readdir, stat, writeFile } from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { createCliCancellationHandlers } from "../cli-cancellation.js";
|
|
@@ -180,7 +180,7 @@ async function runReadSwarm(parsed, objective, context) {
|
|
|
180
180
|
env: context.env,
|
|
181
181
|
});
|
|
182
182
|
const prompt = buildSwarmPrompt(objective, parsed.cap);
|
|
183
|
-
const jobId =
|
|
183
|
+
const jobId = allocateJobId(context);
|
|
184
184
|
const logPath = path.join(paths.logsDir, `swarm-${jobId}.jsonl`);
|
|
185
185
|
const job = store.createJob({
|
|
186
186
|
job_id: jobId,
|
|
@@ -257,7 +257,7 @@ async function runWriteSwarm(parsed, objective, context) {
|
|
|
257
257
|
context.stderr.write("[kimi-plugin-cc] /kimi:swarm --write bases its worktree on HEAD — your uncommitted changes are NOT visible to the swarm. Commit or stash them first if the swarm needs them.\n");
|
|
258
258
|
}
|
|
259
259
|
const prompt = buildSwarmWritePrompt(objective, parsed.cap);
|
|
260
|
-
const jobId =
|
|
260
|
+
const jobId = allocateJobId(context);
|
|
261
261
|
const logPath = path.join(paths.logsDir, `swarm-write-${jobId}.jsonl`);
|
|
262
262
|
// Namespace worktrees per repo so the cross-repo sweep below only ever
|
|
263
263
|
// touches THIS repo's subtree (the worktreesDir is a single global dir shared
|
|
@@ -493,6 +493,7 @@ writeMode) {
|
|
|
493
493
|
model: job.model ?? undefined,
|
|
494
494
|
thinkingEffort: job.thinking_effort ?? undefined,
|
|
495
495
|
logPath: job.stream_log_path,
|
|
496
|
+
onSpawn: (pid) => { store.updateRunningJob(job.job_id, { kimi_pid: pid }); },
|
|
496
497
|
signal: handlers.signal,
|
|
497
498
|
}, budgetMs, "swarm.prompt");
|
|
498
499
|
store.updateRunningJob(job.job_id, observedExecutionFields(result));
|
package/dist/companion.js
CHANGED
|
@@ -1,20 +1,17 @@
|
|
|
1
1
|
import { companionHelp } from "./help.js";
|
|
2
|
-
import { runAsk } from "./commands/ask.js";
|
|
3
2
|
import { runModelInventory } from "./commands/models.js";
|
|
4
3
|
import { runCancel } from "./commands/cancel.js";
|
|
5
|
-
import { notImplementedCompanionCommand } from "./commands/not-implemented.js";
|
|
6
4
|
import { runReplay } from "./commands/replay.js";
|
|
7
5
|
import { runRepairSessions } from "./commands/repair-sessions.js";
|
|
8
6
|
import { runResult } from "./commands/result.js";
|
|
9
|
-
import { runReview } from "./commands/review.js";
|
|
10
|
-
import { runRescue } from "./commands/rescue.js";
|
|
11
|
-
import { runPursue } from "./commands/pursue.js";
|
|
12
|
-
import { runSwarm } from "./commands/swarm.js";
|
|
13
7
|
import { renderSetupResult, runSetup } from "./commands/setup.js";
|
|
14
8
|
import { runStatus } from "./commands/status.js";
|
|
15
9
|
import { runWorker } from "./commands/worker.js";
|
|
16
10
|
import { formatError, RuntimeError } from "./errors.js";
|
|
17
11
|
import { resolveKimiHome } from "./kimi-home.js";
|
|
12
|
+
import { runModelCommand } from "./model-command.js";
|
|
13
|
+
import { runMcpChild } from "./mcp/supervised-child.js";
|
|
14
|
+
import { runMcpServer } from "./mcp/server.js";
|
|
18
15
|
async function main(argv) {
|
|
19
16
|
const help = companionHelp(argv);
|
|
20
17
|
if (help !== undefined) {
|
|
@@ -27,11 +24,16 @@ async function main(argv) {
|
|
|
27
24
|
cwd,
|
|
28
25
|
env: {
|
|
29
26
|
...process.env,
|
|
30
|
-
KIMI_CODE_HOME: resolveKimiHome(process.env, cwd),
|
|
27
|
+
KIMI_CODE_HOME: command === "mcp" ? process.env.KIMI_CODE_HOME : resolveKimiHome(process.env, cwd),
|
|
31
28
|
},
|
|
32
29
|
stdout: process.stdout,
|
|
33
30
|
stderr: process.stderr,
|
|
34
31
|
};
|
|
32
|
+
const modelResult = await runModelCommand(argv, context);
|
|
33
|
+
if (modelResult !== undefined) {
|
|
34
|
+
context.stdout.write(modelResult);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
35
37
|
switch (command) {
|
|
36
38
|
case "setup": {
|
|
37
39
|
if (rest.includes("--models")) {
|
|
@@ -57,41 +59,12 @@ async function main(argv) {
|
|
|
57
59
|
}
|
|
58
60
|
return;
|
|
59
61
|
}
|
|
60
|
-
case "
|
|
61
|
-
|
|
62
|
-
context.stdout.write(`${result}\n`);
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
case "task": {
|
|
66
|
-
const [taskType, ...taskArgs] = rest;
|
|
67
|
-
if (taskType === "rescue") {
|
|
68
|
-
const result = await runRescue(taskArgs, context);
|
|
69
|
-
context.stdout.write(result);
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
if (taskType === "challenge") {
|
|
73
|
-
const result = await runReview(taskArgs, context, "challenge");
|
|
74
|
-
context.stdout.write(`${result}\n`);
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
if (taskType === "pursue") {
|
|
78
|
-
const result = await runPursue(taskArgs, context);
|
|
79
|
-
context.stdout.write(result);
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
if (taskType === "swarm") {
|
|
83
|
-
const result = await runSwarm(taskArgs, context);
|
|
84
|
-
context.stdout.write(result);
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
notImplementedCompanionCommand(taskType ? `task ${taskType}` : "task");
|
|
62
|
+
case "mcp":
|
|
63
|
+
await runMcpServer(context);
|
|
88
64
|
return;
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
const result = await runAsk(rest, context);
|
|
92
|
-
context.stdout.write(`${result}\n`);
|
|
65
|
+
case "mcp-child":
|
|
66
|
+
context.stdout.write(await runMcpChild(rest, context));
|
|
93
67
|
return;
|
|
94
|
-
}
|
|
95
68
|
case "status":
|
|
96
69
|
context.stdout.write(await runStatus(rest, context));
|
|
97
70
|
return;
|
package/dist/help.js
CHANGED
|
@@ -35,6 +35,8 @@ export function companionHelp(argv) {
|
|
|
35
35
|
case "replay": return usage(command, "<job-id>", REPLAY_SUPPORTED_FLAGS, "Replay a completed job's persisted stream log.");
|
|
36
36
|
case "repair-sessions": return usage(command, "[options]", REPAIR_SESSIONS_SUPPORTED_FLAGS, "Preview session metadata repair; --apply writes, --all selects all host-owned repositories.");
|
|
37
37
|
case "worker": return "Usage: companion.sh worker <ask|rescue> <job-id>. Internal detached-worker entry point; launched by ask/rescue, not manually.";
|
|
38
|
+
case "mcp": return "Usage: companion.sh mcp. Run the Kimi MCP stdio server; normally launched by Claude Code or Codex plugin configuration.";
|
|
39
|
+
case "mcp-child": return "Usage: companion.sh mcp-child <job-uuid> <model-operation> [arguments]. Internal supervised MCP child; launched by the MCP server, not manually.";
|
|
38
40
|
default: return undefined;
|
|
39
41
|
}
|
|
40
42
|
}
|
package/dist/job-id.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { RuntimeError } from "./errors.js";
|
|
3
|
+
const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
4
|
+
export function allocateJobId(context) {
|
|
5
|
+
const fixed = context.mcpJobId;
|
|
6
|
+
if (fixed === undefined)
|
|
7
|
+
return randomUUID();
|
|
8
|
+
if (!UUID_PATTERN.test(fixed)) {
|
|
9
|
+
throw new RuntimeError("MCP_INVALID_JOB_ID", "The supervised job ID must be a valid UUID.", "mcp.child");
|
|
10
|
+
}
|
|
11
|
+
return fixed;
|
|
12
|
+
}
|