@nowcrew/daemon 0.5.45 → 0.5.46
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 +22 -3
- package/dist/agent-memory/bridge.js +101 -10
- package/dist/execution-journal.js +1 -1
- package/dist/execution-protocol.js +3 -11
- package/dist/execution-runner.js +13 -4
- package/dist/external-output.js +4 -0
- package/dist/list-models.js +102 -8
- package/dist/local-executor.js +87 -14
- package/dist/local-memory-diagnostics.js +336 -0
- package/dist/local-memory-telemetry.js +224 -0
- package/dist/machine-info.js +22 -7
- package/dist/main.js +6 -1
- package/dist/normalize.js +14 -4
- package/dist/runtime-capabilities.js +11 -1
- package/dist/runtime-probe.js +26 -0
- package/dist/runtime-startup-gate.js +2 -0
- package/dist/runtimes/codex-app-server-runner.js +10 -0
- package/dist/runtimes/hermes-models.js +117 -0
- package/dist/runtimes/hermes.js +6 -0
- package/dist/runtimes/kimi-acp-runner.js +116 -36
- package/dist/runtimes/opencode-runner.js +122 -0
- package/dist/runtimes/opencode.js +181 -0
- package/dist/serve.js +27 -4
- package/dist/slog.js +21 -3
- package/dist/supervised-runtime.js +22 -1
- package/dist/workspace.js +34 -3
- package/package.json +1 -1
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { promisify } from "node:util";
|
|
3
|
+
import { isWin } from "../platform.js";
|
|
4
|
+
import { augmentedPath } from "../runtime-path.js";
|
|
5
|
+
const execFileP = promisify(execFile);
|
|
6
|
+
const REQUIRED_RUN_FLAGS = [
|
|
7
|
+
"--format",
|
|
8
|
+
"--dangerously-skip-permissions",
|
|
9
|
+
"--dir",
|
|
10
|
+
"--model",
|
|
11
|
+
"--variant",
|
|
12
|
+
"--session",
|
|
13
|
+
];
|
|
14
|
+
export async function probeOpenCodeRun(signal) {
|
|
15
|
+
try {
|
|
16
|
+
const { stdout, stderr } = await execFileP("opencode", ["run", "--help"], {
|
|
17
|
+
shell: isWin(),
|
|
18
|
+
timeout: 8_000,
|
|
19
|
+
killSignal: "SIGKILL",
|
|
20
|
+
maxBuffer: 1024 * 1024,
|
|
21
|
+
signal,
|
|
22
|
+
env: { ...process.env, PATH: augmentedPath() },
|
|
23
|
+
});
|
|
24
|
+
const help = `${stdout}\n${stderr}`;
|
|
25
|
+
return REQUIRED_RUN_FLAGS.every((flag) => help.includes(flag));
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
export function buildOpenCodeArgs(options) {
|
|
32
|
+
return [
|
|
33
|
+
"run", "--format", "json", "--dangerously-skip-permissions",
|
|
34
|
+
"--dir", options.cwd,
|
|
35
|
+
...(options.model ? ["--model", options.model] : []),
|
|
36
|
+
...(options.reasoning ? ["--variant", options.reasoning] : []),
|
|
37
|
+
...(options.sessionId ? ["--session", options.sessionId] : []),
|
|
38
|
+
];
|
|
39
|
+
}
|
|
40
|
+
function outputText(value) {
|
|
41
|
+
if (typeof value === "string")
|
|
42
|
+
return value;
|
|
43
|
+
if (value === undefined || value === null)
|
|
44
|
+
return "";
|
|
45
|
+
try {
|
|
46
|
+
return JSON.stringify(value);
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return String(value);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function reportedUsage(part) {
|
|
53
|
+
if ((part.cost ?? 0) > 0)
|
|
54
|
+
return true;
|
|
55
|
+
const tokens = part.tokens;
|
|
56
|
+
if (!tokens)
|
|
57
|
+
return false;
|
|
58
|
+
return (tokens.input ?? 0) > 0 || (tokens.output ?? 0) > 0
|
|
59
|
+
|| (tokens.reasoning ?? 0) > 0 || (tokens.total ?? 0) > 0
|
|
60
|
+
|| (tokens.cache?.read ?? 0) > 0 || (tokens.cache?.write ?? 0) > 0;
|
|
61
|
+
}
|
|
62
|
+
export function createOpenCodeEventDecoder() {
|
|
63
|
+
let sessionId = null;
|
|
64
|
+
let announcedSession = null;
|
|
65
|
+
let finalText = "";
|
|
66
|
+
let error = null;
|
|
67
|
+
let openStep = false;
|
|
68
|
+
let stepHasContinuationTool = false;
|
|
69
|
+
let awaitingContinuation = false;
|
|
70
|
+
let stepProducedOutput = false;
|
|
71
|
+
let lastStepVoid = false;
|
|
72
|
+
let sawEvent = false;
|
|
73
|
+
let sawStepFinish = false;
|
|
74
|
+
const usage = {
|
|
75
|
+
input_tokens: 0,
|
|
76
|
+
output_tokens: 0,
|
|
77
|
+
cache_read_input_tokens: 0,
|
|
78
|
+
cache_creation_input_tokens: 0,
|
|
79
|
+
};
|
|
80
|
+
return {
|
|
81
|
+
push(raw) {
|
|
82
|
+
const event = (raw ?? {});
|
|
83
|
+
const out = [];
|
|
84
|
+
if (typeof event.type === "string" && event.type)
|
|
85
|
+
sawEvent = true;
|
|
86
|
+
if (typeof event.sessionID === "string" && event.sessionID) {
|
|
87
|
+
sessionId = event.sessionID;
|
|
88
|
+
if (announcedSession !== sessionId) {
|
|
89
|
+
announcedSession = sessionId;
|
|
90
|
+
out.push({ type: "thread.started", thread_id: sessionId });
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const part = event.part ?? {};
|
|
94
|
+
if (event.type === "step_start") {
|
|
95
|
+
openStep = true;
|
|
96
|
+
awaitingContinuation = false;
|
|
97
|
+
stepHasContinuationTool = false;
|
|
98
|
+
stepProducedOutput = false;
|
|
99
|
+
out.push({ type: "opencode.step_start" });
|
|
100
|
+
}
|
|
101
|
+
else if (event.type === "text" && typeof part.text === "string" && part.text) {
|
|
102
|
+
finalText += part.text;
|
|
103
|
+
stepProducedOutput = true;
|
|
104
|
+
out.push({ type: "opencode.text_delta", text: part.text });
|
|
105
|
+
}
|
|
106
|
+
else if (event.type === "tool_use") {
|
|
107
|
+
stepProducedOutput = true;
|
|
108
|
+
if (part.metadata?.providerExecuted !== true)
|
|
109
|
+
stepHasContinuationTool = true;
|
|
110
|
+
out.push({
|
|
111
|
+
type: "opencode.tool_call",
|
|
112
|
+
...(part.callID ? { id: part.callID } : {}),
|
|
113
|
+
...(part.tool ? { title: part.tool } : {}),
|
|
114
|
+
...(part.state?.input === undefined ? {} : { input: part.state.input }),
|
|
115
|
+
});
|
|
116
|
+
if (part.state?.status === "completed" || part.state?.status === "error") {
|
|
117
|
+
const content = part.state.status === "error" && part.state.error
|
|
118
|
+
? part.state.error
|
|
119
|
+
: outputText(part.state.output);
|
|
120
|
+
out.push({
|
|
121
|
+
type: "opencode.tool_result",
|
|
122
|
+
...(part.callID ? { id: part.callID } : {}),
|
|
123
|
+
status: part.state.status,
|
|
124
|
+
...(content ? { content } : {}),
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
else if (event.type === "step_finish") {
|
|
129
|
+
openStep = false;
|
|
130
|
+
sawStepFinish = true;
|
|
131
|
+
awaitingContinuation = part.reason === "tool-calls"
|
|
132
|
+
|| (Boolean(part.reason) && stepHasContinuationTool);
|
|
133
|
+
stepHasContinuationTool = false;
|
|
134
|
+
if (reportedUsage(part))
|
|
135
|
+
stepProducedOutput = true;
|
|
136
|
+
lastStepVoid = !stepProducedOutput;
|
|
137
|
+
const tokens = part.tokens;
|
|
138
|
+
if (tokens) {
|
|
139
|
+
usage.input_tokens += tokens.input ?? 0;
|
|
140
|
+
usage.output_tokens += tokens.output ?? 0;
|
|
141
|
+
usage.cache_read_input_tokens += tokens.cache?.read ?? 0;
|
|
142
|
+
usage.cache_creation_input_tokens += tokens.cache?.write ?? 0;
|
|
143
|
+
}
|
|
144
|
+
out.push({
|
|
145
|
+
type: "opencode.step_finish",
|
|
146
|
+
...(part.reason ? { stop_reason: part.reason } : {}),
|
|
147
|
+
...(tokens ? { usage: {
|
|
148
|
+
input_tokens: tokens.input ?? 0,
|
|
149
|
+
output_tokens: tokens.output ?? 0,
|
|
150
|
+
cache_read_input_tokens: tokens.cache?.read ?? 0,
|
|
151
|
+
cache_creation_input_tokens: tokens.cache?.write ?? 0,
|
|
152
|
+
} } : {}),
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
else if (event.type === "error") {
|
|
156
|
+
error = event.error?.data?.message || event.error?.name || "unknown OpenCode error";
|
|
157
|
+
out.push({ type: "opencode.error", message: error });
|
|
158
|
+
}
|
|
159
|
+
return out;
|
|
160
|
+
},
|
|
161
|
+
finish() {
|
|
162
|
+
const structuralError = openStep
|
|
163
|
+
? "OpenCode stream ended while a step was still open"
|
|
164
|
+
: awaitingContinuation
|
|
165
|
+
? "OpenCode stream ended before the required continuation"
|
|
166
|
+
: lastStepVoid
|
|
167
|
+
? "OpenCode stream ended on an empty step"
|
|
168
|
+
: !sawEvent || !sawStepFinish
|
|
169
|
+
? "OpenCode stream ended with no events proving completion"
|
|
170
|
+
: null;
|
|
171
|
+
const failure = error ?? structuralError;
|
|
172
|
+
return {
|
|
173
|
+
ok: failure === null,
|
|
174
|
+
...(failure === null ? {} : { error: failure }),
|
|
175
|
+
finalText,
|
|
176
|
+
sessionId,
|
|
177
|
+
...(sawStepFinish ? { usage } : {}),
|
|
178
|
+
};
|
|
179
|
+
},
|
|
180
|
+
};
|
|
181
|
+
}
|
package/dist/serve.js
CHANGED
|
@@ -8,7 +8,8 @@ import { randomUUID } from "node:crypto";
|
|
|
8
8
|
import { initSlog, dslog, setSlogDefaults, drainSpool, flushSlog } from "./slog.js";
|
|
9
9
|
import { mergeRunAgentResults, reportScheduledStartFailure, runAgent } from "./runner.js";
|
|
10
10
|
import { buildOriginDecisionRetryPrompt, buildScheduledPrompt } from "./prompt.js";
|
|
11
|
-
import { collectMachineHello, daemonCapabilities, EXECUTION_PROTOCOL, } from "./machine-info.js";
|
|
11
|
+
import { collectMachineHello, cliVersion, daemonVersion, daemonCapabilities, detectExecutionRuntimesWithSignal, EXECUTION_PROTOCOL, } from "./machine-info.js";
|
|
12
|
+
import { conservativeExecutionRuntimes, createRuntimeProbeCoordinator, } from "./runtime-probe.js";
|
|
12
13
|
import { listWorkspace, readWorkspaceFile } from "./workspace-fs.js";
|
|
13
14
|
import { listSkills } from "./skills.js";
|
|
14
15
|
import { inspectRaftWorkspace, importRaftWorkspace } from "./workspace-import.js";
|
|
@@ -79,6 +80,7 @@ export function serve(config, opts = {}) {
|
|
|
79
80
|
const executeProtocol = opts.execution?.runExecution ?? runExecution;
|
|
80
81
|
let detectedExecutionRuntimes = [];
|
|
81
82
|
let runtimeFacts = null;
|
|
83
|
+
const runtimeProbe = createRuntimeProbeCoordinator();
|
|
82
84
|
const hostCoordinator = opts.execution?.hostCoordinator ?? createHostExecutionCoordinator();
|
|
83
85
|
const localSlots = createSharedSlotManager(config.executionLimits);
|
|
84
86
|
const sharedSlots = hostCoordinatedSlotManager(localSlots, hostCoordinator);
|
|
@@ -280,7 +282,7 @@ export function serve(config, opts = {}) {
|
|
|
280
282
|
reservation.release();
|
|
281
283
|
};
|
|
282
284
|
let connectedAt = 0; // 本次 WS 连接建立时刻(断开日志算在线时长用)
|
|
283
|
-
initSlog(config.serverUrl, config.machineToken);
|
|
285
|
+
initSlog(config.serverUrl, config.machineToken, { daemonVersion: daemonVersion(), cliVersion: cliVersion(), ...(opts.profileName === undefined ? {} : { profileName: opts.profileName }), agentsRoot: config.agentsRoot });
|
|
284
286
|
dslog("daemon.start", "daemon 常驻模式启动", { server_url: config.serverUrl, runtime: config.runtimeBin });
|
|
285
287
|
// 并行调度:同一 agent 可并行处理多个【不同任务】(线程/频道),每任务隔离 cwd+work-log。
|
|
286
288
|
// scheduled 重复 run 仍由 running 去重;普通同线程 wake 用 legacyTaskTails 串成 FIFO。
|
|
@@ -303,7 +305,11 @@ export function serve(config, opts = {}) {
|
|
|
303
305
|
// 连上了才有机会把离线期间(断连原因/退出前)落盘的日志补传上去
|
|
304
306
|
void drainSpool();
|
|
305
307
|
// 上报本机信息 (hostname/os/daemon 版本/已装 runtimes)
|
|
308
|
+
const { detectInstalled, detectExecutable = detectExecutionRuntimesWithSignal } = opts.machineInfo ?? {};
|
|
306
309
|
const helloPromise = collectMachineHello(config.agentsRoot, config.executionLimits, process.platform, {
|
|
310
|
+
...(detectInstalled ? { detectInstalled } : {}),
|
|
311
|
+
// First hello must not wait for third-party handshakes; optional transports arrive in the refresh.
|
|
312
|
+
detectExecutable: async (installed) => conservativeExecutionRuntimes(installed),
|
|
307
313
|
additionalCapabilities: async () => {
|
|
308
314
|
try {
|
|
309
315
|
return (await updateEligibility()).eligible ? ["daemon_update_v1"] : [];
|
|
@@ -313,7 +319,21 @@ export function serve(config, opts = {}) {
|
|
|
313
319
|
}
|
|
314
320
|
},
|
|
315
321
|
});
|
|
316
|
-
runtimeFacts = helloPromise
|
|
322
|
+
runtimeFacts = helloPromise
|
|
323
|
+
.then(async (hello) => {
|
|
324
|
+
if (hello.executionProtocol === undefined)
|
|
325
|
+
return [];
|
|
326
|
+
const detected = await runtimeProbe.detect(hello.runtimes, detectExecutable);
|
|
327
|
+
if (ws === openedSocket && openedSocket.readyState === WebSocket.OPEN
|
|
328
|
+
&& detected.some((runtime) => !hello.executionRuntimes.includes(runtime))) {
|
|
329
|
+
detectedExecutionRuntimes = detected;
|
|
330
|
+
latestMachineHello = { ...hello, executionRuntimes: [...detected] };
|
|
331
|
+
latestHelloSocket = openedSocket;
|
|
332
|
+
sendEffectiveMachineHello();
|
|
333
|
+
}
|
|
334
|
+
return detected;
|
|
335
|
+
})
|
|
336
|
+
.catch((error) => {
|
|
317
337
|
dslog("execution.runtime_detection_failed", "runtime 探测失败", {
|
|
318
338
|
level: "ERROR", error_message: error.message,
|
|
319
339
|
});
|
|
@@ -552,7 +572,9 @@ export function serve(config, opts = {}) {
|
|
|
552
572
|
let availableRuntimes;
|
|
553
573
|
try {
|
|
554
574
|
availableRuntimes = opts.execution?.availableRuntimes?.()
|
|
555
|
-
??
|
|
575
|
+
?? (detectedExecutionRuntimes.includes(spec.runtime.name)
|
|
576
|
+
? detectedExecutionRuntimes
|
|
577
|
+
: await (runtimeFacts ?? Promise.resolve(detectedExecutionRuntimes)));
|
|
556
578
|
}
|
|
557
579
|
catch (error) {
|
|
558
580
|
cleanupExecutionReservation();
|
|
@@ -1111,6 +1133,7 @@ export function serve(config, opts = {}) {
|
|
|
1111
1133
|
return stopPromise;
|
|
1112
1134
|
stopPromise = (async () => {
|
|
1113
1135
|
stopped = true;
|
|
1136
|
+
runtimeProbe.stop();
|
|
1114
1137
|
completionRetransmitter.stop();
|
|
1115
1138
|
const deadline = createShutdownDeadline(shutdownTimeoutMs);
|
|
1116
1139
|
if (reconnectTimer !== null) {
|
package/dist/slog.js
CHANGED
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import { hostname } from "node:os";
|
|
14
14
|
import { homedir } from "node:os";
|
|
15
|
-
import { join } from "node:path";
|
|
15
|
+
import { join, resolve } from "node:path";
|
|
16
|
+
import { createHash } from "node:crypto";
|
|
16
17
|
import { appendFileSync, existsSync, mkdirSync, readdirSync, readFileSync, statSync, unlinkSync } from "node:fs";
|
|
17
18
|
const FLUSH_INTERVAL_MS = 1500;
|
|
18
19
|
const BATCH_SIZE = 50;
|
|
@@ -30,11 +31,28 @@ function spoolDir() {
|
|
|
30
31
|
return process.env.CREW_SLS_SPOOL_DIR ?? join(homedir(), ".crew", "logs", "sls-spool");
|
|
31
32
|
}
|
|
32
33
|
/** serve/run 启动时调用一次;disabled(CREW_SLS_LOG=off)则保持 no-op。 */
|
|
33
|
-
export function initSlog(serverUrl, machineToken) {
|
|
34
|
+
export function initSlog(serverUrl, machineToken, identity = {}) {
|
|
34
35
|
if (process.env.CREW_SLS_LOG === "off" || process.env.CREW_SLS_LOG === "0")
|
|
35
36
|
return;
|
|
36
37
|
cfg = { serverUrl: serverUrl.replace(/\/+$/, ""), token: machineToken };
|
|
37
|
-
|
|
38
|
+
let serverHost;
|
|
39
|
+
try {
|
|
40
|
+
serverHost = new URL(serverUrl).hostname || undefined;
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
serverHost = undefined;
|
|
44
|
+
}
|
|
45
|
+
defaults = {
|
|
46
|
+
host: hostname(),
|
|
47
|
+
pid: process.pid,
|
|
48
|
+
daemon_version: identity.daemonVersion,
|
|
49
|
+
cli_version: identity.cliVersion,
|
|
50
|
+
daemon_profile: identity.profileName,
|
|
51
|
+
server_host: serverHost,
|
|
52
|
+
agents_root_fingerprint: identity.agentsRoot === undefined
|
|
53
|
+
? undefined
|
|
54
|
+
: createHash("sha256").update(resolve(identity.agentsRoot), "utf8").digest("hex"),
|
|
55
|
+
};
|
|
38
56
|
// 退出兜底:残余队列同步落 spool(exit 回调只能做同步工作,append 正合适)
|
|
39
57
|
if (!exitHookInstalled) {
|
|
40
58
|
exitHookInstalled = true;
|
|
@@ -56,15 +56,36 @@ export function supervisorLaunch(request) {
|
|
|
56
56
|
}),
|
|
57
57
|
};
|
|
58
58
|
}
|
|
59
|
+
if (request.runtime === "opencode") {
|
|
60
|
+
if (request.effectivePermission !== "full_access") {
|
|
61
|
+
throw new Error(`OpenCode cannot enforce ${request.effectivePermission} permission`);
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
command: process.execPath,
|
|
65
|
+
args: [
|
|
66
|
+
fileURLToPath(new URL("./runtimes/opencode-runner.js", import.meta.url)),
|
|
67
|
+
"--bin", request.bin,
|
|
68
|
+
...(request.model === undefined ? [] : ["--model", request.model]),
|
|
69
|
+
...(request.reasoning === undefined ? [] : ["--reasoning", request.reasoning]),
|
|
70
|
+
...(request.sessionId === undefined ? [] : ["--session", request.sessionId]),
|
|
71
|
+
],
|
|
72
|
+
cwd: request.cwd,
|
|
73
|
+
env: { ...request.env, PWD: request.cwd },
|
|
74
|
+
stdinText: `${request.systemPrompt}\n\n${request.wakePrompt}`,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
59
77
|
if (request.effectivePermission !== "full_access") {
|
|
60
|
-
|
|
78
|
+
const displayName = request.runtime === "hermes" ? "Hermes" : "Kimi";
|
|
79
|
+
throw new Error(`${displayName} ACP cannot enforce ${request.effectivePermission} permission`);
|
|
61
80
|
}
|
|
62
81
|
return {
|
|
63
82
|
command: process.execPath,
|
|
64
83
|
args: [
|
|
65
84
|
fileURLToPath(new URL("./runtimes/kimi-acp-runner.js", import.meta.url)),
|
|
85
|
+
"--provider", request.runtime,
|
|
66
86
|
"--bin", request.bin,
|
|
67
87
|
...(request.model === undefined ? [] : ["--model", request.model]),
|
|
88
|
+
...(request.reasoning === undefined ? [] : ["--reasoning", request.reasoning]),
|
|
68
89
|
...(request.sessionId === undefined ? [] : ["--session", request.sessionId]),
|
|
69
90
|
...(request.resume ? ["--resume"] : []),
|
|
70
91
|
],
|
package/dist/workspace.js
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
import { mkdir, writeFile, readFile, chmod, access } from "node:fs/promises";
|
|
11
11
|
import { createHash, randomUUID } from "node:crypto";
|
|
12
12
|
import { join } from "node:path";
|
|
13
|
+
import { dslog } from "./slog.js";
|
|
13
14
|
/** 文件系统安全的 taskKey:仅留 [\w.-],其余转 _,截断,避免路径穿越/超长。 */
|
|
14
15
|
export function safeKey(key) {
|
|
15
16
|
return key.replace(/[^\w.-]+/g, "_").replace(/^[-.]+/, "").slice(0, 80) || "default";
|
|
@@ -38,10 +39,24 @@ export async function prepareWorkspace(input) {
|
|
|
38
39
|
await mkdir(crewDir, { recursive: true });
|
|
39
40
|
// MEMORY.md:首次创建"索引 + Active Context"骨架,之后由 agent 自己维护
|
|
40
41
|
const memoryPath = join(dir, "MEMORY.md");
|
|
41
|
-
|
|
42
|
-
|
|
42
|
+
const memorySeedCreated = !(await exists(memoryPath));
|
|
43
|
+
if (memorySeedCreated) {
|
|
44
|
+
try {
|
|
45
|
+
await writeFile(memoryPath, memorySeed(input.handle, input.description), "utf8");
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
logMemoryPrepareFailure(input, "memory_seed_write", error);
|
|
49
|
+
throw error;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
let memory;
|
|
53
|
+
try {
|
|
54
|
+
memory = await readFile(memoryPath, "utf8");
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
logMemoryPrepareFailure(input, "memory_read", error);
|
|
58
|
+
throw error;
|
|
43
59
|
}
|
|
44
|
-
const memory = await readFile(memoryPath, "utf8");
|
|
45
60
|
// 系统提示词按 execution 隔离;省略 identity 的兼容调用每次使用随机路径。
|
|
46
61
|
const promptDir = join(crewDir, "prompts");
|
|
47
62
|
await mkdir(promptDir, { recursive: true });
|
|
@@ -93,6 +108,7 @@ export async function prepareWorkspace(input) {
|
|
|
93
108
|
crewDir,
|
|
94
109
|
systemPromptPath,
|
|
95
110
|
memory,
|
|
111
|
+
memorySeedCreated,
|
|
96
112
|
homeDir,
|
|
97
113
|
runDir,
|
|
98
114
|
workLogPath,
|
|
@@ -102,6 +118,21 @@ export async function prepareWorkspace(input) {
|
|
|
102
118
|
sessionDir,
|
|
103
119
|
};
|
|
104
120
|
}
|
|
121
|
+
function logMemoryPrepareFailure(input, phase, error) {
|
|
122
|
+
try {
|
|
123
|
+
dslog("local_memory.context_prepare_failed", "本地记忆上下文准备失败", {
|
|
124
|
+
level: "WARN",
|
|
125
|
+
execution_id: input.executionId,
|
|
126
|
+
agent_handle: input.handle,
|
|
127
|
+
failure_phase: phase,
|
|
128
|
+
error_name: error instanceof Error ? error.name : "unknown",
|
|
129
|
+
error_code: error?.code,
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
// Diagnostics cannot replace the original workspace error.
|
|
134
|
+
}
|
|
135
|
+
}
|
|
105
136
|
/**
|
|
106
137
|
* 冷启动时轮换会话 id:写入新 uuid 到 <runDir>/.session 并返回。
|
|
107
138
|
* 已有会话但本轮决定不续用(warm 窗口过期 / CREW_RESUME=off)时调用——起一个全新会话,
|