kimi-plugin-cc 2.0.8 → 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 +27 -0
- package/README.md +5 -5
- package/agents/kimi-ask.md +5 -1
- package/agents/kimi-challenge.md +4 -2
- package/agents/kimi-pursue.md +3 -1
- package/agents/kimi-rescue.md +5 -1
- package/agents/kimi-review.md +4 -2
- package/agents/kimi-swarm-write.md +3 -1
- package/agents/kimi-swarm.md +3 -1
- package/commands/ask.md +5 -3
- package/commands/challenge.md +5 -3
- package/commands/pursue.md +5 -1
- package/commands/rescue.md +5 -3
- package/commands/review.md +5 -3
- package/commands/swarm.md +5 -1
- package/dist/cli-client.js +42 -10
- package/dist/commands/ask.js +14 -7
- package/dist/commands/cli-helpers.js +66 -4
- package/dist/commands/pursue.js +7 -3
- package/dist/commands/rescue.js +16 -10
- package/dist/commands/result.js +5 -1
- package/dist/commands/review-gate.js +21 -14
- package/dist/commands/review.js +9 -5
- package/dist/commands/status.js +2 -2
- package/dist/commands/swarm.js +10 -4
- package/dist/help.js +1 -1
- package/dist/hook-escape.js +71 -0
- package/dist/hooks/approval-policy.js +17 -27
- package/dist/hooks/config-safety.js +15 -7
- package/dist/job-store.js +52 -7
- 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/session-recovery.js +203 -0
- package/dist/vendor/smol-toml/date.js +50 -21
- package/dist/vendor/smol-toml/error.js +6 -2
- package/dist/vendor/smol-toml/extract.js +259 -66
- package/dist/vendor/smol-toml/parse.js +64 -48
- package/dist/vendor/smol-toml/primitive.js +107 -135
- package/dist/vendor/smol-toml/struct.js +106 -134
- package/dist/vendor/smol-toml/util.js +22 -78
- package/dist/version.js +1 -1
- package/docs/models.md +10 -0
- package/docs/registry-releases.md +24 -1
- package/docs/safety.md +59 -2
- package/package.json +1 -1
- package/plugins/kimi-codex/.codex-plugin/plugin.json +1 -1
- package/plugins/kimi-codex/dist/cli-client.js +42 -10
- package/plugins/kimi-codex/dist/commands/ask.js +14 -7
- package/plugins/kimi-codex/dist/commands/cli-helpers.js +66 -4
- package/plugins/kimi-codex/dist/commands/pursue.js +7 -3
- package/plugins/kimi-codex/dist/commands/rescue.js +16 -10
- package/plugins/kimi-codex/dist/commands/result.js +5 -1
- package/plugins/kimi-codex/dist/commands/review-gate.js +21 -14
- package/plugins/kimi-codex/dist/commands/review.js +9 -5
- package/plugins/kimi-codex/dist/commands/status.js +2 -2
- package/plugins/kimi-codex/dist/commands/swarm.js +10 -4
- 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/hooks/config-safety.js +15 -7
- package/plugins/kimi-codex/dist/job-store.js +52 -7
- 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/session-recovery.js +203 -0
- package/plugins/kimi-codex/dist/vendor/smol-toml/date.js +50 -21
- package/plugins/kimi-codex/dist/vendor/smol-toml/error.js +6 -2
- package/plugins/kimi-codex/dist/vendor/smol-toml/extract.js +259 -66
- package/plugins/kimi-codex/dist/vendor/smol-toml/parse.js +64 -48
- package/plugins/kimi-codex/dist/vendor/smol-toml/primitive.js +107 -135
- package/plugins/kimi-codex/dist/vendor/smol-toml/struct.js +106 -134
- package/plugins/kimi-codex/dist/vendor/smol-toml/util.js +22 -78
- package/plugins/kimi-codex/dist/version.js +1 -1
- package/plugins/kimi-codex/skills/kimi-ask/SKILL.md +4 -2
- package/plugins/kimi-codex/skills/kimi-challenge/SKILL.md +4 -2
- package/plugins/kimi-codex/skills/kimi-pursue/SKILL.md +3 -1
- package/plugins/kimi-codex/skills/kimi-rescue/SKILL.md +4 -2
- package/plugins/kimi-codex/skills/kimi-review/SKILL.md +4 -2
- 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
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { decideHookOutcomePure } from "./hooks/approval-policy.js";
|
|
2
|
+
// Upstream decorates results it still delivers verbatim otherwise (2.2.0,
|
|
3
|
+
// `agent/toolDedupe/toolDedupeService.ts`): the repeat breaker appends one
|
|
4
|
+
// `\n\n<system-reminder>…</system-reminder>` block from the 3rd identical call
|
|
5
|
+
// (`appendReminder`, also for vetoed results — `finalizeToolResult` runs the
|
|
6
|
+
// `onDidExecuteTool` hooks on every settled result), and at the 12th call it
|
|
7
|
+
// replaces the result with a fixed "not executed" text (`HANDOFF_VETO_TEXT`).
|
|
8
|
+
// Both still mean the hook's veto stood. Re-verify each certification.
|
|
9
|
+
const REMINDER_SUFFIX = /^(?:\n\n<system-reminder>[\s\S]*?<\/system-reminder>)+$/;
|
|
10
|
+
const HANDOFF_VETO_PREFIX = "This turn was ended by the repeat breaker after the same tool call was issued ";
|
|
11
|
+
/** True when `content` is the hook's denial, possibly carrying upstream's repeat-breaker decoration. */
|
|
12
|
+
export function carriesDenial(content, reason) {
|
|
13
|
+
if (content === reason)
|
|
14
|
+
return true;
|
|
15
|
+
if (content.startsWith(reason) && REMINDER_SUFFIX.test(content.slice(reason.length)))
|
|
16
|
+
return true;
|
|
17
|
+
return content.startsWith(HANDOFF_VETO_PREFIX);
|
|
18
|
+
}
|
|
19
|
+
/** Detection evidence, never enforcement. Results arrive after a tool may have
|
|
20
|
+
* executed, and the stream carries no outcome field: a result other than the
|
|
21
|
+
* managed denial is also what upstream emits for calls it rejected BEFORE the
|
|
22
|
+
* hook ran (unknown tool, disabled tool, invalid args, sensitive path, per-tool
|
|
23
|
+
* resolve errors) and what a coexisting hook or permission policy emits when it
|
|
24
|
+
* blocks first. So an escape is reported — logged, persisted, shown — and the
|
|
25
|
+
* run continues; failing the job needs positive execution evidence this stream
|
|
26
|
+
* does not provide. Filesystem policy is never replayed here. */
|
|
27
|
+
export function createHookEscapeMonitor(ctx) {
|
|
28
|
+
const pending = new Map();
|
|
29
|
+
return {
|
|
30
|
+
observe(record) {
|
|
31
|
+
if (!ctx.commandLabel)
|
|
32
|
+
return;
|
|
33
|
+
if (record.role === "assistant") {
|
|
34
|
+
for (const call of record.tool_calls ?? []) {
|
|
35
|
+
let args;
|
|
36
|
+
try {
|
|
37
|
+
args = JSON.parse(call.function.arguments);
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
args = {};
|
|
41
|
+
}
|
|
42
|
+
if (typeof args !== "object" || args === null || Array.isArray(args))
|
|
43
|
+
args = {};
|
|
44
|
+
const result = decideHookOutcomePure({
|
|
45
|
+
hook_event_name: "PreToolUse", tool_name: call.function.name,
|
|
46
|
+
tool_input: args, tool_call_id: call.id, cwd: ctx.cwd,
|
|
47
|
+
}, ctx);
|
|
48
|
+
if (!("evaluator" in result) && result.decision === "deny") {
|
|
49
|
+
pending.set(call.id, { reason: result.reason, tool_name: call.function.name });
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
else if (record.role === "tool") {
|
|
54
|
+
const denied = pending.get(record.tool_call_id);
|
|
55
|
+
if (denied) {
|
|
56
|
+
pending.delete(record.tool_call_id);
|
|
57
|
+
if (!carriesDenial(record.content, denied.reason)) {
|
|
58
|
+
return { tool_call_id: record.tool_call_id, tool_name: denied.tool_name,
|
|
59
|
+
kind: "mismatch", content_excerpt: record.content.slice(0, 200) };
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
/** Every denied call still without a result; call once at settlement. */
|
|
65
|
+
settle() {
|
|
66
|
+
const unresolved = [...pending.entries()].map(([tool_call_id, { tool_name }]) => ({ tool_call_id, tool_name, kind: "unresolved" }));
|
|
67
|
+
pending.clear();
|
|
68
|
+
return unresolved;
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
}
|
|
@@ -81,6 +81,21 @@ const KNOWN_LABELS = new Set([
|
|
|
81
81
|
"swarm-write",
|
|
82
82
|
]);
|
|
83
83
|
export async function decideHookOutcome(input, ctx) {
|
|
84
|
+
const result = decideHookOutcomePure(input, ctx);
|
|
85
|
+
if (!("evaluator" in result))
|
|
86
|
+
return result;
|
|
87
|
+
const toolName = typeof input.tool_name === "string" ? input.tool_name : "";
|
|
88
|
+
if (ctx.rescueEvaluator !== undefined) {
|
|
89
|
+
return await ctx.rescueEvaluator(result.workspaceRoot, toolName, input.tool_input);
|
|
90
|
+
}
|
|
91
|
+
if (READ_ONLY_TOOLS.has(toolName))
|
|
92
|
+
return { decision: "allow" };
|
|
93
|
+
return {
|
|
94
|
+
decision: "deny",
|
|
95
|
+
reason: `kimi-plugin-cc safety hook: ${ctx.commandLabel} evaluator not configured; tool "${toolName}" denied as a safety default.`,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
export function decideHookOutcomePure(input, ctx) {
|
|
84
99
|
const label = typeof ctx.commandLabel === "string" ? ctx.commandLabel : undefined;
|
|
85
100
|
// Out-of-plugin context: any kimi process spawned without our env var
|
|
86
101
|
// is direct user usage. The hook MUST NOT restrict it.
|
|
@@ -176,18 +191,7 @@ export async function decideHookOutcome(input, ctx) {
|
|
|
176
191
|
"This is a plugin misconfiguration — writes are refused rather than risk the user's working tree.",
|
|
177
192
|
};
|
|
178
193
|
}
|
|
179
|
-
|
|
180
|
-
return await ctx.rescueEvaluator(workspaceRoot, toolName, input.tool_input);
|
|
181
|
-
}
|
|
182
|
-
// Stub for callers that didn't inject the evaluator (tests of the policy
|
|
183
|
-
// function alone). The production entry script always injects it.
|
|
184
|
-
if (READ_ONLY_TOOLS.has(toolName)) {
|
|
185
|
-
return { decision: "allow" };
|
|
186
|
-
}
|
|
187
|
-
return {
|
|
188
|
-
decision: "deny",
|
|
189
|
-
reason: `kimi-plugin-cc safety hook: swarm-write evaluator not configured; tool "${toolName}" denied as a safety default.`,
|
|
190
|
-
};
|
|
194
|
+
return { evaluator: "rescue", workspaceRoot };
|
|
191
195
|
}
|
|
192
196
|
case "rescue": {
|
|
193
197
|
const workspaceRoot = typeof ctx.trustedWorkspaceRoot === "string" ? ctx.trustedWorkspaceRoot : "";
|
|
@@ -200,21 +204,7 @@ export async function decideHookOutcome(input, ctx) {
|
|
|
200
204
|
"This is a plugin misconfiguration — writes are refused rather than trusting hook payload cwd.",
|
|
201
205
|
};
|
|
202
206
|
}
|
|
203
|
-
|
|
204
|
-
return await ctx.rescueEvaluator(workspaceRoot, toolName, input.tool_input);
|
|
205
|
-
}
|
|
206
|
-
// Stub for callers that didn't inject the evaluator (e.g.,
|
|
207
|
-
// tests that exercise only the policy function). Allow
|
|
208
|
-
// Read/Grep/Glob and friends; deny everything else. The
|
|
209
|
-
// production entry script wires the real evaluator from
|
|
210
|
-
// runtime/rescue-approval.ts.
|
|
211
|
-
if (READ_ONLY_TOOLS.has(toolName)) {
|
|
212
|
-
return { decision: "allow" };
|
|
213
|
-
}
|
|
214
|
-
return {
|
|
215
|
-
decision: "deny",
|
|
216
|
-
reason: `kimi-plugin-cc safety hook: rescue evaluator not configured; tool "${toolName}" denied as a safety default.`,
|
|
217
|
-
};
|
|
207
|
+
return { evaluator: "rescue", workspaceRoot };
|
|
218
208
|
}
|
|
219
209
|
default:
|
|
220
210
|
if (KNOWN_LABELS.has(label)) {
|
|
@@ -417,15 +417,23 @@ export function normalizeTopLevelInlineHooks(contents, lineEnding) {
|
|
|
417
417
|
entryCount: hooks.length,
|
|
418
418
|
};
|
|
419
419
|
}
|
|
420
|
+
// Same defaults as smol-toml 1.9.1 parse(); this walker only locates assignments.
|
|
421
|
+
// tests/runtime/toml-walker-context.test.ts asserts these against the vendored parse().
|
|
422
|
+
export function assignmentParseContext(s) {
|
|
423
|
+
return { s, p: s.charCodeAt(0) === 0xfeff ? 1 : 0, d: 1_000, bi: false, ld: true, uk: 0 };
|
|
424
|
+
}
|
|
420
425
|
function findTopLevelHooksAssignment(contents) {
|
|
421
|
-
|
|
422
|
-
|
|
426
|
+
const ctx = assignmentParseContext(contents);
|
|
427
|
+
skipVoid(ctx);
|
|
428
|
+
while (ctx.p < contents.length) {
|
|
423
429
|
// A root-level direct assignment cannot resume after entering a table.
|
|
424
|
-
if (contents[
|
|
430
|
+
if (contents[ctx.p] === "[")
|
|
425
431
|
return null;
|
|
426
|
-
const declarationStart =
|
|
427
|
-
const
|
|
428
|
-
|
|
432
|
+
const declarationStart = ctx.p;
|
|
433
|
+
const key = parseKey(ctx);
|
|
434
|
+
skipVoid(ctx, true, true);
|
|
435
|
+
extractValue(ctx, undefined);
|
|
436
|
+
const valueEnd = ctx.p;
|
|
429
437
|
if (key.length === 1 && key[0] === "hooks") {
|
|
430
438
|
const lineStart = contents.lastIndexOf("\n", declarationStart - 1) + 1;
|
|
431
439
|
const start = /^[\t ]*$/.test(contents.slice(lineStart, declarationStart))
|
|
@@ -445,7 +453,7 @@ function findTopLevelHooksAssignment(contents) {
|
|
|
445
453
|
}
|
|
446
454
|
return { start, end, hadLineEnding };
|
|
447
455
|
}
|
|
448
|
-
|
|
456
|
+
skipVoid(ctx);
|
|
449
457
|
}
|
|
450
458
|
return null;
|
|
451
459
|
}
|
package/dist/job-store.js
CHANGED
|
@@ -11,8 +11,11 @@ export class JobStore {
|
|
|
11
11
|
let db;
|
|
12
12
|
try {
|
|
13
13
|
db = createSqliteAdapter(paths.stateDbPath);
|
|
14
|
-
|
|
14
|
+
// busy_timeout FIRST: switching the journal mode takes a brief write lock,
|
|
15
|
+
// and a second opener (status/result racing a worker) otherwise fails
|
|
16
|
+
// immediately with SQLITE_BUSY instead of waiting (seen 2026-10-14).
|
|
15
17
|
db.pragma("busy_timeout = 5000");
|
|
18
|
+
db.pragma("journal_mode = WAL");
|
|
16
19
|
}
|
|
17
20
|
catch (error) {
|
|
18
21
|
try {
|
|
@@ -70,6 +73,11 @@ export class JobStore {
|
|
|
70
73
|
if (!tableHasColumn(this.db, "budget_ms")) {
|
|
71
74
|
this.db.exec(`ALTER TABLE jobs ADD COLUMN budget_ms INTEGER;`);
|
|
72
75
|
}
|
|
76
|
+
for (const column of ["kimi_session_id_source", "budget_source", "thinking_effort", "hook_escapes"]) {
|
|
77
|
+
if (!tableHasColumn(this.db, column)) {
|
|
78
|
+
this.db.exec("ALTER TABLE jobs ADD COLUMN " + column + " TEXT;");
|
|
79
|
+
}
|
|
80
|
+
}
|
|
73
81
|
// Additive provenance migration. Existing rows deliberately remain NULL:
|
|
74
82
|
// v1.9.5 plus kimi-code 0.33/0.34 could have routed to native v2 before the
|
|
75
83
|
// legacy pin landed, so a blanket historical v1 backfill would invent fact.
|
|
@@ -98,6 +106,8 @@ export class JobStore {
|
|
|
98
106
|
CREATE INDEX IF NOT EXISTS jobs_repo_type_updated_idx
|
|
99
107
|
ON jobs (repo_id, command_type, updated_at DESC);
|
|
100
108
|
|
|
109
|
+
CREATE INDEX IF NOT EXISTS jobs_session_id_idx ON jobs (kimi_session_id);
|
|
110
|
+
|
|
101
111
|
CREATE INDEX IF NOT EXISTS jobs_session_idx
|
|
102
112
|
ON jobs (repo_id, kimi_session_id, updated_at DESC);
|
|
103
113
|
|
|
@@ -162,15 +172,15 @@ export class JobStore {
|
|
|
162
172
|
try {
|
|
163
173
|
this.db.run(`
|
|
164
174
|
INSERT INTO jobs (
|
|
165
|
-
job_id, repo_id, command_type, created_at, updated_at, cwd, model, thinking,
|
|
166
|
-
background, pid, kimi_pid, status, kimi_session_id,
|
|
175
|
+
job_id, repo_id, command_type, created_at, updated_at, cwd, model, thinking, thinking_effort,
|
|
176
|
+
background, pid, kimi_pid, status, kimi_session_id, kimi_session_id_source, budget_source,
|
|
167
177
|
operation_kind, intended_engine, observed_engine, kimi_version, system_version,
|
|
168
178
|
kimi_command, kimi_prefix_args, plan_certification, resumed_from_job_id, safety_profile,
|
|
169
179
|
agent_profile, prompt_digest, summary, phase, budget_ms, final_output_path, stream_log_path, error
|
|
170
180
|
)
|
|
171
181
|
VALUES (
|
|
172
|
-
@job_id, @repo_id, @command_type, @created_at, @updated_at, @cwd, @model, @thinking,
|
|
173
|
-
@background, @pid, @kimi_pid, @status, @kimi_session_id,
|
|
182
|
+
@job_id, @repo_id, @command_type, @created_at, @updated_at, @cwd, @model, @thinking, @thinking_effort,
|
|
183
|
+
@background, @pid, @kimi_pid, @status, @kimi_session_id, @kimi_session_id_source, @budget_source,
|
|
174
184
|
@operation_kind, @intended_engine, @observed_engine, @kimi_version, @system_version,
|
|
175
185
|
@kimi_command, @kimi_prefix_args, @plan_certification, @resumed_from_job_id, @safety_profile,
|
|
176
186
|
@agent_profile, @prompt_digest, @summary, @phase, @budget_ms, @final_output_path, @stream_log_path, @error
|
|
@@ -198,6 +208,10 @@ export class JobStore {
|
|
|
198
208
|
const row = this.db.get("SELECT * FROM jobs WHERE job_id = ?", jobId);
|
|
199
209
|
return row ? hydrateRow(row) : null;
|
|
200
210
|
}
|
|
211
|
+
/** Global across repos and terminal states; optionally exclude the current row. */
|
|
212
|
+
hasSessionId(sessionId, exceptJobId = "") {
|
|
213
|
+
return Boolean(this.db.get("SELECT 1 FROM jobs WHERE kimi_session_id = ? AND job_id != ? LIMIT 1", sessionId, exceptJobId));
|
|
214
|
+
}
|
|
201
215
|
findLatestJob(options) {
|
|
202
216
|
const conditions = ["repo_id = @repo_id"];
|
|
203
217
|
const params = {
|
|
@@ -247,6 +261,18 @@ export class JobStore {
|
|
|
247
261
|
updateRunningJob(jobId, patch) {
|
|
248
262
|
return this.updateWhere(jobId, patch, "status = 'running'");
|
|
249
263
|
}
|
|
264
|
+
/** Recheck uniqueness atomically: another worker can claim an ID during the
|
|
265
|
+
* asynchronous directory scan. Ordinary announced/resumed IDs may be shared. */
|
|
266
|
+
claimRecoveredSessionId(jobId, sessionId) {
|
|
267
|
+
this.db.run(`UPDATE jobs
|
|
268
|
+
SET kimi_session_id = @session_id, kimi_session_id_source = 'recovered_session_dir',
|
|
269
|
+
updated_at = @updated_at
|
|
270
|
+
WHERE job_id = @job_id AND status = 'running' AND kimi_session_id IS NULL
|
|
271
|
+
AND NOT EXISTS (SELECT 1 FROM jobs WHERE kimi_session_id = @session_id AND job_id != @job_id)`, { job_id: jobId, session_id: sessionId, updated_at: new Date().toISOString() });
|
|
272
|
+
const job = this.getJob(jobId);
|
|
273
|
+
return job?.status === "running" && job.kimi_session_id === sessionId &&
|
|
274
|
+
job.kimi_session_id_source === "recovered_session_dir" ? job : null;
|
|
275
|
+
}
|
|
250
276
|
/**
|
|
251
277
|
* Evidence-only backfill for pre-provenance rows. COALESCE prevents forensic
|
|
252
278
|
* inspection from overwriting a field already recorded at execution time.
|
|
@@ -280,10 +306,10 @@ export class JobStore {
|
|
|
280
306
|
return this.updateWhere(jobId, { ...patch, status: "cancelled", pid: null, kimi_pid: null }, "status = 'running'");
|
|
281
307
|
}
|
|
282
308
|
static JOBS_COLUMNS = new Set([
|
|
283
|
-
"status", "summary", "phase", "budget_ms", "pid", "kimi_pid",
|
|
309
|
+
"status", "summary", "phase", "budget_ms", "budget_source", "kimi_session_id_source", "pid", "kimi_pid",
|
|
284
310
|
"final_output_path", "stream_log_path", "kimi_session_id",
|
|
285
311
|
"error", "command_type", "prompt_digest", "repo_id",
|
|
286
|
-
"cwd", "model", "thinking", "background", "agent_profile",
|
|
312
|
+
"cwd", "model", "thinking", "thinking_effort", "hook_escapes", "background", "agent_profile",
|
|
287
313
|
"operation_kind", "intended_engine", "observed_engine", "kimi_version",
|
|
288
314
|
"system_version", "kimi_command", "kimi_prefix_args", "plan_certification",
|
|
289
315
|
"resumed_from_job_id", "safety_profile",
|
|
@@ -323,8 +349,11 @@ function hydrateRow(row) {
|
|
|
323
349
|
function serializeRecord(record) {
|
|
324
350
|
return {
|
|
325
351
|
...record,
|
|
352
|
+
thinking_effort: record.thinking_effort ?? null,
|
|
326
353
|
phase: record.phase ?? null,
|
|
327
354
|
budget_ms: record.budget_ms ?? null,
|
|
355
|
+
budget_source: record.budget_source ?? null,
|
|
356
|
+
kimi_session_id_source: record.kimi_session_id_source ?? null,
|
|
328
357
|
operation_kind: record.operation_kind ?? null,
|
|
329
358
|
intended_engine: record.intended_engine ?? null,
|
|
330
359
|
observed_engine: record.observed_engine ?? null,
|
|
@@ -562,3 +591,19 @@ export function withJobStoreSync(paths, fn) {
|
|
|
562
591
|
store?.close();
|
|
563
592
|
}
|
|
564
593
|
}
|
|
594
|
+
/** Old non-null budgets have unknown provenance; never invent explicit intent. */
|
|
595
|
+
/** Parsed `hook_escapes` column for JSON envelopes: [] when NULL or unreadable. */
|
|
596
|
+
export function parseHookEscapes(raw) {
|
|
597
|
+
if (!raw)
|
|
598
|
+
return [];
|
|
599
|
+
try {
|
|
600
|
+
const parsed = JSON.parse(raw);
|
|
601
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
602
|
+
}
|
|
603
|
+
catch {
|
|
604
|
+
return [];
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
export function renderBudgetSource(job) {
|
|
608
|
+
return job.budget_source ?? (job.budget_ms === null ? "default" : "unknown");
|
|
609
|
+
}
|
package/dist/kimi-engine.js
CHANGED
|
@@ -206,6 +206,8 @@ export function observedExecutionFields(result) {
|
|
|
206
206
|
return {
|
|
207
207
|
observed_engine: result.observedEngine,
|
|
208
208
|
system_version: result.systemVersion ?? null,
|
|
209
|
+
// Detection evidence from runtime/hook-escape.ts, JSON array or NULL.
|
|
210
|
+
hook_escapes: result.hookEscapes && result.hookEscapes.length > 0 ? JSON.stringify(result.hookEscapes) : null,
|
|
209
211
|
};
|
|
210
212
|
}
|
|
211
213
|
export function executionPlanFromPersisted(fields) {
|
package/dist/kimi-errors.js
CHANGED
|
@@ -45,7 +45,7 @@ export function classifyManagedCommandFailure(error, commandType, jobId, options
|
|
|
45
45
|
if (error instanceof RuntimeError) {
|
|
46
46
|
details.cause_code = error.code;
|
|
47
47
|
if (error.code === "RESPONSE_TIMEOUT") {
|
|
48
|
-
for (const key of ["budget_ms", "record_count", "partial_output_chars", "aborted", "settled_post_abort"]) {
|
|
48
|
+
for (const key of ["budget_ms", "record_count", "partial_output_chars", "aborted", "settled_post_abort", "hook_escapes"]) {
|
|
49
49
|
if (error.details[key] !== undefined)
|
|
50
50
|
details[key] = error.details[key];
|
|
51
51
|
}
|
package/dist/kimi-timeouts.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
// startup failures surface as CLI_SPAWN_FAILED / CLI_PROCESS_ERROR —
|
|
9
9
|
// not as separate timeouts. So only the per-command response budgets
|
|
10
10
|
// remain.
|
|
11
|
-
// Budgets
|
|
11
|
+
// Budgets accommodate thinking-capable workflows; per-run effort is only a request.
|
|
12
12
|
// kimi-code spends real wallclock on extended reasoning before emitting
|
|
13
13
|
// the first assistant record; pre-alpha.4 budgets (300s/600s) were sized
|
|
14
14
|
// against the non-thinking path and timed out in production smoke
|
|
@@ -49,15 +49,11 @@ export const KIMI_SWARM_DEFAULT_BUDGET_MS = 1_800_000; // 30 minutes
|
|
|
49
49
|
* Review-gate budget. Fires inside Claude Code's Stop hook so any value
|
|
50
50
|
* above the user's perceptible wait makes the gate feel broken.
|
|
51
51
|
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
* selected for review-gate (via KIMI_PLUGIN_CC_REVIEW_GATE_MODEL).
|
|
58
|
-
* Under thinking-on the gate will time out — the gate is fail-open on
|
|
59
|
-
* timeout by design, so the worst case is the gate becomes
|
|
60
|
-
* always-allow. See docs/safety.md.
|
|
52
|
+
* The gate requests off through KIMI_MODEL_THINKING_EFFORT by default;
|
|
53
|
+
* KIMI_PLUGIN_CC_REVIEW_GATE_THINKING can inherit or request another effort.
|
|
54
|
+
* BYO/non-trait-driven providers and always-thinking models can ignore it.
|
|
55
|
+
* The 8s timeout remains fail-open; a request never guarantees completion.
|
|
56
|
+
* Hook-enforcement escape is a separate blocking failure. See docs/safety.md.
|
|
61
57
|
*/
|
|
62
58
|
export const KIMI_REVIEW_GATE_TIMEOUT_MS = 8_000;
|
|
63
59
|
export function isTimeoutCode(code) {
|
|
@@ -198,8 +198,8 @@ export const KIMI_TESTED_MINORS = [
|
|
|
198
198
|
// import it; `-p` stdout stays the direct PromptJsonWriter), session-lifecycle
|
|
199
199
|
// changes that only HELP our cancellation story (active-turn cancel on close +
|
|
200
200
|
// BACKGROUND_KEEP_ALIVE_ON_EXIT default flipped true→false), an `alwaysThinking`
|
|
201
|
-
// model-capability flag (
|
|
202
|
-
//
|
|
201
|
+
// model-capability flag (the H5 env request ships in plugin 2.2.1,
|
|
202
|
+
// but always-thinking models need not honour it), a SIGHUP cleanup handler (exit 129; SIGTERM still
|
|
203
203
|
// exits 143 and runs cleanup — our SIGTERM→SIGKILL reaping is unaffected), and
|
|
204
204
|
// a new builtin `import-from-cc-codex` skill (not a plugin surface).
|
|
205
205
|
// See .claude/kimi-code-research/reports/72-76 for the audit reports.
|
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
|
}
|