kimi-plugin-cc 2.0.7 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +50 -0
- package/README.md +22 -6
- package/agents/kimi-ask.md +3 -0
- package/agents/kimi-challenge.md +3 -1
- package/agents/kimi-rescue.md +3 -0
- package/agents/kimi-review.md +3 -1
- package/commands/ask.md +6 -2
- package/commands/challenge.md +6 -2
- package/commands/rescue.md +6 -2
- package/commands/review.md +6 -2
- package/dist/cli-client.js +36 -13
- package/dist/commands/ask.js +16 -7
- package/dist/commands/cli-helpers.js +80 -0
- package/dist/commands/pursue.js +4 -2
- package/dist/commands/repair-sessions.js +2 -1
- package/dist/commands/replay.js +3 -2
- package/dist/commands/rescue.js +19 -9
- package/dist/commands/result.js +7 -1
- package/dist/commands/review-gate.js +2 -1
- package/dist/commands/review.js +8 -5
- package/dist/commands/setup.js +2 -1
- package/dist/commands/status.js +2 -2
- package/dist/commands/swarm.js +7 -3
- package/dist/companion.js +6 -0
- package/dist/help.js +40 -0
- package/dist/hooks/config-safety.js +18 -9
- package/dist/job-store.js +43 -6
- package/dist/jobs.js +7 -3
- package/dist/kimi-engine.js +1 -0
- package/dist/kimi-errors.js +35 -1
- package/dist/kimi-timeouts.js +7 -7
- package/dist/parsing.js +87 -13
- package/dist/render.js +19 -1
- 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/migration.md +3 -3
- package/docs/registry-releases.md +35 -1
- package/docs/safety.md +49 -2
- package/package.json +2 -2
- package/plugins/kimi-codex/.codex-plugin/plugin.json +1 -1
- package/plugins/kimi-codex/dist/cli-client.js +36 -13
- package/plugins/kimi-codex/dist/commands/ask.js +16 -7
- package/plugins/kimi-codex/dist/commands/cli-helpers.js +80 -0
- package/plugins/kimi-codex/dist/commands/pursue.js +4 -2
- package/plugins/kimi-codex/dist/commands/repair-sessions.js +2 -1
- package/plugins/kimi-codex/dist/commands/replay.js +3 -2
- package/plugins/kimi-codex/dist/commands/rescue.js +19 -9
- package/plugins/kimi-codex/dist/commands/result.js +7 -1
- package/plugins/kimi-codex/dist/commands/review-gate.js +2 -1
- package/plugins/kimi-codex/dist/commands/review.js +8 -5
- package/plugins/kimi-codex/dist/commands/setup.js +2 -1
- 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/companion.js +6 -0
- package/plugins/kimi-codex/dist/help.js +40 -0
- package/plugins/kimi-codex/dist/hooks/config-safety.js +18 -9
- package/plugins/kimi-codex/dist/job-store.js +43 -6
- package/plugins/kimi-codex/dist/jobs.js +7 -3
- package/plugins/kimi-codex/dist/kimi-engine.js +1 -0
- package/plugins/kimi-codex/dist/kimi-errors.js +35 -1
- package/plugins/kimi-codex/dist/kimi-timeouts.js +7 -7
- package/plugins/kimi-codex/dist/parsing.js +87 -13
- package/plugins/kimi-codex/dist/render.js +19 -1
- 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 +3 -1
- package/plugins/kimi-codex/skills/kimi-challenge/SKILL.md +2 -1
- package/plugins/kimi-codex/skills/kimi-rescue/SKILL.md +3 -1
- package/plugins/kimi-codex/skills/kimi-review/SKILL.md +3 -2
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import { RuntimeError } from "../errors.js";
|
|
2
|
+
import { ResponseTimeoutError } from "../cli-client.js";
|
|
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";
|
|
2
7
|
/**
|
|
3
8
|
* Reassemble prose final text from cli-client records.
|
|
4
9
|
*
|
|
@@ -110,3 +115,78 @@ export function assertCliResultSuccess(result, stage) {
|
|
|
110
115
|
});
|
|
111
116
|
}
|
|
112
117
|
}
|
|
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;
|
|
123
|
+
const timeout = classified.cause;
|
|
124
|
+
if (!(timeout instanceof ResponseTimeoutError) || !timeout.partialResult)
|
|
125
|
+
return undefined;
|
|
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;
|
|
154
|
+
// Best-effort metadata only. The row is still `running`, and a session id
|
|
155
|
+
// written here can collide with `jobs_running_session_idx` when a resume of
|
|
156
|
+
// the same session was started while this job ran. A store error must never
|
|
157
|
+
// prevent markJobFailed from running — the partial artifact matters more
|
|
158
|
+
// than the metadata, so swallow and fall through.
|
|
159
|
+
try {
|
|
160
|
+
store.updateRunningJob(job.job_id, observedExecutionFields(result));
|
|
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";
|
|
171
|
+
}
|
|
172
|
+
}
|
|
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";
|
|
177
|
+
// Diagnostics stay in the stream log; the failure transition proceeds.
|
|
178
|
+
}
|
|
179
|
+
return {
|
|
180
|
+
text: reassembleProseFromRecords(result.records),
|
|
181
|
+
toolCalls: result.records.some((record) => record.role === "tool" ||
|
|
182
|
+
(record.role === "assistant" && Array.isArray(record.tool_calls) &&
|
|
183
|
+
record.tool_calls.length > 0)),
|
|
184
|
+
recordCount: result.records.length,
|
|
185
|
+
budgetMs: timeout.details.budget_ms,
|
|
186
|
+
};
|
|
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
|
+
}
|
package/dist/commands/pursue.js
CHANGED
|
@@ -143,6 +143,8 @@ export async function runPursue(argv, context) {
|
|
|
143
143
|
model: parsed.model ?? null,
|
|
144
144
|
thinking: parsed.thinking ?? null,
|
|
145
145
|
background: false,
|
|
146
|
+
budget_ms: parsed.budgetMs ?? KIMI_PURSUE_DEFAULT_BUDGET_MS,
|
|
147
|
+
budget_source: parsed.budgetMs !== undefined ? "explicit" : "default",
|
|
146
148
|
pid: process.pid,
|
|
147
149
|
kimi_pid: null,
|
|
148
150
|
status: "running",
|
|
@@ -168,7 +170,7 @@ export async function runPursue(argv, context) {
|
|
|
168
170
|
await markJobFailed(store, paths, job, classified, "Pursue failed.", { phase: "failed" });
|
|
169
171
|
throw classified;
|
|
170
172
|
}
|
|
171
|
-
const completed = await executePursueJob(job.job_id, prompt, objective,
|
|
173
|
+
const completed = await executePursueJob(job.job_id, prompt, objective, job.budget_ms, context);
|
|
172
174
|
if (!completed.final_output_path) {
|
|
173
175
|
throw new RuntimeError("PURSUE_RESULT_MISSING", "Pursue finished without a rendered result.", "pursue.result");
|
|
174
176
|
}
|
|
@@ -249,7 +251,7 @@ async function executePursueJob(jobId, prompt, objective, budgetMs, context) {
|
|
|
249
251
|
if (result.sessionId !== undefined &&
|
|
250
252
|
result.sessionId.length > 0 &&
|
|
251
253
|
result.sessionId !== job.kimi_session_id) {
|
|
252
|
-
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 });
|
|
253
255
|
}
|
|
254
256
|
await syncKimiSessionTitle({
|
|
255
257
|
promptText: objective,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { REPAIR_SESSIONS_SUPPORTED_FLAGS } from "../parsing.js";
|
|
1
2
|
import { constants } from "node:fs";
|
|
2
3
|
import { lstat, open } from "node:fs/promises";
|
|
3
4
|
import { createRequire } from "node:module";
|
|
@@ -15,7 +16,7 @@ export async function runRepairSessions(argv, context) {
|
|
|
15
16
|
const seen = new Set();
|
|
16
17
|
for (const arg of argv) {
|
|
17
18
|
if ((arg !== "--apply" && arg !== "--all") || seen.has(arg)) {
|
|
18
|
-
throw new RuntimeError("INVALID_ARGS",
|
|
19
|
+
throw new RuntimeError("INVALID_ARGS", `Usage: repair-sessions. Supported flags: ${REPAIR_SESSIONS_SUPPORTED_FLAGS}.`, "repair-sessions.args");
|
|
19
20
|
}
|
|
20
21
|
seen.add(arg);
|
|
21
22
|
}
|
package/dist/commands/replay.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { REPLAY_SUPPORTED_FLAGS } from "../parsing.js";
|
|
1
2
|
import { access, stat, readFile } from "node:fs/promises";
|
|
2
3
|
import { constants as fsConstants } from "node:fs";
|
|
3
4
|
import { RuntimeError } from "../errors.js";
|
|
@@ -9,8 +10,8 @@ import { sweepStaleJobs } from "../jobs.js";
|
|
|
9
10
|
const MAX_REPLAY_LOG_BYTES = 32 * 1024 * 1024;
|
|
10
11
|
export async function runReplay(argv, context) {
|
|
11
12
|
const [jobId, ...rest] = argv;
|
|
12
|
-
if (!jobId || rest.length > 0) {
|
|
13
|
-
throw new RuntimeError("INVALID_ARGS",
|
|
13
|
+
if (!jobId || jobId.startsWith("-") || rest.length > 0) {
|
|
14
|
+
throw new RuntimeError("INVALID_ARGS", `replay expects exactly one job id: replay <job-id>. Supported flags: ${REPLAY_SUPPORTED_FLAGS}.`, "replay.parse");
|
|
14
15
|
}
|
|
15
16
|
const paths = resolvePluginPaths(context.env);
|
|
16
17
|
await ensurePluginPaths(paths);
|
package/dist/commands/rescue.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { companionHelp } from "../help.js";
|
|
1
2
|
import { randomUUID } from "node:crypto";
|
|
2
3
|
import path from "node:path";
|
|
3
4
|
import { createCliCancellationHandlers } from "../cli-cancellation.js";
|
|
@@ -16,7 +17,7 @@ import { parseRescueArgs } from "../parsing.js";
|
|
|
16
17
|
import { readArtifact, renderManagedJobOutput, writeArtifact } from "../render.js";
|
|
17
18
|
import { startBackgroundJob } from "../background-spawn.js";
|
|
18
19
|
import { hookRefusalDetails, hookRefusalRetryProtocol, maybeWarnHookMissing, verifyHookInstalled, } from "../hooks/install.js";
|
|
19
|
-
import { assertCliResultSuccess, reassembleProseFromRecords, warnIfSessionIdMissing } from "./cli-helpers.js";
|
|
20
|
+
import { warnInheritedBudget, captureTimeoutPartialOutput, assertCliResultSuccess, reassembleProseFromRecords, warnIfSessionIdMissing } from "./cli-helpers.js";
|
|
20
21
|
import { buildKimiSessionTitle, syncKimiSessionTitle } from "../session-title.js";
|
|
21
22
|
export { describeMissingResult } from "../background-spawn.js";
|
|
22
23
|
// v1.0 cutover note (PR 3):
|
|
@@ -41,6 +42,8 @@ const RESCUE_SUMMARY_MAX = 120;
|
|
|
41
42
|
const RESCUE_AGENT_PROFILE_PLACEHOLDER = "<cli-client>";
|
|
42
43
|
export async function runRescue(argv, context) {
|
|
43
44
|
const parsed = parseRescueArgs(argv);
|
|
45
|
+
if (parsed.help)
|
|
46
|
+
return companionHelp(["task", "rescue", ...argv]);
|
|
44
47
|
const paths = resolvePluginPaths(context.env);
|
|
45
48
|
await ensurePluginPaths(paths);
|
|
46
49
|
const repoIdentity = await resolveRepoIdentity(context.cwd);
|
|
@@ -68,10 +71,14 @@ export async function runRescue(argv, context) {
|
|
|
68
71
|
model: parsed.model ?? null,
|
|
69
72
|
thinking: parsed.thinking ?? null,
|
|
70
73
|
background: parsed.background,
|
|
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",
|
|
71
77
|
pid: parsed.background ? null : process.pid,
|
|
72
78
|
kimi_pid: null,
|
|
73
79
|
status: "running",
|
|
74
80
|
kimi_session_id: sessionResolution.kimiSessionId,
|
|
81
|
+
kimi_session_id_source: sessionResolution.sourceJob?.kimi_session_id_source ?? null,
|
|
75
82
|
...persistedExecutionPlanFields(executionPlan),
|
|
76
83
|
agent_profile: RESCUE_AGENT_PROFILE_PLACEHOLDER,
|
|
77
84
|
prompt_digest: digestPrompt(prompt),
|
|
@@ -93,6 +100,7 @@ export async function runRescue(argv, context) {
|
|
|
93
100
|
await markJobFailed(store, paths, job, classified, rescueConfig.cancellation.failedSummary, { phase: "failed" });
|
|
94
101
|
throw classified;
|
|
95
102
|
}
|
|
103
|
+
warnInheritedBudget(job, context.stderr);
|
|
96
104
|
if (parsed.background) {
|
|
97
105
|
return startBackgroundJob(job, prompt, context, paths, {
|
|
98
106
|
workerKind: "rescue",
|
|
@@ -185,18 +193,17 @@ export async function executeRescueJob(jobId, prompt, context, options) {
|
|
|
185
193
|
signal: handlers.signal,
|
|
186
194
|
// SIGKILL escalation defaults to 1500ms inside cli-client —
|
|
187
195
|
// matches v0.4's cancellation.ts behavior for the wire client.
|
|
188
|
-
}, KIMI_RESCUE_PROMPT_TIMEOUT_MS, "rescue.prompt");
|
|
196
|
+
}, job.budget_ms ?? KIMI_RESCUE_PROMPT_TIMEOUT_MS, "rescue.prompt");
|
|
189
197
|
store.updateRunningJob(job.job_id, observedExecutionFields(result));
|
|
190
198
|
if (handlers.cancelling) {
|
|
191
199
|
throw new RuntimeError(cancel.errorCodes.cancelled, cancel.cancelMessages.afterPrompt, "rescue.runtime");
|
|
192
200
|
}
|
|
193
201
|
assertCliResultSuccess(result, "rescue.runtime");
|
|
194
202
|
if (result.sessionId !== undefined &&
|
|
195
|
-
result.sessionId.length > 0
|
|
196
|
-
result.sessionId !== job.kimi_session_id) {
|
|
203
|
+
result.sessionId.length > 0) {
|
|
197
204
|
// length>0 guard: empty-string capture would poison the row. (Kimi
|
|
198
205
|
// alpha.4 challenge finding #3.)
|
|
199
|
-
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 });
|
|
200
207
|
}
|
|
201
208
|
await syncKimiSessionTitle({
|
|
202
209
|
promptText: prompt,
|
|
@@ -242,14 +249,17 @@ export async function executeRescueJob(jobId, prompt, context, options) {
|
|
|
242
249
|
}) ?? job);
|
|
243
250
|
}
|
|
244
251
|
catch (error) {
|
|
252
|
+
const classified = classifyManagedCommandFailure(error, "rescue", job.job_id, {
|
|
253
|
+
preserveStage: true,
|
|
254
|
+
background: job.background,
|
|
255
|
+
budgetMs: job.budget_ms ?? KIMI_RESCUE_PROMPT_TIMEOUT_MS,
|
|
256
|
+
});
|
|
257
|
+
const partialOutput = await captureTimeoutPartialOutput(classified, store, job, context.env, handlers.signal);
|
|
245
258
|
if (handlers.cancelling) {
|
|
246
259
|
const cancelledError = new RuntimeError(cancel.errorCodes.cancelled, cancel.cancelMessages.default, "rescue.runtime", error instanceof Error ? { cause: error } : undefined);
|
|
247
260
|
return await markJobCancelled(store, paths, job, cancel.cancelledSummary, cancelledError, { phase: "cancelled" });
|
|
248
261
|
}
|
|
249
|
-
|
|
250
|
-
preserveStage: true,
|
|
251
|
-
});
|
|
252
|
-
return await markJobFailed(store, paths, job, classified, cancel.failedSummary, { phase: "failed" });
|
|
262
|
+
return await markJobFailed(store, paths, job, classified, cancel.failedSummary, { phase: "failed", partialOutput });
|
|
253
263
|
}
|
|
254
264
|
finally {
|
|
255
265
|
handlers.dispose();
|
package/dist/commands/result.js
CHANGED
|
@@ -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";
|
|
@@ -42,6 +42,12 @@ function renderResultEnvelope(job, artifactPath, body) {
|
|
|
42
42
|
job_id: job.job_id,
|
|
43
43
|
kind: job.command_type,
|
|
44
44
|
status: job.status,
|
|
45
|
+
budget_ms: job.budget_ms,
|
|
46
|
+
budget_source: renderBudgetSource(job),
|
|
47
|
+
kimi_session_id_source: job.kimi_session_id_source,
|
|
48
|
+
kimi_session_id: job.kimi_session_id,
|
|
49
|
+
// True when a budget timeout preserved assistant prose in the artifact.
|
|
50
|
+
partial_output: job.status === "failed" && Number(job.error?.details?.partial_output_chars ?? 0) > 0,
|
|
45
51
|
summary: job.summary,
|
|
46
52
|
error: job.error,
|
|
47
53
|
artifact_path: artifactPath,
|
|
@@ -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);
|
package/dist/commands/review.js
CHANGED
|
@@ -17,7 +17,7 @@ import { renderManagedJobOutput, writeArtifact } from "../render.js";
|
|
|
17
17
|
import { RuntimeError } from "../errors.js";
|
|
18
18
|
import { resolveRepoIdentity } from "../git.js";
|
|
19
19
|
import { hookRefusalDetails, hookRefusalRetryProtocol, maybeWarnHookMissing, verifyHookInstalled, } from "../hooks/install.js";
|
|
20
|
-
import { assertCliResultSuccess, reassembleProseFromRecords, warnIfSessionIdMissing } from "./cli-helpers.js";
|
|
20
|
+
import { 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
|
//
|
|
@@ -81,6 +81,8 @@ export async function runReview(argv, context, commandType) {
|
|
|
81
81
|
model: parsed.model ?? null,
|
|
82
82
|
thinking: parsed.thinking ?? null,
|
|
83
83
|
background: false,
|
|
84
|
+
budget_ms: parsed.budgetMs ?? null,
|
|
85
|
+
budget_source: parsed.budgetMs !== undefined ? "explicit" : "default",
|
|
84
86
|
pid: process.pid,
|
|
85
87
|
kimi_pid: null,
|
|
86
88
|
status: "running",
|
|
@@ -113,7 +115,7 @@ export async function runReview(argv, context, commandType) {
|
|
|
113
115
|
model: parsed.model,
|
|
114
116
|
logPath,
|
|
115
117
|
signal: handlers.signal,
|
|
116
|
-
}, KIMI_REVIEW_PROMPT_TIMEOUT_MS, `${commandType}.prompt`);
|
|
118
|
+
}, job.budget_ms ?? KIMI_REVIEW_PROMPT_TIMEOUT_MS, `${commandType}.prompt`);
|
|
117
119
|
store.updateRunningJob(job.job_id, observedExecutionFields(result));
|
|
118
120
|
if (handlers.cancelling) {
|
|
119
121
|
throw new RuntimeError(cancel.errorCodes.cancelled, cancel.cancelMessages.afterPrompt, `${commandType}.runtime`);
|
|
@@ -127,7 +129,7 @@ export async function runReview(argv, context, commandType) {
|
|
|
127
129
|
// poison the row — consumers reading SQLite distinguish "missing"
|
|
128
130
|
// (NULL) from "captured" (non-empty UUID) only via NULL-ness.
|
|
129
131
|
// (Kimi alpha.4 challenge finding #3.)
|
|
130
|
-
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 });
|
|
131
133
|
}
|
|
132
134
|
await syncKimiSessionTitle({
|
|
133
135
|
promptText: previewPrompt,
|
|
@@ -158,13 +160,14 @@ export async function runReview(argv, context, commandType) {
|
|
|
158
160
|
return rendered.output;
|
|
159
161
|
}
|
|
160
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);
|
|
161
165
|
if (handlers.cancelling) {
|
|
162
166
|
const cancelledError = new RuntimeError(cancel.errorCodes.cancelled, cancel.cancelMessages.default, `${commandType}.runtime`, error instanceof Error ? { cause: error } : undefined);
|
|
163
167
|
await markJobCancelled(store, paths, job, cancel.cancelledSummary, cancelledError);
|
|
164
168
|
throw cancelledError;
|
|
165
169
|
}
|
|
166
|
-
|
|
167
|
-
await markJobFailed(store, paths, job, classified, cancel.failedSummary);
|
|
170
|
+
await markJobFailed(store, paths, job, classified, cancel.failedSummary, { partialOutput });
|
|
168
171
|
throw classified;
|
|
169
172
|
}
|
|
170
173
|
finally {
|
package/dist/commands/setup.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { SETUP_SUPPORTED_FLAGS } from "../parsing.js";
|
|
1
2
|
// Managed-block installer for the kimi-code PreToolUse hook.
|
|
2
3
|
//
|
|
3
4
|
// Replaces the v0.4 Wire-based setup probe. In v1.0 the load-bearing
|
|
@@ -127,7 +128,7 @@ function parseArgs(argv) {
|
|
|
127
128
|
disableReviewGate = true;
|
|
128
129
|
break;
|
|
129
130
|
default:
|
|
130
|
-
throw new RuntimeError("INVALID_ARGS", `Unknown setup flag ${token}. Supported flags:
|
|
131
|
+
throw new RuntimeError("INVALID_ARGS", `Unknown setup flag ${token}. Supported flags: ${SETUP_SUPPORTED_FLAGS}.`, "setup.parse");
|
|
131
132
|
}
|
|
132
133
|
}
|
|
133
134
|
if (enableReviewGate && disableReviewGate) {
|
package/dist/commands/status.js
CHANGED
|
@@ -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
|
}
|
package/dist/commands/swarm.js
CHANGED
|
@@ -210,6 +210,8 @@ async function runReadSwarm(parsed, objective, context) {
|
|
|
210
210
|
model: parsed.model ?? null,
|
|
211
211
|
thinking: parsed.thinking ?? null,
|
|
212
212
|
background: false,
|
|
213
|
+
budget_ms: parsed.budgetMs ?? KIMI_SWARM_DEFAULT_BUDGET_MS,
|
|
214
|
+
budget_source: parsed.budgetMs !== undefined ? "explicit" : "default",
|
|
213
215
|
pid: process.pid,
|
|
214
216
|
kimi_pid: null,
|
|
215
217
|
status: "running",
|
|
@@ -235,7 +237,7 @@ async function runReadSwarm(parsed, objective, context) {
|
|
|
235
237
|
await markJobFailed(store, paths, job, classified, "Swarm failed.", { phase: "failed" });
|
|
236
238
|
throw classified;
|
|
237
239
|
}
|
|
238
|
-
const completed = await executeSwarmJob(job.job_id, prompt, objective,
|
|
240
|
+
const completed = await executeSwarmJob(job.job_id, prompt, objective, job.budget_ms, resolveSwarmMaxConcurrency(parsed.maxConcurrency), context);
|
|
239
241
|
if (!completed.final_output_path) {
|
|
240
242
|
throw new RuntimeError("SWARM_RESULT_MISSING", "Swarm finished without a rendered result.", "swarm.result");
|
|
241
243
|
}
|
|
@@ -308,6 +310,8 @@ async function runWriteSwarm(parsed, objective, context) {
|
|
|
308
310
|
model: parsed.model ?? null,
|
|
309
311
|
thinking: parsed.thinking ?? null,
|
|
310
312
|
background: false,
|
|
313
|
+
budget_ms: parsed.budgetMs ?? KIMI_SWARM_DEFAULT_BUDGET_MS,
|
|
314
|
+
budget_source: parsed.budgetMs !== undefined ? "explicit" : "default",
|
|
311
315
|
pid: process.pid,
|
|
312
316
|
kimi_pid: null,
|
|
313
317
|
status: "running",
|
|
@@ -335,7 +339,7 @@ async function runWriteSwarm(parsed, objective, context) {
|
|
|
335
339
|
});
|
|
336
340
|
throw classified;
|
|
337
341
|
}
|
|
338
|
-
const completed = await executeSwarmJob(job.job_id, prompt, objective,
|
|
342
|
+
const completed = await executeSwarmJob(job.job_id, prompt, objective, job.budget_ms, resolveSwarmWriteMaxConcurrency(parsed.maxConcurrency), context, { spawnCwd: worktreePath, workspaceRoot: worktreePath });
|
|
339
343
|
if (!completed.final_output_path) {
|
|
340
344
|
throw new RuntimeError("SWARM_RESULT_MISSING", "Swarm (write) finished without a rendered result.", "swarm.result");
|
|
341
345
|
}
|
|
@@ -500,7 +504,7 @@ writeMode) {
|
|
|
500
504
|
if (result.sessionId !== undefined &&
|
|
501
505
|
result.sessionId.length > 0 &&
|
|
502
506
|
result.sessionId !== job.kimi_session_id) {
|
|
503
|
-
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 });
|
|
504
508
|
}
|
|
505
509
|
await syncKimiSessionTitle({
|
|
506
510
|
promptText: objective,
|
package/dist/companion.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { companionHelp } from "./help.js";
|
|
1
2
|
import { runAsk } from "./commands/ask.js";
|
|
2
3
|
import { runModelInventory } from "./commands/models.js";
|
|
3
4
|
import { runCancel } from "./commands/cancel.js";
|
|
@@ -15,6 +16,11 @@ import { runWorker } from "./commands/worker.js";
|
|
|
15
16
|
import { formatError, RuntimeError } from "./errors.js";
|
|
16
17
|
import { resolveKimiHome } from "./kimi-home.js";
|
|
17
18
|
async function main(argv) {
|
|
19
|
+
const help = companionHelp(argv);
|
|
20
|
+
if (help !== undefined) {
|
|
21
|
+
process.stdout.write(`${help}\n`);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
18
24
|
const [command, ...rest] = argv;
|
|
19
25
|
const cwd = process.env.KIMI_PLUGIN_CC_WORKSPACE_CWD || process.cwd();
|
|
20
26
|
const context = {
|
package/dist/help.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { hasHelpFlag, ASK_SUPPORTED_FLAGS, RESCUE_SUPPORTED_FLAGS, REVIEW_SUPPORTED_FLAGS, PURSUE_SUPPORTED_FLAGS, SWARM_SUPPORTED_FLAGS, JOB_LOOKUP_SUPPORTED_FLAGS, SETUP_SUPPORTED_FLAGS, REPAIR_SESSIONS_SUPPORTED_FLAGS, REPLAY_SUPPORTED_FLAGS, } from "./parsing.js";
|
|
2
|
+
const SUBCOMMANDS = "setup, review, task, ask, status, result, cancel, replay, repair-sessions";
|
|
3
|
+
const TASK_KINDS = "rescue, challenge, pursue, swarm";
|
|
4
|
+
/** Help is a dispatch-only, side-effect-free exception to normal argument parsing. */
|
|
5
|
+
export function companionHelp(argv) {
|
|
6
|
+
const [command, kind] = argv;
|
|
7
|
+
// Scan only the argument region: the subcommand (and a task kind) are
|
|
8
|
+
// positional names, not prose, so they must not end the flag region.
|
|
9
|
+
const argumentStart = command === "--help" || command === "-h" ? 0
|
|
10
|
+
: command === "task" && kind !== undefined && !kind.startsWith("-") ? 2
|
|
11
|
+
: 1;
|
|
12
|
+
if (!hasHelpFlag(argv.slice(argumentStart)))
|
|
13
|
+
return undefined;
|
|
14
|
+
const suffix = " Use --help or -h among the leading flags (before any prompt text or --) to show this usage.";
|
|
15
|
+
const usage = (name, positionals, flags, description) => `Usage: companion.sh ${name}${positionals ? ` ${positionals}` : ""}. ${description} Supported flags: ${flags}.${suffix}`;
|
|
16
|
+
switch (command) {
|
|
17
|
+
case "--help":
|
|
18
|
+
case "-h":
|
|
19
|
+
return `Usage: companion.sh <subcommand> [arguments]. Subcommands: ${SUBCOMMANDS}. Use <subcommand> --help for usage.`;
|
|
20
|
+
case "task":
|
|
21
|
+
switch (kind) {
|
|
22
|
+
case "rescue": return usage("task rescue", "[options] [--] <task>", RESCUE_SUPPORTED_FLAGS, "Write-capable task; default budget 45m. Backgrounding does not extend the budget.");
|
|
23
|
+
case "challenge": return usage("task challenge", "[options] [--] [focus]", REVIEW_SUPPORTED_FLAGS, "Read-only challenge; default budget 30m. Foreground-only (no --background/--wait).");
|
|
24
|
+
case "pursue": return usage("task pursue", "[options] [--] <objective>", PURSUE_SUPPORTED_FLAGS, "Autonomous goal; default budget 45m. Runtime foreground-only.");
|
|
25
|
+
case "swarm": return usage("task swarm", "[options] [--] <objective>", SWARM_SUPPORTED_FLAGS, "Parallel task; default budget 30m. Runtime foreground-only; --write produces a patch.");
|
|
26
|
+
default: return `Usage: companion.sh task <kind> [arguments]. Task kinds: ${TASK_KINDS}. Use task <kind> --help for usage.`;
|
|
27
|
+
}
|
|
28
|
+
case "ask": return usage(command, "[options] [--] [prompt]", ASK_SUPPORTED_FLAGS, "Read-only question; default budget 30m. Backgrounding does not extend the budget.");
|
|
29
|
+
case "review": return usage(command, "[options] [--] [focus]", REVIEW_SUPPORTED_FLAGS, "Read-only review; default budget 30m. Foreground-only (no --background/--wait).");
|
|
30
|
+
case "setup": return usage(command, "[options]", SETUP_SUPPORTED_FLAGS, "Install, check, or remove this host's managed hook, or list configured models.");
|
|
31
|
+
case "status":
|
|
32
|
+
case "result":
|
|
33
|
+
case "cancel":
|
|
34
|
+
return usage(command, "[job-id] [options]", JOB_LOOKUP_SUPPORTED_FLAGS, command === "status" ? "Show job state as JSON." : command === "result" ? "Read a terminal artifact, optionally in a JSON envelope." : "Cancel a running job; without an id, target the repository's latest running job.");
|
|
35
|
+
case "replay": return usage(command, "<job-id>", REPLAY_SUPPORTED_FLAGS, "Replay a completed job's persisted stream log.");
|
|
36
|
+
case "repair-sessions": return usage(command, "[options]", REPAIR_SESSIONS_SUPPORTED_FLAGS, "Preview session metadata repair; --apply writes, --all selects all host-owned repositories.");
|
|
37
|
+
case "worker": return "Usage: companion.sh worker <ask|rescue> <job-id>. Internal detached-worker entry point; launched by ask/rescue, not manually.";
|
|
38
|
+
default: return undefined;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -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
|
}
|
|
@@ -585,14 +593,15 @@ function validateVersionSensitiveEvents(events, version) {
|
|
|
585
593
|
return invalidHookSet(`version-sensitive event ${JSON.stringify(first.event)} requires a verified kimi-code version`, first.entry, first.line);
|
|
586
594
|
}
|
|
587
595
|
// Exact schemas reviewed at 2.0.0 (1b89e4b0), 2.0.1 (caf7d4e2),
|
|
588
|
-
// 2.0.2 (9d07f634), 2.1.0 (52437299),
|
|
596
|
+
// 2.0.2 (9d07f634), 2.1.0 (52437299), 2.1.1 (f67e6398) and 2.2.0 (472192c3):
|
|
589
597
|
// the additive events and HookDefSchema are unchanged. Require the full
|
|
590
598
|
// version string AND matching components; no inferred 2.x compatibility.
|
|
591
599
|
if (version.major === 2 && ((version.minor === 0 && ((version.version === "2.0.0" && version.patch === 0) ||
|
|
592
600
|
(version.version === "2.0.1" && version.patch === 1) ||
|
|
593
601
|
(version.version === "2.0.2" && version.patch === 2))) ||
|
|
594
602
|
(version.minor === 1 && ((version.version === "2.1.0" && version.patch === 0) ||
|
|
595
|
-
(version.version === "2.1.1" && version.patch === 1)))
|
|
603
|
+
(version.version === "2.1.1" && version.patch === 1))) ||
|
|
604
|
+
(version.minor === 2 && ((version.version === "2.2.0" && version.patch === 0))))) {
|
|
596
605
|
return { valid: true };
|
|
597
606
|
}
|
|
598
607
|
if (version.major !== 0) {
|