@nowcrew/daemon 0.5.26 → 0.5.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/package.json +1 -1
  2. package/dist/attachments.js +0 -196
  3. package/dist/bound-im-decision.js +0 -22
  4. package/dist/completion-retransmitter.js +0 -77
  5. package/dist/computer-cli.js +0 -274
  6. package/dist/computer-profile-lock.js +0 -395
  7. package/dist/computer-profile.js +0 -364
  8. package/dist/computer-service.js +0 -358
  9. package/dist/config.js +0 -82
  10. package/dist/console-collapse.js +0 -13
  11. package/dist/console-formatter.js +0 -77
  12. package/dist/console-payload.js +0 -73
  13. package/dist/console.js +0 -329
  14. package/dist/daemon-startup-error.js +0 -30
  15. package/dist/execution-backend.js +0 -44
  16. package/dist/execution-event-limit.js +0 -64
  17. package/dist/execution-journal-lock.js +0 -421
  18. package/dist/execution-journal.js +0 -716
  19. package/dist/execution-protocol.js +0 -342
  20. package/dist/execution-recovery.js +0 -95
  21. package/dist/execution-runner.js +0 -659
  22. package/dist/execution-supervisor-child.js +0 -236
  23. package/dist/execution-supervisor.js +0 -302
  24. package/dist/execution-telemetry-journal.js +0 -71
  25. package/dist/external-output.js +0 -114
  26. package/dist/i18n.js +0 -64
  27. package/dist/json-result.js +0 -27
  28. package/dist/list-models.js +0 -92
  29. package/dist/local-executor.js +0 -439
  30. package/dist/log-format.js +0 -10
  31. package/dist/machine-info.js +0 -124
  32. package/dist/main.js +0 -118
  33. package/dist/normalize.js +0 -170
  34. package/dist/origin-decision.js +0 -44
  35. package/dist/platform.js +0 -8
  36. package/dist/prompt.js +0 -307
  37. package/dist/provider-env.js +0 -90
  38. package/dist/runner.js +0 -234
  39. package/dist/runtime-cancellation.js +0 -74
  40. package/dist/runtime-capabilities.js +0 -43
  41. package/dist/runtime-path.js +0 -60
  42. package/dist/runtimes/claude.js +0 -51
  43. package/dist/runtimes/codex-app-server-runner.js +0 -340
  44. package/dist/runtimes/codex-deepseek-catalog.js +0 -7
  45. package/dist/runtimes/codex-deepseek-config.js +0 -50
  46. package/dist/runtimes/codex.js +0 -53
  47. package/dist/runtimes/kimi-acp-runner.js +0 -364
  48. package/dist/runtimes/kimi.js +0 -45
  49. package/dist/runtimes/progress-watchdog.js +0 -26
  50. package/dist/scheduled-report.js +0 -51
  51. package/dist/scheduled-run-report.js +0 -57
  52. package/dist/serve-lifecycle.js +0 -82
  53. package/dist/serve.js +0 -868
  54. package/dist/session.js +0 -82
  55. package/dist/shared-execution-slots.js +0 -68
  56. package/dist/shutdown-deadline.js +0 -32
  57. package/dist/skill-preview.js +0 -21
  58. package/dist/skills.js +0 -56
  59. package/dist/slog.js +0 -228
  60. package/dist/supervised-runtime.js +0 -104
  61. package/dist/token.js +0 -24
  62. package/dist/unified-diff.js +0 -84
  63. package/dist/websocket-shutdown.js +0 -53
  64. package/dist/win32-job-object.js +0 -193
  65. package/dist/workspace-fs.js +0 -80
  66. package/dist/workspace-import.js +0 -127
  67. package/dist/workspace.js +0 -148
