@nowcrew/daemon 0.6.101-beta.6 → 0.6.101-beta.8

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 (57) hide show
  1. package/dist/cloud-report-upload.js +10 -1
  2. package/dist/execution-journal.js +1 -3
  3. package/dist/execution-protocol.js +1 -1
  4. package/dist/execution-runner.js +1 -4
  5. package/dist/local-execution-prompt.js +3 -8
  6. package/dist/local-executor.js +55 -78
  7. package/dist/local-memory/index-budget.js +3 -9
  8. package/dist/local-memory/inventory-cli.js +8 -52
  9. package/dist/local-memory/inventory-source.js +8 -15
  10. package/dist/local-memory/pending-query.js +2 -5
  11. package/dist/local-memory/pending-schema.js +1 -1
  12. package/dist/local-memory/pending.js +2 -16
  13. package/dist/local-memory/prompt.js +5 -21
  14. package/dist/local-memory/read-cli.js +76 -98
  15. package/dist/local-memory/rollout.js +11 -79
  16. package/dist/local-memory/runtime.js +17 -191
  17. package/dist/local-memory/write.js +21 -190
  18. package/dist/local-memory-diagnostics.js +15 -96
  19. package/dist/local-memory-telemetry.js +3 -31
  20. package/dist/machine-info.js +0 -3
  21. package/dist/marketplace-protocol.js +1 -1
  22. package/dist/marketplace-remote.js +1 -1
  23. package/dist/marketplace-runner.js +1 -1
  24. package/dist/memory-prune-diagnostics.js +4 -74
  25. package/dist/message-file-preview.js +1 -11
  26. package/dist/prompt.js +17 -9
  27. package/dist/runner.js +1 -1
  28. package/dist/runtimes/claude-thread-runner.js +4 -25
  29. package/dist/serve.js +3 -38
  30. package/dist/token.js +0 -1
  31. package/dist/workspace-control-handler.js +3 -16
  32. package/dist/workspace.js +4 -23
  33. package/package.json +1 -1
  34. package/dist/local-memory/commit-telemetry.js +0 -30
  35. package/dist/local-memory/execution-workspace.js +0 -117
  36. package/dist/local-memory/full-index-search.js +0 -47
  37. package/dist/local-memory/initialization-identity.js +0 -36
  38. package/dist/local-memory/inventory-v2.js +0 -362
  39. package/dist/local-memory/legacy-adapter.js +0 -233
  40. package/dist/local-memory/migration-access.js +0 -242
  41. package/dist/local-memory/migration-cli.js +0 -54
  42. package/dist/local-memory/migration-io.js +0 -199
  43. package/dist/local-memory/migration-pending.js +0 -9
  44. package/dist/local-memory/migration-plan.js +0 -432
  45. package/dist/local-memory/migration-schema.js +0 -58
  46. package/dist/local-memory/migration-state-invariants.js +0 -65
  47. package/dist/local-memory/migration-store.js +0 -414
  48. package/dist/local-memory/migration-tool-entry.js +0 -21
  49. package/dist/local-memory/release-artifact-guard.js +0 -55
  50. package/dist/local-memory/source-path.js +0 -13
  51. package/dist/local-memory/storage-format.js +0 -249
  52. package/dist/local-memory/writer-mode.js +0 -13
  53. package/dist/memory-migration-controller.js +0 -89
  54. package/dist/memory-migration-inspect.js +0 -116
  55. package/dist/memory-migration-journal.js +0 -14
  56. package/dist/memory-migration-protocol.js +0 -14
  57. package/dist/memory-migration-runtime.js +0 -367
