kimi-plugin-cc 2.0.8 → 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 (62) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +21 -0
  4. package/README.md +5 -5
  5. package/agents/kimi-ask.md +1 -1
  6. package/agents/kimi-challenge.md +1 -1
  7. package/agents/kimi-rescue.md +1 -1
  8. package/agents/kimi-review.md +1 -1
  9. package/commands/ask.md +1 -1
  10. package/commands/challenge.md +1 -1
  11. package/commands/rescue.md +1 -1
  12. package/commands/review.md +1 -1
  13. package/dist/cli-client.js +9 -3
  14. package/dist/commands/ask.js +10 -6
  15. package/dist/commands/cli-helpers.js +53 -4
  16. package/dist/commands/pursue.js +2 -1
  17. package/dist/commands/rescue.js +12 -9
  18. package/dist/commands/result.js +3 -1
  19. package/dist/commands/review-gate.js +2 -1
  20. package/dist/commands/review.js +4 -3
  21. package/dist/commands/status.js +2 -2
  22. package/dist/commands/swarm.js +3 -1
  23. package/dist/hooks/config-safety.js +15 -7
  24. package/dist/job-store.js +36 -4
  25. package/dist/session-recovery.js +203 -0
  26. package/dist/vendor/smol-toml/date.js +50 -21
  27. package/dist/vendor/smol-toml/error.js +6 -2
  28. package/dist/vendor/smol-toml/extract.js +259 -66
  29. package/dist/vendor/smol-toml/parse.js +64 -48
  30. package/dist/vendor/smol-toml/primitive.js +107 -135
  31. package/dist/vendor/smol-toml/struct.js +106 -134
  32. package/dist/vendor/smol-toml/util.js +22 -78
  33. package/dist/version.js +1 -1
  34. package/docs/registry-releases.md +22 -1
  35. package/docs/safety.md +49 -2
  36. package/package.json +1 -1
  37. package/plugins/kimi-codex/.codex-plugin/plugin.json +1 -1
  38. package/plugins/kimi-codex/dist/cli-client.js +9 -3
  39. package/plugins/kimi-codex/dist/commands/ask.js +10 -6
  40. package/plugins/kimi-codex/dist/commands/cli-helpers.js +53 -4
  41. package/plugins/kimi-codex/dist/commands/pursue.js +2 -1
  42. package/plugins/kimi-codex/dist/commands/rescue.js +12 -9
  43. package/plugins/kimi-codex/dist/commands/result.js +3 -1
  44. package/plugins/kimi-codex/dist/commands/review-gate.js +2 -1
  45. package/plugins/kimi-codex/dist/commands/review.js +4 -3
  46. package/plugins/kimi-codex/dist/commands/status.js +2 -2
  47. package/plugins/kimi-codex/dist/commands/swarm.js +3 -1
  48. package/plugins/kimi-codex/dist/hooks/config-safety.js +15 -7
  49. package/plugins/kimi-codex/dist/job-store.js +36 -4
  50. package/plugins/kimi-codex/dist/session-recovery.js +203 -0
  51. package/plugins/kimi-codex/dist/vendor/smol-toml/date.js +50 -21
  52. package/plugins/kimi-codex/dist/vendor/smol-toml/error.js +6 -2
  53. package/plugins/kimi-codex/dist/vendor/smol-toml/extract.js +259 -66
  54. package/plugins/kimi-codex/dist/vendor/smol-toml/parse.js +64 -48
  55. package/plugins/kimi-codex/dist/vendor/smol-toml/primitive.js +107 -135
  56. package/plugins/kimi-codex/dist/vendor/smol-toml/struct.js +106 -134
  57. package/plugins/kimi-codex/dist/vendor/smol-toml/util.js +22 -78
  58. package/plugins/kimi-codex/dist/version.js +1 -1
  59. package/plugins/kimi-codex/skills/kimi-ask/SKILL.md +1 -1
  60. package/plugins/kimi-codex/skills/kimi-challenge/SKILL.md +1 -1
  61. package/plugins/kimi-codex/skills/kimi-rescue/SKILL.md +1 -1
  62. package/plugins/kimi-codex/skills/kimi-review/SKILL.md +1 -1
