kimi-plugin-cc 2.4.4 → 2.5.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 +21 -0
- package/README.md +50 -34
- package/dist/commands/swarm.js +1 -1
- package/dist/dsh/companion.js +174 -0
- package/dist/dsh/index.js +156 -0
- package/dist/hooks/config-safety.js +3 -2
- package/dist/hooks/install-paths.js +2 -2
- package/dist/kimi-engine.js +1 -0
- package/dist/swarm-activation.js +1 -1
- package/dist/version.js +1 -1
- package/docs/migration.md +4 -4
- package/docs/models.md +5 -1
- package/docs/registry-releases.md +20 -17
- package/docs/safety.md +28 -15
- package/docs/swarm-recovery.md +1 -1
- package/docs/upstream-2.4.1-certification.md +54 -0
- package/package.json +41 -6
- package/plugins/kimi-codex/.codex-plugin/plugin.json +1 -1
- package/plugins/kimi-codex/dist/commands/swarm.js +1 -1
- package/plugins/kimi-codex/dist/dsh/companion.js +174 -0
- package/plugins/kimi-codex/dist/dsh/index.js +156 -0
- package/plugins/kimi-codex/dist/hooks/config-safety.js +3 -2
- package/plugins/kimi-codex/dist/hooks/install-paths.js +2 -2
- package/plugins/kimi-codex/dist/kimi-engine.js +1 -0
- package/plugins/kimi-codex/dist/swarm-activation.js +1 -1
- package/plugins/kimi-codex/dist/version.js +1 -1
- package/plugins/kimi-dsh/LICENSE +201 -0
- package/plugins/kimi-dsh/README.md +80 -0
- package/plugins/kimi-dsh/cordis.patch.yml +45 -0
- package/plugins/kimi-dsh/dist/background-spawn.js +216 -0
- package/plugins/kimi-dsh/dist/cli-cancellation.js +43 -0
- package/plugins/kimi-dsh/dist/cli-client.js +876 -0
- package/plugins/kimi-dsh/dist/commands/ask.js +336 -0
- package/plugins/kimi-dsh/dist/commands/cancel.js +216 -0
- package/plugins/kimi-dsh/dist/commands/cli-helpers.js +201 -0
- package/plugins/kimi-dsh/dist/commands/models.js +140 -0
- package/plugins/kimi-dsh/dist/commands/not-implemented.js +4 -0
- package/plugins/kimi-dsh/dist/commands/pursue.js +307 -0
- package/plugins/kimi-dsh/dist/commands/recover-swarm.js +135 -0
- package/plugins/kimi-dsh/dist/commands/registry.js +74 -0
- package/plugins/kimi-dsh/dist/commands/repair-sessions.js +160 -0
- package/plugins/kimi-dsh/dist/commands/replay.js +124 -0
- package/plugins/kimi-dsh/dist/commands/rescue.js +347 -0
- package/plugins/kimi-dsh/dist/commands/result.js +66 -0
- package/plugins/kimi-dsh/dist/commands/review-gate.js +390 -0
- package/plugins/kimi-dsh/dist/commands/review.js +220 -0
- package/plugins/kimi-dsh/dist/commands/setup.js +1047 -0
- package/plugins/kimi-dsh/dist/commands/status.js +27 -0
- package/plugins/kimi-dsh/dist/commands/swarm.js +754 -0
- package/plugins/kimi-dsh/dist/commands/worker.js +25 -0
- package/plugins/kimi-dsh/dist/companion.js +97 -0
- package/plugins/kimi-dsh/dist/config.js +22 -0
- package/plugins/kimi-dsh/dist/dsh/companion.js +174 -0
- package/plugins/kimi-dsh/dist/dsh/index.js +156 -0
- package/plugins/kimi-dsh/dist/errors.js +40 -0
- package/plugins/kimi-dsh/dist/git.js +224 -0
- package/plugins/kimi-dsh/dist/help.js +43 -0
- package/plugins/kimi-dsh/dist/hook-escape.js +71 -0
- package/plugins/kimi-dsh/dist/hooks/approval-hook.js +101 -0
- package/plugins/kimi-dsh/dist/hooks/approval-policy.js +211 -0
- package/plugins/kimi-dsh/dist/hooks/config-safety.js +653 -0
- package/plugins/kimi-dsh/dist/hooks/install-paths.js +428 -0
- package/plugins/kimi-dsh/dist/hooks/install.js +210 -0
- package/plugins/kimi-dsh/dist/hooks/managed-block.js +619 -0
- package/plugins/kimi-dsh/dist/hooks/review-gate-stop.js +19 -0
- package/plugins/kimi-dsh/dist/job-id.js +12 -0
- package/plugins/kimi-dsh/dist/job-store.js +659 -0
- package/plugins/kimi-dsh/dist/jobs.js +217 -0
- package/plugins/kimi-dsh/dist/kimi-command.js +70 -0
- package/plugins/kimi-dsh/dist/kimi-engine.js +632 -0
- package/plugins/kimi-dsh/dist/kimi-errors.js +260 -0
- package/plugins/kimi-dsh/dist/kimi-home.js +9 -0
- package/plugins/kimi-dsh/dist/kimi-timeouts.js +52 -0
- package/plugins/kimi-dsh/dist/kimi-version-probe.js +216 -0
- package/plugins/kimi-dsh/dist/logging.js +22 -0
- package/plugins/kimi-dsh/dist/mcp/binding.js +58 -0
- package/plugins/kimi-dsh/dist/mcp/job-control.js +345 -0
- package/plugins/kimi-dsh/dist/mcp/launcher.js +257 -0
- package/plugins/kimi-dsh/dist/mcp/operations.js +301 -0
- package/plugins/kimi-dsh/dist/mcp/protocol.js +275 -0
- package/plugins/kimi-dsh/dist/mcp/server.js +8 -0
- package/plugins/kimi-dsh/dist/mcp/stdio.js +78 -0
- package/plugins/kimi-dsh/dist/mcp/supervised-child.js +15 -0
- package/plugins/kimi-dsh/dist/mcp/tool-result.js +7 -0
- package/plugins/kimi-dsh/dist/mcp/workspace.js +34 -0
- package/plugins/kimi-dsh/dist/model-command.js +32 -0
- package/plugins/kimi-dsh/dist/native-v2-preflight.js +417 -0
- package/plugins/kimi-dsh/dist/package.json +1 -0
- package/plugins/kimi-dsh/dist/parsing.js +620 -0
- package/plugins/kimi-dsh/dist/paths.js +27 -0
- package/plugins/kimi-dsh/dist/plugin-data.js +98 -0
- package/plugins/kimi-dsh/dist/process-tree.js +479 -0
- package/plugins/kimi-dsh/dist/prompt-size.js +18 -0
- package/plugins/kimi-dsh/dist/render.js +177 -0
- package/plugins/kimi-dsh/dist/rescue-approval.js +865 -0
- package/plugins/kimi-dsh/dist/schemas/review-gate-output.js +43 -0
- package/plugins/kimi-dsh/dist/session-recovery.js +200 -0
- package/plugins/kimi-dsh/dist/session-title.js +293 -0
- package/plugins/kimi-dsh/dist/stream-json.js +346 -0
- package/plugins/kimi-dsh/dist/swarm-activation.js +834 -0
- package/plugins/kimi-dsh/dist/swarm-recovery.js +134 -0
- package/plugins/kimi-dsh/dist/types.js +1 -0
- package/plugins/kimi-dsh/dist/vendor/shell-quote/LICENSE +24 -0
- package/plugins/kimi-dsh/dist/vendor/shell-quote/index.js +1 -0
- package/plugins/kimi-dsh/dist/vendor/shell-quote/parse.js +229 -0
- package/plugins/kimi-dsh/dist/vendor/smol-toml/LICENSE +24 -0
- package/plugins/kimi-dsh/dist/vendor/smol-toml/date.js +156 -0
- package/plugins/kimi-dsh/dist/vendor/smol-toml/error.js +67 -0
- package/plugins/kimi-dsh/dist/vendor/smol-toml/extract.js +293 -0
- package/plugins/kimi-dsh/dist/vendor/smol-toml/parse.js +164 -0
- package/plugins/kimi-dsh/dist/vendor/smol-toml/primitive.js +151 -0
- package/plugins/kimi-dsh/dist/vendor/smol-toml/struct.js +156 -0
- package/plugins/kimi-dsh/dist/vendor/smol-toml/util.js +57 -0
- package/plugins/kimi-dsh/dist/version.js +7 -0
- package/plugins/kimi-dsh/package.json +48 -0
- package/plugins/kimi-dsh/scripts/companion.sh +37 -0
- package/plugins/kimi-dsh/scripts/review-gate-hook.sh +32 -0
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { RuntimeError } from "./errors.js";
|
|
3
|
+
import { formatBudgetDuration, renderTerminalJobArtifact, writeArtifact } from "./render.js";
|
|
4
|
+
export function digestPrompt(prompt) {
|
|
5
|
+
return createHash("sha256").update(prompt).digest("hex");
|
|
6
|
+
}
|
|
7
|
+
export function normalizeJobError(error) {
|
|
8
|
+
if (error instanceof RuntimeError) {
|
|
9
|
+
const normalized = {
|
|
10
|
+
code: error.code,
|
|
11
|
+
message: error.message,
|
|
12
|
+
stage: error.stage,
|
|
13
|
+
};
|
|
14
|
+
if (Object.keys(error.details).length > 0) {
|
|
15
|
+
normalized.details = error.details;
|
|
16
|
+
}
|
|
17
|
+
return normalized;
|
|
18
|
+
}
|
|
19
|
+
if (error instanceof Error) {
|
|
20
|
+
return {
|
|
21
|
+
code: "UNEXPECTED_ERROR",
|
|
22
|
+
message: error.message,
|
|
23
|
+
stage: "runtime",
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
code: "UNEXPECTED_ERROR",
|
|
28
|
+
message: String(error),
|
|
29
|
+
stage: "runtime",
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
export async function markJobFailed(store, paths, job, error, summary = "Job failed.", options) {
|
|
33
|
+
const current = store.getJob(job.job_id) ?? job;
|
|
34
|
+
if (current.status !== "running")
|
|
35
|
+
return current;
|
|
36
|
+
if (options?.partialOutput) {
|
|
37
|
+
const label = job.command_type[0].toUpperCase() + job.command_type.slice(1);
|
|
38
|
+
summary = `${label} timed out after ${formatBudgetDuration(options.partialOutput.budgetMs)}; partial output captured (${options.partialOutput.text.trim().length} chars).`;
|
|
39
|
+
}
|
|
40
|
+
const normalized = normalizeJobError(error);
|
|
41
|
+
const phase = options?.phase;
|
|
42
|
+
const artifactJob = {
|
|
43
|
+
...(store.getJob(job.job_id) ?? job),
|
|
44
|
+
status: "failed",
|
|
45
|
+
summary,
|
|
46
|
+
error: normalized,
|
|
47
|
+
final_output_path: null,
|
|
48
|
+
pid: null,
|
|
49
|
+
kimi_pid: null,
|
|
50
|
+
...(phase !== undefined ? { phase } : {}),
|
|
51
|
+
};
|
|
52
|
+
const artifactPath = await writeArtifact(paths, artifactJob, renderTerminalJobArtifact(artifactJob, options?.partialOutput));
|
|
53
|
+
return (store.markFailed(job.job_id, {
|
|
54
|
+
summary,
|
|
55
|
+
error: normalized,
|
|
56
|
+
final_output_path: artifactPath,
|
|
57
|
+
...(phase !== undefined ? { phase } : {}),
|
|
58
|
+
}) ?? artifactJob);
|
|
59
|
+
}
|
|
60
|
+
export async function markJobCancelled(store, paths, job, summary, error, options) {
|
|
61
|
+
const current = store.getJob(job.job_id) ?? job;
|
|
62
|
+
if (current.status !== "running")
|
|
63
|
+
return current;
|
|
64
|
+
const normalized = error ? normalizeJobError(error) : null;
|
|
65
|
+
const phase = options?.phase;
|
|
66
|
+
const artifactJob = {
|
|
67
|
+
...job,
|
|
68
|
+
status: "cancelled",
|
|
69
|
+
summary,
|
|
70
|
+
error: normalized,
|
|
71
|
+
final_output_path: null,
|
|
72
|
+
pid: null,
|
|
73
|
+
kimi_pid: null,
|
|
74
|
+
...(phase !== undefined ? { phase } : {}),
|
|
75
|
+
};
|
|
76
|
+
const artifactPath = await writeArtifact(paths, artifactJob, renderTerminalJobArtifact(artifactJob));
|
|
77
|
+
return (store.markCancelled(job.job_id, {
|
|
78
|
+
summary,
|
|
79
|
+
error: normalized,
|
|
80
|
+
final_output_path: artifactPath,
|
|
81
|
+
...(phase !== undefined ? { phase } : {}),
|
|
82
|
+
}) ?? artifactJob);
|
|
83
|
+
}
|
|
84
|
+
export async function sweepStaleJobs(store, paths) {
|
|
85
|
+
for (const job of store.listRunningJobsWithProcessHints()) {
|
|
86
|
+
if (store.getMcpTask(job.job_id))
|
|
87
|
+
continue;
|
|
88
|
+
// Recovery owns orphaned swarm resources; PID-only sweeping must not signal replacements.
|
|
89
|
+
if (store.getSwarmRecovery(job.job_id))
|
|
90
|
+
continue;
|
|
91
|
+
if (!isStaleEnoughToSweep(job)) {
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
const companionMissing = job.pid !== null && !isPidAlive(job.pid);
|
|
95
|
+
const kimiMissing = job.kimi_pid !== null && !isPidAlive(job.kimi_pid);
|
|
96
|
+
if (job.background) {
|
|
97
|
+
if (!companionMissing && !kimiMissing) {
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
terminateRecordedProcesses(job);
|
|
101
|
+
await markJobFailed(store, paths, job, new RuntimeError(companionMissing ? "WORKER_DISAPPEARED" : "KIMI_PROCESS_DISAPPEARED", companionMissing
|
|
102
|
+
? `Background worker pid ${job.pid} is no longer running.`
|
|
103
|
+
: `Kimi pid ${job.kimi_pid} is no longer running while background worker pid ${job.pid} is still recorded.`, "jobs.sweep"), companionMissing
|
|
104
|
+
? "Background worker disappeared before reporting a terminal state."
|
|
105
|
+
: "Kimi process disappeared before reporting a terminal state.", job.command_type === "ask" || job.command_type === "rescue" ? { phase: "failed" } : undefined);
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
if (!companionMissing && !kimiMissing) {
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
// Sweep-vs-live-companion race fix: if the foreground companion pid is
|
|
112
|
+
// still alive, the companion process is microseconds-to-seconds away
|
|
113
|
+
// from writing its own terminal state. SIGTERMing it here would kill
|
|
114
|
+
// its render path mid-flight (and from a sibling shell's read-only
|
|
115
|
+
// /kimi:status, no less). Skip — let the companion finish on its own.
|
|
116
|
+
if (!companionMissing && job.pid !== null) {
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
terminateRecordedProcesses(job);
|
|
120
|
+
await markJobFailed(store, paths, job, new RuntimeError("FOREGROUND_PROCESS_DISAPPEARED", describeMissingForegroundProcess(job, companionMissing, kimiMissing), "jobs.sweep"), "Foreground Kimi job disappeared before reporting a terminal state.", job.command_type === "ask" || job.command_type === "rescue" ? { phase: "failed" } : undefined);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
export const STALE_SWEEP_GRACE_MS = 15_000;
|
|
124
|
+
function isStaleEnoughToSweep(job) {
|
|
125
|
+
const updatedAt = Date.parse(job.updated_at);
|
|
126
|
+
return Number.isFinite(updatedAt) && Date.now() - updatedAt >= STALE_SWEEP_GRACE_MS;
|
|
127
|
+
}
|
|
128
|
+
function describeMissingForegroundProcess(job, companionMissing, kimiMissing) {
|
|
129
|
+
if (companionMissing && kimiMissing) {
|
|
130
|
+
return `Foreground companion pid ${job.pid} and Kimi pid ${job.kimi_pid} are no longer running.`;
|
|
131
|
+
}
|
|
132
|
+
if (companionMissing) {
|
|
133
|
+
return `Foreground companion pid ${job.pid} is no longer running.`;
|
|
134
|
+
}
|
|
135
|
+
return `Kimi pid ${job.kimi_pid} is no longer running.`;
|
|
136
|
+
}
|
|
137
|
+
function terminateRecordedProcesses(job) {
|
|
138
|
+
const pids = new Set();
|
|
139
|
+
if (job.pid !== null)
|
|
140
|
+
pids.add(job.pid);
|
|
141
|
+
if (job.kimi_pid !== null)
|
|
142
|
+
pids.add(job.kimi_pid);
|
|
143
|
+
for (const pid of pids) {
|
|
144
|
+
if (!isPidAlive(pid)) {
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
killPid(pid, "SIGTERM");
|
|
148
|
+
// SIGKILL escalation: if the process ignores SIGTERM (stuck Kimi child,
|
|
149
|
+
// uninterruptible sleep, signal mask), force-kill after 2s. The job is
|
|
150
|
+
// already determined stale at this point, so leaving a survivor running
|
|
151
|
+
// would just orphan resources. Timer is unref'd so it doesn't keep the
|
|
152
|
+
// process alive.
|
|
153
|
+
const timer = setTimeout(() => {
|
|
154
|
+
if (isPidAlive(pid)) {
|
|
155
|
+
killPid(pid, "SIGKILL");
|
|
156
|
+
}
|
|
157
|
+
}, 2_000);
|
|
158
|
+
timer.unref();
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
function killPid(pid, signal) {
|
|
162
|
+
try {
|
|
163
|
+
process.kill(pid, signal);
|
|
164
|
+
}
|
|
165
|
+
catch (error) {
|
|
166
|
+
const code = error.code;
|
|
167
|
+
// ESRCH: process already gone. EPERM: pid was likely reused by an
|
|
168
|
+
// unrelated process we don't own — silently skip.
|
|
169
|
+
if (code !== "ESRCH" && code !== "EPERM") {
|
|
170
|
+
throw error;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
function isPidAlive(pid) {
|
|
175
|
+
try {
|
|
176
|
+
process.kill(pid, 0);
|
|
177
|
+
return true;
|
|
178
|
+
}
|
|
179
|
+
catch (error) {
|
|
180
|
+
const code = error.code;
|
|
181
|
+
// ESRCH: no such process — definitively dead.
|
|
182
|
+
// EPERM: process exists but we can't signal it. We treat this as "dead
|
|
183
|
+
// for our purposes" because it most likely means the pid was reused by
|
|
184
|
+
// an unrelated higher-privilege process. The companion stores its own
|
|
185
|
+
// pid in v0.2.2+; under normal usage we should always have permission
|
|
186
|
+
// to signal our own descendants. Returning true here lets stale jobs
|
|
187
|
+
// hide forever (the sweeper relies on isPidAlive=false to flip them to
|
|
188
|
+
// failed). signalJobProcesses and killPid already swallow EPERM
|
|
189
|
+
// consistently — this brings the read path in line with the write path.
|
|
190
|
+
if (code === "ESRCH" || code === "EPERM") {
|
|
191
|
+
return false;
|
|
192
|
+
}
|
|
193
|
+
return true;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
export async function waitForTerminalJob(storeFactory, jobId, timeoutMs = 60_000) {
|
|
197
|
+
const deadline = Date.now() + timeoutMs;
|
|
198
|
+
while (Date.now() < deadline) {
|
|
199
|
+
const store = storeFactory();
|
|
200
|
+
try {
|
|
201
|
+
const job = store.getJob(jobId);
|
|
202
|
+
if (job && job.status !== "running") {
|
|
203
|
+
return job;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
finally {
|
|
207
|
+
store.close();
|
|
208
|
+
}
|
|
209
|
+
await sleep(200);
|
|
210
|
+
}
|
|
211
|
+
throw new RuntimeError("JOB_WAIT_TIMEOUT", `Timed out after ${timeoutMs}ms while waiting for job ${jobId} to finish. The worker may still be running; use /kimi:status ${jobId} to check progress or /kimi:result ${jobId} once it completes.`, "jobs.wait");
|
|
212
|
+
}
|
|
213
|
+
function sleep(ms) {
|
|
214
|
+
return new Promise((resolve) => {
|
|
215
|
+
setTimeout(resolve, ms);
|
|
216
|
+
});
|
|
217
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { RuntimeError } from "./errors.js";
|
|
2
|
+
export function resolveKimiCliCommand(env) {
|
|
3
|
+
const command = env.KIMI_PLUGIN_CC_KIMI_BIN || "kimi";
|
|
4
|
+
const raw = env.KIMI_PLUGIN_CC_KIMI_PREFIX_ARGS;
|
|
5
|
+
if (!raw) {
|
|
6
|
+
return { command, prefixArgs: [] };
|
|
7
|
+
}
|
|
8
|
+
let parsed;
|
|
9
|
+
try {
|
|
10
|
+
parsed = JSON.parse(raw);
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
// Plain-text values are space-split for launcher-style overrides.
|
|
14
|
+
const plainArgs = raw.split(" ").filter(Boolean);
|
|
15
|
+
assertPrefixArgsSafe(plainArgs, raw);
|
|
16
|
+
return { command, prefixArgs: plainArgs };
|
|
17
|
+
}
|
|
18
|
+
if (!Array.isArray(parsed)) {
|
|
19
|
+
throw new RuntimeError("INVALID_ENV", "KIMI_PLUGIN_CC_KIMI_PREFIX_ARGS must be a JSON array of strings.", "kimi-command.env", { details: { env_var: "KIMI_PLUGIN_CC_KIMI_PREFIX_ARGS", value: raw, retryable_after_setup: false } });
|
|
20
|
+
}
|
|
21
|
+
for (const entry of parsed) {
|
|
22
|
+
if (typeof entry !== "string") {
|
|
23
|
+
throw new RuntimeError("INVALID_ENV", "KIMI_PLUGIN_CC_KIMI_PREFIX_ARGS entries must be strings.", "kimi-command.env", { details: { env_var: "KIMI_PLUGIN_CC_KIMI_PREFIX_ARGS", value: raw, retryable_after_setup: false } });
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
const prefixArgs = parsed;
|
|
27
|
+
assertPrefixArgsSafe(prefixArgs, raw);
|
|
28
|
+
return { command, prefixArgs };
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Flags the plugin owns on the `kimi -p` argv. The prefix is only a launcher
|
|
32
|
+
* shim (e.g. `["run", "<companion-dist>"]`); it must never carry a kimi flag
|
|
33
|
+
* that changes the session, engine, plan, prompt, or output format. In
|
|
34
|
+
* particular a smuggled `-r <session>` would resume a session the plugin never
|
|
35
|
+
* scanned for plan-mode taint (the journal scan keys only on the plugin-chosen
|
|
36
|
+
* resumeSessionId), so it is refused here — the single resolution point reached
|
|
37
|
+
* by both foreground and detached-worker spawns.
|
|
38
|
+
*/
|
|
39
|
+
const RESERVED_PREFIX_FLAGS = new Set([
|
|
40
|
+
// Session selection — incl. the HIDDEN `-C` alias of `--continue`
|
|
41
|
+
// (apps/kimi-code/src/cli/commands.ts), which resumes the latest session for
|
|
42
|
+
// the cwd without any plugin-chosen resumeSessionId, so no journal scan runs.
|
|
43
|
+
"-r", "--resume", "-S", "--session", "-c", "-C", "--continue",
|
|
44
|
+
// Prompt / output.
|
|
45
|
+
"-p", "--prompt", "--output-format",
|
|
46
|
+
// Permission / plan mode.
|
|
47
|
+
"--plan", "--auto", "-y", "--yolo", "--yes", "--auto-approve", "--manual",
|
|
48
|
+
// Model / profile / roots.
|
|
49
|
+
"-m", "--model", "--skills-dir", "--agent", "--agent-file", "--add-dir",
|
|
50
|
+
// Terminal informational flags (would replace the run with a banner).
|
|
51
|
+
"-V", "--version", "-h", "--help",
|
|
52
|
+
]);
|
|
53
|
+
export function assertPrefixArgsSafe(prefixArgs, raw) {
|
|
54
|
+
for (const token of prefixArgs) {
|
|
55
|
+
// Compare the flag name only, so `--session=x` is caught as well as `-S x`.
|
|
56
|
+
const flag = token.startsWith("-") ? token.split("=", 1)[0] : token;
|
|
57
|
+
if (RESERVED_PREFIX_FLAGS.has(flag)) {
|
|
58
|
+
throw new RuntimeError("INVALID_ENV", `KIMI_PLUGIN_CC_KIMI_PREFIX_ARGS must not contain the reserved kimi flag "${flag}". ` +
|
|
59
|
+
"The prefix is a launcher shim only; session, engine, plan, prompt, model, and " +
|
|
60
|
+
"output-format flags are owned by the plugin and would bypass its safety checks.", "kimi-command.env", {
|
|
61
|
+
details: {
|
|
62
|
+
env_var: "KIMI_PLUGIN_CC_KIMI_PREFIX_ARGS",
|
|
63
|
+
reserved_flag: flag,
|
|
64
|
+
value: raw,
|
|
65
|
+
retryable_after_setup: false,
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|