@@ -1,50 +0,0 @@
1
- import { randomUUID } from "node:crypto";
2
- import { chmod, mkdir, rename, rm, writeFile } from "node:fs/promises";
3
- import { dirname, join, resolve } from "node:path";
4
- import { DEEPSEEK_CODEX_CATALOG } from "./codex-deepseek-catalog.js";
5
- export const DEEPSEEK_CODEX_MODEL = "deepseek-v4-flash";
6
- export const DEEPSEEK_CODEX_BASE_URL = "https://api.deepseek.com/";
7
- export const DEEPSEEK_CODEX_KEY_ENV = "NOWCREW_DEEPSEEK_API_KEY";
8
- export const DEEPSEEK_CODEX_REASONING_LEVELS = ["low", "high", "max"];
9
- const PRIVATE_CODEX_HOME = ".codex-deepseek";
10
- export function deepSeekCodexHome(homeDir) {
11
- return resolve(homeDir, PRIVATE_CODEX_HOME);
12
- }
13
- export function renderDeepSeekConfig(codexHome) {
14
- const catalogPath = join(codexHome, "models.json");
15
- return [
16
- `model = ${JSON.stringify(DEEPSEEK_CODEX_MODEL)}`,
17
- 'model_provider = "deepseek"',
18
- 'model_reasoning_effort = "high"',
19
- `model_catalog_json = ${JSON.stringify(catalogPath)}`,
20
- "",
21
- "[model_providers.deepseek]",
22
- 'name = "deepseek"',
23
- `base_url = ${JSON.stringify(DEEPSEEK_CODEX_BASE_URL)}`,
24
- 'wire_api = "responses"',
25
- `env_key = ${JSON.stringify(DEEPSEEK_CODEX_KEY_ENV)}`,
26
- "",
27
- ].join("\n");
28
- }
29
- async function writePrivateAtomic(path, content) {
30
- const directory = dirname(path);
31
- await mkdir(directory, { recursive: true, mode: 0o700 });
32
- await chmod(directory, 0o700);
33
- const temporary = `${path}.${randomUUID()}.tmp`;
34
- try {
35
- await writeFile(temporary, content, { encoding: "utf8", mode: 0o600, flag: "wx" });
36
- await rename(temporary, path);
37
- await chmod(path, 0o600);
38
- }
39
- finally {
40
- await rm(temporary, { force: true });
41
- }
42
- }
43
- export async function materializeDeepSeekCodexHome(homeDir) {
44
- const codexHome = deepSeekCodexHome(homeDir);
45
- await mkdir(codexHome, { recursive: true, mode: 0o700 });
46
- await chmod(codexHome, 0o700);
47
- await writePrivateAtomic(join(codexHome, "models.json"), DEEPSEEK_CODEX_CATALOG);
48
- await writePrivateAtomic(join(codexHome, "config.toml"), renderDeepSeekConfig(codexHome));
49
- return codexHome;
50
- }
@@ -1,53 +0,0 @@
1
- /**
2
- * Codex CLI runtime adapter: non-interactive exec mode with JSONL output.
3
- */
4
- // cross-spawn:win32 上 npm CLI 是 .cmd shim,node 原生 spawn 不带 shell 无法执行(ENOENT/EINVAL)
5
- import spawn from "cross-spawn";
6
- // Codex CLI 原生 model_reasoning_effort 档位(codex 0.135.0 实测:非法值时 config 解析报错枚举这六档)。
7
- // 注意:codex 对非法值是硬失败(进程直接退出),所以必须白名单过滤;白名单外(含 "default"、
8
- // claude 专属的 "max")回落 CODEX_DEFAULT_EFFORT。
9
- export const CODEX_EFFORT_LEVELS = ["none", "minimal", "low", "medium", "high", "xhigh"];
10
- // 未配置/非法档位时的默认思考强度:medium 开启 reasoning(终端透传要展示思考过程);
11
- // 配置白名单档位(含显式 none 关思考)可覆盖。
12
- export const CODEX_DEFAULT_EFFORT = "medium";
13
- export function buildCodexArgs(input) {
14
- // agent 运行目录由 daemon 管理,不是 git 仓库;不带 --skip-git-repo-check 时 codex exec
15
- // 会以 "Not inside a trusted directory" 秒退(且只报在本地 stderr),表现为 agent 静默不回复。
16
- const args = ["exec", "--json", "--skip-git-repo-check"];
17
- if (input.model)
18
- args.push("--model", input.model);
19
- const effort = input.reasoning && CODEX_EFFORT_LEVELS.includes(input.reasoning)
20
- ? input.reasoning
21
- : CODEX_DEFAULT_EFFORT;
22
- args.push("-c", `model_reasoning_effort=${effort}`);
23
- if (input.effectivePermission === "sandboxed")
24
- args.push("--sandbox", "read-only");
25
- else if (input.effectivePermission === "workspace_write")
26
- args.push("--sandbox", "workspace-write");
27
- else if (input.effectivePermission === "full_access" || (input.effectivePermission === undefined && input.dangerous)) {
28
- args.push("--dangerously-bypass-approvals-and-sandbox");
29
- }
30
- for (const imagePath of input.imagePaths ?? [])
31
- args.push("--image", imagePath);
32
- // `-` instructs codex exec to read the prompt from stdin. Keeping the complete prompt out of argv
33
- // avoids Windows' command-line length limit when a thread carries a large wake context.
34
- args.push("-");
35
- return args;
36
- }
37
- export function spawnCodex(input) {
38
- // stdio 固定 pipe/pipe/pipe;cross-spawn 类型不带该细化,断言之。
39
- const child = spawn(input.bin, buildCodexArgs(input), {
40
- cwd: input.cwd,
41
- env: input.env,
42
- stdio: ["pipe", "pipe", "pipe"],
43
- });
44
- // Decode at the pipe boundary so split multi-byte characters are buffered correctly before
45
- // readline, stderr forwarding, activity reporting, and websocket JSON serialization consume them.
46
- child.stdout.setEncoding("utf8");
47
- child.stderr.setEncoding("utf8");
48
- // Codex may exit before consuming a large prompt (for example on config/auth failure). In that
49
- // case the pipe can emit EPIPE; the child exit code and stderr remain the authoritative failure.
50
- child.stdin.on("error", () => undefined);
51
- child.stdin.end(input.wakePrompt);
52
- return child;
53
- }
@@ -1,364 +0,0 @@
1
- import { once } from "node:events";
2
- import { parseArgs } from "node:util";
3
- import { Readable, Writable } from "node:stream";
4
- import { pathToFileURL } from "node:url";
5
- import spawn from "cross-spawn";
6
- import { PROTOCOL_VERSION, client, methods, ndJsonStream, } from "@agentclientprotocol/sdk";
7
- import { augmentedPath } from "../runtime-path.js";
8
- import { startFirstProgressWatchdog } from "./progress-watchdog.js";
9
- import { assertKimiLegacyPromptFits, buildKimiArgs } from "./kimi.js";
10
- const ERROR_MESSAGE_CAP = 2_000;
11
- const PROBE_TIMEOUT_MS = 5_000;
12
- function jsonLine(event) {
13
- if (process.stdout.write(`${JSON.stringify(event)}\n`))
14
- return Promise.resolve();
15
- return once(process.stdout, "drain").then(() => undefined);
16
- }
17
- function textContent(content) {
18
- if (typeof content === "string")
19
- return content;
20
- if (!Array.isArray(content))
21
- return "";
22
- return content.flatMap((part) => {
23
- if (part && typeof part === "object" && "text" in part && typeof part.text === "string") {
24
- return [part.text];
25
- }
26
- if (part && typeof part === "object" && "content" in part
27
- && part.content && typeof part.content === "object"
28
- && "type" in part.content && part.content.type === "text"
29
- && "text" in part.content && typeof part.content.text === "string") {
30
- return [part.content.text];
31
- }
32
- return [];
33
- }).join("\n");
34
- }
35
- /** Translate stable ACP updates into daemon-owned NDJSON, without exposing thought chunks. */
36
- export function mapKimiAcpUpdate(update) {
37
- if (update.sessionUpdate === "agent_message_chunk" && update.content.type === "text") {
38
- return [{ type: "kimi.acp.text_delta", text: update.content.text }];
39
- }
40
- if (update.sessionUpdate === "tool_call") {
41
- return [{
42
- type: "kimi.acp.tool_call",
43
- id: update.toolCallId,
44
- title: update.title,
45
- ...(update.kind === undefined ? {} : { kind: update.kind }),
46
- ...(update.status === undefined ? {} : { status: update.status }),
47
- ...(update.rawInput === undefined ? {} : { input: update.rawInput }),
48
- }];
49
- }
50
- if (update.sessionUpdate === "tool_call_update") {
51
- const output = textContent(update.content);
52
- return [{
53
- type: "kimi.acp.tool_result",
54
- id: update.toolCallId,
55
- ...(update.status === undefined ? {} : { status: update.status }),
56
- ...(output ? { content: output } : {}),
57
- }];
58
- }
59
- return [];
60
- }
61
- function safeErrorMessage(error, prompt) {
62
- const raw = error instanceof Error ? error.message : String(error);
63
- const redacted = prompt && raw.includes(prompt) ? raw.replaceAll(prompt, "[prompt redacted]") : raw;
64
- return redacted.slice(0, ERROR_MESSAGE_CAP);
65
- }
66
- export function isKimiAuthenticationRequired(error) {
67
- const message = error instanceof Error ? error.message : String(error);
68
- return /\bauthentication required\b/i.test(message);
69
- }
70
- export function kimiResumeMethod(capabilities) {
71
- if (capabilities?.sessionCapabilities?.resume != null)
72
- return "resume";
73
- if (capabilities?.loadSession)
74
- return "load";
75
- return null;
76
- }
77
- /** Full access may approve an operation, but it must never fabricate an answer to an agent question. */
78
- export function selectKimiPermission(params) {
79
- const allowOnce = params.options.filter((option) => option.kind === "allow_once");
80
- if (params.toolCall.title === "AskUserQuestion" || allowOnce.length > 1) {
81
- return { outcome: { outcome: "cancelled" } };
82
- }
83
- const allowed = allowOnce[0]
84
- ?? (params.options.filter((option) => option.kind === "allow_always").length === 1
85
- ? params.options.find((option) => option.kind === "allow_always")
86
- : undefined);
87
- return allowed === undefined
88
- ? { outcome: { outcome: "cancelled" } }
89
- : { outcome: { outcome: "selected", optionId: allowed.optionId } };
90
- }
91
- async function readPrompt() {
92
- process.stdin.setEncoding("utf8");
93
- let prompt = "";
94
- for await (const chunk of process.stdin)
95
- prompt += String(chunk);
96
- if (!prompt)
97
- throw new Error("Kimi ACP prompt is empty");
98
- return prompt;
99
- }
100
- async function stopChild(child) {
101
- if (child.exitCode !== null || child.signalCode !== null)
102
- return;
103
- const closed = once(child, "close").then(() => undefined);
104
- child.kill("SIGTERM");
105
- let timer;
106
- const graceful = await Promise.race([
107
- closed.then(() => true),
108
- new Promise((resolve) => { timer = setTimeout(() => resolve(false), 1_000); }),
109
- ]);
110
- if (timer !== undefined)
111
- clearTimeout(timer);
112
- if (!graceful && child.exitCode === null && child.signalCode === null) {
113
- child.kill("SIGKILL");
114
- await closed;
115
- }
116
- }
117
- /** Probe the ACP transport without starting a session or forcing an optional interactive login flow. */
118
- export async function probeKimiAcp(options, spawnProcess = spawn) {
119
- const child = spawnProcess(options.bin, ["acp"], {
120
- cwd: process.cwd(),
121
- // probe 在 daemon 自身 PATH 下运行,补上用户级 CLI 目录,与 which 探测保持一致。
122
- env: { ...process.env, PATH: augmentedPath() },
123
- stdio: ["pipe", "pipe", "pipe"],
124
- });
125
- if (child.stdin === null || child.stdout === null || child.stderr === null)
126
- return false;
127
- child.stderr.resume();
128
- const app = client({ name: "nowcrew-daemon-kimi-probe" })
129
- .onRequest(methods.client.session.requestPermission, () => ({
130
- outcome: { outcome: "cancelled" },
131
- }))
132
- .onNotification(methods.client.session.update, () => undefined);
133
- let timeout;
134
- try {
135
- const stream = ndJsonStream(Writable.toWeb(child.stdin), Readable.toWeb(child.stdout));
136
- const connected = app.connectWith(stream, async (context) => {
137
- await context.request(methods.agent.initialize, {
138
- protocolVersion: PROTOCOL_VERSION,
139
- clientCapabilities: {},
140
- clientInfo: { name: "nowcrew-daemon", version: "1" },
141
- });
142
- return true;
143
- });
144
- const result = await Promise.race([
145
- connected,
146
- new Promise((resolve) => {
147
- timeout = setTimeout(() => {
148
- void stopChild(child);
149
- resolve(false);
150
- }, PROBE_TIMEOUT_MS);
151
- }),
152
- ]);
153
- return result;
154
- }
155
- catch {
156
- return false;
157
- }
158
- finally {
159
- if (timeout !== undefined)
160
- clearTimeout(timeout);
161
- await stopChild(child);
162
- }
163
- }
164
- export async function runKimiAcp(options) {
165
- const prompt = await readPrompt();
166
- const child = spawn(options.bin, ["acp"], {
167
- cwd: process.cwd(),
168
- env: process.env,
169
- stdio: ["pipe", "pipe", "pipe"],
170
- });
171
- let runtimeChild = child;
172
- if (child.stdin === null || child.stdout === null || child.stderr === null) {
173
- throw new Error("Kimi ACP process did not expose stdio");
174
- }
175
- child.stderr.pipe(process.stderr, { end: false });
176
- let context = null;
177
- let sessionId = null;
178
- let acpSemanticProgress = false;
179
- let progressTimedOut = false;
180
- let cancelling = false;
181
- const cancel = async () => {
182
- if (cancelling)
183
- return;
184
- cancelling = true;
185
- if (context !== null && sessionId !== null) {
186
- await context.notify(methods.agent.session.cancel, { sessionId }).catch(() => undefined);
187
- }
188
- await stopChild(runtimeChild);
189
- };
190
- const onSignal = () => {
191
- void cancel().finally(() => process.exit(130));
192
- };
193
- process.once("SIGTERM", onSignal);
194
- process.once("SIGINT", onSignal);
195
- const app = client({ name: "nowcrew-daemon-kimi" })
196
- .onRequest(methods.client.session.requestPermission, ({ params }) => selectKimiPermission(params))
197
- .onNotification(methods.client.session.update, async ({ params }) => {
198
- acpSemanticProgress = true;
199
- firstProgress.observe();
200
- for (const event of mapKimiAcpUpdate(params.update))
201
- await jsonLine(event);
202
- });
203
- let firstProgress = startFirstProgressWatchdog(() => undefined);
204
- firstProgress.stop();
205
- try {
206
- const stream = ndJsonStream(Writable.toWeb(child.stdin), Readable.toWeb(child.stdout));
207
- const result = await app.connectWith(stream, async (nextContext) => {
208
- context = nextContext;
209
- const initialized = await nextContext.request(methods.agent.initialize, {
210
- protocolVersion: PROTOCOL_VERSION,
211
- clientCapabilities: {},
212
- clientInfo: { name: "nowcrew-daemon", version: "1" },
213
- });
214
- if (process.env.CREW_KIMI_ACP_DEBUG === "1") {
215
- process.stderr.write(`Kimi ACP auth methods: ${JSON.stringify(initialized.authMethods ?? [])}\n`);
216
- }
217
- if (options.resume && options.sessionId) {
218
- const resumeMethod = kimiResumeMethod(initialized.agentCapabilities);
219
- if (resumeMethod === "resume") {
220
- await nextContext.request(methods.agent.session.resume, {
221
- sessionId: options.sessionId,
222
- cwd: process.cwd(),
223
- mcpServers: [],
224
- });
225
- }
226
- else if (resumeMethod === "load") {
227
- await nextContext.request(methods.agent.session.load, {
228
- sessionId: options.sessionId,
229
- cwd: process.cwd(),
230
- mcpServers: [],
231
- });
232
- }
233
- else {
234
- throw new Error("Kimi ACP does not advertise session resume support");
235
- }
236
- sessionId = options.sessionId;
237
- }
238
- else {
239
- const session = await nextContext.request(methods.agent.session.new, {
240
- cwd: process.cwd(),
241
- mcpServers: [],
242
- });
243
- sessionId = session.sessionId;
244
- }
245
- await jsonLine({ type: "thread.started", thread_id: sessionId });
246
- if (options.model) {
247
- await nextContext.request(methods.agent.session.setConfigOption, {
248
- sessionId,
249
- configId: "model",
250
- value: options.model,
251
- });
252
- }
253
- firstProgress = startFirstProgressWatchdog(() => {
254
- progressTimedOut = true;
255
- void cancel();
256
- });
257
- return nextContext.request(methods.agent.session.prompt, {
258
- sessionId,
259
- prompt: [{ type: "text", text: prompt }],
260
- });
261
- });
262
- firstProgress.stop();
263
- if (progressTimedOut) {
264
- process.stderr.write("Kimi produced no semantic progress within the startup window\n");
265
- return 1;
266
- }
267
- const usage = result.usage;
268
- await jsonLine({
269
- type: "turn.completed",
270
- stop_reason: result.stopReason,
271
- ...(usage === undefined || usage === null ? {} : {
272
- usage: {
273
- input_tokens: usage.inputTokens,
274
- output_tokens: usage.outputTokens,
275
- cache_read_input_tokens: usage.cachedReadTokens ?? 0,
276
- cache_creation_input_tokens: usage.cachedWriteTokens ?? 0,
277
- },
278
- }),
279
- });
280
- return result.stopReason === "end_turn" ? 0 : result.stopReason === "cancelled" ? 130 : 1;
281
- }
282
- catch (error) {
283
- if (progressTimedOut) {
284
- process.stderr.write("Kimi produced no semantic progress within the startup window\n");
285
- return 1;
286
- }
287
- if (!acpSemanticProgress && isKimiAuthenticationRequired(error)) {
288
- firstProgress.stop();
289
- await stopChild(child);
290
- process.stderr.write("Kimi ACP requires account login; falling back to configured CLI provider transport\n");
291
- assertKimiLegacyPromptFits(prompt);
292
- const fallback = spawn(options.bin, buildKimiArgs({
293
- wakePrompt: prompt,
294
- effectivePermission: "full_access",
295
- ...(options.model === undefined ? {} : { model: options.model }),
296
- ...(options.resume && options.sessionId ? { sessionId: options.sessionId } : {}),
297
- }), {
298
- cwd: process.cwd(),
299
- env: process.env,
300
- stdio: ["ignore", "pipe", "pipe"],
301
- });
302
- runtimeChild = fallback;
303
- if (fallback.stdout === null || fallback.stderr === null) {
304
- throw new Error("Kimi CLI fallback did not expose output streams");
305
- }
306
- firstProgress = startFirstProgressWatchdog(() => {
307
- progressTimedOut = true;
308
- void stopChild(fallback);
309
- });
310
- fallback.stdout.on("data", () => firstProgress.observe());
311
- fallback.stdout.pipe(process.stdout, { end: false });
312
- fallback.stderr.pipe(process.stderr, { end: false });
313
- const code = await new Promise((resolve, reject) => {
314
- fallback.once("error", reject);
315
- fallback.once("close", (exitCode, signal) => {
316
- resolve(exitCode ?? (signal === null ? 1 : 128));
317
- });
318
- });
319
- firstProgress.stop();
320
- if (progressTimedOut) {
321
- process.stderr.write("Kimi CLI fallback produced no output within the startup window\n");
322
- return 1;
323
- }
324
- return code;
325
- }
326
- process.stderr.write(`Kimi ACP execution failed: ${safeErrorMessage(error, prompt)}\n`);
327
- return 1;
328
- }
329
- finally {
330
- firstProgress.stop();
331
- process.off("SIGTERM", onSignal);
332
- process.off("SIGINT", onSignal);
333
- await stopChild(child);
334
- }
335
- }
336
- function optionsFromArgv(argv) {
337
- const { values } = parseArgs({
338
- args: [...argv],
339
- options: {
340
- bin: { type: "string" },
341
- model: { type: "string" },
342
- session: { type: "string" },
343
- resume: { type: "boolean", default: false },
344
- },
345
- });
346
- if (!values.bin)
347
- throw new Error("--bin is required");
348
- if (values.resume && !values.session)
349
- throw new Error("--resume requires --session");
350
- return {
351
- bin: values.bin,
352
- ...(values.model ? { model: values.model } : {}),
353
- ...(values.session ? { sessionId: values.session } : {}),
354
- ...(values.resume ? { resume: true } : {}),
355
- };
356
- }
357
- if (process.argv[1] !== undefined && import.meta.url === pathToFileURL(process.argv[1]).href) {
358
- runKimiAcp(optionsFromArgv(process.argv.slice(2)))
359
- .then((code) => { process.exitCode = code; })
360
- .catch((error) => {
361
- process.stderr.write(`Kimi ACP runner failed: ${safeErrorMessage(error, "")}\n`);
362
- process.exitCode = 1;
363
- });
364
- }
@@ -1,45 +0,0 @@
1
- /**
2
- * Kimi Code CLI runtime adapter: non-interactive prompt mode with stream-json output.
3
- *
4
- * 事实依据(kimi-code 0.23.0 本机实测 + 官方文档 www.kimi.com/code/docs):
5
- * - `kimi -p <prompt> --output-format stream-json`:单次非交互执行,stdout 每行一个 JSON。
6
- * - `-p` 固定 auto 权限(自动批准普通工具调用),且与 --yolo/--auto/--plan 互斥,
7
- * 故 dangerous 无需(也不能)映射任何 flag。
8
- * - 无 system prompt 注入参数 → 与 codex 同法:systemPrompt 拼在 wakePrompt 前。
9
- * - 鉴权是机器级的(`kimi login` 或 ~/.kimi-code/config.toml),不读 shell 环境变量。
10
- */
11
- // cross-spawn:win32 上 npm CLI 是 .cmd shim,node 原生 spawn 不带 shell 无法执行(ENOENT/EINVAL)
12
- import spawn from "cross-spawn";
13
- // Windows CreateProcess receives one UTF-16 command line. Reserve room for the executable, flags,
14
- // model and cmd shim quoting instead of relying on the theoretical 32767-character ceiling.
15
- export const KIMI_LEGACY_PROMPT_MAX_UTF16 = 28_000;
16
- export function assertKimiLegacyPromptFits(prompt, platform = process.platform) {
17
- if (platform !== "win32" || prompt.length <= KIMI_LEGACY_PROMPT_MAX_UTF16)
18
- return;
19
- throw new Error(`Kimi legacy prompt exceeds the Windows argv limit (${prompt.length}/${KIMI_LEGACY_PROMPT_MAX_UTF16}); `
20
- + "shorten the prompt (protocol-v1 remains disabled on Windows until Job Object ownership is available)");
21
- }
22
- // Kimi Code 思考强度档位(kimi-code 0.23.0 实测+源码):无 CLI 参数,
23
- // 由 runner 经 KIMI_MODEL_THINKING_EFFORT env 注入;白名单外的值不注。
24
- export const KIMI_EFFORT_LEVELS = ["low", "medium", "high", "xhigh", "max"];
25
- export function buildKimiArgs(input) {
26
- if (input.effectivePermission !== undefined && input.effectivePermission !== "full_access") {
27
- throw new Error(`Kimi prompt mode cannot enforce ${input.effectivePermission} permission`);
28
- }
29
- const args = ["--output-format", "stream-json"];
30
- if (input.model)
31
- args.push("--model", input.model);
32
- if (input.sessionId)
33
- args.push("--session", input.sessionId);
34
- args.push("--prompt", input.wakePrompt);
35
- return args;
36
- }
37
- export function spawnKimi(input) {
38
- assertKimiLegacyPromptFits(input.wakePrompt);
39
- // stdio 固定 ignore/pipe/pipe,stdout/stderr 必为 Readable;cross-spawn 类型不带该细化,断言之
40
- return spawn(input.bin, buildKimiArgs(input), {
41
- cwd: input.cwd,
42
- env: input.env,
43
- stdio: ["ignore", "pipe", "pipe"],
44
- });
45
- }
@@ -1,26 +0,0 @@
1
- export const DEFAULT_FIRST_PROGRESS_TIMEOUT_MS = 120_000;
2
- /**
3
- * Bound the silent gap after a protocol turn starts. Once any semantic notification arrives,
4
- * the runtime's configured total timeout remains authoritative; long-running tools are not killed
5
- * merely because they produce no output.
6
- */
7
- export function startFirstProgressWatchdog(onTimeout, timeoutMs = DEFAULT_FIRST_PROGRESS_TIMEOUT_MS) {
8
- if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
9
- throw new RangeError("First-progress timeout must be a positive finite number");
10
- }
11
- let active = true;
12
- const timer = setTimeout(() => {
13
- if (!active)
14
- return;
15
- active = false;
16
- onTimeout();
17
- }, timeoutMs);
18
- timer.unref?.();
19
- const stop = () => {
20
- if (!active)
21
- return;
22
- active = false;
23
- clearTimeout(timer);
24
- };
25
- return { observe: stop, stop };
26
- }
@@ -1,51 +0,0 @@
1
- export function normalizeScheduledPolicy(value) {
2
- return value === "always_report" ? "always_report" : "silent_unless_report";
3
- }
4
- export function normalizeExternalNotificationPolicy(value) {
5
- return value === "agent_decides" ? "agent_decides" : "disabled";
6
- }
7
- export function normalizeScheduledContext(input) {
8
- return {
9
- jobId: input.jobId,
10
- runId: input.runId,
11
- title: input.title?.trim() || "Scheduled job",
12
- outputPolicy: normalizeScheduledPolicy(input.outputPolicy),
13
- externalNotificationPolicy: normalizeExternalNotificationPolicy(input.externalNotificationPolicy),
14
- };
15
- }
16
- export async function deliverScheduledReport(input) {
17
- const title = input.title.trim() || "Scheduled job";
18
- let source = "none";
19
- let content = null;
20
- if (input.exitCode !== 0) {
21
- source = "failure_notice";
22
- const detail = input.errorMessage?.trim().slice(0, 500);
23
- content = `Scheduled job "${title}" failed${detail ? `: ${detail}` : ` (exit ${input.exitCode})`}.`;
24
- }
25
- else if (input.policy === "always_report") {
26
- if (input.finalText?.trim()) {
27
- source = "runtime_final";
28
- content = input.finalText.trim();
29
- }
30
- else {
31
- source = "empty_notice";
32
- content = `Scheduled job "${title}" completed without a usable report.`;
33
- }
34
- }
35
- if (!content) {
36
- return { required: false, attempted: false, delivered: false, source: "none" };
37
- }
38
- try {
39
- const sent = await input.send(content);
40
- return {
41
- required: true,
42
- attempted: true,
43
- delivered: sent.delivered,
44
- status: sent.status,
45
- source,
46
- };
47
- }
48
- catch {
49
- return { required: true, attempted: true, delivered: false, source };
50
- }
51
- }
@@ -1,57 +0,0 @@
1
- function fields(input) {
2
- return {
3
- ...(input.scheduledRunId ? { scheduled_run_id: input.scheduledRunId } : {}),
4
- run_id: input.runId,
5
- agent_handle: input.agentHandle,
6
- channel_id: input.channelId,
7
- exit_code: input.exitCode,
8
- ...(input.runtime ? { runtime: input.runtime } : {}),
9
- ...(input.model ? { model: input.model } : {}),
10
- };
11
- }
12
- export function reportAgentRunComplete(socket, input, log) {
13
- const eventPrefix = input.scheduledRunId ? "scheduled_run" : "run";
14
- if (!socket) {
15
- log(`${eventPrefix}.complete_send_failed`, "Agent 完成回报未发送:控制面连接不可用", {
16
- level: "WARN", ...fields(input), error_message: "control socket unavailable",
17
- });
18
- return;
19
- }
20
- const payload = JSON.stringify({
21
- type: "agent:run-complete",
22
- runId: input.runId,
23
- agentHandle: input.agentHandle,
24
- channelId: input.channelId,
25
- ...(input.threadId !== undefined ? { threadId: input.threadId } : {}),
26
- ...(input.scheduledRunId ? { scheduledRunId: input.scheduledRunId } : {}),
27
- exitCode: input.exitCode,
28
- ...(input.wakeOrigin ? { wakeOrigin: input.wakeOrigin } : {}),
29
- ...(input.originDecision ? { originDecision: input.originDecision } : {}),
30
- ...(input.contextUpToSeq !== undefined ? { contextUpToSeq: input.contextUpToSeq } : {}),
31
- ...(input.runtime ? { runtime: input.runtime } : {}),
32
- ...(input.model !== undefined ? { model: input.model } : {}),
33
- ...(input.resumed !== undefined ? { resumed: input.resumed } : {}),
34
- ...(input.errorMessage ? { errorMessage: input.errorMessage } : {}),
35
- ...(input.usage ? { usage: input.usage } : {}),
36
- ...(input.report ? { report: input.report } : {}),
37
- });
38
- try {
39
- socket.send(payload, (error) => {
40
- if (error) {
41
- log(`${eventPrefix}.complete_send_failed`, "Agent 完成回报发送失败", {
42
- level: "WARN", ...fields(input), error_message: error.message,
43
- });
44
- return;
45
- }
46
- log(`${eventPrefix}.complete_sent`, "Agent 完成回报已发送", fields(input));
47
- });
48
- }
49
- catch (error) {
50
- log(`${eventPrefix}.complete_send_failed`, "Agent 完成回报发送失败", {
51
- level: "WARN", ...fields(input), error_message: error.message,
52
- });
53
- }
54
- }
55
- export function reportScheduledRunComplete(socket, input, log) {
56
- reportAgentRunComplete(socket, input, log);
57
- }