@@ -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.8"
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.8",
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,27 @@
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
+
5
26
  ## 2.0.8 — 2026-10-14
6
27
 
7
28
  - Certify exact Kimi Code CLI 2.2.0 (`472192c3`) for all eight native-v2 operations under the
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.1.1, the recommended version for plugin 2.0.7
43
+ - Kimi Code CLI 2.2.0 (2.1.1 also certified), the recommended version for plugin 2.2.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.0.7
111
+ npm install --prefix ~/.local/share/kimi-plugin-cc kimi-plugin-cc@2.2.0
112
112
  claude --plugin-dir ~/.local/share/kimi-plugin-cc/node_modules/kimi-plugin-cc
113
113
  ```
114
114
 
@@ -249,7 +249,7 @@ Contributors need Bun to run `bun run check`. The detailed engineering documents
249
249
  - macOS 或 Linux
250
250
  - 支持插件的 Claude Code 或 Codex
251
251
  - Node.js 22.5 或更高版本
252
- - Kimi Code CLI 2.1.1,这是插件 2.0.7 的推荐版本
252
+ - Kimi Code CLI 2.2.0(2.1.1 亦已认证),这是插件 2.2.0 的推荐版本
253
253
 
254
254
  目前不支持 Windows。在终端检查版本:
255
255
 
@@ -433,7 +433,7 @@ Il vous faut :
433
433
  - macOS ou Linux
434
434
  - une version de Claude Code ou de Codex prenant en charge les plugins
435
435
  - Node.js 22.5 ou une version ultérieure
436
- - Kimi Code CLI 2.1.1, la version recommandée pour le plugin 2.0.7
436
+ - Kimi Code CLI 2.2.0 (2.1.1 également certifiée), la version recommandée pour le plugin 2.2.0
437
437
 
438
438
  Windows n'est pas pris en charge actuellement. Vérifiez les versions dans un terminal :
439
439
 
@@ -617,7 +617,7 @@ Les contributeurs ont besoin de Bun pour exécuter `bun run check`. La documenta
617
617
  - macOS または Linux
618
618
  - プラグインに対応した Claude Code または Codex
619
619
  - Node.js 22.5 以降
620
- - Kimi Code CLI 2.1.1(プラグイン 2.0.7 の推奨バージョン)
620
+ - Kimi Code CLI 2.2.0(2.1.1 も認証済み。プラグイン 2.2.0 の推奨バージョン)
621
621
 
622
622
  現在、Windows には対応していません。ターミナルでバージョンを確認します。
623
623
 
@@ -39,7 +39,7 @@ 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`. Cancellation does not capture partial output. A resume without `--budget` inherits the parent job's budget; an explicit budget overrides it.
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
43
 
44
44
  - decide whether the task belongs to free-form ask rather than diff review (kimi-review / kimi-challenge) or implementation (kimi-rescue)
45
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
@@ -39,7 +39,7 @@ 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`. Cancellation does not capture partial output.
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
43
 
44
44
  - decide whether the task belongs to adversarial challenge review rather than ordinary defect review (see kimi-review)
45
45
  - preserve any focus text the user supplies after the flags — the user's framing is what steers the challenge
@@ -44,7 +44,7 @@ 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`. Cancellation does not capture partial output. A resume without `--budget` inherits the parent job's budget; an explicit budget overrides it.
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
48
 
49
49
  - decide whether the task belongs to rescue rather than read-only review or ask
50
50
  - preserve the user's task text and explicit constraints with minimal reframing
