@nowcrew/daemon 0.6.93-beta.11 → 0.6.93-beta.12
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/dist/cloud-report-upload.js +1 -10
- package/dist/execution-journal.js +3 -1
- package/dist/execution-protocol.js +1 -1
- package/dist/execution-runner.js +4 -1
- package/dist/local-execution-prompt.js +8 -3
- package/dist/local-executor.js +63 -39
- package/dist/local-memory/commit-telemetry.js +30 -0
- package/dist/local-memory/execution-workspace.js +62 -0
- package/dist/local-memory/full-index-search.js +47 -0
- package/dist/local-memory/index-budget.js +9 -3
- package/dist/local-memory/initialization-identity.js +36 -0
- package/dist/local-memory/inventory-cli.js +49 -8
- package/dist/local-memory/inventory-source.js +15 -8
- package/dist/local-memory/inventory-v2.js +361 -0
- package/dist/local-memory/legacy-adapter.js +233 -0
- package/dist/local-memory/migration-access.js +242 -0
- package/dist/local-memory/migration-cli.js +54 -0
- package/dist/local-memory/migration-io.js +199 -0
- package/dist/local-memory/migration-pending.js +9 -0
- package/dist/local-memory/migration-plan.js +317 -0
- package/dist/local-memory/migration-schema.js +55 -0
- package/dist/local-memory/migration-state-invariants.js +65 -0
- package/dist/local-memory/migration-store.js +414 -0
- package/dist/local-memory/migration-tool-entry.js +21 -0
- package/dist/local-memory/pending-query.js +5 -2
- package/dist/local-memory/pending-schema.js +1 -1
- package/dist/local-memory/pending.js +16 -2
- package/dist/local-memory/prompt.js +21 -5
- package/dist/local-memory/read-cli.js +98 -76
- package/dist/local-memory/rollout.js +79 -11
- package/dist/local-memory/runtime.js +181 -13
- package/dist/local-memory/storage-format.js +152 -0
- package/dist/local-memory/write.js +189 -21
- package/dist/local-memory/writer-mode.js +13 -0
- package/dist/local-memory-diagnostics.js +94 -13
- package/dist/local-memory-telemetry.js +19 -2
- package/dist/machine-info.js +3 -0
- package/dist/marketplace-protocol.js +1 -1
- package/dist/marketplace-remote.js +1 -1
- package/dist/marketplace-runner.js +1 -1
- package/dist/memory-migration-controller.js +86 -0
- package/dist/memory-migration-inspect.js +108 -0
- package/dist/memory-migration-journal.js +14 -0
- package/dist/memory-migration-protocol.js +14 -0
- package/dist/memory-migration-runtime.js +305 -0
- package/dist/memory-prune-diagnostics.js +60 -4
- package/dist/message-file-preview.js +11 -1
- package/dist/prompt.js +6 -2
- package/dist/runner.js +1 -1
- package/dist/runtimes/claude-thread-runner.js +25 -4
- package/dist/serve.js +38 -3
- package/dist/token.js +1 -0
- package/dist/workspace-control-handler.js +16 -3
- package/dist/workspace.js +13 -2
- package/package.json +1 -1
package/dist/serve.js
CHANGED
|
@@ -14,6 +14,7 @@ import { reconcileExecutionJournal } from "./execution-recovery.js";
|
|
|
14
14
|
import { initReleaseSlog, reportDaemonConnectionClose, reportReleaseError } from "./release-channel-guard.js";
|
|
15
15
|
import { handleWorkspaceControlRequest } from "./workspace-control-handler.js";
|
|
16
16
|
import { inspectRaftWorkspace, importRaftWorkspace } from "./workspace-import.js";
|
|
17
|
+
import { withMemoryAccess } from "./local-memory/migration-access.js";
|
|
17
18
|
import { normalizeScheduledContext } from "./scheduled-report.js";
|
|
18
19
|
import { formatDaemonLogLine } from "./log-format.js";
|
|
19
20
|
import { reportAgentRunComplete } from "./scheduled-run-report.js";
|
|
@@ -50,6 +51,8 @@ import { probeRuntimeHealth } from "./runtime-health.js";
|
|
|
50
51
|
import { createServeMarketplace } from "./marketplace-serve.js";
|
|
51
52
|
import { buildControlPlaneUrl } from "./control-plane-url.js";
|
|
52
53
|
import { resolveDaemonLocalStateRootFromEnv } from "./daemon-local-home.js";
|
|
54
|
+
import { createMemoryMigrationController } from "./memory-migration-controller.js";
|
|
55
|
+
import { createMemoryMigrationJournal, replayExecutionJournals } from "./memory-migration-journal.js";
|
|
53
56
|
import { bindStorageServerIdentity, createStorageIdentityAdmission, } from "./storage-serve-adapter.js";
|
|
54
57
|
export { buildControlPlaneUrl } from "./control-plane-url.js";
|
|
55
58
|
// normalize.ts 的活动种类 → activity 枚举
|
|
@@ -71,6 +74,8 @@ export function serve(config, opts = {}) {
|
|
|
71
74
|
let reconnectTimer = null;
|
|
72
75
|
let stopPromise = null;
|
|
73
76
|
let executionJournal;
|
|
77
|
+
let migrationExecutionJournal;
|
|
78
|
+
let migrationJournalReady = false;
|
|
74
79
|
const injectedExecutionJournal = opts.execution?.journal;
|
|
75
80
|
const agentMemory = opts.execution?.dependencies?.agentMemory
|
|
76
81
|
?? (config.agentMemory === null ? undefined : createAgentMemoryBridge(config.agentMemory));
|
|
@@ -85,6 +90,15 @@ export function serve(config, opts = {}) {
|
|
|
85
90
|
startupGapMs: config.executionLimits.startupGapMs,
|
|
86
91
|
});
|
|
87
92
|
const localSlots = createSharedSlotManager(config.executionLimits);
|
|
93
|
+
const memoryMigrations = createMemoryMigrationController({ config, coordinator: hostCoordinator,
|
|
94
|
+
isAdmissionClosed: () => stopped || storageIdentityAdmission.isAdmissionClosed(),
|
|
95
|
+
journal: () => { if (!migrationJournalReady)
|
|
96
|
+
throw new Error("memory_migration_journal_unavailable"); return migrationExecutionJournal; }, send: frame => {
|
|
97
|
+
if (frame.type === "execution:completed")
|
|
98
|
+
completionRetransmitter.track(frame);
|
|
99
|
+
else if (ws?.readyState === WebSocket.OPEN)
|
|
100
|
+
ws.send(JSON.stringify(frame));
|
|
101
|
+
} });
|
|
88
102
|
const sharedSlots = hostCoordinatedSlotManager(localSlots, hostCoordinator);
|
|
89
103
|
const concurrencyTelemetry = createExecutionConcurrencyTelemetry(sharedSlots, {
|
|
90
104
|
...(opts.execution?.concurrencySnapshotIntervalMs === undefined
|
|
@@ -232,7 +246,7 @@ export function serve(config, opts = {}) {
|
|
|
232
246
|
return null;
|
|
233
247
|
};
|
|
234
248
|
const sendSnapshot = async (reqId) => {
|
|
235
|
-
const entries = (await executionJournal
|
|
249
|
+
const entries = (await replayExecutionJournals(executionJournal, migrationExecutionJournal)).map(snapshotEntry).filter((entry) => entry !== null);
|
|
236
250
|
safeExecutionSend(ExecutionSnapshotSchema.parse({
|
|
237
251
|
type: "execution:snapshot", protocolVersion: 1, reqId, entries,
|
|
238
252
|
}));
|
|
@@ -313,6 +327,12 @@ export function serve(config, opts = {}) {
|
|
|
313
327
|
machineRuntime.open(openedSocket, { capabilities: capabilities.machineHello, beforePublish: Promise.resolve(), connectionId });
|
|
314
328
|
opts.onOpen?.(ws);
|
|
315
329
|
void sendSnapshot(`reconnect-${randomUUID()}`);
|
|
330
|
+
// 普通回执不等待维护日志IO;两类记录分别重放到同一发送器。
|
|
331
|
+
void migrationExecutionJournal.replay().then(entries => {
|
|
332
|
+
for (const entry of entries)
|
|
333
|
+
if (entry.completion && !entry.completionAcknowledged)
|
|
334
|
+
completionRetransmitter.track(entry.completion);
|
|
335
|
+
}).catch(() => dslog("memory_migration.journal_unavailable", "维护回执重放失败,普通回执继续", { level: "ERROR" }));
|
|
316
336
|
void executionJournal.replay()
|
|
317
337
|
.then((entries) => {
|
|
318
338
|
for (const entry of entries) {
|
|
@@ -387,7 +407,8 @@ export function serve(config, opts = {}) {
|
|
|
387
407
|
return;
|
|
388
408
|
if (frame.type === "execution:completion-ack") {
|
|
389
409
|
try {
|
|
390
|
-
await executionJournal.
|
|
410
|
+
const journal = await executionJournal.get(frame.executionId) ? executionJournal : migrationExecutionJournal;
|
|
411
|
+
await journal.acknowledgeCompletion(frame.executionId);
|
|
391
412
|
const attempts = completionRetransmitter.acknowledge(frame.executionId);
|
|
392
413
|
dslog("execution.completion_acknowledged", "execution completion ACK 已持久化", {
|
|
393
414
|
execution_id: frame.executionId,
|
|
@@ -413,6 +434,8 @@ export function serve(config, opts = {}) {
|
|
|
413
434
|
return;
|
|
414
435
|
}
|
|
415
436
|
if (frame.type === "execution:cancel") {
|
|
437
|
+
if (memoryMigrations.cancel(frame.executionId))
|
|
438
|
+
return;
|
|
416
439
|
if (!knownExecutionHashes.has(frame.executionId)
|
|
417
440
|
&& !executionRuns.has(frame.executionId)
|
|
418
441
|
&& await executionJournal.get(frame.executionId) === null)
|
|
@@ -645,6 +668,7 @@ export function serve(config, opts = {}) {
|
|
|
645
668
|
const execution = executeProtocol(config, spec, {
|
|
646
669
|
...opts.execution?.dependencies,
|
|
647
670
|
machineEnvironment: machineRuntime.provider,
|
|
671
|
+
...(memoryMigrations.getIdentity() === null ? {} : { controlIdentity: memoryMigrations.getIdentity() }),
|
|
648
672
|
capabilities: capabilities.executionRunner,
|
|
649
673
|
projectSkills: opts.execution?.dependencies?.projectSkills ?? initializedProjectSkills,
|
|
650
674
|
abilityRelease: opts.execution?.dependencies?.abilityRelease ?? agentAbilityRuntime.materializer,
|
|
@@ -735,6 +759,8 @@ export function serve(config, opts = {}) {
|
|
|
735
759
|
// ready 帧带 server 视角的 machineId/workspaceId → 作为后续所有日志的默认关联键
|
|
736
760
|
const r = msg;
|
|
737
761
|
setSlogDefaults({ machine_id: r.machineId, workspace_id: r.workspaceId });
|
|
762
|
+
if (r.workspaceId && r.machineId)
|
|
763
|
+
memoryMigrations.setIdentity({ workspaceId: r.workspaceId, machineId: r.machineId });
|
|
738
764
|
if (opts.storageRuntime !== undefined && typeof r.workspaceId === "string" && typeof r.machineId === "string") {
|
|
739
765
|
const bindingSocket = ws;
|
|
740
766
|
bindStorageServerIdentity(opts.storageRuntime, { workspaceId: r.workspaceId, machineId: r.machineId }, {
|
|
@@ -801,6 +827,8 @@ export function serve(config, opts = {}) {
|
|
|
801
827
|
}
|
|
802
828
|
catch { /* server 会按请求超时处理 */ } }))
|
|
803
829
|
return;
|
|
830
|
+
if (await memoryMigrations.handle(msg))
|
|
831
|
+
return;
|
|
804
832
|
if (await agentAbilityRuntime.tryHandleControlMessage(msg, serverCapabilities, (frame) => ws?.send(JSON.stringify(frame))))
|
|
805
833
|
return;
|
|
806
834
|
// 导入 raft agent 工作区:inspect 反填 name/description;import 复制用户内容
|
|
@@ -815,7 +843,7 @@ export function serve(config, opts = {}) {
|
|
|
815
843
|
try {
|
|
816
844
|
const data2 = req.type === "raft:inspect"
|
|
817
845
|
? await inspectRaftWorkspace(req.path)
|
|
818
|
-
: await importRaftWorkspace(req.path, join(config.agentsRoot, req.handle));
|
|
846
|
+
: await withMemoryAccess(join(config.agentsRoot, req.handle), () => importRaftWorkspace(req.path, join(config.agentsRoot, req.handle)), { create: true });
|
|
819
847
|
if (req.type === "raft:import") {
|
|
820
848
|
log(`📦 已导入 raft 工作区 → ${req.handle}: 复制 ${data2.copied.length} 项`);
|
|
821
849
|
}
|
|
@@ -1207,6 +1235,7 @@ export function serve(config, opts = {}) {
|
|
|
1207
1235
|
return;
|
|
1208
1236
|
// server 能力随连接失效;下个连接的 ready 帧重新声明(防降级重连后沿用过期能力)。
|
|
1209
1237
|
serverCapabilities = new Set();
|
|
1238
|
+
memoryMigrations.clearIdentity();
|
|
1210
1239
|
completionRetransmitter.pause();
|
|
1211
1240
|
// 4001 = 控制面应用级「鉴权失败」关闭码(见 server control-plane.ts)。即使上面的
|
|
1212
1241
|
// error 帧因 close 抢先而丢失,也能据关闭码识别这是凭证失效——退避拉满,不再每秒热循环。
|
|
@@ -1259,6 +1288,9 @@ export function serve(config, opts = {}) {
|
|
|
1259
1288
|
writeStderr: (line) => process.stderr.write(line),
|
|
1260
1289
|
});
|
|
1261
1290
|
await runProjectSkillStartupCleanup(config.agentsRoot, executionJournal, dslog);
|
|
1291
|
+
migrationExecutionJournal = createMemoryMigrationJournal(config.agentsRoot);
|
|
1292
|
+
await migrationExecutionJournal.reconcileAfterRestart().then(() => { migrationJournalReady = true; })
|
|
1293
|
+
.catch(() => dslog("memory_migration.journal_unavailable", "迁移日志恢复失败,普通执行继续,迁移请求单独报错", { level: "ERROR" }));
|
|
1262
1294
|
await connect();
|
|
1263
1295
|
})();
|
|
1264
1296
|
const stop = () => {
|
|
@@ -1276,6 +1308,7 @@ export function serve(config, opts = {}) {
|
|
|
1276
1308
|
const webSocketClosed = closeWebSocketWithinDeadline(ws, deadline.signal);
|
|
1277
1309
|
const pending = [
|
|
1278
1310
|
machineRuntime.stop(),
|
|
1311
|
+
memoryMigrations.stop(),
|
|
1279
1312
|
webSocketClosed,
|
|
1280
1313
|
updateController.drain(),
|
|
1281
1314
|
marketplace.stop(),
|
|
@@ -1294,6 +1327,8 @@ export function serve(config, opts = {}) {
|
|
|
1294
1327
|
if (executionJournal !== undefined) {
|
|
1295
1328
|
await executionJournal.close({ signal: deadline.signal });
|
|
1296
1329
|
}
|
|
1330
|
+
if (migrationExecutionJournal !== undefined)
|
|
1331
|
+
await migrationExecutionJournal.close({ signal: deadline.signal });
|
|
1297
1332
|
}
|
|
1298
1333
|
finally {
|
|
1299
1334
|
deadline.dispose();
|
package/dist/token.js
CHANGED
|
@@ -7,6 +7,7 @@ export async function mintAgentToken(serverUrl, machineToken, handle, displayNam
|
|
|
7
7
|
},
|
|
8
8
|
// 把本轮唤醒的线程根钉到 per-launch 令牌:服务端据此让 task/消息默认绑/回当前线程。
|
|
9
9
|
body: JSON.stringify({
|
|
10
|
+
memoryStateGuardVersion: 1,
|
|
10
11
|
handle,
|
|
11
12
|
...(displayName ? { displayName } : {}),
|
|
12
13
|
...(options.wakeThreadRoot ? { wakeThreadRoot: options.wakeThreadRoot } : {}),
|
|
@@ -1,9 +1,22 @@
|
|
|
1
1
|
/** 控制面文件/技能查看的按需处理,独立于执行生命周期。 */
|
|
2
|
-
import {
|
|
2
|
+
import { lstat } from "node:fs/promises";
|
|
3
|
+
import { join, resolve } from "node:path";
|
|
3
4
|
import { readSharedMessageFile } from "./message-file-preview.js";
|
|
4
5
|
import { listWorkspace, readWorkspaceFile } from "./workspace-fs.js";
|
|
5
6
|
import { listSkills } from "./skills.js";
|
|
6
7
|
import { listRuntimeModels } from "./list-models.js";
|
|
8
|
+
import { withMemoryAccess } from "./local-memory/migration-access.js";
|
|
9
|
+
async function listProtectedWorkspace(root, path) {
|
|
10
|
+
try {
|
|
11
|
+
await lstat(root);
|
|
12
|
+
}
|
|
13
|
+
catch (error) {
|
|
14
|
+
if (error.code !== "ENOENT")
|
|
15
|
+
throw error;
|
|
16
|
+
return { root: resolve(root), path: path.replace(/^\/+/, ""), entries: [] };
|
|
17
|
+
}
|
|
18
|
+
return withMemoryAccess(root, () => listWorkspace(root, path));
|
|
19
|
+
}
|
|
7
20
|
export async function handleWorkspaceControlRequest(raw, agentsRoot, registry, models = listRuntimeModels) {
|
|
8
21
|
if (!raw || typeof raw !== "object")
|
|
9
22
|
return null;
|
|
@@ -15,8 +28,8 @@ export async function handleWorkspaceControlRequest(raw, agentsRoot, registry, m
|
|
|
15
28
|
try {
|
|
16
29
|
const root = join(agentsRoot, request.handle);
|
|
17
30
|
const data = request.type === "fs:read-shared" ? await readSharedMessageFile(agentsRoot, raw, registry)
|
|
18
|
-
: request.type === "fs:list" ? await
|
|
19
|
-
: request.type === "fs:read" ? await readWorkspaceFile(root, request.path)
|
|
31
|
+
: request.type === "fs:list" ? await listProtectedWorkspace(root, request.path)
|
|
32
|
+
: request.type === "fs:read" ? await withMemoryAccess(root, () => readWorkspaceFile(root, request.path))
|
|
20
33
|
: request.type === "skills:list" ? await listSkills(request.runtime)
|
|
21
34
|
: { models: await models(request.runtime ?? "") };
|
|
22
35
|
return { type: "fs:result", reqId: request.reqId, ok: true, data };
|
package/dist/workspace.js
CHANGED
|
@@ -8,12 +8,15 @@
|
|
|
8
8
|
*
|
|
9
9
|
* spawn runtime 时把 <ws>/.crew 置于 PATH 最前,于是 agent 的 `crew ...` 命中 wrapper。
|
|
10
10
|
*/
|
|
11
|
-
import { mkdir, writeFile, readFile, chmod, access, lstat } from "node:fs/promises";
|
|
11
|
+
import { mkdir, writeFile, readFile, chmod, access, lstat, realpath } from "node:fs/promises";
|
|
12
12
|
import { createHash, randomUUID } from "node:crypto";
|
|
13
13
|
import { dirname, join } from "node:path";
|
|
14
14
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
15
15
|
import { createRequire } from "node:module";
|
|
16
16
|
import { dslog } from "./slog.js";
|
|
17
|
+
import { MigrationStateSchema } from "./local-memory/migration-schema.js";
|
|
18
|
+
import { assertMigrationStateIntegrity } from "./local-memory/migration-state-invariants.js";
|
|
19
|
+
import { detectLocalMemoryStorageVersion } from "./local-memory/storage-format.js";
|
|
17
20
|
/** 文件系统安全的 taskKey:仅留 [\w.-],其余转 _,截断,避免路径穿越/超长。 */
|
|
18
21
|
export function safeKey(key) {
|
|
19
22
|
return key.replace(/[^\w.-]+/g, "_").replace(/^[-.]+/, "").slice(0, 80) || "default";
|
|
@@ -49,7 +52,15 @@ async function hasLocalMemoryV2Marker(agentDir) {
|
|
|
49
52
|
const stat = await lstat(markerPath);
|
|
50
53
|
if (!stat.isFile() || stat.isSymbolicLink())
|
|
51
54
|
return false;
|
|
52
|
-
|
|
55
|
+
if ((await readFile(markerPath, "utf8")) !== "1\n")
|
|
56
|
+
return false;
|
|
57
|
+
const root = await realpath(agentDir);
|
|
58
|
+
const state = MigrationStateSchema.parse(JSON.parse(await readFile(join(memoryDir, "migration.v1.json"), "utf8")));
|
|
59
|
+
assertMigrationStateIntegrity(state, state.identity);
|
|
60
|
+
return state.phase === "committed" && state.storageVersion === 2
|
|
61
|
+
&& state.identity.rootFingerprint === createHash("sha256").update(root).digest("hex")
|
|
62
|
+
&& state.identity.ownerIdentity === String((await lstat(root)).uid)
|
|
63
|
+
&& detectLocalMemoryStorageVersion(await readFile(join(root, "MEMORY.md"), "utf8")) === 2;
|
|
53
64
|
}
|
|
54
65
|
catch {
|
|
55
66
|
return false;
|