kimi-plugin-cc 2.0.7 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +50 -0
  4. package/README.md +22 -6
  5. package/agents/kimi-ask.md +3 -0
  6. package/agents/kimi-challenge.md +3 -1
  7. package/agents/kimi-rescue.md +3 -0
  8. package/agents/kimi-review.md +3 -1
  9. package/commands/ask.md +6 -2
  10. package/commands/challenge.md +6 -2
  11. package/commands/rescue.md +6 -2
  12. package/commands/review.md +6 -2
  13. package/dist/cli-client.js +36 -13
  14. package/dist/commands/ask.js +16 -7
  15. package/dist/commands/cli-helpers.js +80 -0
  16. package/dist/commands/pursue.js +4 -2
  17. package/dist/commands/repair-sessions.js +2 -1
  18. package/dist/commands/replay.js +3 -2
  19. package/dist/commands/rescue.js +19 -9
  20. package/dist/commands/result.js +7 -1
  21. package/dist/commands/review-gate.js +2 -1
  22. package/dist/commands/review.js +8 -5
  23. package/dist/commands/setup.js +2 -1
  24. package/dist/commands/status.js +2 -2
  25. package/dist/commands/swarm.js +7 -3
  26. package/dist/companion.js +6 -0
  27. package/dist/help.js +40 -0
  28. package/dist/hooks/config-safety.js +18 -9
  29. package/dist/job-store.js +43 -6
  30. package/dist/jobs.js +7 -3
  31. package/dist/kimi-engine.js +1 -0
  32. package/dist/kimi-errors.js +35 -1
  33. package/dist/kimi-timeouts.js +7 -7
  34. package/dist/parsing.js +87 -13
  35. package/dist/render.js +19 -1
  36. package/dist/session-recovery.js +203 -0
  37. package/dist/vendor/smol-toml/date.js +50 -21
  38. package/dist/vendor/smol-toml/error.js +6 -2
  39. package/dist/vendor/smol-toml/extract.js +259 -66
  40. package/dist/vendor/smol-toml/parse.js +64 -48
  41. package/dist/vendor/smol-toml/primitive.js +107 -135
  42. package/dist/vendor/smol-toml/struct.js +106 -134
  43. package/dist/vendor/smol-toml/util.js +22 -78
  44. package/dist/version.js +1 -1
  45. package/docs/migration.md +3 -3
  46. package/docs/registry-releases.md +35 -1
  47. package/docs/safety.md +49 -2
  48. package/package.json +2 -2
  49. package/plugins/kimi-codex/.codex-plugin/plugin.json +1 -1
  50. package/plugins/kimi-codex/dist/cli-client.js +36 -13
  51. package/plugins/kimi-codex/dist/commands/ask.js +16 -7
  52. package/plugins/kimi-codex/dist/commands/cli-helpers.js +80 -0
  53. package/plugins/kimi-codex/dist/commands/pursue.js +4 -2
  54. package/plugins/kimi-codex/dist/commands/repair-sessions.js +2 -1
  55. package/plugins/kimi-codex/dist/commands/replay.js +3 -2
  56. package/plugins/kimi-codex/dist/commands/rescue.js +19 -9
  57. package/plugins/kimi-codex/dist/commands/result.js +7 -1
  58. package/plugins/kimi-codex/dist/commands/review-gate.js +2 -1
  59. package/plugins/kimi-codex/dist/commands/review.js +8 -5
  60. package/plugins/kimi-codex/dist/commands/setup.js +2 -1
  61. package/plugins/kimi-codex/dist/commands/status.js +2 -2
  62. package/plugins/kimi-codex/dist/commands/swarm.js +7 -3
  63. package/plugins/kimi-codex/dist/companion.js +6 -0
  64. package/plugins/kimi-codex/dist/help.js +40 -0
  65. package/plugins/kimi-codex/dist/hooks/config-safety.js +18 -9
  66. package/plugins/kimi-codex/dist/job-store.js +43 -6
  67. package/plugins/kimi-codex/dist/jobs.js +7 -3
  68. package/plugins/kimi-codex/dist/kimi-engine.js +1 -0
  69. package/plugins/kimi-codex/dist/kimi-errors.js +35 -1
  70. package/plugins/kimi-codex/dist/kimi-timeouts.js +7 -7
  71. package/plugins/kimi-codex/dist/parsing.js +87 -13
  72. package/plugins/kimi-codex/dist/render.js +19 -1
  73. package/plugins/kimi-codex/dist/session-recovery.js +203 -0
  74. package/plugins/kimi-codex/dist/vendor/smol-toml/date.js +50 -21
  75. package/plugins/kimi-codex/dist/vendor/smol-toml/error.js +6 -2
  76. package/plugins/kimi-codex/dist/vendor/smol-toml/extract.js +259 -66
  77. package/plugins/kimi-codex/dist/vendor/smol-toml/parse.js +64 -48
  78. package/plugins/kimi-codex/dist/vendor/smol-toml/primitive.js +107 -135
  79. package/plugins/kimi-codex/dist/vendor/smol-toml/struct.js +106 -134
  80. package/plugins/kimi-codex/dist/vendor/smol-toml/util.js +22 -78
  81. package/plugins/kimi-codex/dist/version.js +1 -1
  82. package/plugins/kimi-codex/skills/kimi-ask/SKILL.md +3 -1
  83. package/plugins/kimi-codex/skills/kimi-challenge/SKILL.md +2 -1
  84. package/plugins/kimi-codex/skills/kimi-rescue/SKILL.md +3 -1
  85. package/plugins/kimi-codex/skills/kimi-review/SKILL.md +3 -2
@@ -5,7 +5,7 @@
5
5
  },
6
6
  "metadata": {
7
7
  "description": "Kimi for Claude Code: code questions, reviews, fixes, and parallel tasks through the local Kimi Code CLI.",
8
- "version": "2.0.7"
8
+ "version": "2.2.0"
9
9
  },