@@ -1,367 +0,0 @@
1
- import { createHash } from "node:crypto";
2
- import { mkdir, lstat, realpath, readFile } from "node:fs/promises";
3
- import { join } from "node:path";
4
- import { fileURLToPath, pathToFileURL } from "node:url";
5
- import { createRequire } from "node:module";
6
- import { z } from "zod";
7
- import { ExecutionCompletedSchema } from "./execution-protocol.js";
8
- import { startDormantSupervisor } from "./execution-supervisor.js";
9
- import { MemoryMigrationStartSchema } from "./memory-migration-protocol.js";
10
- import { MigrationHistorySchema, MigrationIdentitySchema, MigrationStateSchema } from "./local-memory/migration-schema.js";
11
- import { readMemoryInventorySource, inventoryLimits } from "./local-memory/inventory-source.js";
12
- const digest = (value) => createHash("sha256").update(value).digest("hex");
13
- const PROTECTED_WINDOW_PROBE_MS = 5 * 60_000;
14
- const protectedWindows = new Map();
15
- const Operation = z.object({ id: z.string(), revision: z.number().int(), executionId: z.string().nullable(),
16
- status: z.string(), sourceDigest: z.string().nullable(), planDigest: z.string().nullable(),
17
- writeIntentConfirmedAt: z.string().nullable(), newAgentConfirmed: z.boolean().optional(),
18
- cancelRequestedAt: z.string().nullable(),
19
- }).passthrough();
20
- const Summary = z.object({ result: z.enum(["success", "failed", "needs_review", "repair_required", "missing"]),
21
- phase: z.string().nullable().optional(), issues: z.array(z.object({ path: z.string(), reason: z.string() }).passthrough()) }).passthrough();
22
- const HistoryEnvelope = z.object({ operations: z.array(Operation) }).passthrough();
23
- function canReleaseProtectedWindow(operation) {
24
- if (!operation)
25
- return true;
26
- if (["committed", "rolled_back"].includes(operation.status))
27
- return true;
28
- return operation.writeIntentConfirmedAt === null
29
- && ["pending", "needs_review", "waiting_maintenance_window"].includes(operation.status);
30
- }
31
- function clearProtectedWindow(key, window) {
32
- if (window.timer !== undefined)
33
- clearTimeout(window.timer);
34
- if (protectedWindows.get(key) === window)
35
- protectedWindows.delete(key);
36
- return window.reservation.release();
37
- }
38
- function scheduleProtectedWindowProbe(key, window, deps, common) {
39
- if (window.timer !== undefined)
40
- clearTimeout(window.timer);
41
- const timer = setTimeout(async () => {
42
- if (protectedWindows.get(key) !== window || window.active)
43
- return;
44
- try {
45
- const history = HistoryEnvelope.parse(await deps.control("history", {}));
46
- if (protectedWindows.get(key) !== window || window.active)
47
- return;
48
- const operation = history.operations.find(entry => entry.id === common.migrationId);
49
- if (canReleaseProtectedWindow(operation)) {
50
- await clearProtectedWindow(key, window);
51
- return;
52
- }
53
- }
54
- catch {
55
- // Keep the protection when the control plane is unavailable. A later
56
- // probe or the next recovery request must re-establish the proof.
57
- }
58
- scheduleProtectedWindowProbe(key, window, deps, common);
59
- }, PROTECTED_WINDOW_PROBE_MS);
60
- timer.unref?.();
61
- window.timer = timer;
62
- }
63
- /** 系统维护在普通执行槽之前分流;只有 server 持久确认意图后才运行活动替换。 */
64
- export async function runMemoryMigration(raw, deps) {
65
- const frame = MemoryMigrationStartSchema.parse(raw);
66
- const common = { agentId: frame.agentId, migrationId: frame.migrationId, machineId: frame.machineId,
67
- executionId: frame.executionId, attemptKey: frame.attemptKey };
68
- const protectionKey = `${deps.agentsRoot}:${frame.migrationId}`;
69
- const protectedWindow = protectedWindows.get(protectionKey);
70
- if (protectedWindow) {
71
- protectedWindow.active = true;
72
- if (protectedWindow.timer !== undefined)
73
- clearTimeout(protectedWindow.timer);
74
- protectedWindow.timer = undefined;
75
- }
76
- const exclusive = protectedWindow?.reservation ?? await deps.coordinator.tryAcquireExclusiveExecution();
77
- if (!exclusive) {
78
- await deps.control("busy", { ...common, expectedRevision: frame.expectedRevision });
79
- return { kind: "busy" };
80
- }
81
- let revision = frame.expectedRevision, intentConfirmed = false, acquired = false, acquisitionRequested = false, keepProtection = false;
82
- let bound;
83
- let identity, state;
84
- let serverHistory;
85
- let intentReceipt;
86
- let lastPid = null, stopped = true, phaseCount = 0;
87
- const startedAt = new Date().toISOString();
88
- const dir = join(deps.agentsRoot, frame.handle);
89
- const timeoutMs = deps.timeoutMs ?? 60_000;
90
- const guard = () => ({ ...common, expectedRevision: revision, ...bound });
91
- const control = async (step, extra = {}) => {
92
- const response = await deps.control(step, { ...guard(), ...extra });
93
- const parsed = z.object({ operation: Operation }).parse(response);
94
- revision = parsed.operation.revision;
95
- return parsed.operation;
96
- };
97
- const readKnownState = async (path) => {
98
- const source = await readMemoryInventorySource(await realpath(dir), path, inventoryLimits.fileBytes);
99
- return MigrationStateSchema.parse(JSON.parse(source.content));
100
- };
101
- const readState = () => readKnownState(".crew/memory/migration.v1.json");
102
- const runTool = async (command, extra = {}) => {
103
- if (!identity)
104
- throw new Error("migration_identity_missing");
105
- if (command === "recover" && deps.cancellation?.isRequested())
106
- extra = { ...extra, cancelled: true };
107
- const rollback = command === "recover" && extra.cancelled === true;
108
- if (!rollback && deps.cancellation?.isRequested())
109
- throw new Error("migration_cancelled");
110
- if (phaseCount++ > 0) {
111
- if (!stopped || !deps.journal.retry)
112
- throw new Error("migration_stop_unproven");
113
- await deps.journal.retry(frame.executionId);
114
- }
115
- const sourceMode = import.meta.url.endsWith(".ts");
116
- const entry = fileURLToPath(new URL(`./local-memory/migration-tool-entry.${sourceMode ? "ts" : "js"}`, import.meta.url));
117
- const args = sourceMode ? ["--import", pathToFileURL(createRequire(import.meta.url).resolve("tsx/esm")).href, entry] : [entry];
118
- let stdout = "";
119
- const launched = await deps.journal.startGuarded(frame.executionId, new Date().toISOString(), () => (deps.startSupervisor ?? startDormantSupervisor)({ command: process.execPath, args, cwd: dir,
120
- env: { PATH: process.env.PATH ?? "", CREW_SLS_LOG: "off" },
121
- stdinText: JSON.stringify({ command, agentDir: dir, identity, ...extra }),
122
- }, sourceMode ? {
123
- childEntry: fileURLToPath(new URL("./execution-supervisor-child.ts", import.meta.url)),
124
- execArgv: ["--import", pathToFileURL(createRequire(import.meta.url).resolve("tsx/esm")).href],
125
- } : {}), { beforeRelease: async ({ handle }) => {
126
- lastPid = handle.pid;
127
- stopped = false;
128
- handle.stdout.on("data", chunk => { stdout += String(chunk); if (stdout.length > 2 * 1024 * 1024)
129
- void handle.cancel(); });
130
- handle.stderr.resume();
131
- } });
132
- if (launched.kind !== "started")
133
- throw new Error("migration_execution_already_running");
134
- const processHandle = launched.handle;
135
- deps.send({ type: "execution:started", protocolVersion: 1, executionId: frame.executionId, at: new Date().toISOString() });
136
- let timedOut = false;
137
- const timer = setTimeout(() => { timedOut = true; void processHandle.cancel(); }, timeoutMs);
138
- if (!rollback)
139
- deps.cancellation?.register(async () => { if (!stopped)
140
- await processHandle.cancel(); });
141
- try {
142
- await processHandle.exit;
143
- await processHandle.cancel();
144
- stopped = true;
145
- if (timedOut)
146
- throw new Error("migration_window_timeout");
147
- return Summary.parse(JSON.parse(stdout.trim()));
148
- }
149
- finally {
150
- clearTimeout(timer);
151
- }
152
- };
153
- const finish = async (outcome, reason) => {
154
- const completion = ExecutionCompletedSchema.parse({ type: "execution:completed", protocolVersion: 1,
155
- executionId: frame.executionId, runtime: "memory_migration", resumed: false, outcome,
156
- exitCode: outcome === "succeeded" ? 0 : 1, ...(reason ? { errorCode: reason, errorMessage: reason } : {}),
157
- startedAt, finishedAt: new Date().toISOString() });
158
- await deps.journal.complete(frame.executionId, completion);
159
- deps.send(completion);
160
- };
161
- try {
162
- const history = z.object({ operations: z.array(Operation), serverHistory: MigrationHistorySchema }).passthrough().parse(await deps.control("history", {}));
163
- serverHistory = history.serverHistory;
164
- const operation = history.operations.find(op => op.id === frame.migrationId);
165
- if (!operation || operation.revision !== revision || operation.executionId !== frame.executionId)
166
- throw new Error("migration_server_identity_mismatch");
167
- intentConfirmed = operation.writeIntentConfirmedAt !== null;
168
- await mkdir(dir, { recursive: true });
169
- const root = await realpath(dir);
170
- if ((await lstat(dir)).isSymbolicLink())
171
- throw new Error("migration_root_invalid");
172
- bound = { storageRootFingerprint: digest(root), ownerIdentity: String((await lstat(root)).uid) };
173
- identity = MigrationIdentitySchema.parse({ agentId: frame.agentId, migrationId: frame.migrationId,
174
- machineId: frame.machineId, executionId: frame.executionId, workspaceId: frame.workspaceId,
175
- rootFingerprint: bound.storageRootFingerprint, ownerIdentity: bound.ownerIdentity,
176
- sourceStorageVersion: frame.sourceStorageVersion, targetStorageVersion: 2 });
177
- const { reqId: _transportRequest, ...stableFrame } = frame;
178
- const accepted = await deps.journal.accept(frame.executionId, digest(JSON.stringify(stableFrame)), { runtime: "memory_migration" });
179
- if (accepted.kind === "existing" && accepted.completion) {
180
- deps.send(accepted.completion);
181
- return { kind: "completed" };
182
- }
183
- deps.send({ type: "execution:accepted", protocolVersion: 1, executionId: frame.executionId,
184
- state: "ready", effectivePermission: "workspace_write", at: new Date().toISOString() });
185
- acquisitionRequested = true;
186
- await control("acquired");
187
- acquired = true;
188
- if (frame.action === "recover") {
189
- state = await readState().catch(() => undefined);
190
- if (state && state.migrationId !== frame.migrationId && state.phase === "rolled_back"
191
- && history.serverHistory.rolledBackOperations?.some(previous => previous.migrationId === state.migrationId
192
- && previous.sourceDigest === state.sourceDigest && previous.planDigest === state.planDigest))
193
- state = undefined;
194
- if (state) {
195
- if (state.migrationId !== frame.migrationId || state.identity.rootFingerprint !== bound.storageRootFingerprint
196
- || state.identity.machineId !== frame.machineId || state.identity.agentId !== frame.agentId)
197
- throw new Error("migration_state_identity_mismatch");
198
- identity = state.identity;
199
- }
200
- if (!state) {
201
- state = await readKnownState(`.crew/memory/migrations/${frame.migrationId}/terminal.json`).catch((e) => {
202
- if (e.code !== "ENOENT")
203
- throw e;
204
- return readKnownState(`.crew/memory/migrations/${frame.migrationId}/manifest.json`);
205
- });
206
- identity = state.identity;
207
- }
208
- if (state.planDigest !== operation.planDigest || state.sourceDigest !== operation.sourceDigest)
209
- throw new Error("migration_server_plan_mismatch");
210
- const prior = await deps.journal.get(identity.executionId);
211
- if (state.phase !== "committed" && (!prior || (prior.state !== "completed" && prior.state !== "interrupted"))) {
212
- throw new Error("migration_stop_unproven");
213
- }
214
- const result = await runTool("recover", { serverHistory: history.serverHistory, cancelled: !intentConfirmed || operation.cancelRequestedAt !== null,
215
- stoppedReceipt: { kind: "migration_process_stopped", migrationId: frame.migrationId, machineId: frame.machineId,
216
- executionId: identity.executionId, ownerIdentity: identity.ownerIdentity, pid: null,
217
- stoppedAt: new Date().toISOString(), proof: "journal_reconciled" } });
218
- if (result.result !== "success")
219
- throw new Error("migration_recovery_failed");
220
- }
221
- else {
222
- const prepared = await runTool("prepare", { serverHistory: history.serverHistory,
223
- ...(frame.recordMappings ? { recordMappings: frame.recordMappings } : {}) });
224
- if (prepared.result === "needs_review") {
225
- await control("needs-review", { issues: prepared.issues });
226
- await finish("failed", "migration_needs_review");
227
- return { kind: "needs_review" };
228
- }
229
- if (prepared.result !== "success")
230
- throw new Error("migration_prepare_failed");
231
- state = await readState();
232
- const confirmed = await control("intent", { sourceDigest: state.sourceDigest, planDigest: state.planDigest,
233
- minReaderCapabilities: ["memory_storage_v2", "memory_state_guard_v1"], minWriterCapabilities: ["memory_storage_v2", "memory_state_guard_v1"] });
234
- intentConfirmed = true;
235
- intentReceipt = { ...identity, kind: "server_write_intent", sourceDigest: state.sourceDigest,
236
- planDigest: state.planDigest, intentVersion: confirmed.revision, confirmedAt: confirmed.writeIntentConfirmedAt };
237
- const result = await runTool("apply", { intentReceipt });
238
- if (result.result !== "success")
239
- throw new Error("migration_apply_failed");
240
- }
241
- state = await readState();
242
- if (state.phase !== "committed" && state.phase !== "rolled_back")
243
- throw new Error("migration_terminal_unverified");
244
- if (!intentConfirmed) {
245
- if (state.phase !== "rolled_back")
246
- throw new Error("migration_commit_without_intent");
247
- await control("no-write", { sourceDigest: state.sourceDigest, planDigest: state.planDigest, verified: true });
248
- }
249
- else
250
- await control("result", { outcome: state.phase, storageVersion: state.storageVersion,
251
- sourceDigest: state.sourceDigest, planDigest: state.planDigest, origin: state.origin, verified: true });
252
- await finish("succeeded");
253
- return { kind: "completed", phase: state.phase };
254
- }
255
- catch (error) {
256
- // A recovery window must remain protected when the control plane cannot
257
- // prove that no migration is active. The next request or bounded probe
258
- // can release it after reading a terminal/no-write operation.
259
- if (protectedWindow !== undefined && !acquisitionRequested)
260
- keepProtection = true;
261
- const reason = error instanceof Error && /^[a-z_]+$/u.test(error.message) ? error.message : "migration_execution_failed";
262
- if (acquisitionRequested) {
263
- try {
264
- const latest = z.object({ operations: z.array(Operation) }).passthrough().parse(await deps.control("history", {}))
265
- .operations.find(op => op.id === frame.migrationId);
266
- if (!latest || latest.executionId !== frame.executionId)
267
- throw new Error("migration_control_owner_changed");
268
- revision = latest.revision;
269
- intentConfirmed = latest.writeIntentConfirmedAt !== null;
270
- acquired = !["needs_review", "rolled_back"].includes(latest.status);
271
- if (intentConfirmed && identity && state && latest.sourceDigest === state.sourceDigest && latest.planDigest === state.planDigest) {
272
- intentReceipt = { ...identity, kind: "server_write_intent", sourceDigest: state.sourceDigest, planDigest: state.planDigest,
273
- intentVersion: latest.revision, confirmedAt: latest.writeIntentConfirmedAt };
274
- }
275
- }
276
- catch {
277
- keepProtection = true;
278
- }
279
- }
280
- if (acquired) {
281
- if (intentConfirmed) {
282
- keepProtection = true;
283
- if (stopped && identity && lastPid !== null) {
284
- try {
285
- const recovered = await runTool("recover", { cancelled: true, ...(intentReceipt ? { intentReceipt } : {}),
286
- stoppedReceipt: { kind: "migration_process_stopped", migrationId: frame.migrationId, machineId: frame.machineId,
287
- executionId: identity.executionId, ownerIdentity: identity.ownerIdentity, pid: lastPid,
288
- stoppedAt: new Date().toISOString(), proof: "supervisor_exit" } });
289
- state = await readState();
290
- if (recovered.result === "success" && (state.phase === "committed" || state.phase === "rolled_back")) {
291
- await control("result", { outcome: state.phase, storageVersion: state.storageVersion,
292
- sourceDigest: state.sourceDigest, planDigest: state.planDigest, origin: state.origin, verified: true });
293
- keepProtection = false;
294
- }
295
- }
296
- catch { /* 未证明提交或完整回退时保留保护。 */ }
297
- }
298
- if (keepProtection)
299
- await control("repair", { reason }).catch(() => undefined);
300
- }
301
- else if (stopped) {
302
- let digests;
303
- try {
304
- let local = await readState().catch((error) => { if (error.code === "ENOENT")
305
- return undefined; throw error; });
306
- if (local?.migrationId !== frame.migrationId) {
307
- local = await readKnownState(`.crew/memory/migrations/${frame.migrationId}/manifest.json`).catch(async (error) => {
308
- if (error.code !== "ENOENT")
309
- throw error;
310
- const residue = await lstat(join(dir, ".crew/memory/migrations", frame.migrationId)).catch((statError) => {
311
- if (statError.code === "ENOENT")
312
- return undefined;
313
- throw statError;
314
- });
315
- if (residue)
316
- throw new Error("migration_prepare_residue_unverified");
317
- return undefined;
318
- });
319
- }
320
- if (local?.migrationId === frame.migrationId) {
321
- if (!identity || lastPid === null || Object.keys(local.identity).some(key => local.identity[key] !== identity[key])
322
- || local.intentReceipt || !["prepared", "rolled_back"].includes(local.phase))
323
- throw new Error("migration_no_write_state_unverified");
324
- digests = { sourceDigest: local.sourceDigest, planDigest: local.planDigest };
325
- const recovered = await runTool("recover", { cancelled: true,
326
- serverHistory: { kind: "existing", migrationId: frame.migrationId, writeIntentConfirmed: false,
327
- ...(serverHistory?.rolledBackOperations ? { rolledBackOperations: serverHistory.rolledBackOperations } : {}) },
328
- stoppedReceipt: { kind: "migration_process_stopped", migrationId: frame.migrationId, machineId: frame.machineId,
329
- executionId: identity.executionId, ownerIdentity: identity.ownerIdentity, pid: lastPid,
330
- stoppedAt: new Date().toISOString(), proof: "supervisor_exit" } });
331
- state = await readState();
332
- if (recovered.result !== "success" || state.phase !== "rolled_back")
333
- throw new Error("migration_no_write_rollback_unverified");
334
- digests = { sourceDigest: state.sourceDigest, planDigest: state.planDigest };
335
- }
336
- await control("no-write", { verified: true, ...digests });
337
- keepProtection = false;
338
- }
339
- catch {
340
- keepProtection = true;
341
- await control("repair", { reason: "migration_no_write_rollback_unverified", ...digests }).catch(() => undefined);
342
- }
343
- }
344
- else
345
- keepProtection = true;
346
- }
347
- if (stopped)
348
- await finish("failed", reason).catch(() => undefined);
349
- return { kind: "failed", reason, processStopped: stopped, lastPid };
350
- }
351
- finally {
352
- if (!keepProtection && stopped) {
353
- if (protectedWindow !== undefined) {
354
- await clearProtectedWindow(protectionKey, protectedWindow);
355
- }
356
- else {
357
- await exclusive.release();
358
- }
359
- }
360
- else {
361
- const window = protectedWindow ?? { reservation: exclusive, executionId: frame.executionId, active: false, timer: undefined };
362
- window.active = false;
363
- protectedWindows.set(protectionKey, window);
364
- scheduleProtectedWindowProbe(protectionKey, window, deps, common);
365
- }
366
- }
367
- }