@botlearn-course/daemon 0.0.1 → 0.0.3
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/README.md +12 -1
- package/dist/agent-service-session.d.ts +67 -0
- package/dist/agent-service-session.js +796 -0
- package/dist/agent-service-ws-protocol.d.ts +28 -0
- package/dist/agent-service-ws-protocol.js +128 -0
- package/dist/cli.d.ts +1 -2
- package/dist/cli.js +114 -13
- package/dist/course-client.js +4 -2
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/mcp/report-progress-server.d.ts +21 -0
- package/dist/mcp/report-progress-server.js +136 -0
- package/dist/mcp/report-progress.d.ts +29 -0
- package/dist/mcp/report-progress.js +60 -0
- package/dist/run-dispatcher.d.ts +15 -0
- package/dist/run-dispatcher.js +126 -6
- package/dist/runtime-env.d.ts +9 -0
- package/dist/runtime-env.js +40 -0
- package/dist/runtime-profile.js +25 -13
- package/dist/runtimes/acp-stream.js +2 -0
- package/dist/runtimes/codex.d.ts +1 -1
- package/dist/runtimes/codex.js +2 -2
- package/dist/runtimes/deepseek-tui.d.ts +6 -2
- package/dist/runtimes/deepseek-tui.js +238 -41
- package/dist/runtimes/engine.d.ts +14 -3
- package/dist/runtimes/engine.js +32 -5
- package/dist/runtimes/hermes-agent.d.ts +1 -1
- package/dist/runtimes/hermes-agent.js +3 -2
- package/dist/runtimes/ndjson-stream.d.ts +1 -1
- package/dist/runtimes/ndjson-stream.js +4 -2
- package/dist/runtimes/openclaw-acp.js +3 -1
- package/dist/runtimes/progress.d.ts +50 -0
- package/dist/runtimes/progress.js +339 -0
- package/dist/sandbox-supervisor.d.ts +3 -0
- package/dist/sandbox-supervisor.js +176 -0
- package/dist/transcript.js +6 -0
- package/dist/types.d.ts +29 -3
- package/dist/websocket-client.d.ts +43 -0
- package/dist/websocket-client.js +320 -0
- package/dist/workspace.d.ts +9 -0
- package/dist/workspace.js +43 -2
- package/package.json +3 -2
package/dist/run-dispatcher.js
CHANGED
|
@@ -2,6 +2,7 @@ import { randomUUID } from "node:crypto";
|
|
|
2
2
|
import { CourseClientError, isRunTerminal } from "./course-client.js";
|
|
3
3
|
import { reportFileCandidates } from "./file-candidates.js";
|
|
4
4
|
import { log as defaultLog } from "./log.js";
|
|
5
|
+
import { MAX_PROGRESS_EVENTS_PER_ATTEMPT, tryNormalizeProgressReport, } from "./mcp/report-progress.js";
|
|
5
6
|
import { errorInfo, redactSecretString, truncateText } from "./redaction.js";
|
|
6
7
|
import { missingRunCapabilities } from "./runtime-capabilities.js";
|
|
7
8
|
import { RunQueue } from "./run-queue.js";
|
|
@@ -35,10 +36,13 @@ export class RunDispatcher {
|
|
|
35
36
|
runtimes;
|
|
36
37
|
queue = new RunQueue();
|
|
37
38
|
inflight = new Map();
|
|
39
|
+
scheduledRunIds = new Set();
|
|
40
|
+
pendingCancellations = new Set();
|
|
38
41
|
defaultRuntimeId;
|
|
39
42
|
log;
|
|
40
43
|
scanLimits;
|
|
41
44
|
now;
|
|
45
|
+
persistentSession;
|
|
42
46
|
constructor(client, runtimes, opts = {}) {
|
|
43
47
|
this.client = client;
|
|
44
48
|
this.runtimes = runtimes;
|
|
@@ -46,22 +50,37 @@ export class RunDispatcher {
|
|
|
46
50
|
this.log = opts.log ?? defaultLog;
|
|
47
51
|
this.scanLimits = opts.scanLimits;
|
|
48
52
|
this.now = opts.now ?? Date.now;
|
|
53
|
+
this.persistentSession = opts.persistentSession;
|
|
49
54
|
}
|
|
50
55
|
/** 排队执行一个 run。返回的 Promise 不 reject(失败已归一化回报为 run.failed)。 */
|
|
51
56
|
dispatch(payload) {
|
|
52
57
|
const key = payload.agent_instance_id ?? payload.agent_run_id;
|
|
53
|
-
|
|
58
|
+
this.scheduledRunIds.add(payload.agent_run_id);
|
|
59
|
+
return this.queue
|
|
60
|
+
.enqueue(key, () => this.execute(payload))
|
|
61
|
+
.finally(() => {
|
|
62
|
+
this.scheduledRunIds.delete(payload.agent_run_id);
|
|
63
|
+
this.pendingCancellations.delete(payload.agent_run_id);
|
|
64
|
+
});
|
|
54
65
|
}
|
|
55
66
|
cancel(agentRunId) {
|
|
56
67
|
const controller = this.inflight.get(agentRunId);
|
|
57
|
-
if (
|
|
68
|
+
if (controller) {
|
|
69
|
+
controller.abort();
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
if (!this.scheduledRunIds.has(agentRunId))
|
|
58
73
|
return false;
|
|
59
|
-
|
|
74
|
+
// dispatch() queues execution on a microtask. A durable cancel may arrive after the
|
|
75
|
+
// command ACK but before execute() installs its AbortController.
|
|
76
|
+
this.pendingCancellations.add(agentRunId);
|
|
60
77
|
return true;
|
|
61
78
|
}
|
|
62
79
|
cancelAll() {
|
|
63
80
|
for (const controller of this.inflight.values())
|
|
64
81
|
controller.abort();
|
|
82
|
+
for (const runId of this.scheduledRunIds)
|
|
83
|
+
this.pendingCancellations.add(runId);
|
|
65
84
|
}
|
|
66
85
|
get activeCount() {
|
|
67
86
|
return this.inflight.size;
|
|
@@ -80,6 +99,8 @@ export class RunDispatcher {
|
|
|
80
99
|
const runId = payload.agent_run_id;
|
|
81
100
|
const controller = new AbortController();
|
|
82
101
|
this.inflight.set(runId, controller);
|
|
102
|
+
if (this.pendingCancellations.delete(runId))
|
|
103
|
+
controller.abort();
|
|
83
104
|
let seq = 0;
|
|
84
105
|
// 服务端已把本 run 判为终态(409):停止一切后续上报。
|
|
85
106
|
let serverTerminal = false;
|
|
@@ -90,6 +111,15 @@ export class RunDispatcher {
|
|
|
90
111
|
let profileApplied = false;
|
|
91
112
|
let toolCalls = 0;
|
|
92
113
|
let toolLimitExceeded = false;
|
|
114
|
+
let progressEvents = 0;
|
|
115
|
+
let lastProgressKey = null;
|
|
116
|
+
const progressDisposition = {
|
|
117
|
+
accepted: 0,
|
|
118
|
+
deduplicated: 0,
|
|
119
|
+
invalid: 0,
|
|
120
|
+
over_limit: 0,
|
|
121
|
+
server_rejected: 0,
|
|
122
|
+
};
|
|
93
123
|
const startedAt = this.now();
|
|
94
124
|
const usage = () => ({
|
|
95
125
|
wall_time_ms: Math.max(0, this.now() - startedAt),
|
|
@@ -163,12 +193,13 @@ export class RunDispatcher {
|
|
|
163
193
|
...runtimeProfileInstructions(payload, applied),
|
|
164
194
|
];
|
|
165
195
|
}
|
|
166
|
-
const
|
|
196
|
+
const persistentTurn = this.persistentSession?.prepareTurn(payload);
|
|
197
|
+
const { workspaceDir } = persistentTurn ?? ensureRunWorkspace(runId);
|
|
167
198
|
const missingCapabilities = missingRunCapabilities(payload, workspaceDir);
|
|
168
199
|
if (missingCapabilities.length > 0) {
|
|
169
200
|
throw new RuntimeExecutionError(`runtime is missing required capabilities: ${missingCapabilities.join(", ")}`, "runtime_unavailable");
|
|
170
201
|
}
|
|
171
|
-
const transcript = new TranscriptWriter(transcriptPath(runId));
|
|
202
|
+
const transcript = new TranscriptWriter(persistentTurn?.transcriptFile ?? transcriptPath(runId));
|
|
172
203
|
const timeoutSeconds = clampTimeoutSeconds(payload.limits.timeout_seconds);
|
|
173
204
|
timer = setTimeout(() => {
|
|
174
205
|
timedOut = true;
|
|
@@ -178,7 +209,74 @@ export class RunDispatcher {
|
|
|
178
209
|
// 上一次真正上了 wire 的块 kind:thinking/status 只在 kind 切换时上报一次,避免刷屏。
|
|
179
210
|
let lastReportedKind = null;
|
|
180
211
|
const sink = {
|
|
212
|
+
progressDispositions: async (dispositions) => {
|
|
213
|
+
progressDisposition.invalid += dispositions.invalid;
|
|
214
|
+
progressDisposition.deduplicated += dispositions.deduplicated;
|
|
215
|
+
progressDisposition.over_limit += dispositions.over_limit;
|
|
216
|
+
},
|
|
181
217
|
block: async (block) => {
|
|
218
|
+
if (block.kind === "progress") {
|
|
219
|
+
const normalized = tryNormalizeProgressReport({
|
|
220
|
+
summary: block.summary,
|
|
221
|
+
status: block.status,
|
|
222
|
+
});
|
|
223
|
+
if (!normalized) {
|
|
224
|
+
progressDisposition.invalid += 1;
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
// Redact before the local transcript and every reporting client boundary.
|
|
228
|
+
const progress = tryNormalizeProgressReport({
|
|
229
|
+
summary: redactSecretString(normalized.summary),
|
|
230
|
+
status: normalized.status,
|
|
231
|
+
});
|
|
232
|
+
// Redaction can expand a token marker past 240 code points; never truncate or send it.
|
|
233
|
+
if (!progress) {
|
|
234
|
+
progressDisposition.invalid += 1;
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
const key = `${progress.status}\0${progress.summary}`;
|
|
238
|
+
if (key === lastProgressKey) {
|
|
239
|
+
progressDisposition.deduplicated += 1;
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
if (progressEvents >= MAX_PROGRESS_EVENTS_PER_ATTEMPT) {
|
|
243
|
+
progressDisposition.over_limit += 1;
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
lastProgressKey = key;
|
|
247
|
+
progressEvents += 1;
|
|
248
|
+
transcript.writeBlock({
|
|
249
|
+
kind: "progress",
|
|
250
|
+
runtime: runtime.id,
|
|
251
|
+
summary: progress.summary,
|
|
252
|
+
status: progress.status,
|
|
253
|
+
});
|
|
254
|
+
if (serverTerminal)
|
|
255
|
+
return;
|
|
256
|
+
try {
|
|
257
|
+
await send({
|
|
258
|
+
type: "run.block",
|
|
259
|
+
payload: {
|
|
260
|
+
schema_version: "agent-progress/0.1",
|
|
261
|
+
kind: "progress",
|
|
262
|
+
runtime: runtime.id,
|
|
263
|
+
summary: progress.summary,
|
|
264
|
+
status: progress.status,
|
|
265
|
+
},
|
|
266
|
+
});
|
|
267
|
+
if (!serverTerminal)
|
|
268
|
+
progressDisposition.accepted += 1;
|
|
269
|
+
}
|
|
270
|
+
catch (error) {
|
|
271
|
+
if (error instanceof CourseClientError &&
|
|
272
|
+
(error.status === 400 || error.status === 422)) {
|
|
273
|
+
progressDisposition.server_rejected += 1;
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
throw error;
|
|
277
|
+
}
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
182
280
|
transcript.writeBlock(block);
|
|
183
281
|
if (block.kind === "tool_call") {
|
|
184
282
|
toolCalls += 1;
|
|
@@ -212,8 +310,23 @@ export class RunDispatcher {
|
|
|
212
310
|
file: async (file) => {
|
|
213
311
|
await this.client.postFile(runId, file);
|
|
214
312
|
},
|
|
313
|
+
runtimeSession: async (sessionId) => {
|
|
314
|
+
this.persistentSession?.persistNativeSession(sessionId);
|
|
315
|
+
},
|
|
215
316
|
};
|
|
216
|
-
await runtime.run({
|
|
317
|
+
await runtime.run({
|
|
318
|
+
payload,
|
|
319
|
+
workspaceDir,
|
|
320
|
+
...(persistentTurn
|
|
321
|
+
? {
|
|
322
|
+
nativeSessionId: persistentTurn.nativeSessionId,
|
|
323
|
+
contextRevision: persistentTurn.contextRevision,
|
|
324
|
+
...(persistentTurn.runtimeEnv
|
|
325
|
+
? { runtimeEnv: persistentTurn.runtimeEnv }
|
|
326
|
+
: {}),
|
|
327
|
+
}
|
|
328
|
+
: {}),
|
|
329
|
+
}, sink, controller.signal);
|
|
217
330
|
clearTimeout(timer);
|
|
218
331
|
if (serverTerminal)
|
|
219
332
|
return;
|
|
@@ -288,6 +401,13 @@ export class RunDispatcher {
|
|
|
288
401
|
clearTimeout(timer);
|
|
289
402
|
if (profileApplied)
|
|
290
403
|
cleanupRunRuntimeProfile(runId);
|
|
404
|
+
if (Object.values(progressDisposition).some((count) => count > 0)) {
|
|
405
|
+
this.log.info("progress event dispositions", {
|
|
406
|
+
agentRunId: runId,
|
|
407
|
+
runtime: runtimeId,
|
|
408
|
+
...progressDisposition,
|
|
409
|
+
});
|
|
410
|
+
}
|
|
291
411
|
this.inflight.delete(runId);
|
|
292
412
|
}
|
|
293
413
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** Remove Course control-plane coordinates before any model/runtime child is created. */
|
|
2
|
+
export declare function clearAgentServiceControlEnv(env?: NodeJS.ProcessEnv): void;
|
|
3
|
+
/** Copy an environment for a runtime child without leaking Agent Service control values. */
|
|
4
|
+
export declare function runtimeChildEnv(env?: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
|
|
5
|
+
/** Run model processes as the Template's untrusted runtime UID when configured. */
|
|
6
|
+
export declare function runtimeChildIdentity(env?: NodeJS.ProcessEnv): {
|
|
7
|
+
uid?: number;
|
|
8
|
+
gid?: number;
|
|
9
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
const AGENT_SERVICE_CONTROL_ENV_KEYS = [
|
|
2
|
+
"BOTLEARN_COURSE_API_URL",
|
|
3
|
+
"BOTLEARN_AGENT_SERVICE_RUN_ID",
|
|
4
|
+
"BOTLEARN_AGENT_SERVICE_RUN_TOKEN",
|
|
5
|
+
"BOTLEARN_AGENT_SERVICE_WORKER_ID",
|
|
6
|
+
"BOTLEARN_AGENT_SERVICE_WS_URL",
|
|
7
|
+
"BOTLEARN_AGENT_SERVICE_RUNTIME_SESSION_ID",
|
|
8
|
+
"BOTLEARN_AGENT_SERVICE_SESSION_TOKEN",
|
|
9
|
+
];
|
|
10
|
+
const AGENT_SERVICE_SUPERVISOR_ENV_KEYS = [
|
|
11
|
+
"BOTLEARN_RUNTIME_UID",
|
|
12
|
+
"BOTLEARN_RUNTIME_GID",
|
|
13
|
+
"BOTLEARN_AGENT_SERVICE_WORKSPACE_ROOT",
|
|
14
|
+
"BOTLEARN_AGENT_SERVICE_PROFILE_ROOT",
|
|
15
|
+
];
|
|
16
|
+
/** Remove Course control-plane coordinates before any model/runtime child is created. */
|
|
17
|
+
export function clearAgentServiceControlEnv(env = process.env) {
|
|
18
|
+
for (const key of AGENT_SERVICE_CONTROL_ENV_KEYS)
|
|
19
|
+
delete env[key];
|
|
20
|
+
}
|
|
21
|
+
/** Copy an environment for a runtime child without leaking Agent Service control values. */
|
|
22
|
+
export function runtimeChildEnv(env = process.env) {
|
|
23
|
+
const childEnv = { ...env };
|
|
24
|
+
clearAgentServiceControlEnv(childEnv);
|
|
25
|
+
for (const key of AGENT_SERVICE_SUPERVISOR_ENV_KEYS)
|
|
26
|
+
delete childEnv[key];
|
|
27
|
+
return childEnv;
|
|
28
|
+
}
|
|
29
|
+
/** Run model processes as the Template's untrusted runtime UID when configured. */
|
|
30
|
+
export function runtimeChildIdentity(env = process.env) {
|
|
31
|
+
const uid = Number(env.BOTLEARN_RUNTIME_UID);
|
|
32
|
+
const gid = Number(env.BOTLEARN_RUNTIME_GID);
|
|
33
|
+
if (!Number.isInteger(uid) ||
|
|
34
|
+
uid < 1 ||
|
|
35
|
+
!Number.isInteger(gid) ||
|
|
36
|
+
gid < 1) {
|
|
37
|
+
return {};
|
|
38
|
+
}
|
|
39
|
+
return { uid, gid };
|
|
40
|
+
}
|
package/dist/runtime-profile.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
import { mkdirSync, mkdtempSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { chmodSync, mkdirSync, mkdtempSync, renameSync, rmSync, writeFileSync, } from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
import { runtimeProfileDir,
|
|
4
|
+
import { runtimeProfileDir, runtimeProfileRunRootDir } from "./workspace.js";
|
|
5
5
|
const SAFE_ASSET_ID = /^[a-z0-9][a-z0-9._-]{0,159}$/;
|
|
6
6
|
const SHA256 = /^sha256:[0-9a-f]{64}$/;
|
|
7
7
|
const MAX_SKILL_PACKAGES = 32;
|
|
@@ -20,13 +20,19 @@ export function applyRunRuntimeProfile(payload, raw) {
|
|
|
20
20
|
if (payload.context.profileHash !== profile.profileHash) {
|
|
21
21
|
throw new RuntimeProfileApplyError("run.start profileHash does not match runtime profile");
|
|
22
22
|
}
|
|
23
|
-
const runDir =
|
|
23
|
+
const runDir = runtimeProfileRunRootDir(payload.agent_run_id);
|
|
24
24
|
const target = runtimeProfileDir(payload.agent_run_id);
|
|
25
|
-
|
|
25
|
+
const managedReadView = Boolean(process.env.BOTLEARN_AGENT_SERVICE_PROFILE_ROOT?.trim());
|
|
26
|
+
const directoryMode = managedReadView ? 0o750 : 0o700;
|
|
27
|
+
const fileMode = managedReadView ? 0o640 : 0o600;
|
|
28
|
+
mkdirSync(runDir, { recursive: true, mode: directoryMode });
|
|
29
|
+
chmodSync(runDir, directoryMode);
|
|
26
30
|
const staging = mkdtempSync(path.join(runDir, ".runtime-profile-"));
|
|
31
|
+
chmodSync(staging, directoryMode);
|
|
27
32
|
try {
|
|
28
33
|
const skillsRoot = path.join(staging, "skills");
|
|
29
|
-
mkdirSync(skillsRoot, { recursive: true, mode:
|
|
34
|
+
mkdirSync(skillsRoot, { recursive: true, mode: directoryMode });
|
|
35
|
+
chmodSync(skillsRoot, directoryMode);
|
|
30
36
|
const skillRefs = [];
|
|
31
37
|
const skillIds = new Set();
|
|
32
38
|
for (const skill of profile.skillPackages) {
|
|
@@ -34,12 +40,14 @@ export function applyRunRuntimeProfile(payload, raw) {
|
|
|
34
40
|
throw new RuntimeProfileApplyError(`duplicate runtime Skill id: ${skill.id}`);
|
|
35
41
|
}
|
|
36
42
|
skillIds.add(skill.id);
|
|
37
|
-
installSkillPackage(skillsRoot, skill);
|
|
43
|
+
installSkillPackage(skillsRoot, skill, { directoryMode, fileMode });
|
|
38
44
|
skillRefs.push(`${skill.id}@${skill.version}`);
|
|
39
45
|
}
|
|
40
46
|
const promptPackPath = path.join(staging, "prompt-pack.md");
|
|
41
|
-
writeFileSync(promptPackPath, profile.promptPack.systemInstructions, { mode:
|
|
42
|
-
|
|
47
|
+
writeFileSync(promptPackPath, profile.promptPack.systemInstructions, { mode: fileMode });
|
|
48
|
+
chmodSync(promptPackPath, fileMode);
|
|
49
|
+
const manifestPath = path.join(staging, "profile.json");
|
|
50
|
+
writeFileSync(manifestPath, `${JSON.stringify({
|
|
43
51
|
schemaVersion: profile.schemaVersion,
|
|
44
52
|
profileId: profile.profileId,
|
|
45
53
|
profileHash: profile.profileHash,
|
|
@@ -51,7 +59,8 @@ export function applyRunRuntimeProfile(payload, raw) {
|
|
|
51
59
|
},
|
|
52
60
|
skillRefs,
|
|
53
61
|
requiredCapabilities: profile.requiredCapabilities,
|
|
54
|
-
}, null, 2)}\n`, { mode:
|
|
62
|
+
}, null, 2)}\n`, { mode: fileMode });
|
|
63
|
+
chmodSync(manifestPath, fileMode);
|
|
55
64
|
rmSync(target, { recursive: true, force: true });
|
|
56
65
|
renameSync(staging, target);
|
|
57
66
|
return {
|
|
@@ -94,7 +103,7 @@ export function runtimeProfileInstructions(payload, applied) {
|
|
|
94
103
|
: []),
|
|
95
104
|
];
|
|
96
105
|
}
|
|
97
|
-
function installSkillPackage(skillsRoot, skill) {
|
|
106
|
+
function installSkillPackage(skillsRoot, skill, modes) {
|
|
98
107
|
assertSafeAssetId(skill.id, "Skill id");
|
|
99
108
|
if (!SHA256.test(skill.digest)) {
|
|
100
109
|
throw new RuntimeProfileApplyError(`Skill ${skill.id} has an invalid digest`);
|
|
@@ -104,11 +113,14 @@ function installSkillPackage(skillsRoot, skill) {
|
|
|
104
113
|
throw new RuntimeProfileApplyError(`Skill ${skill.id}@${skill.version} digest mismatch`);
|
|
105
114
|
}
|
|
106
115
|
const skillDir = path.join(skillsRoot, skill.id);
|
|
107
|
-
mkdirSync(skillDir, { recursive: true, mode:
|
|
116
|
+
mkdirSync(skillDir, { recursive: true, mode: modes.directoryMode });
|
|
117
|
+
chmodSync(skillDir, modes.directoryMode);
|
|
108
118
|
for (const entry of entries) {
|
|
109
119
|
const destination = path.join(skillDir, entry.path);
|
|
110
|
-
mkdirSync(path.dirname(destination), { recursive: true, mode:
|
|
111
|
-
|
|
120
|
+
mkdirSync(path.dirname(destination), { recursive: true, mode: modes.directoryMode });
|
|
121
|
+
chmodSync(path.dirname(destination), modes.directoryMode);
|
|
122
|
+
writeFileSync(destination, entry.content, { mode: modes.fileMode });
|
|
123
|
+
chmodSync(destination, modes.fileMode);
|
|
112
124
|
}
|
|
113
125
|
}
|
|
114
126
|
function archiveEntries(skill) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
|
+
import { runtimeChildIdentity } from "../runtime-env.js";
|
|
2
3
|
import { sanitizeRuntimeFailureText } from "../redaction.js";
|
|
3
4
|
import { sliceUtf8Bytes, utf8ByteLength } from "./text-cap.js";
|
|
4
5
|
import { consoleLogger, } from "./engine.js";
|
|
@@ -206,6 +207,7 @@ export class AcpRuntimeAdapter {
|
|
|
206
207
|
const child = spawn(binary, args, {
|
|
207
208
|
cwd: opts.cwd,
|
|
208
209
|
env: this.spawnEnv(opts),
|
|
210
|
+
...runtimeChildIdentity(),
|
|
209
211
|
stdio: ["pipe", "pipe", "pipe"],
|
|
210
212
|
});
|
|
211
213
|
let killTimer = null;
|
package/dist/runtimes/codex.d.ts
CHANGED
|
@@ -38,7 +38,7 @@ export declare class CodexAdapter extends NdjsonStreamAdapter {
|
|
|
38
38
|
* `--` 隔开 flags 与 positionals,防止以 `-` 开头的 prompt 被解析成选项。
|
|
39
39
|
*/
|
|
40
40
|
protected buildArgs(opts: EngineRunOptions): string[];
|
|
41
|
-
protected spawnEnv(
|
|
41
|
+
protected spawnEnv(opts: EngineRunOptions): NodeJS.ProcessEnv;
|
|
42
42
|
protected handleEvent(raw: unknown, ctx: NdjsonEventCtx): void;
|
|
43
43
|
}
|
|
44
44
|
export declare const codexModule: RuntimeModule;
|
package/dist/runtimes/codex.js
CHANGED
|
@@ -181,9 +181,9 @@ export class CodexAdapter extends NdjsonStreamAdapter {
|
|
|
181
181
|
}
|
|
182
182
|
return ["exec", ...tail, "--", prompt];
|
|
183
183
|
}
|
|
184
|
-
spawnEnv(
|
|
184
|
+
spawnEnv(opts) {
|
|
185
185
|
return {
|
|
186
|
-
...
|
|
186
|
+
...super.spawnEnv(opts),
|
|
187
187
|
// 保证 JSONL 输出不混入 ANSI 转义。
|
|
188
188
|
FORCE_COLOR: "0",
|
|
189
189
|
NO_COLOR: "1",
|
|
@@ -2,13 +2,15 @@ import { spawn } from "node:child_process";
|
|
|
2
2
|
import { type ProbeDeps } from "./probe.js";
|
|
3
3
|
import { type EngineAdapter, type EngineRunOptions, type EngineRunResult } from "./engine.js";
|
|
4
4
|
import type { RuntimeModule, RuntimeProbe } from "../types.js";
|
|
5
|
-
interface DeepseekAdapterDeps {
|
|
5
|
+
export interface DeepseekAdapterDeps {
|
|
6
6
|
binary?: string;
|
|
7
7
|
/** 测试注入:使用现成的兼容 server,不 spawn `deepseek`。 */
|
|
8
8
|
serverUrl?: string;
|
|
9
9
|
authToken?: string;
|
|
10
10
|
fetchFn?: typeof fetch;
|
|
11
11
|
spawnFn?: typeof spawn;
|
|
12
|
+
/** External servers must explicitly attest compatible progress tool events. */
|
|
13
|
+
progressEventMapping?: boolean;
|
|
12
14
|
}
|
|
13
15
|
/** 解析 PATH 上的 `deepseek` dispatcher CLI。 */
|
|
14
16
|
export declare function resolveDeepseekCommand(deps?: ProbeDeps): string | null;
|
|
@@ -28,6 +30,8 @@ export declare class DeepseekTuiAdapter implements EngineAdapter {
|
|
|
28
30
|
private readonly explicitAuthToken;
|
|
29
31
|
private readonly fetchFn;
|
|
30
32
|
private readonly spawnFn;
|
|
33
|
+
readonly progressEventMappingEnabled: boolean;
|
|
34
|
+
readonly progressPromptInjectionEnabled: boolean;
|
|
31
35
|
private resolvedBinary;
|
|
32
36
|
constructor(deps?: DeepseekAdapterDeps);
|
|
33
37
|
run(opts: EngineRunOptions): Promise<EngineRunResult>;
|
|
@@ -41,10 +45,10 @@ export declare class DeepseekTuiAdapter implements EngineAdapter {
|
|
|
41
45
|
private createThread;
|
|
42
46
|
private patchThreadSystemContext;
|
|
43
47
|
private startTurnAndReadEvents;
|
|
48
|
+
private interruptTurn;
|
|
44
49
|
private readEvents;
|
|
45
50
|
private requestJson;
|
|
46
51
|
}
|
|
47
52
|
/** 仅测试用:清空进程池。 */
|
|
48
53
|
export declare function __resetDeepseekTuiPoolForTests(): void;
|
|
49
54
|
export declare const deepseekTuiModule: RuntimeModule;
|
|
50
|
-
export {};
|