@@ -39,7 +39,7 @@ 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`. Cancellation does not capture partial output.
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
43
 
44
44
  - decide whether the task belongs to a diff review rather than a free-form ask or a write-capable rescue
45
45
  - preserve the user's scope hints (`--base <ref>`, focus text) with minimal reframing
package/commands/ask.md CHANGED
@@ -21,7 +21,7 @@ Supported flags:
21
21
 
22
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
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 only if Kimi announced it before the deadline; native v2 announces it at session end, so a timed-out v2 turn is normally not resumable from the plugin. Cancellation does not capture partial output. A resume without `--budget` inherits the parent job's budget; an explicit budget overrides it. 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.
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.
25
25
 
26
26
  Return the companion stdout verbatim.
27
27
 
@@ -18,7 +18,7 @@ Supported flags:
18
18
 
19
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
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 only if Kimi announced it before the deadline; native v2 announces it at session end, so a timed-out v2 turn is normally not resumable from the plugin. 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.
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.
22
22
 
23
23
  Return the companion stdout verbatim.
24
24
 
@@ -21,7 +21,7 @@ Supported flags:
21
21
 
22
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
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 only if Kimi announced it before the deadline; native v2 announces it at session end, so a timed-out v2 turn is normally not resumable from the plugin. Cancellation does not capture partial output. A resume without `--budget` inherits the parent job's budget; an explicit budget overrides it. 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.
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.
25
25
 
26
26
  Return the companion stdout verbatim.
27
27
 
@@ -18,7 +18,7 @@ Supported flags:
18
18
 
19
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
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 only if Kimi announced it before the deadline; native v2 announces it at session end, so a timed-out v2 turn is normally not resumable from the plugin. 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.
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.
22
22
 
23
23
  Return the companion stdout verbatim.
24
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,7 +660,9 @@ export function requireSessionId(result, context) {
656
660
  },
657
661
  });
658
662
  }
659
- /** Settled timeout data; stream records are deliberately non-serializable. */
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. */
660
666
  export class ResponseTimeoutError extends RuntimeError {
661
667
  constructor(budgetMs, stage, commandLabel, partialResult, cause) {
662
668
  super("RESPONSE_TIMEOUT", `${stage} timed out after ${budgetMs}ms.`, stage, {
@@ -666,7 +672,7 @@ export class ResponseTimeoutError extends RuntimeError {
666
672
  command_label: commandLabel ?? null,
667
673
  record_count: partialResult?.records.length ?? 0,
668
674
  partial_output_chars: partialResult?.records.reduce((count, record) => count + (record.role === "assistant" && typeof record.content === "string" ? record.content.length : 0), 0) ?? 0,
669
- aborted: true,
675
+ aborted: partialResult?.aborted ?? true,
670
676
  ...(partialResult ? { settled_post_abort: true } : {}),
671
677
  },
672
678
  });
@@ -17,7 +17,7 @@ import { readArtifact, renderManagedJobOutput, writeArtifact } from "../render.j
17
17
  import { classifyManagedCommandFailure } from "../kimi-errors.js";
18
18
  import { startBackgroundJob } from "../background-spawn.js";
19
19
  import { hookRefusalDetails, hookRefusalRetryProtocol, maybeWarnHookMissing, verifyHookInstalled, } from "../hooks/install.js";
20
- import { captureTimeoutPartialOutput, assertCliResultSuccess, reassembleProseFromRecords, warnIfSessionIdMissing } from "./cli-helpers.js";
20
+ import { warnInheritedBudget, captureTimeoutPartialOutput, assertCliResultSuccess, reassembleProseFromRecords, warnIfSessionIdMissing } from "./cli-helpers.js";
21
21
  import { buildKimiSessionTitle, syncKimiSessionTitle } from "../session-title.js";
22
22
  // v1.0 cutover note (PR 2):
23
23
  //
@@ -80,10 +80,13 @@ export async function runAsk(argv, context) {
80
80
  thinking: parsed.thinking ?? null,
81
81
  background: parsed.background,
82
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",
83
85
  pid: parsed.background ? null : process.pid,
84
86
  kimi_pid: null,
85
87
  status: "running",
86
88
  kimi_session_id: sessionResolution.kimiSessionId,
89
+ kimi_session_id_source: sessionResolution.sourceJob?.kimi_session_id_source ?? null,
87
90
  ...persistedExecutionPlanFields(executionPlan),
88
91
  agent_profile: ASK_AGENT_PROFILE_PLACEHOLDER,
89
92
  prompt_digest: digestPrompt(askPrompt),
@@ -105,6 +108,7 @@ export async function runAsk(argv, context) {
105
108
  await markJobFailed(store, paths, job, classified, askConfig.cancellation.failedSummary, { phase: "failed" });
106
109
  throw classified;
107
110
  }
111
+ warnInheritedBudget(job, context.stderr);
108
112
  if (parsed.background) {
109
113
  return startBackgroundJob(job, askPrompt, context, paths, {
110
114
  workerKind: "ask",
@@ -184,12 +188,11 @@ export async function executeAskJob(jobId, prompt, context, options) {
184
188
  // sessions it should match the input, but capture anyway in case
185
189
  // kimi-code mints a new id on each turn.
186
190
  if (result.sessionId !== undefined &&
187
- result.sessionId.length > 0 &&
188
- result.sessionId !== job.kimi_session_id) {
191
+ result.sessionId.length > 0) {
189
192
  // length>0 guard: empty-string capture would poison the row. The
190
193
  // warning helper's null-ish check would also fire but only after
191
194
  // the bad write. (Kimi alpha.4 challenge finding #3.)
192
- 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 });
193
196
  }
194
197
  await syncKimiSessionTitle({
195
198
  promptText: prompt,
@@ -219,6 +222,9 @@ export async function executeAskJob(jobId, prompt, context, options) {
219
222
  }) ?? job);
220
223
  }
221
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);
222
228
  if (handlers?.cancelling) {
223
229
  const cancelledError = new RuntimeError(cancel.errorCodes.cancelled, cancel.cancelMessages.default, "ask.runtime", error instanceof Error ? { cause: error } : undefined);
224
230
  return await markJobCancelled(store, paths, job, cancel.cancelledSummary, cancelledError, { phase: "cancelled" });
@@ -231,8 +237,6 @@ export async function executeAskJob(jobId, prompt, context, options) {
231
237
  await markJobFailed(store, paths, job, error, cancel.failedSummary, { phase: "failed" });
232
238
  throw error;
233
239
  }
234
- const classified = classifyManagedCommandFailure(error, "ask", job.job_id, { background: job.background, budgetMs: job.budget_ms ?? KIMI_ASK_PROMPT_TIMEOUT_MS });
235
- const partialOutput = captureTimeoutPartialOutput(classified, store, job);
236
240
  await markJobFailed(store, paths, job, classified, cancel.failedSummary, { phase: "failed", partialOutput });
237
241
  throw classified;
238
242
  }
@@ -1,6 +1,9 @@
1
1
  import { RuntimeError } from "../errors.js";
2
2
  import { ResponseTimeoutError } from "../cli-client.js";
3
3
  import { observedExecutionFields } from "../kimi-engine.js";
4
+ import { resolveKimiHome } from "../kimi-home.js";
5
+ import { recoverSessionIdFromStore } from "../session-recovery.js";
6
+ import { formatBudgetDuration } from "../render.js";
4
7
  /**
5
8
  * Reassemble prose final text from cli-client records.
6
9
  *
@@ -112,12 +115,42 @@ export function assertCliResultSuccess(result, stage) {
112
115
  });
113
116
  }
114
117
  }
115
- /** Timeout-only capture, called after cancellation has taken precedence. No title sync on failure. */
116
- export function captureTimeoutPartialOutput(classified, store, job) {
118
+ /** Timeout-only capture. Cancellation takes precedence, including while the
119
+ * asynchronous store scan is in flight. No title sync on failure. */
120
+ export async function captureTimeoutPartialOutput(classified, store, job, env, cancellationSignal) {
121
+ if (cancellationSignal?.aborted)
122
+ return undefined;
117
123
  const timeout = classified.cause;
118
124
  if (!(timeout instanceof ResponseTimeoutError) || !timeout.partialResult)
119
125
  return undefined;
120
126
  const result = timeout.partialResult;
127
+ let sessionId = result.sessionId;
128
+ let source = sessionId ? "announced" : null;
129
+ const details = classified instanceof RuntimeError ? classified.details : timeout.details;
130
+ details.kimi_session_id_source = job.kimi_session_id_source;
131
+ // The persisted plan is plugin-owned. Recovery never runs on cancellation,
132
+ // v1, successful turns, teardown rejection, or an already-known resume id.
133
+ if (!sessionId && !job.kimi_session_id && job.intended_engine === "native-v2") {
134
+ // Without the system.version marker the row has no proven engine, and
135
+ // resume would refuse the id as lineage-unknown; do not advertise one.
136
+ if (result.observedEngine === null) {
137
+ details.session_recovery = "engine_unobserved";
138
+ }
139
+ else {
140
+ const recovered = await recoverSessionIdFromStore({
141
+ kimiHome: resolveKimiHome(env, job.cwd), cwd: job.cwd,
142
+ spawnedAt: result.spawnedAt, settledAt: result.settledAt, promptDigest: job.prompt_digest,
143
+ store, jobId: job.job_id,
144
+ });
145
+ sessionId = recovered.sessionId;
146
+ if (sessionId)
147
+ source = "recovered_session_dir";
148
+ else
149
+ details.session_recovery = recovered.reason;
150
+ }
151
+ }
152
+ if (cancellationSignal?.aborted)
153
+ return undefined;
121
154
  // Best-effort metadata only. The row is still `running`, and a session id
122
155
  // written here can collide with `jobs_running_session_idx` when a resume of
123
156
  // the same session was started while this job ran. A store error must never
@@ -125,11 +158,22 @@ export function captureTimeoutPartialOutput(classified, store, job) {
125
158
  // than the metadata, so swallow and fall through.
126
159
  try {
127
160
  store.updateRunningJob(job.job_id, observedExecutionFields(result));
128
- if (result.sessionId !== undefined && result.sessionId.length > 0 && result.sessionId !== job.kimi_session_id) {
129
- store.updateRunningJob(job.job_id, { kimi_session_id: result.sessionId });
161
+ if (sessionId) {
162
+ const updated = source === "recovered_session_dir"
163
+ ? store.claimRecoveredSessionId(job.job_id, sessionId)
164
+ : store.updateRunningJob(job.job_id, { kimi_session_id: sessionId, kimi_session_id_source: source });
165
+ if (updated?.kimi_session_id === sessionId && updated.kimi_session_id_source === source) {
166
+ details.kimi_session_id = sessionId;
167
+ details.kimi_session_id_source = source;
168
+ }
169
+ else
170
+ details[source === "recovered_session_dir" ? "session_recovery" : "session_id_persistence"] = "persistence_failed";
130
171
  }
131
172
  }
132
173
  catch {
174
+ // `session_recovery` describes only the recovery branch; an announced id or
175
+ // the provenance update failing is a different fact.
176
+ details[source === "recovered_session_dir" ? "session_recovery" : "session_id_persistence"] = "persistence_failed";
133
177
  // Diagnostics stay in the stream log; the failure transition proceeds.
134
178
  }
135
179
  return {
@@ -141,3 +185,8 @@ export function captureTimeoutPartialOutput(classified, store, job) {
141
185
  budgetMs: timeout.details.budget_ms,
142
186
  };
143
187
  }
188
+ export function warnInheritedBudget(job, stderr) {
189
+ if (job.budget_source === "inherited" && job.budget_ms !== null) {
190
+ stderr.write(`[${job.command_type}] budget ${formatBudgetDuration(job.budget_ms)} inherited from job ${job.resumed_from_job_id}; pass --budget to override\n`);
191
+ }
192
+ }
@@ -144,6 +144,7 @@ export async function runPursue(argv, context) {
144
144
  thinking: parsed.thinking ?? null,
145
145
  background: false,
146
146
  budget_ms: parsed.budgetMs ?? KIMI_PURSUE_DEFAULT_BUDGET_MS,
147
+ budget_source: parsed.budgetMs !== undefined ? "explicit" : "default",
147
148
  pid: process.pid,
148
149
  kimi_pid: null,
149
150
  status: "running",
@@ -250,7 +251,7 @@ async function executePursueJob(jobId, prompt, objective, budgetMs, context) {
250
251
  if (result.sessionId !== undefined &&
251
252
  result.sessionId.length > 0 &&
252
253
  result.sessionId !== job.kimi_session_id) {
253
- store.updateRunningJob(job.job_id, { kimi_session_id: result.sessionId });
254
+ store.updateRunningJob(job.job_id, { kimi_session_id_source: "announced", kimi_session_id: result.sessionId });
254
255
  }
255
256
  await syncKimiSessionTitle({
256
257
  promptText: objective,
@@ -17,7 +17,7 @@ import { parseRescueArgs } from "../parsing.js";
17
17
  import { readArtifact, renderManagedJobOutput, writeArtifact } from "../render.js";
18
18
  import { startBackgroundJob } from "../background-spawn.js";
19
19
  import { hookRefusalDetails, hookRefusalRetryProtocol, maybeWarnHookMissing, verifyHookInstalled, } from "../hooks/install.js";
20
- import { captureTimeoutPartialOutput, assertCliResultSuccess, reassembleProseFromRecords, warnIfSessionIdMissing } from "./cli-helpers.js";
20
+ import { warnInheritedBudget, captureTimeoutPartialOutput, assertCliResultSuccess, reassembleProseFromRecords, warnIfSessionIdMissing } from "./cli-helpers.js";
21
21
  import { buildKimiSessionTitle, syncKimiSessionTitle } from "../session-title.js";
22
22
  export { describeMissingResult } from "../background-spawn.js";
23
23
  // v1.0 cutover note (PR 3):
@@ -72,10 +72,13 @@ export async function runRescue(argv, context) {
72
72
  thinking: parsed.thinking ?? null,
73
73
  background: parsed.background,
74
74
  budget_ms: parsed.budgetMs ?? (sessionResolution.sourceJob?.operation_kind === "rescue" ? sessionResolution.sourceJob.budget_ms : null) ?? null,
75
+ budget_source: parsed.budgetMs !== undefined ? "explicit"
76
+ : sessionResolution.sourceJob?.operation_kind === "rescue" && sessionResolution.sourceJob.budget_ms !== null ? "inherited" : "default",
75
77
  pid: parsed.background ? null : process.pid,
76
78
  kimi_pid: null,
77
79
  status: "running",
78
80
  kimi_session_id: sessionResolution.kimiSessionId,
81
+ kimi_session_id_source: sessionResolution.sourceJob?.kimi_session_id_source ?? null,
79
82
  ...persistedExecutionPlanFields(executionPlan),
80
83
  agent_profile: RESCUE_AGENT_PROFILE_PLACEHOLDER,
81
84
  prompt_digest: digestPrompt(prompt),
@@ -97,6 +100,7 @@ export async function runRescue(argv, context) {
97
100
  await markJobFailed(store, paths, job, classified, rescueConfig.cancellation.failedSummary, { phase: "failed" });
98
101
  throw classified;
99
102
  }
103
+ warnInheritedBudget(job, context.stderr);
100
104
  if (parsed.background) {
101
105
  return startBackgroundJob(job, prompt, context, paths, {
102
106
  workerKind: "rescue",
@@ -196,11 +200,10 @@ export async function executeRescueJob(jobId, prompt, context, options) {
196
200
  }
197
201
  assertCliResultSuccess(result, "rescue.runtime");
198
202
  if (result.sessionId !== undefined &&
199
- result.sessionId.length > 0 &&
200
- result.sessionId !== job.kimi_session_id) {
203
+ result.sessionId.length > 0) {
201
204
  // length>0 guard: empty-string capture would poison the row. (Kimi
202
205
  // alpha.4 challenge finding #3.)
203
- store.updateRunningJob(job.job_id, { kimi_session_id: result.sessionId });
206
+ store.updateRunningJob(job.job_id, { kimi_session_id_source: "announced", kimi_session_id: result.sessionId });
204
207
  }
205
208
  await syncKimiSessionTitle({
206
209
  promptText: prompt,
@@ -246,16 +249,16 @@ export async function executeRescueJob(jobId, prompt, context, options) {
246
249
  }) ?? job);
247
250
  }
248
251
  catch (error) {
249
- if (handlers.cancelling) {
250
- const cancelledError = new RuntimeError(cancel.errorCodes.cancelled, cancel.cancelMessages.default, "rescue.runtime", error instanceof Error ? { cause: error } : undefined);
251
- return await markJobCancelled(store, paths, job, cancel.cancelledSummary, cancelledError, { phase: "cancelled" });
252
- }
253
252
  const classified = classifyManagedCommandFailure(error, "rescue", job.job_id, {
254
253
  preserveStage: true,
255
254
  background: job.background,
256
255
  budgetMs: job.budget_ms ?? KIMI_RESCUE_PROMPT_TIMEOUT_MS,
257
256
  });
258
- const partialOutput = captureTimeoutPartialOutput(classified, store, job);
257
+ const partialOutput = await captureTimeoutPartialOutput(classified, store, job, context.env, handlers.signal);
258
+ if (handlers.cancelling) {
259
+ const cancelledError = new RuntimeError(cancel.errorCodes.cancelled, cancel.cancelMessages.default, "rescue.runtime", error instanceof Error ? { cause: error } : undefined);
260
+ return await markJobCancelled(store, paths, job, cancel.cancelledSummary, cancelledError, { phase: "cancelled" });
261
+ }
259
262
  return await markJobFailed(store, paths, job, classified, cancel.failedSummary, { phase: "failed", partialOutput });
260
263
  }
261
264
  finally {
@@ -2,7 +2,7 @@ import { RuntimeError } from "../errors.js";
2
2
  import { resolveRepoIdentity } from "../git.js";
3
3
  import { sweepStaleJobs } from "../jobs.js";
4
4
  import { reconcileHistoricalJobProvenance } from "../kimi-engine.js";
5
- import { withJobStore } from "../job-store.js";
5
+ import { renderBudgetSource, withJobStore } from "../job-store.js";
6
6
  import { ensurePluginPaths, resolvePluginPaths } from "../paths.js";
7
7
  import { parseJobLookupArgs } from "../parsing.js";
8
8
  import { readArtifact, renderTerminalJobArtifact } from "../render.js";
@@ -43,6 +43,8 @@ function renderResultEnvelope(job, artifactPath, body) {
43
43
  kind: job.command_type,
44
44
  status: job.status,
45
45
  budget_ms: job.budget_ms,
46
+ budget_source: renderBudgetSource(job),
47
+ kimi_session_id_source: job.kimi_session_id_source,
46
48
  kimi_session_id: job.kimi_session_id,
47
49
  // True when a budget timeout preserved assistant prose in the artifact.
48
50
  partial_output: job.status === "failed" && Number(job.error?.details?.partial_output_chars ?? 0) > 0,
@@ -153,6 +153,7 @@ async function executeReviewGate(payload, assistantMessage, context) {
153
153
  kimi_pid: null,
154
154
  status: "running",
155
155
  kimi_session_id: null,
156
+ budget_source: "default",
156
157
  ...persistedExecutionPlanFields(executionPlan),
157
158
  agent_profile: REVIEW_GATE_AGENT_PROFILE_PLACEHOLDER,
158
159
  prompt_digest: digestPrompt(prompt),
@@ -195,7 +196,7 @@ async function executeReviewGate(payload, assistantMessage, context) {
195
196
  // can't synthesize empty strings today, but the guard locks the
196
197
  // SQLite contract independent of upstream behavior).
197
198
  activeStore.updateRunningJob(job.job_id, {
198
- kimi_session_id: result.sessionId,
199
+ kimi_session_id_source: "announced", kimi_session_id: result.sessionId,
199
200
  });
200
201
  }
201
202
  const finalText = reassembleProseFromRecords(result.records);
@@ -82,6 +82,7 @@ export async function runReview(argv, context, commandType) {
82
82
  thinking: parsed.thinking ?? null,
83
83
  background: false,
84
84
  budget_ms: parsed.budgetMs ?? null,
85
+ budget_source: parsed.budgetMs !== undefined ? "explicit" : "default",
85
86
  pid: process.pid,
86
87
  kimi_pid: null,
87
88
  status: "running",
@@ -128,7 +129,7 @@ export async function runReview(argv, context, commandType) {
128
129
  // poison the row — consumers reading SQLite distinguish "missing"
129
130
  // (NULL) from "captured" (non-empty UUID) only via NULL-ness.
130
131
  // (Kimi alpha.4 challenge finding #3.)
131
- store.updateRunningJob(job.job_id, { kimi_session_id: result.sessionId });
132
+ store.updateRunningJob(job.job_id, { kimi_session_id_source: "announced", kimi_session_id: result.sessionId });
132
133
  }
133
134
  await syncKimiSessionTitle({
134
135
  promptText: previewPrompt,
@@ -159,13 +160,13 @@ export async function runReview(argv, context, commandType) {
159
160
  return rendered.output;
160
161
  }
161
162
  catch (error) {
163
+ const classified = classifyManagedCommandFailure(error, commandType, job.job_id, { background: job.background, budgetMs: job.budget_ms ?? KIMI_REVIEW_PROMPT_TIMEOUT_MS });
164
+ const partialOutput = await captureTimeoutPartialOutput(classified, store, job, context.env, handlers.signal);
162
165
  if (handlers.cancelling) {
163
166
  const cancelledError = new RuntimeError(cancel.errorCodes.cancelled, cancel.cancelMessages.default, `${commandType}.runtime`, error instanceof Error ? { cause: error } : undefined);
164
167
  await markJobCancelled(store, paths, job, cancel.cancelledSummary, cancelledError);
165
168
  throw cancelledError;
166
169
  }
167
- const classified = classifyManagedCommandFailure(error, commandType, job.job_id, { background: job.background, budgetMs: job.budget_ms ?? KIMI_REVIEW_PROMPT_TIMEOUT_MS });
168
- const partialOutput = captureTimeoutPartialOutput(classified, store, job);
169
170
  await markJobFailed(store, paths, job, classified, cancel.failedSummary, { partialOutput });
170
171
  throw classified;
171
172
  }
@@ -2,7 +2,7 @@ import { RuntimeError } from "../errors.js";
2
2
  import { resolveRepoIdentity } from "../git.js";
3
3
  import { sweepStaleJobs } from "../jobs.js";
4
4
  import { reconcileHistoricalJobProvenance } from "../kimi-engine.js";
5
- import { withJobStore } from "../job-store.js";
5
+ import { renderBudgetSource, withJobStore } from "../job-store.js";
6
6
  import { ensurePluginPaths, resolvePluginPaths } from "../paths.js";
7
7
  import { parseJobLookupArgs } from "../parsing.js";
8
8
  export async function runStatus(argv, context) {
@@ -22,6 +22,6 @@ export async function runStatus(argv, context) {
22
22
  throw new RuntimeError("JOB_NOT_FOUND", "No matching job was found for status.", "status.lookup");
23
23
  }
24
24
  const job = await reconcileHistoricalJobProvenance(store, found);
25
- return `${JSON.stringify(job, null, 2)}\n`;
25
+ return `${JSON.stringify({ ...job, budget_source: renderBudgetSource(job) }, null, 2)}\n`;
26
26
  });
27
27
  }
@@ -211,6 +211,7 @@ async function runReadSwarm(parsed, objective, context) {
211
211
  thinking: parsed.thinking ?? null,
212
212
  background: false,
213
213
  budget_ms: parsed.budgetMs ?? KIMI_SWARM_DEFAULT_BUDGET_MS,
214
+ budget_source: parsed.budgetMs !== undefined ? "explicit" : "default",
214
215
  pid: process.pid,
215
216
  kimi_pid: null,
216
217
  status: "running",
@@ -310,6 +311,7 @@ async function runWriteSwarm(parsed, objective, context) {
310
311
  thinking: parsed.thinking ?? null,
311
312
  background: false,
312
313
  budget_ms: parsed.budgetMs ?? KIMI_SWARM_DEFAULT_BUDGET_MS,
314
+ budget_source: parsed.budgetMs !== undefined ? "explicit" : "default",
313
315
  pid: process.pid,
314
316
  kimi_pid: null,
315
317
  status: "running",
@@ -502,7 +504,7 @@ writeMode) {
502
504
  if (result.sessionId !== undefined &&
503
505
  result.sessionId.length > 0 &&
504
506
  result.sessionId !== job.kimi_session_id) {
505
- store.updateRunningJob(job.job_id, { kimi_session_id: result.sessionId });
507
+ store.updateRunningJob(job.job_id, { kimi_session_id_source: "announced", kimi_session_id: result.sessionId });
506
508
  }
507
509
  await syncKimiSessionTitle({
508
510
  promptText: objective,