kimi-plugin-cc 2.2.0 → 2.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +6 -0
- package/README.md +1 -1
- package/agents/kimi-ask.md +4 -0
- package/agents/kimi-challenge.md +3 -1
- package/agents/kimi-pursue.md +3 -1
- package/agents/kimi-rescue.md +4 -0
- package/agents/kimi-review.md +3 -1
- package/agents/kimi-swarm-write.md +3 -1
- package/agents/kimi-swarm.md +3 -1
- package/commands/ask.md +4 -2
- package/commands/challenge.md +4 -2
- package/commands/pursue.md +5 -1
- package/commands/rescue.md +4 -2
- package/commands/review.md +4 -2
- package/commands/swarm.md +5 -1
- package/dist/cli-client.js +33 -7
- package/dist/commands/ask.js +5 -2
- package/dist/commands/cli-helpers.js +13 -0
- package/dist/commands/pursue.js +5 -2
- package/dist/commands/rescue.js +5 -2
- package/dist/commands/result.js +3 -1
- package/dist/commands/review-gate.js +19 -13
- package/dist/commands/review.js +5 -2
- package/dist/commands/status.js +2 -2
- package/dist/commands/swarm.js +7 -3
- package/dist/help.js +1 -1
- package/dist/hook-escape.js +71 -0
- package/dist/hooks/approval-policy.js +17 -27
- package/dist/job-store.js +17 -4
- package/dist/kimi-engine.js +2 -0
- package/dist/kimi-errors.js +1 -1
- package/dist/kimi-timeouts.js +6 -10
- package/dist/kimi-version-probe.js +2 -2
- package/dist/parsing.js +59 -53
- package/dist/version.js +1 -1
- package/docs/models.md +10 -0
- package/docs/registry-releases.md +5 -3
- package/docs/safety.md +10 -0
- package/package.json +1 -1
- package/plugins/kimi-codex/.codex-plugin/plugin.json +1 -1
- package/plugins/kimi-codex/dist/cli-client.js +33 -7
- package/plugins/kimi-codex/dist/commands/ask.js +5 -2
- package/plugins/kimi-codex/dist/commands/cli-helpers.js +13 -0
- package/plugins/kimi-codex/dist/commands/pursue.js +5 -2
- package/plugins/kimi-codex/dist/commands/rescue.js +5 -2
- package/plugins/kimi-codex/dist/commands/result.js +3 -1
- package/plugins/kimi-codex/dist/commands/review-gate.js +19 -13
- package/plugins/kimi-codex/dist/commands/review.js +5 -2
- package/plugins/kimi-codex/dist/commands/status.js +2 -2
- package/plugins/kimi-codex/dist/commands/swarm.js +7 -3
- package/plugins/kimi-codex/dist/help.js +1 -1
- package/plugins/kimi-codex/dist/hook-escape.js +71 -0
- package/plugins/kimi-codex/dist/hooks/approval-policy.js +17 -27
- package/plugins/kimi-codex/dist/job-store.js +17 -4
- package/plugins/kimi-codex/dist/kimi-engine.js +2 -0
- package/plugins/kimi-codex/dist/kimi-errors.js +1 -1
- package/plugins/kimi-codex/dist/kimi-timeouts.js +6 -10
- package/plugins/kimi-codex/dist/kimi-version-probe.js +2 -2
- package/plugins/kimi-codex/dist/parsing.js +59 -53
- package/plugins/kimi-codex/dist/version.js +1 -1
- package/plugins/kimi-codex/skills/kimi-ask/SKILL.md +3 -1
- package/plugins/kimi-codex/skills/kimi-challenge/SKILL.md +3 -1
- package/plugins/kimi-codex/skills/kimi-pursue/SKILL.md +3 -1
- package/plugins/kimi-codex/skills/kimi-rescue/SKILL.md +3 -1
- package/plugins/kimi-codex/skills/kimi-review/SKILL.md +3 -1
- package/plugins/kimi-codex/skills/kimi-setup/SKILL.md +2 -0
- package/plugins/kimi-codex/skills/kimi-swarm/SKILL.md +3 -1
- package/plugins/kimi-codex/skills/kimi-swarm-write/SKILL.md +3 -1
package/dist/parsing.js
CHANGED
|
@@ -21,38 +21,30 @@ function looksLikeFlag(token) {
|
|
|
21
21
|
// Short flag: a single dash followed by exactly one letter (`-r`, `-m`).
|
|
22
22
|
return /^-[a-zA-Z]$/.test(token);
|
|
23
23
|
}
|
|
24
|
-
export const ASK_SUPPORTED_FLAGS = "-m/--model <name>, --budget <30m|1h|90s>, --background, --wait, --fresh, -r, --resume <job-id|session-id>";
|
|
25
|
-
export const RESCUE_SUPPORTED_FLAGS = "-m/--model <name>, --budget <30m|1h|90s>, --background, --wait, --fresh, -r, --resume <job-id|session-id>";
|
|
26
|
-
export const REVIEW_SUPPORTED_FLAGS = "--base <ref>, -m/--model <name>, --budget <30m|1h|90s>";
|
|
24
|
+
export const ASK_SUPPORTED_FLAGS = "-m/--model <name>, --thinking <off|low|medium|high>, --budget <30m|1h|90s>, --background, --wait, --fresh, -r, --resume <job-id|session-id>";
|
|
25
|
+
export const RESCUE_SUPPORTED_FLAGS = "-m/--model <name>, --thinking <off|low|medium|high>, --budget <30m|1h|90s>, --background, --wait, --fresh, -r, --resume <job-id|session-id>";
|
|
26
|
+
export const REVIEW_SUPPORTED_FLAGS = "--base <ref>, -m/--model <name>, --thinking <off|low|medium|high>, --budget <30m|1h|90s>";
|
|
27
27
|
export const JOB_LOOKUP_SUPPORTED_FLAGS = "--type <review|challenge|rescue|review_gate|ask>, --json";
|
|
28
28
|
export const SETUP_SUPPORTED_FLAGS = "--check, --uninstall, --all (with --uninstall), --enable-review-gate, --disable-review-gate, --models [--json] (without other setup flags)";
|
|
29
29
|
export const REPAIR_SESSIONS_SUPPORTED_FLAGS = "--apply, --all";
|
|
30
30
|
export const REPLAY_SUPPORTED_FLAGS = "none (one job-id required)";
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
// and a wrapper agent reading the error would think the cure is to drop
|
|
39
|
-
// the `=value` rather than the flag itself.
|
|
40
|
-
const THINKING_FLAG_REMOVED_MESSAGE = "--thinking / --no-thinking were removed in v1.0. Thinking is always on for user-facing commands.";
|
|
41
|
-
function isRemovedThinkingFlag(token) {
|
|
42
|
-
return (token === "--thinking" ||
|
|
43
|
-
token === "--no-thinking" ||
|
|
44
|
-
token.startsWith("--thinking=") ||
|
|
45
|
-
token.startsWith("--no-thinking="));
|
|
31
|
+
/** Validate syntax only; upstream per-model supportEfforts remains authoritative. */
|
|
32
|
+
export function validateThinkingEffort(value, stage = "args.parse", lowercase = true) {
|
|
33
|
+
const effort = lowercase ? value?.toLowerCase() : value;
|
|
34
|
+
if (!effort || !/^[a-z][a-z0-9-]{0,31}$/.test(effort)) {
|
|
35
|
+
throw new RuntimeError("INVALID_ARGS", "--thinking requires an effort matching [a-z][a-z0-9-]{0,31} (e.g. off, low, medium, high).", stage);
|
|
36
|
+
}
|
|
37
|
+
return effort;
|
|
46
38
|
}
|
|
47
|
-
export const PURSUE_SUPPORTED_FLAGS = "-m/--model <name>, --budget <30m|1h|90s>, --turns <N>";
|
|
48
|
-
export const SWARM_SUPPORTED_FLAGS = "-m/--model <name>, --budget <30m|1h|90s>, --cap <N>, --max-concurrency <N>, --write";
|
|
39
|
+
export const PURSUE_SUPPORTED_FLAGS = "-m/--model <name>, --thinking <off|low|medium|high>, --budget <30m|1h|90s>, --turns <N>";
|
|
40
|
+
export const SWARM_SUPPORTED_FLAGS = "-m/--model <name>, --thinking <off|low|medium|high>, --budget <30m|1h|90s>, --cap <N>, --max-concurrency <N>, --write";
|
|
49
41
|
/**
|
|
50
42
|
* Parser for /kimi:swarm (read-only parallel fan-out). Foreground-only:
|
|
51
43
|
* no --background/--fresh/--resume. Trailing tokens are the objective.
|
|
52
44
|
*/
|
|
53
45
|
export function parseSwarmArgs(argv) {
|
|
54
46
|
let model;
|
|
55
|
-
|
|
47
|
+
let thinkingEffort;
|
|
56
48
|
let budgetMs;
|
|
57
49
|
let cap;
|
|
58
50
|
let maxConcurrency;
|
|
@@ -60,6 +52,13 @@ export function parseSwarmArgs(argv) {
|
|
|
60
52
|
let trailingTokens;
|
|
61
53
|
for (let index = 0; index < argv.length; index += 1) {
|
|
62
54
|
const token = argv[index];
|
|
55
|
+
if (token === "--thinking" || token.startsWith("--thinking=")) {
|
|
56
|
+
thinkingEffort = validateThinkingEffort(token === "--thinking" ? argv[++index] : token.slice("--thinking=".length));
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
if (token === "--no-thinking" || token.startsWith("--no-thinking=")) {
|
|
60
|
+
throw new RuntimeError("INVALID_ARGS", "--no-thinking is unsupported; use --thinking off", "args.parse");
|
|
61
|
+
}
|
|
63
62
|
if (token === "--") {
|
|
64
63
|
trailingTokens = argv.slice(index + 1);
|
|
65
64
|
break;
|
|
@@ -105,9 +104,6 @@ export function parseSwarmArgs(argv) {
|
|
|
105
104
|
break;
|
|
106
105
|
}
|
|
107
106
|
default:
|
|
108
|
-
if (isRemovedThinkingFlag(token)) {
|
|
109
|
-
throw new RuntimeError("INVALID_ARGS", THINKING_FLAG_REMOVED_MESSAGE, "swarm.parse");
|
|
110
|
-
}
|
|
111
107
|
if (looksLikeFlag(token)) {
|
|
112
108
|
throw new RuntimeError("INVALID_ARGS", `Unknown flag ${token} for swarm. Supported flags: ${SWARM_SUPPORTED_FLAGS}. Use \`--\` before flag-shaped objective text to pass it through.`, "swarm.parse");
|
|
113
109
|
}
|
|
@@ -122,7 +118,7 @@ export function parseSwarmArgs(argv) {
|
|
|
122
118
|
maxConcurrency,
|
|
123
119
|
write,
|
|
124
120
|
model,
|
|
125
|
-
|
|
121
|
+
thinkingEffort,
|
|
126
122
|
objective: trailingTokens?.join(" "),
|
|
127
123
|
};
|
|
128
124
|
}
|
|
@@ -186,12 +182,19 @@ export function parseDurationMs(token, flag, stage, budgetKind) {
|
|
|
186
182
|
*/
|
|
187
183
|
export function parsePursueArgs(argv) {
|
|
188
184
|
let model;
|
|
189
|
-
|
|
185
|
+
let thinkingEffort;
|
|
190
186
|
let budgetMs;
|
|
191
187
|
let turns;
|
|
192
188
|
let trailingTokens;
|
|
193
189
|
for (let index = 0; index < argv.length; index += 1) {
|
|
194
190
|
const token = argv[index];
|
|
191
|
+
if (token === "--thinking" || token.startsWith("--thinking=")) {
|
|
192
|
+
thinkingEffort = validateThinkingEffort(token === "--thinking" ? argv[++index] : token.slice("--thinking=".length));
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
if (token === "--no-thinking" || token.startsWith("--no-thinking=")) {
|
|
196
|
+
throw new RuntimeError("INVALID_ARGS", "--no-thinking is unsupported; use --thinking off", "args.parse");
|
|
197
|
+
}
|
|
195
198
|
if (token === "--") {
|
|
196
199
|
trailingTokens = argv.slice(index + 1);
|
|
197
200
|
break;
|
|
@@ -237,9 +240,6 @@ export function parsePursueArgs(argv) {
|
|
|
237
240
|
break;
|
|
238
241
|
}
|
|
239
242
|
default:
|
|
240
|
-
if (isRemovedThinkingFlag(token)) {
|
|
241
|
-
throw new RuntimeError("INVALID_ARGS", THINKING_FLAG_REMOVED_MESSAGE, "pursue.parse");
|
|
242
|
-
}
|
|
243
243
|
if (looksLikeFlag(token)) {
|
|
244
244
|
throw new RuntimeError("INVALID_ARGS", `Unknown flag ${token} for pursue. Supported flags: ${PURSUE_SUPPORTED_FLAGS}. Use \`--\` before flag-shaped objective text to pass it through.`, "pursue.parse");
|
|
245
245
|
}
|
|
@@ -252,12 +252,12 @@ export function parsePursueArgs(argv) {
|
|
|
252
252
|
budgetMs,
|
|
253
253
|
turns,
|
|
254
254
|
model,
|
|
255
|
-
|
|
255
|
+
thinkingEffort,
|
|
256
256
|
objective: trailingTokens?.join(" "),
|
|
257
257
|
};
|
|
258
258
|
}
|
|
259
259
|
/** Help before -- wins even over malformed flags or prose trailing-token mode. */
|
|
260
|
-
const VALUE_TAKING_FLAGS = new Set(["-m", "--model", "--budget", "--resume", "--base", "--cap", "--max-concurrency", "--turns", "--type"]);
|
|
260
|
+
const VALUE_TAKING_FLAGS = new Set(["--thinking", "-m", "--model", "--budget", "--resume", "--base", "--cap", "--max-concurrency", "--turns", "--type"]);
|
|
261
261
|
/**
|
|
262
262
|
* `--help` / `-h` counts only inside the leading flag region: before `--`
|
|
263
263
|
* and before the first prose token. The value of a value-taking flag is
|
|
@@ -289,10 +289,7 @@ export function parseAskArgs(argv) {
|
|
|
289
289
|
return { help: true, background: false, wait: false, fresh: false, resume: false };
|
|
290
290
|
let budgetMs;
|
|
291
291
|
let model;
|
|
292
|
-
|
|
293
|
-
// parser rejects --thinking / --no-thinking; review-gate's runtime
|
|
294
|
-
// caller pins thinking=false via CliClientOptions instead.
|
|
295
|
-
let thinking = true;
|
|
292
|
+
let thinkingEffort;
|
|
296
293
|
let background = false;
|
|
297
294
|
let wait = false;
|
|
298
295
|
let fresh = false;
|
|
@@ -302,6 +299,13 @@ export function parseAskArgs(argv) {
|
|
|
302
299
|
let trailingTokens;
|
|
303
300
|
for (let index = 0; index < argv.length; index += 1) {
|
|
304
301
|
const token = argv[index];
|
|
302
|
+
if (token === "--thinking" || token.startsWith("--thinking=")) {
|
|
303
|
+
thinkingEffort = validateThinkingEffort(token === "--thinking" ? argv[++index] : token.slice("--thinking=".length));
|
|
304
|
+
continue;
|
|
305
|
+
}
|
|
306
|
+
if (token === "--no-thinking" || token.startsWith("--no-thinking=")) {
|
|
307
|
+
throw new RuntimeError("INVALID_ARGS", "--no-thinking is unsupported; use --thinking off", "args.parse");
|
|
308
|
+
}
|
|
305
309
|
if (token === "--") {
|
|
306
310
|
trailingTokens = argv.slice(index + 1);
|
|
307
311
|
break;
|
|
@@ -357,9 +361,6 @@ export function parseAskArgs(argv) {
|
|
|
357
361
|
break;
|
|
358
362
|
}
|
|
359
363
|
default:
|
|
360
|
-
if (isRemovedThinkingFlag(token)) {
|
|
361
|
-
throw new RuntimeError("INVALID_ARGS", THINKING_FLAG_REMOVED_MESSAGE, "args.parse");
|
|
362
|
-
}
|
|
363
364
|
if (looksLikeFlag(token)) {
|
|
364
365
|
throw new RuntimeError("INVALID_ARGS", `Unknown flag ${token} for ask. Supported flags: ${ASK_SUPPORTED_FLAGS}. Use \`--\` before flag-shaped prompt text to pass it through.`, "args.parse");
|
|
365
366
|
}
|
|
@@ -382,7 +383,7 @@ export function parseAskArgs(argv) {
|
|
|
382
383
|
resume,
|
|
383
384
|
resumeTarget,
|
|
384
385
|
model,
|
|
385
|
-
|
|
386
|
+
thinkingEffort,
|
|
386
387
|
prompt: trailingTokens?.join(" "),
|
|
387
388
|
};
|
|
388
389
|
}
|
|
@@ -394,7 +395,7 @@ export function parseReviewArgs(argv, commandName = "review") {
|
|
|
394
395
|
background: parsed.background,
|
|
395
396
|
wait: parsed.wait,
|
|
396
397
|
model: parsed.model,
|
|
397
|
-
|
|
398
|
+
thinkingEffort: parsed.thinkingEffort,
|
|
398
399
|
focus: parsed.trailingText,
|
|
399
400
|
};
|
|
400
401
|
}
|
|
@@ -403,7 +404,7 @@ export function parseRescueArgs(argv) {
|
|
|
403
404
|
return { help: true, background: false, wait: false, fresh: false, resume: false };
|
|
404
405
|
let budgetMs;
|
|
405
406
|
let model;
|
|
406
|
-
let
|
|
407
|
+
let thinkingEffort;
|
|
407
408
|
let background = false;
|
|
408
409
|
let wait = false;
|
|
409
410
|
let fresh = false;
|
|
@@ -413,6 +414,13 @@ export function parseRescueArgs(argv) {
|
|
|
413
414
|
let trailingTokens;
|
|
414
415
|
for (let index = 0; index < argv.length; index += 1) {
|
|
415
416
|
const token = argv[index];
|
|
417
|
+
if (token === "--thinking" || token.startsWith("--thinking=")) {
|
|
418
|
+
thinkingEffort = validateThinkingEffort(token === "--thinking" ? argv[++index] : token.slice("--thinking=".length));
|
|
419
|
+
continue;
|
|
420
|
+
}
|
|
421
|
+
if (token === "--no-thinking" || token.startsWith("--no-thinking=")) {
|
|
422
|
+
throw new RuntimeError("INVALID_ARGS", "--no-thinking is unsupported; use --thinking off", "args.parse");
|
|
423
|
+
}
|
|
416
424
|
if (token === "--") {
|
|
417
425
|
trailingTokens = argv.slice(index + 1);
|
|
418
426
|
break;
|
|
@@ -468,9 +476,6 @@ export function parseRescueArgs(argv) {
|
|
|
468
476
|
break;
|
|
469
477
|
}
|
|
470
478
|
default:
|
|
471
|
-
if (isRemovedThinkingFlag(token)) {
|
|
472
|
-
throw new RuntimeError("INVALID_ARGS", THINKING_FLAG_REMOVED_MESSAGE, "args.parse");
|
|
473
|
-
}
|
|
474
479
|
if (looksLikeFlag(token)) {
|
|
475
480
|
throw new RuntimeError("INVALID_ARGS", `Unknown flag ${token} for rescue. Supported flags: ${RESCUE_SUPPORTED_FLAGS}. Use \`--\` before flag-shaped prompt text to pass it through.`, "args.parse");
|
|
476
481
|
}
|
|
@@ -493,7 +498,7 @@ export function parseRescueArgs(argv) {
|
|
|
493
498
|
resume,
|
|
494
499
|
resumeTarget,
|
|
495
500
|
model,
|
|
496
|
-
|
|
501
|
+
thinkingEffort,
|
|
497
502
|
prompt: trailingTokens?.join(" "),
|
|
498
503
|
};
|
|
499
504
|
}
|
|
@@ -540,21 +545,25 @@ function isManagedCommandType(value) {
|
|
|
540
545
|
function parseKnownFlags(argv, knownFlags, commandName) {
|
|
541
546
|
let budgetMs;
|
|
542
547
|
let model;
|
|
543
|
-
let
|
|
548
|
+
let thinkingEffort;
|
|
544
549
|
let base;
|
|
545
550
|
let background = false;
|
|
546
551
|
let wait = false;
|
|
547
552
|
let trailingTokens;
|
|
548
553
|
for (let index = 0; index < argv.length; index += 1) {
|
|
549
554
|
const token = argv[index];
|
|
555
|
+
if (token === "--thinking" || token.startsWith("--thinking=")) {
|
|
556
|
+
thinkingEffort = validateThinkingEffort(token === "--thinking" ? argv[++index] : token.slice("--thinking=".length));
|
|
557
|
+
continue;
|
|
558
|
+
}
|
|
559
|
+
if (token === "--no-thinking" || token.startsWith("--no-thinking=")) {
|
|
560
|
+
throw new RuntimeError("INVALID_ARGS", "--no-thinking is unsupported; use --thinking off", "args.parse");
|
|
561
|
+
}
|
|
550
562
|
if (token === "--") {
|
|
551
563
|
trailingTokens = argv.slice(index + 1);
|
|
552
564
|
break;
|
|
553
565
|
}
|
|
554
566
|
if (!knownFlags.has(token)) {
|
|
555
|
-
if (isRemovedThinkingFlag(token)) {
|
|
556
|
-
throw new RuntimeError("INVALID_ARGS", THINKING_FLAG_REMOVED_MESSAGE, "args.parse");
|
|
557
|
-
}
|
|
558
567
|
if (looksLikeFlag(token)) {
|
|
559
568
|
// Wrapper agents routinely hallucinate flags like `--file` /
|
|
560
569
|
// `--context` because that shape is common in other CLIs. Silently
|
|
@@ -610,9 +619,6 @@ function parseKnownFlags(argv, knownFlags, commandName) {
|
|
|
610
619
|
// The early `if (!knownFlags.has(token))` gate above means we only
|
|
611
620
|
// reach this switch with tokens that ARE in knownFlags, and every
|
|
612
621
|
// member is handled above. A `default` arm would be unreachable.
|
|
613
|
-
// --thinking and --no-thinking are NOT in knownFlags any more (v1.0
|
|
614
|
-
// removed them); the early gate above hard-fails on them with
|
|
615
|
-
// THINKING_FLAG_REMOVED_MESSAGE.
|
|
616
622
|
}
|
|
617
623
|
}
|
|
618
624
|
return {
|
|
@@ -621,7 +627,7 @@ function parseKnownFlags(argv, knownFlags, commandName) {
|
|
|
621
627
|
background,
|
|
622
628
|
wait,
|
|
623
629
|
model,
|
|
624
|
-
|
|
630
|
+
thinkingEffort,
|
|
625
631
|
trailingText: trailingTokens?.join(" "),
|
|
626
632
|
};
|
|
627
633
|
}
|
package/dist/version.js
CHANGED
package/docs/models.md
CHANGED
|
@@ -80,3 +80,13 @@ validator. Every task still uses the existing exact-version, hook, workspace,
|
|
|
80
80
|
and no-plan checks. Source review is against kimi-code 2.0.0 (`1b89e4b0`),
|
|
81
81
|
including `kosongConfig/configSection.ts`, `envOverlay.ts`, and
|
|
82
82
|
`cli/v2/run-v2-print.ts`.
|
|
83
|
+
|
|
84
|
+
## Requested thinking effort
|
|
85
|
+
|
|
86
|
+
`--thinking <effort>` requests per-run effort through `KIMI_MODEL_THINKING_EFFORT`, never a CLI flag or saved-default change; omitted flags preserve the operator-owned ambient variable, and resume does not inherit a previous job's request. Providers that are not trait-driven silently ignore it, as does a base level already `off`; an `alwaysThinking` model receives `off` unnormalized. Stream-json carries no thinking deltas (`writeThinkingDelta() {}`), so the plugin cannot verify whether the request was honoured.
|
|
87
|
+
|
|
88
|
+
Use `--thinking off`, `--thinking high`, or `--thinking=<effort>` before the prompt. Values are lowercased and must match `/^[a-z][a-z0-9-]{0,31}$/` (a plugin-owned syntax bound; a future upstream token outside it is rejected before upstream sees it); upstream per-model `supportEfforts`, not a plugin enum, determines supported values. `--no-thinking` is an error: use `--thinking off`. In CLI 2.2.0 only the three Moonshot `kimi*` provider definitions are trait-driven. BYO providers ignore the request silently; an always-thinking model may remain thinking.
|
|
89
|
+
|
|
90
|
+
Jobs persist nullable `thinking_effort`; status and `result --json` expose `thinking_effort_requested`, not observed effort. Detached ask/rescue workers replay the request. A resume without `--thinking` exports no new override (any ambient operator value remains). No config.toml write occurs.
|
|
91
|
+
|
|
92
|
+
The review gate requests `off` by default. `KIMI_PLUGIN_CC_REVIEW_GATE_THINKING=inherit` exports no override; another non-empty value is lowercased and must match the same regex. Unset or empty means `off`; an invalid value makes the gate skip visibly. The gate remains fail-open on its 8 s timeout: a BYO provider or always-thinking model can ignore the request, and even an honoured request does not guarantee completion within the budget.
|
|
@@ -6,9 +6,11 @@ remain available. The package has no install scripts and no separate CLI.
|
|
|
6
6
|
|
|
7
7
|
## Publishing status
|
|
8
8
|
|
|
9
|
-
As of October 15, 2026, [2.0
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
As of October 15, 2026, [2.2.0 is live on npm](https://www.npmjs.com/package/kimi-plugin-cc/v/2.2.0), published by
|
|
10
|
+
the release-event run of `publish.yml` (run 38658918855) through GitHub Actions OIDC with a signed
|
|
11
|
+
provenance statement (sigstore log index 3256863983) — the first release where the tag-triggered
|
|
12
|
+
path worked end to end without a manual dispatch. 2.0.8 (same day, run 38650044033) was the first
|
|
13
|
+
OIDC publication; 2.0.7 (September 26) was the hand-published bootstrap.
|
|
12
14
|
The first upload used the validated GitHub Actions artifact and interactive npm
|
|
13
15
|
authentication. Its public tarball matched that artifact byte-for-byte, and a
|
|
14
16
|
fresh registry install passed isolated setup/check for both hosts.
|
package/docs/safety.md
CHANGED
|
@@ -363,3 +363,13 @@ Agents use `retryable_after_setup`, not `drift_axis` alone. They run setup for t
|
|
|
363
363
|
## Reporting safety issues
|
|
364
364
|
|
|
365
365
|
If you find a way to coax /kimi:review, /kimi:challenge, /kimi:review_gate, or /kimi:ask into running a non-read tool — or to make /kimi:rescue mutate `.git/` or escape the workspace — please open a GitHub security advisory rather than a public issue.
|
|
366
|
+
|
|
367
|
+
## Hook-escape monitor: detection evidence
|
|
368
|
+
|
|
369
|
+
**Hook-escape monitor** (§1): the cli-client replays the pure hook policy on every streamed `tool_calls` entry; a call the policy denies whose tool result is not the hook's denial (byte-exact, or with upstream's repeat-breaker `<system-reminder>` decoration / handoff text) is recorded as `mismatch`, and a denied call still without a result at settlement as `unresolved`. Findings are **detection evidence, never a failure**: logged (`hook_escape`), persisted on the job row (`hook_escapes`, JSON), exposed by `status`/`result --json`, printed as a stderr `safety hook notice`, and carried in timeout details; the run continues, the job's outcome is unchanged, and the review gate stays fail-open and only annotates. The stream has no outcome field, so a mismatch is equally an upstream pre-hook rejection (unknown tool, disabled tool, invalid args, sensitive path, per-tool resolve errors), a coexisting hook or permission policy blocking first, or hook drift — failing the job needs positive execution evidence this stream does not provide. Streamed calls under read-only labels are covered (swarm subagents are in-process, not streamed); write labels only for the pure denials (plan/goal tools).
|
|
370
|
+
|
|
371
|
+
A denied attempt alone is not a finding: the hook's denial text in the corresponding tool result (byte-exact, or carrying upstream's repeat-breaker decoration) proves enforcement and clears it. Anything else is recorded as `mismatch` with a 200-character excerpt; a denied call with no result at settlement as `unresolved`. **What a finding means:** the model received something other than the managed denial. On certified 2.2.0 that is produced just as readily by calls upstream rejects before the hook runs (`Tool "LS" not found`, `Invalid args for tool …`, the sensitive-path `PathSecurityError` for `.env`, per-tool resolve errors, `Tool skipped because a previous tool call stopped the turn.`), by a coexisting PreToolUse hook ahead of the managed block or an upstream permission policy blocking first, or by a cancel/budget abort landing before the result — none of which executed anything — as by a hook that failed open. Because the stream carries no outcome field, the plugin cannot tell these apart, so it reports and does not fail: the job completes or fails on its own merits, `status`/`result --json` carry `hook_escapes`, stderr prints a `safety hook notice`, and the review gate annotates `systemMessage` without changing its decision. Treat a finding as a prompt to run `/kimi:setup --check` and read the excerpt, not as proof of a breach; treat a *recurring* `mismatch` whose excerpt looks like tool output (`Wrote N bytes to …`) as a drifted hook. Coverage is streamed calls under read-only labels and pure denials under write labels — not filesystem-dependent rescue allowlist decisions, not swarm subagent calls (in-process, never streamed). Non-vacuous live control (CLI 2.2.0, 2026-10-15): with the hook command pointed at a nonexistent script upstream failed open, `Write` created the file, and the finding was `mismatch` with excerpt `Wrote 7 bytes to ESCAPE.txt`; with the hook intact the same prompt produced the byte-exact denial and no finding. Enforcement (stopping the run, failing the job) is deliberately not shipped: it needs positive execution evidence. The parked upstream ask is an `outcome: executed|vetoed|rejected` field on print-mode tool records; a plugin-side alternative is a per-job trace the hook writes before it exits.
|
|
372
|
+
|
|
373
|
+
## Review-gate thinking request
|
|
374
|
+
|
|
375
|
+
The gate requests `off` through `KIMI_MODEL_THINKING_EFFORT` by default. `KIMI_PLUGIN_CC_REVIEW_GATE_THINKING=inherit` leaves the ambient environment alone; any other non-empty value matching `/^[a-z][a-z0-9-]{0,31}$/` is passed verbatim. This is best-effort, never an enforced latency guarantee: non-trait-driven/BYO providers ignore it silently, an already-off base remains off, and an always-thinking model receives `off` unnormalized and may continue reasoning. Stream-json has no thinking deltas, so honouring the request cannot be observed. The 8 s budget and fail-open-on-timeout contract are unchanged; a hook-escape finding only annotates the gate's `systemMessage`. `KIMI_PLUGIN_CC_REVIEW_GATE_THINKING` is lowercased like `--thinking`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kimi",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.1",
|
|
4
4
|
"description": "Codex plugin that delegates review, challenge, ask, rescue, pursue, and swarm workflows to the local kimi-code CLI through the kimi-plugin-cc companion runtime.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "linxule"
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
import { spawn } from "node:child_process";
|
|
20
20
|
import { appendFile, mkdir } from "node:fs/promises";
|
|
21
21
|
import path from "node:path";
|
|
22
|
+
import { createHookEscapeMonitor } from "./hook-escape.js";
|
|
22
23
|
import { RuntimeError, formatError } from "./errors.js";
|
|
23
24
|
import { assertNoUnsafeExperimentalSelector, assertExecutionPlanMatchesSpawn, } from "./kimi-engine.js";
|
|
24
25
|
import { assertPrefixArgsSafe } from "./kimi-command.js";
|
|
@@ -126,6 +127,14 @@ export async function runCliPrompt(opts) {
|
|
|
126
127
|
let announcedSessionId;
|
|
127
128
|
let announcedGoalSummary;
|
|
128
129
|
let announcedSystemVersion;
|
|
130
|
+
const hookMonitor = createHookEscapeMonitor({ commandLabel: opts.commandLabel,
|
|
131
|
+
operationKind: opts.executionPlan.operationKind, trustedWorkspaceRoot: opts.trustedWorkspaceRoot, cwd: opts.cwd });
|
|
132
|
+
const hookEscapes = [];
|
|
133
|
+
const noteHookEscape = (escape) => {
|
|
134
|
+
hookEscapes.push(escape);
|
|
135
|
+
appendLogLine({ event: "hook_escape", ...escape, command_label: opts.commandLabel,
|
|
136
|
+
operation_kind: opts.executionPlan.operationKind });
|
|
137
|
+
};
|
|
129
138
|
let engineMismatchDetected = false;
|
|
130
139
|
let handleEngineMismatch;
|
|
131
140
|
const plannedEngine = opts.executionPlan.intendedEngine;
|
|
@@ -283,6 +292,9 @@ export async function runCliPrompt(opts) {
|
|
|
283
292
|
}
|
|
284
293
|
if (requireMarkerBeforeOutput(`a ${outcome.record.role} record`))
|
|
285
294
|
return;
|
|
295
|
+
const escape = hookMonitor.observe(outcome.record);
|
|
296
|
+
if (escape)
|
|
297
|
+
noteHookEscape(escape);
|
|
286
298
|
records.push(outcome.record);
|
|
287
299
|
appendLogLine({ event: "record", record: outcome.record });
|
|
288
300
|
invokeOnRecord(outcome.record);
|
|
@@ -314,6 +326,13 @@ export async function runCliPrompt(opts) {
|
|
|
314
326
|
const settle = (kind, payload) => {
|
|
315
327
|
if (settlementStarted)
|
|
316
328
|
return;
|
|
329
|
+
// Denied calls still without a result (a cancel/budget race, an abnormal
|
|
330
|
+
// exit, or a turn that ended without the denial the hook owes) are reported
|
|
331
|
+
// alongside the real outcome, never instead of it.
|
|
332
|
+
for (const escape of hookMonitor.settle())
|
|
333
|
+
noteHookEscape(escape);
|
|
334
|
+
if (kind === "resolve" && hookEscapes.length > 0)
|
|
335
|
+
payload = { ...payload, hookEscapes: [...hookEscapes] };
|
|
317
336
|
settlementStarted = true;
|
|
318
337
|
opts.signal?.removeEventListener("abort", onAbort);
|
|
319
338
|
// Abort teardown can outlive the direct child's close event while a
|
|
@@ -472,6 +491,11 @@ export async function runCliPrompt(opts) {
|
|
|
472
491
|
}
|
|
473
492
|
}
|
|
474
493
|
};
|
|
494
|
+
const stopForPolicyFailure = (error) => {
|
|
495
|
+
engineMismatchDetected = true; // Stops all further consumer output.
|
|
496
|
+
cancellationTeardown ??= teardownChildTree().then(() => ({ ok: true }), (error) => ({ ok: false, error }));
|
|
497
|
+
settle("reject", error);
|
|
498
|
+
};
|
|
475
499
|
let engineMismatchError;
|
|
476
500
|
handleEngineMismatch = (mismatch) => {
|
|
477
501
|
if (engineMismatchError !== undefined)
|
|
@@ -493,8 +517,7 @@ export async function runCliPrompt(opts) {
|
|
|
493
517
|
system_version: mismatch.systemVersion ?? null,
|
|
494
518
|
reason: mismatch.reason,
|
|
495
519
|
});
|
|
496
|
-
|
|
497
|
-
settle("reject", engineMismatchError);
|
|
520
|
+
stopForPolicyFailure(engineMismatchError);
|
|
498
521
|
};
|
|
499
522
|
const onAbort = () => {
|
|
500
523
|
if (aborted)
|
|
@@ -674,6 +697,7 @@ export class ResponseTimeoutError extends RuntimeError {
|
|
|
674
697
|
partial_output_chars: partialResult?.records.reduce((count, record) => count + (record.role === "assistant" && typeof record.content === "string" ? record.content.length : 0), 0) ?? 0,
|
|
675
698
|
aborted: partialResult?.aborted ?? true,
|
|
676
699
|
...(partialResult ? { settled_post_abort: true } : {}),
|
|
700
|
+
...(partialResult?.hookEscapes?.length ? { hook_escapes: partialResult.hookEscapes } : {}),
|
|
677
701
|
},
|
|
678
702
|
});
|
|
679
703
|
this.name = "ResponseTimeoutError";
|
|
@@ -778,11 +802,11 @@ function buildArgs(opts) {
|
|
|
778
802
|
args.push("--skills-dir", dir);
|
|
779
803
|
}
|
|
780
804
|
}
|
|
781
|
-
// NOTE:
|
|
782
|
-
//
|
|
783
|
-
//
|
|
784
|
-
//
|
|
785
|
-
//
|
|
805
|
+
// NOTE: thinking effort is requested through the env binding, never CLI argv.
|
|
806
|
+
// Non-trait-driven providers or a base level already off ignore it silently;
|
|
807
|
+
// an alwaysThinking model receives off unnormalized. stream-json carries no
|
|
808
|
+
// thinking deltas (writeThinkingDelta() {} in the JSON writer), so the plugin
|
|
809
|
+
// cannot observe whether the request was honoured.
|
|
786
810
|
// -p accepts the prompt as the option value. We pass it as a separate argv
|
|
787
811
|
// entry so prompts with leading dashes or whitespace are not interpreted as
|
|
788
812
|
// additional flags.
|
|
@@ -791,6 +815,8 @@ function buildArgs(opts) {
|
|
|
791
815
|
}
|
|
792
816
|
function buildEnv(opts) {
|
|
793
817
|
const env = { ...opts.env };
|
|
818
|
+
if (opts.thinkingEffort !== undefined)
|
|
819
|
+
env.KIMI_MODEL_THINKING_EFFORT = opts.thinkingEffort;
|
|
794
820
|
// 2.2.0 audit: ambient trust starts project MCP stdio servers outside PreToolUse.
|
|
795
821
|
delete env.KIMI_CODE_TRUST_WORKSPACE;
|
|
796
822
|
// kimi-code 0.33.0 inverted its CLI engine default: an unflagged `kimi -p`
|
|
@@ -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 { warnInheritedBudget, captureTimeoutPartialOutput, assertCliResultSuccess, reassembleProseFromRecords, warnIfSessionIdMissing } from "./cli-helpers.js";
|
|
20
|
+
import { warnInheritedBudget, captureTimeoutPartialOutput, assertCliResultSuccess, reassembleProseFromRecords, warnIfSessionIdMissing, warnHookEscapes } from "./cli-helpers.js";
|
|
21
21
|
import { buildKimiSessionTitle, syncKimiSessionTitle } from "../session-title.js";
|
|
22
22
|
// v1.0 cutover note (PR 2):
|
|
23
23
|
//
|
|
@@ -77,7 +77,8 @@ export async function runAsk(argv, context) {
|
|
|
77
77
|
command_type: "ask",
|
|
78
78
|
cwd: context.cwd,
|
|
79
79
|
model: parsed.model ?? null,
|
|
80
|
-
thinking:
|
|
80
|
+
thinking: null,
|
|
81
|
+
thinking_effort: parsed.thinkingEffort ?? null,
|
|
81
82
|
background: parsed.background,
|
|
82
83
|
budget_ms: parsed.budgetMs ?? (sessionResolution.sourceJob?.operation_kind === "ask" ? sessionResolution.sourceJob.budget_ms : null) ?? null,
|
|
83
84
|
budget_source: parsed.budgetMs !== undefined ? "explicit"
|
|
@@ -174,6 +175,7 @@ export async function executeAskJob(jobId, prompt, context, options) {
|
|
|
174
175
|
prompt,
|
|
175
176
|
commandLabel: "ask",
|
|
176
177
|
model: job.model ?? undefined,
|
|
178
|
+
thinkingEffort: job.thinking_effort ?? undefined,
|
|
177
179
|
resumeSessionId: job.kimi_session_id ?? undefined,
|
|
178
180
|
logPath: job.stream_log_path,
|
|
179
181
|
signal: handlers.signal,
|
|
@@ -183,6 +185,7 @@ export async function executeAskJob(jobId, prompt, context, options) {
|
|
|
183
185
|
throw new RuntimeError(cancel.errorCodes.cancelled, cancel.cancelMessages.afterPrompt, "ask.runtime");
|
|
184
186
|
}
|
|
185
187
|
assertCliResultSuccess(result, "ask.runtime");
|
|
188
|
+
warnHookEscapes(result, "ask", context.stderr);
|
|
186
189
|
// Persist whichever session id kimi actually announced. For fresh
|
|
187
190
|
// sessions this is the only chance to record it. For resumed
|
|
188
191
|
// sessions it should match the input, but capture anyway in case
|
|
@@ -185,6 +185,19 @@ export async function captureTimeoutPartialOutput(classified, store, job, env, c
|
|
|
185
185
|
budgetMs: timeout.details.budget_ms,
|
|
186
186
|
};
|
|
187
187
|
}
|
|
188
|
+
/** One-line human summary of hook-escape findings, or undefined when none. */
|
|
189
|
+
export function describeHookEscapes(result) {
|
|
190
|
+
const escapes = result.hookEscapes ?? [];
|
|
191
|
+
if (escapes.length === 0)
|
|
192
|
+
return undefined;
|
|
193
|
+
const first = escapes[0];
|
|
194
|
+
return `safety hook notice: ${escapes.length} policy-denied tool call(s) did not come back as the managed hook's denial (first: ${first.tool_name} ${first.kind}). Detection evidence only — the call may have been rejected upstream before the hook ran, blocked by another hook, or executed because the hook drifted. Run /kimi:setup --check; details in \`result <jobId> --json\` (hook_escapes).`;
|
|
195
|
+
}
|
|
196
|
+
export function warnHookEscapes(result, commandType, stderr) {
|
|
197
|
+
const note = describeHookEscapes(result);
|
|
198
|
+
if (note !== undefined)
|
|
199
|
+
stderr.write(`[${commandType}] ${note}\n`);
|
|
200
|
+
}
|
|
188
201
|
export function warnInheritedBudget(job, stderr) {
|
|
189
202
|
if (job.budget_source === "inherited" && job.budget_ms !== null) {
|
|
190
203
|
stderr.write(`[${job.command_type}] budget ${formatBudgetDuration(job.budget_ms)} inherited from job ${job.resumed_from_job_id}; pass --budget to override\n`);
|
|
@@ -14,7 +14,7 @@ import { ensurePluginPaths, resolvePluginPaths } from "../paths.js";
|
|
|
14
14
|
import { parsePursueArgs } from "../parsing.js";
|
|
15
15
|
import { readArtifact, renderManagedJobOutput, writeArtifact } from "../render.js";
|
|
16
16
|
import { hookRefusalDetails, hookRefusalRetryProtocol, maybeWarnHookMissing, verifyHookInstalled, } from "../hooks/install.js";
|
|
17
|
-
import { assertCliResultSuccess, reassembleProseFromRecords, warnIfSessionIdMissing } from "./cli-helpers.js";
|
|
17
|
+
import { assertCliResultSuccess, reassembleProseFromRecords, warnIfSessionIdMissing, warnHookEscapes } from "./cli-helpers.js";
|
|
18
18
|
import { buildKimiSessionTitle, syncKimiSessionTitle } from "../session-title.js";
|
|
19
19
|
// /kimi:pursue — autonomous goal mode (kimi-code 0.8.0+ headless `/goal`).
|
|
20
20
|
//
|
|
@@ -141,7 +141,8 @@ export async function runPursue(argv, context) {
|
|
|
141
141
|
command_type: "rescue",
|
|
142
142
|
cwd: context.cwd,
|
|
143
143
|
model: parsed.model ?? null,
|
|
144
|
-
thinking:
|
|
144
|
+
thinking: null,
|
|
145
|
+
thinking_effort: parsed.thinkingEffort ?? null,
|
|
145
146
|
background: false,
|
|
146
147
|
budget_ms: parsed.budgetMs ?? KIMI_PURSUE_DEFAULT_BUDGET_MS,
|
|
147
148
|
budget_source: parsed.budgetMs !== undefined ? "explicit" : "default",
|
|
@@ -233,10 +234,12 @@ async function executePursueJob(jobId, prompt, objective, budgetMs, context) {
|
|
|
233
234
|
commandLabel: "rescue",
|
|
234
235
|
trustedWorkspaceRoot: job.cwd,
|
|
235
236
|
model: job.model ?? undefined,
|
|
237
|
+
thinkingEffort: job.thinking_effort ?? undefined,
|
|
236
238
|
logPath: job.stream_log_path,
|
|
237
239
|
signal: handlers.signal,
|
|
238
240
|
}, budgetMs, "pursue.prompt");
|
|
239
241
|
store.updateRunningJob(job.job_id, observedExecutionFields(result));
|
|
242
|
+
warnHookEscapes(result, "pursue", context.stderr);
|
|
240
243
|
if (handlers.cancelling) {
|
|
241
244
|
throw new RuntimeError("PURSUE_CANCELLED", "Pursue cancelled by user request.", "pursue.runtime");
|
|
242
245
|
}
|
|
@@ -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 { warnInheritedBudget, captureTimeoutPartialOutput, assertCliResultSuccess, reassembleProseFromRecords, warnIfSessionIdMissing } from "./cli-helpers.js";
|
|
20
|
+
import { warnInheritedBudget, captureTimeoutPartialOutput, assertCliResultSuccess, reassembleProseFromRecords, warnIfSessionIdMissing, warnHookEscapes } 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):
|
|
@@ -69,7 +69,8 @@ export async function runRescue(argv, context) {
|
|
|
69
69
|
command_type: "rescue",
|
|
70
70
|
cwd: context.cwd,
|
|
71
71
|
model: parsed.model ?? null,
|
|
72
|
-
thinking:
|
|
72
|
+
thinking: null,
|
|
73
|
+
thinking_effort: parsed.thinkingEffort ?? null,
|
|
73
74
|
background: parsed.background,
|
|
74
75
|
budget_ms: parsed.budgetMs ?? (sessionResolution.sourceJob?.operation_kind === "rescue" ? sessionResolution.sourceJob.budget_ms : null) ?? null,
|
|
75
76
|
budget_source: parsed.budgetMs !== undefined ? "explicit"
|
|
@@ -188,6 +189,7 @@ export async function executeRescueJob(jobId, prompt, context, options) {
|
|
|
188
189
|
commandLabel: "rescue",
|
|
189
190
|
trustedWorkspaceRoot: job.cwd,
|
|
190
191
|
model: job.model ?? undefined,
|
|
192
|
+
thinkingEffort: job.thinking_effort ?? undefined,
|
|
191
193
|
resumeSessionId: job.kimi_session_id ?? undefined,
|
|
192
194
|
logPath: job.stream_log_path,
|
|
193
195
|
signal: handlers.signal,
|
|
@@ -199,6 +201,7 @@ export async function executeRescueJob(jobId, prompt, context, options) {
|
|
|
199
201
|
throw new RuntimeError(cancel.errorCodes.cancelled, cancel.cancelMessages.afterPrompt, "rescue.runtime");
|
|
200
202
|
}
|
|
201
203
|
assertCliResultSuccess(result, "rescue.runtime");
|
|
204
|
+
warnHookEscapes(result, "rescue", context.stderr);
|
|
202
205
|
if (result.sessionId !== undefined &&
|
|
203
206
|
result.sessionId.length > 0) {
|
|
204
207
|
// length>0 guard: empty-string capture would poison the row. (Kimi
|
|
@@ -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 { renderBudgetSource, withJobStore } from "../job-store.js";
|
|
5
|
+
import { renderBudgetSource, withJobStore, parseHookEscapes } 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";
|
|
@@ -44,6 +44,8 @@ function renderResultEnvelope(job, artifactPath, body) {
|
|
|
44
44
|
status: job.status,
|
|
45
45
|
budget_ms: job.budget_ms,
|
|
46
46
|
budget_source: renderBudgetSource(job),
|
|
47
|
+
thinking_effort_requested: job.thinking_effort ?? null,
|
|
48
|
+
hook_escapes: parseHookEscapes(job.hook_escapes),
|
|
47
49
|
kimi_session_id_source: job.kimi_session_id_source,
|
|
48
50
|
kimi_session_id: job.kimi_session_id,
|
|
49
51
|
// True when a budget timeout preserved assistant prose in the artifact.
|