10
10
  "plugins": [
11
11
  {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "kimi",
3
3
  "description": "Claude Code plugin that delegates read-only review, challenge, free-form ask, and write-capable rescue work to the local kimi-code CLI. Includes an opt-in stop-time review gate and a PreToolUse hook that enforces per-command safety.",
4
- "version": "2.0.7",
4
+ "version": "2.2.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",
package/CHANGELOG.md CHANGED
@@ -2,6 +2,56 @@
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.2.0 — 2026-10-15
6
+
7
+ - Port the vendored parser to unmodified smol-toml 1.9.1 JavaScript, closing
8
+ GHSA-r4xh-jqrq-34v2; adapt the config assignment walker and retain EOF-comment
9
+ protection with linear-scaling and shipped-mirror regressions.
10
+ - Recover native-v2 timeout session IDs only from exactly one bounded, matching
11
+ session-store candidate: created between spawn and settlement in the job's cwd,
12
+ and whose main journal opens with a user message carrying the job's exact prompt
13
+ (`prompt_digest`), so attribution is proof, not proximity. Persist
14
+ announced/recovered provenance in `kimi_session_id_source`, report recovery
15
+ refusals in error details (`session_recovery`), skip recovery when no
16
+ `system.version` marker was observed, and retain the existing lineage and raw
17
+ journal plan-taint checks on every resume.
18
+ - Expose `budget_source` in status and JSON results, and print inherited ask/rescue
19
+ budgets with their parent job at launch. Historical non-null budgets remain unknown.
20
+ - Allow 30 seconds for installed-state spawn-heavy tests; refresh install examples
21
+ and preserve the settled result's abort flag in timeout details.
22
+ - Permit deliberate plugin/CLI version-number alignment without treating the number
23
+ as compatibility evidence. CLI 2.2.0 remains the certified maximum, unchanged;
24
+ the certification table, hook policy, preflight closures and teardown remain unchanged.
25
+
26
+ ## 2.0.8 — 2026-10-14
27
+
28
+ - Certify exact Kimi Code CLI 2.2.0 (`472192c3`) for all eight native-v2 operations under the
29
+ unchanged no-plan construction: four source reviews, 15/15 tag scan (all twelve pinned files
30
+ byte-identical to 2.1.1), plan-ON/OFF controls (bypass still reproduced; refusal still
31
+ load-bearing) and the full 15-test candidate smoke. Accept only the reviewed exact 2.2.0 hook
32
+ schema. See [certification evidence](docs/upstream-2.2.0-certification.md).
33
+ - Add per-job `--budget` to ask, rescue, review, and challenge, with
34
+ defaults of 30m for ask/review/challenge and 45m for rescue, with a 24h ceiling. Detached workers
35
+ reload the persisted budget from SQLite; resumes inherit it unless overridden.
36
+ Pursue/swarm persist their effective ceilings. Backgrounding does not extend it.
37
+ - Preserve settled partial assistant output on budget timeout (and the session ID
38
+ when Kimi announced it before the deadline; native v2 announces at session end). Jobs remain failed, artifacts label partial output as NOT a final
39
+ answer, and cancellation does not capture partial output.
40
+ - Preserve clean, non-aborted exit-0 success at the timeout boundary; retain
41
+ `settled_post_abort` diagnostics for timeouts. Strip ambient workspace-trust
42
+ overrides from every child to prevent project MCP bootstrap outside tool hooks.
43
+ - Remove misleading timeout race wording, expose budget/count metrics, and give
44
+ context-aware budget/scope advice with a pointer to the failed artifact.
45
+ - Add stdout `--help`/`-h` for companion subcommands from shared flag definitions.
46
+ Review and challenge remain foreground-only.
47
+ - Regenerate both host distributions and document budget and partial-output
48
+ behavior across commands, agents, and Codex skills.
49
+ - Bump the test-only `smol-toml` dependency 1.8.0 → 1.9.1 for GHSA-r4xh-jqrq-34v2
50
+ (quadratic `parseKey`). The vendored 1.6.1 parser subset that the plugin ships shares
51
+ the bounded quadratic behaviour on the operator's own `config.toml` only; measured and
52
+ documented in `runtime/vendor/smol-toml/README.md`, with the 1.9.x port scheduled as its
53
+ own integration review.
54
+
5
55
  ## 2.0.7 — 2026-09-26
6
56
 
7
57
  - Add npm distribution as `kimi-plugin-cc`, with an explicit file allowlist and
package/README.md CHANGED
@@ -4,7 +4,11 @@
4
4
 
5
5
  Use Kimi to explain code, review changes, challenge a design, or work toward a goal — an independent second opinion from a different model family, inside the Claude Code or Codex session you're already in.
6
6
 
7
- [Latest release](https://github.com/linxule/kimi-plugin-cc/releases/latest) · [Roadmap](./ROADMAP-TO-GA.md) · [Report an issue](https://github.com/linxule/kimi-plugin-cc/issues) · [Apache-2.0 license](./LICENSE)
7
+ [![Kimi Ambassador — academia](./assets/kimi-ambassador-academia.webp)](https://www.kimi.ai/lp/kimi-ambassador)
8
+
9
+ I'm a [Kimi Ambassador](https://www.kimi.ai/lp/kimi-ambassador) (academia). Kimi (Moonshot AI) supports this project through that role.
10
+
11
+ [Latest release](https://github.com/linxule/kimi-plugin-cc/releases/latest) · [npm package](https://www.npmjs.com/package/kimi-plugin-cc) · [Roadmap](./ROADMAP-TO-GA.md) · [Report an issue](https://github.com/linxule/kimi-plugin-cc/issues) · [Apache-2.0 license](./LICENSE)
8
12
 
9
13
  ![Claude Code running /kimi:review on this repository, with Kimi's findings verified claim by claim in the same session](./assets/claude-code-kimi-review.jpg)
10
14
 
@@ -36,7 +40,7 @@ You need:
36
40
  - macOS or Linux
37
41
  - Claude Code or Codex with plugin support
38
42
  - Node.js 22.5 or newer
39
- - Kimi Code CLI 2.1.1, the recommended version for plugin 2.0.6
43
+ - Kimi Code CLI 2.2.0 (2.1.1 also certified), the recommended version for plugin 2.2.0
40
44
 
41
45
  Windows is not currently supported. Check your versions in a terminal:
42
46
 
@@ -104,7 +108,7 @@ needed to use the package. Kimi Code CLI and Node are still prerequisites.
104
108
  For an explicitly versioned local Claude Code plugin:
105
109
 
106
110
  ```sh
107
- npm install --prefix ~/.local/share/kimi-plugin-cc kimi-plugin-cc@2.0.7
111
+ npm install --prefix ~/.local/share/kimi-plugin-cc kimi-plugin-cc@2.2.0
108
112
  claude --plugin-dir ~/.local/share/kimi-plugin-cc/node_modules/kimi-plugin-cc
109
113
  ```
110
114
 
@@ -245,7 +249,7 @@ Contributors need Bun to run `bun run check`. The detailed engineering documents
245
249
  - macOS 或 Linux
246
250
  - 支持插件的 Claude Code 或 Codex
247
251
  - Node.js 22.5 或更高版本
248
- - Kimi Code CLI 2.1.1,这是插件 2.0.6 的推荐版本
252
+ - Kimi Code CLI 2.2.0(2.1.1 亦已认证),这是插件 2.2.0 的推荐版本
249
253
 
250
254
  目前不支持 Windows。在终端检查版本:
251
255
 
@@ -302,6 +306,10 @@ codex plugin add kimi@kimi-marketplace
302
306
 
303
307
  如果同时使用 Claude Code 和 Codex,请分别运行 setup。两个应用各有自己的钩子,共用一份 Kimi 配置。
304
308
 
309
+ ## npm 分发(可选)
310
+
311
+ 完整插件也已发布到 [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 插件市场命令。
312
+
305
313
  ## 选择任务
306
314
 
307
315
  | 任务 | Claude Code 命令 | Codex 技能 | 文件操作范围 |
@@ -425,7 +433,7 @@ Il vous faut :
425
433
  - macOS ou Linux
426
434
  - une version de Claude Code ou de Codex prenant en charge les plugins
427
435
  - Node.js 22.5 ou une version ultérieure
428
- - Kimi Code CLI 2.1.1, la version recommandée pour le plugin 2.0.6
436
+ - Kimi Code CLI 2.2.0 (2.1.1 également certifiée), la version recommandée pour le plugin 2.2.0
429
437
 
430
438
  Windows n'est pas pris en charge actuellement. Vérifiez les versions dans un terminal :
431
439
 
@@ -482,6 +490,10 @@ Utilise $kimi-ask pour expliquer le fonctionnement du point d'entrée principal
482
490
 
483
491
  Si vous utilisez Claude Code et Codex, lancez setup dans chaque application. Chacune possède son propre hook dans la configuration Kimi commune.
484
492
 
493
+ ## Distribution npm (facultative)
494
+
495
+ 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.
496
+
485
497
  ## Choisir une tâche
486
498
 
487
499
  | Tâche | Commande Claude Code | Skill Codex | Accès aux fichiers |
@@ -605,7 +617,7 @@ Les contributeurs ont besoin de Bun pour exécuter `bun run check`. La documenta
605
617
  - macOS または Linux
606
618
  - プラグインに対応した Claude Code または Codex
607
619
  - Node.js 22.5 以降
608
- - Kimi Code CLI 2.1.1(プラグイン 2.0.6 の推奨バージョン)
620
+ - Kimi Code CLI 2.2.0(2.1.1 も認証済み。プラグイン 2.2.0 の推奨バージョン)
609
621
 
610
622
  現在、Windows には対応していません。ターミナルでバージョンを確認します。
611
623
 
@@ -662,6 +674,10 @@ $kimi-ask を使って、このプロジェクトのメインのエントリー
662
674
 
663
675
  Claude Code と Codex の両方を使う場合は、それぞれで setup を実行します。共通の Kimi 設定内に、各アプリ専用のフックが作られます。
664
676
 
677
+ ## npm 配布(任意)
678
+
679
+ 完全なプラグインは [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 マーケットプレイスを使います。
680
+
665
681
  ## タスクを選ぶ
666
682
 
667
683
  | タスク | Claude Code コマンド | Codex スキル | ファイル操作の範囲 |
@@ -39,6 +39,8 @@ For subscription auth, guide native Kimi `/login`; for API keys or other provide
39
39
 
40
40
  When invoked:
41
41
 
42
+ - preserve `--budget <dur>` (default 30m; `30m`, `1h`, `90s`, or bare minutes, maximum 24h). Backgrounding does not extend this budget. A timed-out job remains failed and its artifact holds partial assistant output labeled NOT a final answer; read it with `result <jobId>` or `result <jobId> --json`. A session ID is kept if Kimi announced it or, on a native-v2 budget timeout, recovered from the session store when exactly one session matches this spawn — created between spawn and settlement in the same directory AND whose journal opens with this job's exact prompt; `result --json` identifies the source in `kimi_session_id_source`, and `error.details.session_recovery` says why no ID was recovered (`none`, `journal_missing:<n>`, `digest_mismatch:<n>`, `ambiguous:<n>`, `enumeration_capped`, `engine_unobserved`). Cancellation does not capture partial output. A resume without `--budget` inherits the parent job's budget; an explicit budget overrides it. Inherited budgets are printed to stderr at launch; status and `result --json` expose `budget_source` (`explicit`, `inherited`, or `default`; old non-null budgets report `unknown`).
43
+
42
44
  - decide whether the task belongs to free-form ask rather than diff review (kimi-review / kimi-challenge) or implementation (kimi-rescue)
43
45
  - preserve the user's question text and flags exactly — rephrasing a free-form prompt loses the user's framing; pass `--background` / `--wait` when the user supplies them
44
46
  - call the shared companion runtime with one Bash invocation after any model-discovery step: `${CLAUDE_PLUGIN_ROOT}/scripts/companion.sh ask <args>`
@@ -47,6 +49,7 @@ When invoked:
47
49
  - choose foreground for focused, bounded questions that are likely to complete quickly
48
50
  - choose background (`--background`) for broad, open-ended, or long-running questions where the user does not need an immediate answer
49
51
  - when ask starts in background, return the `job_id` so the main thread can use `/kimi:status`, `/kimi:result`, or `/kimi:cancel`
52
+ - a background job's completion is observable ONLY through `/kimi:status` / `/kimi:result <jobId>` (or `companion.sh status` / `result`). No notification reaches the caller when it finishes — the orchestrator re-checks on its own schedule (every few minutes for a long job) and never concludes a job is still running from the absence of a notification. If `result` reports a budget timeout, the artifact holds the partial output.
50
53
  - `/kimi:result <jobId> --json` returns a structured envelope with metadata plus the artifact body.
51
54
  - as an alternative to `--background`, Claude Code's Bash-tool `run_in_background: true` is a valid fire-and-forget mechanism when the user wants to detach without tracking via the job store
52
55
 
@@ -39,11 +39,13 @@ For subscription auth, guide native Kimi `/login`; for API keys or other provide
39
39
 
40
40
  When invoked:
41
41
 
42
+ - preserve `--budget <dur>` (default 30m; `30m`, `1h`, `90s`, or bare minutes, maximum 24h). Backgrounding does not extend this budget. A timed-out job remains failed and its artifact holds partial assistant output labeled NOT a final answer; read it with `result <jobId>` or `result <jobId> --json`. A session ID is kept if Kimi announced it or, on a native-v2 budget timeout, recovered from the session store when exactly one session matches this spawn — created between spawn and settlement in the same directory AND whose journal opens with this job's exact prompt; `result --json` identifies the source in `kimi_session_id_source`, and `error.details.session_recovery` says why no ID was recovered (`none`, `journal_missing:<n>`, `digest_mismatch:<n>`, `ambiguous:<n>`, `enumeration_capped`, `engine_unobserved`). Cancellation does not capture partial output.
43
+
42
44
  - decide whether the task belongs to adversarial challenge review rather than ordinary defect review (see kimi-review)
43
45
  - preserve any focus text the user supplies after the flags — the user's framing is what steers the challenge
44
46
  - call the shared companion runtime with one Bash invocation after any model-discovery step: `${CLAUDE_PLUGIN_ROOT}/scripts/companion.sh task challenge <args>`
45
47
  - if the companion reports `CHALLENGE_HOOK_NOT_INSTALLED`, surface the refusal and tell the user to run `/kimi:setup`; do not reach for `KIMI_PLUGIN_CC_SKIP_HOOK_CHECK`
46
- - the companion accepts a **strict allowlist** of flags: `--base <ref>`, `-m`/`--model <name>`. Everything else is trailing focus text — adversarial framing, not a content channel. Reasoning behavior follows the selected model and Kimi configuration; the parser hard-rejects `--thinking`/`--no-thinking`
48
+ - the companion accepts a **strict allowlist** of flags: `--base <ref>`, `-m`/`--model <name>`, `--budget <dur>`. Everything else is trailing focus text — adversarial framing, not a content channel. Reasoning behavior follows the selected model and Kimi configuration; the parser hard-rejects `--thinking`/`--no-thinking`
47
49
  - do not invent flags (`--file`, `--context`, `--path`, etc.). The runtime hard-fails with `INVALID_ARGS` on unknown flag-shaped tokens. If you need to attach file content or extended context, switch to `kimi-ask` or paste a brief summary into the focus text — challenge's payload is the git diff, not arbitrary file content
48
50
  - do not pass `--background` or `--wait` to the companion — the runtime rejects both with `INVALID_FLAGS` for review and challenge
49
51
  - if the user wants fire-and-forget behavior on a broad challenge, detach the Bash call itself with `run_in_background: true` instead of reaching for a companion flag; after launching, tell the user to check `/kimi:status` for progress
@@ -44,12 +44,15 @@ For subscription auth, guide native Kimi `/login`; for API keys or other provide
44
44
 
45
45
  When invoked:
46
46
 
47
+ - preserve `--budget <dur>` (default 45m; `30m`, `1h`, `90s`, or bare minutes, maximum 24h). Backgrounding does not extend this budget. A timed-out job remains failed and its artifact holds partial assistant output labeled NOT a final answer; read it with `result <jobId>` or `result <jobId> --json`. A session ID is kept if Kimi announced it or, on a native-v2 budget timeout, recovered from the session store when exactly one session matches this spawn — created between spawn and settlement in the same directory AND whose journal opens with this job's exact prompt; `result --json` identifies the source in `kimi_session_id_source`, and `error.details.session_recovery` says why no ID was recovered (`none`, `journal_missing:<n>`, `digest_mismatch:<n>`, `ambiguous:<n>`, `enumeration_capped`, `engine_unobserved`). Cancellation does not capture partial output. A resume without `--budget` inherits the parent job's budget; an explicit budget overrides it. Inherited budgets are printed to stderr at launch; status and `result --json` expose `budget_source` (`explicit`, `inherited`, or `default`; old non-null budgets report `unknown`).
48
+
47
49
  - decide whether the task belongs to rescue rather than read-only review or ask
48
50
  - preserve the user's task text and explicit constraints with minimal reframing
49
51
  - choose foreground only for tightly bounded rescue work that is likely to finish quickly
50
52
  - choose background for open-ended debugging, multi-step implementation, or anything likely to run long
51
53
  - call the shared companion runtime instead of inspecting the repository or orchestrating the task yourself
52
54
  - when rescue starts, return the `job_id` so the main thread can use `/kimi:status`, `/kimi:result`, or `/kimi:cancel`
55
+ - a background job's completion is observable ONLY through `/kimi:status` / `/kimi:result <jobId>` (or `companion.sh status` / `result`). No notification reaches the caller when it finishes — the orchestrator re-checks on its own schedule (every few minutes for a long job) and never concludes a job is still running from the absence of a notification. If `result` reports a budget timeout, the artifact holds the partial output.
53
56
  - `/kimi:result <jobId> --json` returns a structured envelope with metadata plus the artifact body.
54
57
 
55
58
  When rescue completes:
@@ -39,11 +39,13 @@ For subscription auth, guide native Kimi `/login`; for API keys or other provide
39
39
 
40
40
  When invoked:
41
41
 
42
+ - preserve `--budget <dur>` (default 30m; `30m`, `1h`, `90s`, or bare minutes, maximum 24h). Backgrounding does not extend this budget. A timed-out job remains failed and its artifact holds partial assistant output labeled NOT a final answer; read it with `result <jobId>` or `result <jobId> --json`. A session ID is kept if Kimi announced it or, on a native-v2 budget timeout, recovered from the session store when exactly one session matches this spawn — created between spawn and settlement in the same directory AND whose journal opens with this job's exact prompt; `result --json` identifies the source in `kimi_session_id_source`, and `error.details.session_recovery` says why no ID was recovered (`none`, `journal_missing:<n>`, `digest_mismatch:<n>`, `ambiguous:<n>`, `enumeration_capped`, `engine_unobserved`). Cancellation does not capture partial output.
43
+
42
44
  - decide whether the task belongs to a diff review rather than a free-form ask or a write-capable rescue
43
45
  - preserve the user's scope hints (`--base <ref>`, focus text) with minimal reframing
44
46
  - call the shared companion runtime with one Bash invocation after any model-discovery step: `${CLAUDE_PLUGIN_ROOT}/scripts/companion.sh review <args>`
45
47
  - if the companion reports `REVIEW_HOOK_NOT_INSTALLED`, surface the refusal and tell the user to run `/kimi:setup`; do not reach for `KIMI_PLUGIN_CC_SKIP_HOOK_CHECK`
46
- - the companion accepts a **strict allowlist** of flags: `--base <ref>`, `-m`/`--model <name>`. Everything else is trailing focus text — a short scope hint, not a content channel. Reasoning behavior follows the selected model and Kimi configuration; the parser hard-rejects `--thinking`/`--no-thinking`
48
+ - the companion accepts a **strict allowlist** of flags: `--base <ref>`, `-m`/`--model <name>`, `--budget <dur>`. Everything else is trailing focus text — a short scope hint, not a content channel. Reasoning behavior follows the selected model and Kimi configuration; the parser hard-rejects `--thinking`/`--no-thinking`
47
49
  - do not invent flags (`--file`, `--context`, `--path`, etc.). The runtime hard-fails with `INVALID_ARGS` on unknown flag-shaped tokens. If you need to attach file content or extended context, switch to `kimi-ask` or paste a brief summary into the focus text — review's payload is the git diff, not arbitrary file content
48
50
  - do not pass `--background` or `--wait` to the companion — the runtime rejects both with `INVALID_FLAGS` for review and challenge
49
51
  - if the user wants fire-and-forget behavior on a long review (multi-file diff, unclear scope), detach the Bash call itself with `run_in_background: true` instead of reaching for a companion flag; after launching, tell the user to check `/kimi:status` for progress
package/commands/ask.md CHANGED
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  description: Ask Kimi a read-only question in free-form prose mode.
3
- argument-hint: "[--background] [--wait] [-r | --resume <id>] [--fresh] [-m <model>] <prompt>"
3
+ argument-hint: "[--budget <30m|1h|90s>] [--background] [--wait] [-r | --resume <id>] [--fresh] [-m <model>] <prompt>"
4
4
  disable-model-invocation: true
5
5
  ---
6
6
 
@@ -10,6 +10,8 @@ Run the companion with any user-supplied flags appended after `ask`:
10
10
 
11
11
  Supported flags:
12
12
 
13
+ - `--budget <dur>` — wall-clock ceiling (default 30m; `30m`, `1h`, `90s`, or bare minutes; maximum 24h)
14
+
13
15
  - `--background` — spawn as a background job and return `{job_id}` immediately
14
16
  - `--wait` — combined with `--background`, block until terminal state and return the rendered answer
15
17
  - `-r` — resume the latest ask session for the current repo; any trailing text becomes the next prompt
@@ -17,7 +19,9 @@ Supported flags:
17
19
  - `--fresh` — force a new ask session id instead of reusing a prior session
18
20
  - `-m`, `--model <name>`
19
21
 
20
- Reasoning behavior follows the selected model and Kimi configuration. The plugin does not force a thinking mode. The 15-minute budget leaves room for extended reasoning.
22
+ Reasoning behavior follows the selected model and Kimi configuration. The plugin does not force a thinking mode. The default budget is 30m; use `--budget` to change it. Backgrounding does not extend the runtime budget.
23
+
24
+ A budget timeout leaves the job `failed` and saves any partial assistant prose in its artifact, labeled **NOT a final answer**. Read it with `result <jobId>` or `result <jobId> --json`; tool-only runs explicitly report no assistant prose. A session ID is kept if Kimi announced it or, on a native-v2 budget timeout, recovered from the session store when exactly one session matches this spawn — created between spawn and settlement in the same directory AND whose journal opens with this job's exact prompt; `result --json` identifies the source in `kimi_session_id_source`, and `error.details.session_recovery` says why no ID was recovered (`none`, `journal_missing:<n>`, `digest_mismatch:<n>`, `ambiguous:<n>`, `enumeration_capped`, `engine_unobserved`). Cancellation does not capture partial output. A resume without `--budget` inherits the parent job's budget; an explicit budget overrides it. Inherited budgets are printed to stderr at launch; status and `result --json` expose `budget_source` (`explicit`, `inherited`, or `default`; old non-null budgets report `unknown`). Use `--help` or `-h` among the leading flags for usage; once prompt text starts, `-h` is part of the prompt. Other failures (nonzero exit, cancellation) do not capture partial output.
21
25
 
22
26
  Return the companion stdout verbatim.
23
27
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  description: Run a read-only Kimi challenge review that challenges assumptions and surfaces safer alternatives.
3
- argument-hint: "[--base <ref>] [-m <model>] [extra prose]"
3
+ argument-hint: "[--budget <30m|1h|90s>] [--base <ref>] [-m <model>] [extra prose]"
4
4
  disable-model-invocation: true
5
5
  ---
6
6
 
@@ -10,11 +10,15 @@ Run the companion with any user-supplied flags appended after `task challenge`:
10
10
 
11
11
  Supported flags:
12
12
 
13
+ - `--budget <dur>` — wall-clock ceiling (default 30m; `30m`, `1h`, `90s`, or bare minutes; maximum 24h)
14
+
13
15
  - `--base <ref>` — challenge against a branch/commit diff instead of the working tree
14
16
  - `-m`, `--model <name>`
15
17
  - trailing text — adversarial focus or framing to steer the challenge
16
18
 
17
- Reasoning behavior follows the selected model and Kimi configuration. The plugin does not force a thinking mode. Budget is 30 min so adversarial review has time to dig. Challenge runs foreground-synchronously; it does not support `--background` or `--wait`.
19
+ Reasoning behavior follows the selected model and Kimi configuration. The plugin does not force a thinking mode. The default budget is 30m; use `--budget` to change it. Challenge runs foreground-synchronously; it does not support `--background` or `--wait`.
20
+
21
+ A budget timeout leaves the job `failed` and saves any partial assistant prose in its artifact, labeled **NOT a final answer**. Read it with `result <jobId>` or `result <jobId> --json`; tool-only runs explicitly report no assistant prose. A session ID is kept if Kimi announced it or, on a native-v2 budget timeout, recovered from the session store when exactly one session matches this spawn — created between spawn and settlement in the same directory AND whose journal opens with this job's exact prompt; `result --json` identifies the source in `kimi_session_id_source`, and `error.details.session_recovery` says why no ID was recovered (`none`, `journal_missing:<n>`, `digest_mismatch:<n>`, `ambiguous:<n>`, `enumeration_capped`, `engine_unobserved`). Cancellation does not capture partial output. Use `--help` or `-h` among the leading flags for usage; once prompt text starts, `-h` is part of the prompt. Other failures (nonzero exit, cancellation) do not capture partial output.
18
22
 
19
23
  Return the companion stdout verbatim.
20
24
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  description: Run a write-capable Kimi rescue against the current repository with optional background execution and resume behavior.
3
- argument-hint: "[--background] [--wait] [-r | --resume <id>] [--fresh] [-m <model>] <prompt>"
3
+ argument-hint: "[--budget <30m|1h|90s>] [--background] [--wait] [-r | --resume <id>] [--fresh] [-m <model>] <prompt>"
4
4
  disable-model-invocation: true
5
5
  ---
6
6
 
@@ -10,6 +10,8 @@ Run the companion with any user-supplied flags appended after `task rescue`:
10
10
 
11
11
  Supported flags:
12
12
 
13
+ - `--budget <dur>` — wall-clock ceiling (default 45m; `30m`, `1h`, `90s`, or bare minutes; maximum 24h)
14
+
13
15
  - `--background` — spawn as a detached background job and return `{job_id}` immediately
14
16
  - `--wait` — combined with `--background`, block until terminal state and return the rendered result
15
17
  - `-r` — resume the latest rescue session for the current repo; any trailing text becomes the next prompt
@@ -17,7 +19,9 @@ Supported flags:
17
19
  - `--fresh` — force a new rescue session id instead of reusing a prior session
18
20
  - `-m`, `--model <name>`
19
21
 
20
- Reasoning behavior follows the selected model and Kimi configuration. The plugin does not force a thinking mode. Budget is 30 min for multi-step apply/test/verify loops; use `--background` to return control while the task runs. Backgrounding does not extend the runtime budget.
22
+ Reasoning behavior follows the selected model and Kimi configuration. The plugin does not force a thinking mode. The default budget is 45m; use `--budget` to change it for multi-step apply/test/verify loops; use `--background` to return control while the task runs. Backgrounding does not extend the runtime budget.
23
+
24
+ A budget timeout leaves the job `failed` and saves any partial assistant prose in its artifact, labeled **NOT a final answer**. Read it with `result <jobId>` or `result <jobId> --json`; tool-only runs explicitly report no assistant prose. A session ID is kept if Kimi announced it or, on a native-v2 budget timeout, recovered from the session store when exactly one session matches this spawn — created between spawn and settlement in the same directory AND whose journal opens with this job's exact prompt; `result --json` identifies the source in `kimi_session_id_source`, and `error.details.session_recovery` says why no ID was recovered (`none`, `journal_missing:<n>`, `digest_mismatch:<n>`, `ambiguous:<n>`, `enumeration_capped`, `engine_unobserved`). Cancellation does not capture partial output. A resume without `--budget` inherits the parent job's budget; an explicit budget overrides it. Inherited budgets are printed to stderr at launch; status and `result --json` expose `budget_source` (`explicit`, `inherited`, or `default`; old non-null budgets report `unknown`). Use `--help` or `-h` among the leading flags for usage; once prompt text starts, `-h` is part of the prompt. Other failures (nonzero exit, cancellation) do not capture partial output.
21
25
 
22
26
  Return the companion stdout verbatim.
23
27
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  description: Run a read-only Kimi review over the current working tree changes or a base ref diff.
3
- argument-hint: "[--base <ref>] [-m <model>] [extra prose]"
3
+ argument-hint: "[--budget <30m|1h|90s>] [--base <ref>] [-m <model>] [extra prose]"
4
4
  disable-model-invocation: true
5
5
  ---
6
6
 
@@ -10,11 +10,15 @@ Run the companion with any user-supplied flags appended after `review`:
10
10
 
11
11
  Supported flags:
12
12
 
13
+ - `--budget <dur>` — wall-clock ceiling (default 30m; `30m`, `1h`, `90s`, or bare minutes; maximum 24h)
14
+
13
15
  - `--base <ref>` — review against a branch/commit diff instead of the working tree
14
16
  - `-m`, `--model <name>`
15
17
  - trailing text — optional focus hint for the review
16
18
 
17
- Reasoning behavior follows the selected model and Kimi configuration. The plugin does not force a thinking mode. Budget is 30 min so a real workspace-wide analysis has headroom. Review runs foreground-synchronously; it does not support `--background` or `--wait`.
19
+ Reasoning behavior follows the selected model and Kimi configuration. The plugin does not force a thinking mode. The default budget is 30m; use `--budget` to change it. Review runs foreground-synchronously; it does not support `--background` or `--wait`.
20
+
21
+ A budget timeout leaves the job `failed` and saves any partial assistant prose in its artifact, labeled **NOT a final answer**. Read it with `result <jobId>` or `result <jobId> --json`; tool-only runs explicitly report no assistant prose. A session ID is kept if Kimi announced it or, on a native-v2 budget timeout, recovered from the session store when exactly one session matches this spawn — created between spawn and settlement in the same directory AND whose journal opens with this job's exact prompt; `result --json` identifies the source in `kimi_session_id_source`, and `error.details.session_recovery` says why no ID was recovered (`none`, `journal_missing:<n>`, `digest_mismatch:<n>`, `ambiguous:<n>`, `enumeration_capped`, `engine_unobserved`). Cancellation does not capture partial output. Use `--help` or `-h` among the leading flags for usage; once prompt text starts, `-h` is part of the prompt. Other failures (nonzero exit, cancellation) do not capture partial output.
18
22
 
19
23
  Return the companion stdout verbatim.
20
24
 
@@ -84,7 +84,9 @@ export async function runCliPrompt(opts) {
84
84
  });
85
85
  }
86
86
  let child;
87
+ let spawnedAt;
87
88
  try {
89
+ spawnedAt = Date.now();
88
90
  child = spawn(opts.command, args, {
89
91
  cwd: opts.cwd,
90
92
  env,
@@ -329,7 +331,7 @@ export async function runCliPrompt(opts) {
329
331
  return;
330
332
  }
331
333
  if (kind === "resolve")
332
- resolve(payload);
334
+ resolve({ ...payload, settledAt: Date.now() });
333
335
  else
334
336
  reject(payload);
335
337
  });
@@ -580,6 +582,8 @@ export async function runCliPrompt(opts) {
580
582
  // The result itself is in memory; diagnostics are best-effort.
581
583
  await raceWithTimeout(logChain, LOG_DRAIN_TIMEOUT_MS);
582
584
  settle("resolve", {
585
+ spawnedAt,
586
+ settledAt: 0, // Filled at settlement, after the teardown barrier.
583
587
  sessionId,
584
588
  goalSummary: announcedGoalSummary,
585
589
  systemVersion: announcedSystemVersion,
@@ -656,6 +660,27 @@ export function requireSessionId(result, context) {
656
660
  },
657
661
  });
658
662
  }
663
+ /** Settled timeout data; stream records are deliberately non-serializable.
664
+ * Without a partial result (teardown rejection), aborted means abort was issued,
665
+ * not that the process was observed aborting. */
666
+ export class ResponseTimeoutError extends RuntimeError {
667
+ constructor(budgetMs, stage, commandLabel, partialResult, cause) {
668
+ super("RESPONSE_TIMEOUT", `${stage} timed out after ${budgetMs}ms.`, stage, {
669
+ ...(cause instanceof Error ? { cause } : {}),
670
+ details: {
671
+ budget_ms: budgetMs,
672
+ command_label: commandLabel ?? null,
673
+ record_count: partialResult?.records.length ?? 0,
674
+ partial_output_chars: partialResult?.records.reduce((count, record) => count + (record.role === "assistant" && typeof record.content === "string" ? record.content.length : 0), 0) ?? 0,
675
+ aborted: partialResult?.aborted ?? true,
676
+ ...(partialResult ? { settled_post_abort: true } : {}),
677
+ },
678
+ });
679
+ this.name = "ResponseTimeoutError";
680
+ // Never serialize stream records into job error details or error envelopes.
681
+ Object.defineProperty(this, "partialResult", { value: partialResult, enumerable: false });
682
+ }
683
+ }
659
684
  /**
660
685
  * Run `kimi -p` under a budget that also tells the subprocess to die
661
686
  * when the budget expires.
@@ -682,7 +707,8 @@ export function requireSessionId(result, context) {
682
707
  * subprocess/tree teardown, then reject with
683
708
  * `RuntimeError("RESPONSE_TIMEOUT", …)` matching the legacy
684
709
  * `withTimeout` code shape.
685
- * - Result success path is unchanged from `runCliPrompt`.
710
+ * - A clean non-aborted exit-0 result wins same-tick budget expiry.
711
+ * Otherwise a settled result on timeout is carried as partial output.
686
712
  */
687
713
  export async function runCliPromptWithBudget(opts, budgetMs, stage) {
688
714
  const controller = new AbortController();
@@ -698,12 +724,7 @@ export async function runCliPromptWithBudget(opts, budgetMs, stage) {
698
724
  controller.abort();
699
725
  }, budgetMs);
700
726
  timer.unref();
701
- const timeoutError = (raceDetected = false) => new RuntimeError("RESPONSE_TIMEOUT", `${stage} timed out after ${budgetMs}ms${raceDetected ? " (race detected post-result)" : ""}.`, stage, {
702
- details: {
703
- budget_ms: budgetMs,
704
- command_label: opts.commandLabel ?? null,
705
- },
706
- });
727
+ const timeoutError = (partialResult, cause) => new ResponseTimeoutError(budgetMs, stage, opts.commandLabel, partialResult, cause);
707
728
  try {
708
729
  let result;
709
730
  try {
@@ -714,13 +735,13 @@ export async function runCliPromptWithBudget(opts, budgetMs, stage) {
714
735
  }
715
736
  catch (error) {
716
737
  if (timeoutFired)
717
- throw timeoutError();
738
+ throw timeoutError(undefined, error);
718
739
  throw error;
719
740
  }
720
- if (timeoutFired) {
721
- // The timer and close can become runnable in the same event-loop turn.
722
- // Teardown has still completed because runCliPrompt settled first.
723
- throw timeoutError(true);
741
+ if (timeoutFired && (result.aborted || result.exitCode !== 0)) {
742
+ // A clean, non-aborted result won the settle/timeout boundary and is success.
743
+ // Budget aborts normally resolve with records, after owned teardown settles.
744
+ throw timeoutError(result);
724
745
  }
725
746
  return result;
726
747
  }
@@ -770,6 +791,8 @@ function buildArgs(opts) {
770
791
  }
771
792
  function buildEnv(opts) {
772
793
  const env = { ...opts.env };
794
+ // 2.2.0 audit: ambient trust starts project MCP stdio servers outside PreToolUse.
795
+ delete env.KIMI_CODE_TRUST_WORKSPACE;
773
796
  // kimi-code 0.33.0 inverted its CLI engine default: an unflagged `kimi -p`
774
797
  // now selects native agent-core-v2, while this upstream-documented flag pins
775
798
  // the legacy-v1 path. Native v2 still has a final-allow plan-file path that
@@ -1,3 +1,4 @@
1
+ import { companionHelp } from "../help.js";
1
2
  import { randomUUID } from "node:crypto";
2
3
  import path from "node:path";
3
4
  import { createCliCancellationHandlers } from "../cli-cancellation.js";
@@ -16,7 +17,7 @@ import { readArtifact, renderManagedJobOutput, writeArtifact } from "../render.j
16
17
  import { classifyManagedCommandFailure } from "../kimi-errors.js";
17
18
  import { startBackgroundJob } from "../background-spawn.js";
18
19
  import { hookRefusalDetails, hookRefusalRetryProtocol, maybeWarnHookMissing, verifyHookInstalled, } from "../hooks/install.js";
19
- import { assertCliResultSuccess, reassembleProseFromRecords, warnIfSessionIdMissing } from "./cli-helpers.js";
20
+ import { warnInheritedBudget, captureTimeoutPartialOutput, assertCliResultSuccess, reassembleProseFromRecords, warnIfSessionIdMissing } from "./cli-helpers.js";
20
21
  import { buildKimiSessionTitle, syncKimiSessionTitle } from "../session-title.js";
21
22
  // v1.0 cutover note (PR 2):
22
23
  //
@@ -46,6 +47,8 @@ const ASK_PROMPT_PREFIX = [
46
47
  ].join("\n");
47
48
  export async function runAsk(argv, context) {
48
49
  const parsed = parseAskArgs(argv);
50
+ if (parsed.help)
51
+ return companionHelp(["ask", ...argv]);
49
52
  if (context.env.KIMI_PLUGIN_CC_SKIP_HOOK_CHECK !== "1") {
50
53
  await requireAskHookInstalled(context);
51
54
  }
@@ -76,10 +79,14 @@ export async function runAsk(argv, context) {
76
79
  model: parsed.model ?? null,
77
80
  thinking: parsed.thinking ?? null,
78
81
  background: parsed.background,
82
+ budget_ms: parsed.budgetMs ?? (sessionResolution.sourceJob?.operation_kind === "ask" ? sessionResolution.sourceJob.budget_ms : null) ?? null,
83
+ budget_source: parsed.budgetMs !== undefined ? "explicit"
84
+ : sessionResolution.sourceJob?.operation_kind === "ask" && sessionResolution.sourceJob.budget_ms !== null ? "inherited" : "default",
79
85
  pid: parsed.background ? null : process.pid,
80
86
  kimi_pid: null,
81
87
  status: "running",
82
88
  kimi_session_id: sessionResolution.kimiSessionId,
89
+ kimi_session_id_source: sessionResolution.sourceJob?.kimi_session_id_source ?? null,
83
90
  ...persistedExecutionPlanFields(executionPlan),
84
91
  agent_profile: ASK_AGENT_PROFILE_PLACEHOLDER,
85
92
  prompt_digest: digestPrompt(askPrompt),
@@ -101,6 +108,7 @@ export async function runAsk(argv, context) {
101
108
  await markJobFailed(store, paths, job, classified, askConfig.cancellation.failedSummary, { phase: "failed" });
102
109
  throw classified;
103
110
  }
111
+ warnInheritedBudget(job, context.stderr);
104
112
  if (parsed.background) {
105
113
  return startBackgroundJob(job, askPrompt, context, paths, {
106
114
  workerKind: "ask",
@@ -169,7 +177,7 @@ export async function executeAskJob(jobId, prompt, context, options) {
169
177
  resumeSessionId: job.kimi_session_id ?? undefined,
170
178
  logPath: job.stream_log_path,
171
179
  signal: handlers.signal,
172
- }, KIMI_ASK_PROMPT_TIMEOUT_MS, "ask.prompt");
180
+ }, job.budget_ms ?? KIMI_ASK_PROMPT_TIMEOUT_MS, "ask.prompt");
173
181
  store.updateRunningJob(job.job_id, observedExecutionFields(result));
174
182
  if (handlers.cancelling) {
175
183
  throw new RuntimeError(cancel.errorCodes.cancelled, cancel.cancelMessages.afterPrompt, "ask.runtime");
@@ -180,12 +188,11 @@ export async function executeAskJob(jobId, prompt, context, options) {
180
188
  // sessions it should match the input, but capture anyway in case
181
189
  // kimi-code mints a new id on each turn.
182
190
  if (result.sessionId !== undefined &&
183
- result.sessionId.length > 0 &&
184
- result.sessionId !== job.kimi_session_id) {
191
+ result.sessionId.length > 0) {
185
192
  // length>0 guard: empty-string capture would poison the row. The
186
193
  // warning helper's null-ish check would also fire but only after
187
194
  // the bad write. (Kimi alpha.4 challenge finding #3.)
188
- store.updateRunningJob(job.job_id, { kimi_session_id: result.sessionId });
195
+ store.updateRunningJob(job.job_id, { kimi_session_id_source: "announced", kimi_session_id: result.sessionId });
189
196
  }
190
197
  await syncKimiSessionTitle({
191
198
  promptText: prompt,
@@ -215,6 +222,9 @@ export async function executeAskJob(jobId, prompt, context, options) {
215
222
  }) ?? job);
216
223
  }
217
224
  catch (error) {
225
+ const classified = error instanceof RuntimeError && error.stage === "ask.hook-check" ? error
226
+ : classifyManagedCommandFailure(error, "ask", job.job_id, { background: job.background, budgetMs: job.budget_ms ?? KIMI_ASK_PROMPT_TIMEOUT_MS });
227
+ const partialOutput = await captureTimeoutPartialOutput(classified, store, job, context.env, handlers?.signal);
218
228
  if (handlers?.cancelling) {
219
229
  const cancelledError = new RuntimeError(cancel.errorCodes.cancelled, cancel.cancelMessages.default, "ask.runtime", error instanceof Error ? { cause: error } : undefined);
220
230
  return await markJobCancelled(store, paths, job, cancel.cancelledSummary, cancelledError, { phase: "cancelled" });
@@ -227,8 +237,7 @@ export async function executeAskJob(jobId, prompt, context, options) {
227
237
  await markJobFailed(store, paths, job, error, cancel.failedSummary, { phase: "failed" });
228
238
  throw error;
229
239
  }
230
- const classified = classifyManagedCommandFailure(error, "ask", job.job_id);
231
- await markJobFailed(store, paths, job, classified, cancel.failedSummary, { phase: "failed" });
240
+ await markJobFailed(store, paths, job, classified, cancel.failedSummary, { phase: "failed", partialOutput });
232
241
  throw classified;
233
242
  }
234
243
  finally {