@nowcrew/daemon 0.6.58 → 0.6.63

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 (42) hide show
  1. package/dist/agent-ability/materializer.js +1 -20
  2. package/dist/automatic-install-target.js +10 -15
  3. package/dist/config.js +0 -35
  4. package/dist/daemon-migration-controller.js +18 -16
  5. package/dist/execution-runner.js +0 -1
  6. package/dist/local-executor.js +1 -44
  7. package/dist/machine-info.js +2 -3
  8. package/dist/main.js +0 -77
  9. package/dist/profile-layout.js +4 -10
  10. package/dist/runner.js +0 -1
  11. package/dist/serve.js +3 -64
  12. package/dist/workspace.js +1 -24
  13. package/package.json +10 -9
  14. package/dist/local-memory/index-budget.js +0 -74
  15. package/dist/local-memory/pending.js +0 -111
  16. package/dist/local-memory/runtime.js +0 -87
  17. package/dist/local-memory/types.js +0 -1
  18. package/dist/local-memory/write.js +0 -448
  19. package/dist/storage-config.js +0 -175
  20. package/dist/storage-control.js +0 -131
  21. package/dist/storage-copy.js +0 -444
  22. package/dist/storage-credential-guard.js +0 -21
  23. package/dist/storage-doctor.js +0 -175
  24. package/dist/storage-identity.js +0 -29
  25. package/dist/storage-layout.js +0 -143
  26. package/dist/storage-manifest.js +0 -440
  27. package/dist/storage-migration-controller.js +0 -417
  28. package/dist/storage-migration-manifest.js +0 -105
  29. package/dist/storage-migration-policy.js +0 -100
  30. package/dist/storage-runtime-controller.js +0 -311
  31. package/dist/storage-runtime-readiness.js +0 -178
  32. package/dist/storage-runtime.js +0 -39
  33. package/dist/storage-safe-fs.js +0 -293
  34. package/dist/storage-serve-adapter.js +0 -65
  35. package/dist/storage-status-delivery.js +0 -201
  36. package/dist/storage-status-replay.js +0 -125
  37. package/dist/storage-target-confirmation.js +0 -129
  38. package/dist/storage-worker-command.js +0 -27
  39. package/dist/storage-worker-executor.js +0 -380
  40. package/dist/storage-worker-handoff.js +0 -151
  41. package/dist/storage-worker-operation.js +0 -277
  42. package/dist/storage-worker-status.js +0 -105
@@ -249,23 +249,6 @@ const verifyCache = async (agentsRoot, ability) => {
249
249
  };
250
250
  const layerRelativePath = (path, layer) => path.startsWith(`${layer}/`) ? path.slice(layer.length + 1) : path;
251
251
  const readManagedCatalog = async (trainingRoot) => JSON.parse(await readFile(join(trainingRoot, "ACTIVE_RELEASE.json"), "utf8"));
252
- const projectedSkillExists = async (trainingRoot, name) => {
253
- const root = join(trainingRoot, "skills", safeAbilityName(name));
254
- if (!await isRealDirectory(root))
255
- return false;
256
- const direct = await lstat(join(root, "SKILL.md")).catch(() => null);
257
- if (direct?.isFile() && !direct.isSymbolicLink())
258
- return true;
259
- for (const entry of await readdir(root, { withFileTypes: true })) {
260
- if (!entry.isDirectory())
261
- continue;
262
- const skill = await lstat(join(root, entry.name, "SKILL.md")).catch(() => null);
263
- if (skill?.isFile() && !skill.isSymbolicLink())
264
- return true;
265
- }
266
- return false;
267
- };
268
- const projectedSkillsComplete = async (trainingRoot, skills) => (await Promise.all(skills.map((skill) => projectedSkillExists(trainingRoot, skill.name)))).every(Boolean);
269
252
  const trainingSwitchJournalPath = (agentRoot) => join(agentRoot, ".training-switch.json");
270
253
  const trainingRollbackRoot = (agentRoot) => join(agentRoot, ".training-rollback");
271
254
  const trainingDiscardRoot = (agentRoot) => join(agentRoot, ".training-discard");
@@ -439,7 +422,6 @@ const activeTrainingRoot = async (agentsRoot, handle, ability) => {
439
422
  && catalog.releaseId === ability.releaseId
440
423
  && catalog.rootCommit === ability.rootCommit
441
424
  && catalog.artifactDigest === ability.artifactDigest
442
- && await projectedSkillsComplete(trainingRoot, ability.skills)
443
425
  ? trainingRoot
444
426
  : null;
445
427
  };
@@ -481,8 +463,7 @@ const materializeWorkspaceTraining = async (agentsRoot, handle, ability) => {
481
463
  }
482
464
  if (previousCatalog.releaseId === ability.releaseId
483
465
  && previousCatalog.artifactDigest === ability.artifactDigest
484
- && previousCatalog.projectionDigest === projectionDigest
485
- && await projectedSkillsComplete(trainingRoot, ability.skills))
466
+ && previousCatalog.projectionDigest === projectionDigest)
486
467
  return trainingRoot;
487
468
  }
488
469
  await mkdir(agentRoot, { recursive: true });
@@ -3,12 +3,12 @@ import { profileLayout, validateLocalInstanceName } from "./profile-layout.js";
3
3
  const PROFILE_NAME = /^[a-z0-9][a-z0-9_-]{0,47}$/;
4
4
  const KNOWN_TARGETS = {
5
5
  "nowwork-in.nowcoder.com": {
6
- environment: "production",
6
+ profile: "in",
7
7
  daemonDirectory: "daemon",
8
8
  agentsDirectory: "agents",
9
9
  },
10
10
  "nowwork-dev.nowcoder.com": {
11
- environment: "dev",
11
+ profile: "dev",
12
12
  daemonDirectory: "daemon-dev",
13
13
  agentsDirectory: "agents-dev",
14
14
  },
@@ -42,37 +42,32 @@ function requireCustomProfile(hostname, value) {
42
42
  if (!PROFILE_NAME.test(value)) {
43
43
  throw new Error("Invalid custom profile; expected a lowercase name of 1-48 letters, digits, '_' or '-'");
44
44
  }
45
- if (value === "in" || value === "dev" || value === "production") {
45
+ if (value === "in" || value === "dev") {
46
46
  throw new Error(`Profile '${value}' is reserved for a known NowWork hostname`);
47
47
  }
48
48
  return value;
49
49
  }
50
50
  function knownTargetForProfile(environment, requestedProfile, input, parsed) {
51
51
  const paths = input.platform === "win32" ? win32 : posix;
52
- // Keep the no-flag target readable by old daemons; any workspace-scoped profile below
53
- // uses the canonical environment token (production, never in).
54
- const legacyProfile = environment === "production" ? "in" : environment;
52
+ const legacyProfile = environment;
55
53
  if (requestedProfile === undefined || requestedProfile === legacyProfile) {
56
54
  return Object.freeze({
57
55
  kind: "known",
58
56
  profile: legacyProfile,
59
57
  serverUrl: normalizedServerUrl(parsed),
60
- daemonHome: paths.join(input.userHome, ".crew", environment === "production" ? "daemon" : "daemon-dev"),
61
- agentsRoot: paths.join(input.userHome, ".crew", environment === "production" ? "agents" : "agents-dev"),
58
+ daemonHome: paths.join(input.userHome, ".crew", environment === "in" ? "daemon" : "daemon-dev"),
59
+ agentsRoot: paths.join(input.userHome, ".crew", environment === "in" ? "agents" : "agents-dev"),
62
60
  npmPrefix: paths.join(input.userHome, ".crew", "daemon-runtimes", legacyProfile),
63
61
  });
64
62
  }
65
63
  if (requestedProfile === "in" || requestedProfile === "dev") {
66
- throw new Error(`Server hostname '${new URL(input.serverUrl).hostname}' requires profile '${environment}'`);
64
+ throw new Error(`Server hostname '${new URL(input.serverUrl).hostname}' requires profile '${legacyProfile}'`);
67
65
  }
68
66
  const prefix = `nw-${environment}-`;
69
- const legacyPrefix = environment === "production" ? "nw-in-" : prefix;
70
- if (!requestedProfile.startsWith(prefix) && !requestedProfile.startsWith(legacyPrefix)) {
67
+ if (!requestedProfile.startsWith(prefix)) {
71
68
  throw new Error(`Server hostname '${new URL(input.serverUrl).hostname}' requires profile '${prefix}<local-name>'`);
72
69
  }
73
- const localInstanceName = validateLocalInstanceName(requestedProfile.startsWith(prefix)
74
- ? requestedProfile.slice(prefix.length)
75
- : requestedProfile.slice(legacyPrefix.length));
70
+ const localInstanceName = validateLocalInstanceName(requestedProfile.slice(prefix.length));
76
71
  const layout = profileLayout({
77
72
  environment,
78
73
  localInstanceName,
@@ -96,7 +91,7 @@ export function resolveAutomaticInstallTarget(input) {
96
91
  const paths = input.platform === "win32" ? win32 : posix;
97
92
  const known = KNOWN_TARGETS[hostname];
98
93
  if (known !== undefined) {
99
- return knownTargetForProfile(known.environment, input.requestedProfile, input, parsed);
94
+ return knownTargetForProfile(known.profile, input.requestedProfile, input, parsed);
100
95
  }
101
96
  const profile = requireCustomProfile(hostname, input.requestedProfile);
102
97
  return Object.freeze({
package/dist/config.js CHANGED
@@ -59,40 +59,6 @@ function defaultCliPath() {
59
59
  return resolve(here, "../../cli/dist/main.js");
60
60
  }
61
61
  }
62
- // 临时 dev 验收名单。测试完成后删除此常量,恢复未配置时默认关闭的行为。
63
- const TEMP_LOCAL_MEMORY_V2_TEST_HANDLES = [
64
- "memory-v2-test-a",
65
- "memory-v2-test-b",
66
- "memory-v2-test-c",
67
- ];
68
- function isTemporaryDevServer(serverUrl) {
69
- try {
70
- const hostname = new URL(serverUrl).hostname.toLowerCase();
71
- return hostname === "localhost"
72
- || hostname === "127.0.0.1"
73
- || hostname === "::1"
74
- || hostname === "nowwork-dev.nowcoder.com";
75
- }
76
- catch {
77
- return false;
78
- }
79
- }
80
- function localMemoryV2Handles(env, serverUrl) {
81
- const configured = env.CREW_LOCAL_MEMORY_V2_HANDLES;
82
- if (configured === undefined) {
83
- return isTemporaryDevServer(serverUrl)
84
- ? new Set(TEMP_LOCAL_MEMORY_V2_TEST_HANDLES)
85
- : new Set();
86
- }
87
- const raw = configured.trim();
88
- if (raw === "")
89
- return new Set();
90
- const handles = raw.split(",").map((value) => value.trim()).filter(Boolean);
91
- if (handles.some((handle) => !/^[A-Za-z0-9][A-Za-z0-9._-]*$/u.test(handle))) {
92
- throw new ConfigError("CREW_LOCAL_MEMORY_V2_HANDLES contains an invalid agent handle");
93
- }
94
- return new Set(handles);
95
- }
96
62
  export const DEFAULT_SERVER_URL = "http://127.0.0.1:3001";
97
63
  function codexHomeModeEnv(env) {
98
64
  const value = env.CREW_CODEX_HOME_MODE;
@@ -142,7 +108,6 @@ export function loadConfig(env = process.env) {
142
108
  sessionMaxTurns: env.CREW_SESSION_MAX_TURNS != null ? Number(env.CREW_SESSION_MAX_TURNS) : 30,
143
109
  productName: env.CREW_PRODUCT_NAME ?? "nowwork",
144
110
  agentMemory: loadAgentMemoryConfig(env),
145
- localMemoryV2Handles: localMemoryV2Handles(env, serverUrl),
146
111
  executionLimits,
147
112
  };
148
113
  }
@@ -10,7 +10,6 @@ import { buildServiceSpec, captureExactService, serviceStatus, systemCommandRunn
10
10
  import { readManagedServiceRegistry } from "./managed-service-registry.js";
11
11
  import { uninstallManagedService } from "./managed-service-lifecycle.js";
12
12
  import { resolveAutomaticInstallTarget } from "./automatic-install-target.js";
13
- import { buildStorageMigrationMappings, createSafeServiceManifest, CredentialMaterialError, } from "./storage-migration-manifest.js";
14
13
  const MigrationMessageSchema = z.object({
15
14
  type: z.literal("daemon:migrate"),
16
15
  migrationId: z.string().uuid(),
@@ -48,30 +47,33 @@ async function hashedFiles(root, directory = root) {
48
47
  return files;
49
48
  }
50
49
  async function backupAndCopy(input, id, targetHome, targetAgents, service) {
51
- // Validate and project service identity before creating any backup bytes. A descriptor containing
52
- // a token must stop the migration before it can be copied into the local backup root.
53
- const safeService = createSafeServiceManifest({
54
- spec: service.spec,
55
- snapshot: service.snapshot,
56
- managedRecord: service.managedRecord,
57
- });
58
50
  const backup = migrationBackupRoot(input, id);
59
51
  await mkdir(backup, { recursive: true, mode: 0o700 });
60
52
  await cp(input.currentDaemonHome, resolve(backup, "daemon-home"), { recursive: true, force: false });
61
53
  await cp(input.currentAgentsRoot, resolve(backup, "agents-root"), { recursive: true, force: false });
62
54
  await cp(input.currentDaemonHome, targetHome, { recursive: true, force: false });
63
55
  await cp(input.currentAgentsRoot, targetAgents, { recursive: true, force: false });
64
- const daemonFiles = await hashedFiles(resolve(backup, "daemon-home"));
65
- const agentFiles = await hashedFiles(resolve(backup, "agents-root"));
66
- const mappings = buildStorageMigrationMappings({ daemonFiles, agentFiles });
56
+ const files = [
57
+ ...(await hashedFiles(resolve(backup, "daemon-home"))).map((file) => ({ ...file, path: `daemon-home/${file.path}` })),
58
+ ...(await hashedFiles(resolve(backup, "agents-root"))).map((file) => ({ ...file, path: `agents-root/${file.path}` })),
59
+ ];
67
60
  await writeFile(resolve(backup, "manifest.json"), `${JSON.stringify({
68
- version: 2,
61
+ version: 1,
69
62
  migrationId: id,
70
63
  profile: input.profileName,
71
64
  daemonHome: input.currentDaemonHome,
72
65
  agentsRoot: input.currentAgentsRoot,
73
- service: safeService,
74
- mappings,
66
+ service: {
67
+ id: service.spec.id,
68
+ platform: service.spec.platform,
69
+ descriptorPath: service.spec.descriptorPath,
70
+ descriptor: service.spec.descriptor,
71
+ daemonCommand: service.spec.daemonCommand,
72
+ installed: service.snapshot.installed,
73
+ running: service.snapshot.running,
74
+ managedRecord: service.managedRecord,
75
+ },
76
+ files,
75
77
  }, null, 2)}\n`, { mode: 0o600 });
76
78
  return backup;
77
79
  }
@@ -145,7 +147,7 @@ export function createDaemonMigrationController(input) {
145
147
  }
146
148
  }
147
149
  catch (error) {
148
- sendFailure(message.migrationId, error instanceof CredentialMaterialError ? "credential_material" : "target_conflict", error);
150
+ sendFailure(message.migrationId, "target_conflict", error);
149
151
  return;
150
152
  }
151
153
  input.sendStatus({ type: "daemon:migration-status", migrationId: message.migrationId, status: "backing_up" });
@@ -156,7 +158,7 @@ export function createDaemonMigrationController(input) {
156
158
  }
157
159
  catch (error) {
158
160
  await Promise.allSettled([rm(targetPath, { recursive: true, force: true }), rm(targetAgents, { recursive: true, force: true })]);
159
- sendFailure(message.migrationId, error instanceof CredentialMaterialError ? "credential_material" : "backup_failed", error);
161
+ sendFailure(message.migrationId, "backup_failed", error);
160
162
  return;
161
163
  }
162
164
  const install = input.install ?? installDaemonAutomatically;
@@ -847,7 +847,6 @@ export async function runExecution(config, input, dependencies) {
847
847
  softTokens: config.sessionSoftTokens,
848
848
  maxTurns: config.sessionMaxTurns,
849
849
  },
850
- localMemoryV2Enabled: config.localMemoryV2Handles?.has(spec.agent.handle) === true,
851
850
  };
852
851
  const result = await Promise.race([
853
852
  execute(localInput, callbacks, localDependencies),
@@ -31,7 +31,6 @@ import { diffMemoryPruneNotes, evaluateMemoryPrunePostcondition, inspectMemoryPr
31
31
  import { createLocalMemoryTelemetry, logLocalMemoryContextPrepareFailure, logLocalMemoryDiagnosticsFailure, } from "./local-memory-telemetry.js";
32
32
  import { CodexStartupStageParser } from "./codex-startup-stage.js";
33
33
  import { formatRuntimeStartupError } from "./runtime-startup-error.js";
34
- import { commitLocalMemoryResultAfterExit, initializeLocalMemoryV2ForWorkspace, isLocalMemoryV2Eligible, localMemoryResultPath as makeLocalMemoryResultPath, renderLocalMemoryResultInstructions, } from "./local-memory/runtime.js";
35
34
  function memoryPruneFileFactFields(fileFacts) {
36
35
  const fields = {};
37
36
  for (const [label, fact] of Object.entries(fileFacts)) {
@@ -278,7 +277,6 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
278
277
  }, "workspace_prepare", error);
279
278
  throw error;
280
279
  }
281
- const localMemoryV2Initialized = await initializeLocalMemoryV2ForWorkspace(workspace, input.localMemoryV2Enabled === true);
282
280
  let materialized = null;
283
281
  let knownAttachmentDirectory = null;
284
282
  const startupReservationState = { current: null };
@@ -288,7 +286,6 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
288
286
  let memoryPruneFailurePhase = "diagnostics_before";
289
287
  let memoryPruneBeforeSnapshot = null;
290
288
  let memoryPruneSharedWriteKey = null;
291
- let localMemoryResultPath = null;
292
289
  let executionWorkspace = workspace;
293
290
  let localMemoryTelemetry = null;
294
291
  const inheritedEnv = { ...process.env };
@@ -374,6 +371,7 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
374
371
  nearBudget,
375
372
  ...(input.projectContext === undefined ? {} : { projectContext: input.projectContext }),
376
373
  };
374
+ const systemPrompt = resolvePrompt(input.systemPrompt, promptContext);
377
375
  if (input.attachments && input.attachments.length > 0) {
378
376
  if (dependencies.cancellation?.isRequested())
379
377
  throw new RuntimeCancelledError();
@@ -407,29 +405,6 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
407
405
  const attachmentPlan = routeRuntimeAttachments(runtime.name, materialized?.attachments ?? []);
408
406
  const wakePrompt = `${resolvePrompt(input.wakePrompt, promptContext)}${attachmentPlan.promptSuffix}`;
409
407
  memoryPruneTraceId = parseMemoryPruneTraceId(wakePrompt);
410
- if (input.localMemoryV2Enabled === true && memoryPruneTraceId !== null
411
- && isLocalMemoryV2Eligible({
412
- enabledForHandle: true,
413
- agentHomePreexisted: workspace.agentHomePreexisted === true,
414
- memorySeedCreated: executionWorkspace.memorySeedCreated === true && (localMemoryV2Initialized || workspace.localMemoryV2Initialized !== false),
415
- localMemoryV2Initialized,
416
- memoryPrune: true,
417
- })) {
418
- localMemoryResultPath = makeLocalMemoryResultPath(executionWorkspace.runDir, input.executionId);
419
- }
420
- const baseSystemPrompt = resolvePrompt(input.systemPrompt, promptContext);
421
- const structuredMemoryInstructions = localMemoryResultPath === null
422
- ? null
423
- : renderLocalMemoryResultInstructions(localMemoryResultPath);
424
- const structuredPromptFits = structuredMemoryInstructions === null
425
- || input.maxSystemPromptBytes === undefined
426
- || Buffer.byteLength(baseSystemPrompt, "utf8") + Buffer.byteLength(structuredMemoryInstructions, "utf8")
427
- <= input.maxSystemPromptBytes;
428
- if (!structuredPromptFits)
429
- localMemoryResultPath = null;
430
- const systemPrompt = localMemoryResultPath === null
431
- ? baseSystemPrompt
432
- : `${baseSystemPrompt}${structuredMemoryInstructions ?? ""}`;
433
408
  try {
434
409
  localMemoryTelemetry = createLocalMemoryTelemetry({
435
410
  executionId: input.executionId,
@@ -917,24 +892,6 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
917
892
  const { exitCode, spawnError, terminationSignal } = runtimeExit;
918
893
  localMemoryTelemetry?.markRuntimeExited(exitCode);
919
894
  memoryPruneRuntimeExitCode = exitCode;
920
- if (localMemoryResultPath !== null) {
921
- const committed = await commitLocalMemoryResultAfterExit({
922
- resultPath: localMemoryResultPath,
923
- agentDir: executionWorkspace.dir,
924
- executionId: input.executionId,
925
- pruneTraceId: memoryPruneTraceId,
926
- exitCode,
927
- });
928
- if (committed.status === "failed") {
929
- dslog("local_memory.commit_failed", "结构化本地记忆候选未落地,保留结果文件", {
930
- level: "WARN",
931
- execution_id: input.executionId,
932
- agent_handle: input.handle,
933
- error_code: committed.error,
934
- result_path: localMemoryResultPath,
935
- });
936
- }
937
- }
938
895
  const errorTail = [
939
896
  stderrTail.trim(),
940
897
  spawnError === undefined
@@ -191,11 +191,10 @@ export async function collectMachineHello(agentsRoot, executionLimits, runtimePl
191
191
  profileName: dependencies.profileName,
192
192
  daemonHome: dependencies.daemonHome ?? daemonHome(),
193
193
  agentsRoot,
194
- ...(dependencies.profileName.match(/^nw-(?:production|in|dev)-([a-z0-9][a-z0-9_-]{0,39})$/u)?.[1] === undefined
194
+ ...(dependencies.profileName.match(/^nw-(?:in|dev)-([a-z0-9][a-z0-9_-]{0,39})$/u)?.[1] === undefined
195
195
  ? {}
196
- : { localInstanceName: dependencies.profileName.match(/^nw-(?:production|in|dev)-([a-z0-9][a-z0-9_-]{0,39})$/u)[1] }),
196
+ : { localInstanceName: dependencies.profileName.match(/^nw-(?:in|dev)-([a-z0-9][a-z0-9_-]{0,39})$/u)[1] }),
197
197
  }),
198
- ...(dependencies.storageIdentity ?? {}),
199
198
  ...(backend.supported ? {
200
199
  executionProtocol: EXECUTION_PROTOCOL,
201
200
  executionLimits: Object.freeze({
package/dist/main.js CHANGED
@@ -7,7 +7,6 @@
7
7
  */
8
8
  import { parseArgs } from "node:util";
9
9
  import { homedir } from "node:os";
10
- import { join } from "node:path";
11
10
  import { loadConfig, ConfigError } from "./config.js";
12
11
  import { detectDaemonLang, formatDaemonText, translateDaemon } from "./i18n.js";
13
12
  import { cliVersion, daemonVersion } from "./machine-info.js";
@@ -22,18 +21,7 @@ import { formatDaemonStartupError } from "./daemon-startup-error.js";
22
21
  import { assertManagedServiceStartup } from "./managed-service-startup.js";
23
22
  import { daemonGlobalInstallation } from "./daemon-installation.js";
24
23
  import { acquireDaemonInstallationLease, runWithDaemonInstallationLease, } from "./daemon-installation-lease.js";
25
- import { loadStorageRuntime } from "./storage-runtime.js";
26
- import { readStorageMigrationReceipt } from "./storage-migration-controller.js";
27
- import { storageMigrationStatusReplay } from "./storage-status-replay.js";
28
- import { runStorageWorkerCommand } from "./storage-worker-command.js";
29
- import { probeStorageMigrationRuntime } from "./storage-runtime-readiness.js";
30
- import { createStorageTargetConfirmationHandler } from "./storage-target-confirmation.js";
31
24
  async function main() {
32
- const workerResult = await runStorageWorkerCommand(process.argv.slice(2));
33
- if (workerResult !== null) {
34
- process.exitCode = workerResult;
35
- return;
36
- }
37
25
  const computerResult = await runComputerCommand(process.argv.slice(2));
38
26
  if (computerResult !== null) {
39
27
  process.exitCode = computerResult;
@@ -69,10 +57,8 @@ async function main() {
69
57
  // 不把 machine token 放进 argv / plist / systemd unit / scheduled task。
70
58
  if (values["daemon-home"])
71
59
  process.env.CREW_DAEMON_HOME = values["daemon-home"];
72
- let activeDaemonHome;
73
60
  if (values.profile) {
74
61
  const home = daemonHome();
75
- activeDaemonHome = home;
76
62
  const profile = await loadProfile(values.profile, home);
77
63
  if (cmd === "serve") {
78
64
  await assertProfileAgentsRootUnique(profile, home, homedir());
@@ -114,72 +100,9 @@ async function main() {
114
100
  const installationLease = installation === null
115
101
  ? null
116
102
  : await acquireDaemonInstallationLease(installation.npmPrefix);
117
- const storageRuntime = values.profile === undefined
118
- ? null
119
- : await loadStorageRuntime({
120
- profile: values.profile,
121
- serverUrl: config.serverUrl,
122
- userHome: homedir(),
123
- platform: process.platform,
124
- environment: process.env,
125
- });
126
- const migrationRuntime = values.profile === undefined || activeDaemonHome === undefined || storageRuntime !== null
127
- ? { ready: false, reason: "not_legacy_source" }
128
- : await probeStorageMigrationRuntime({
129
- profile: values.profile,
130
- serverUrl: config.serverUrl,
131
- currentDaemonHome: activeDaemonHome,
132
- currentAgentsRoot: config.agentsRoot,
133
- userHome: homedir(),
134
- platform: process.platform,
135
- ...(process.getuid === undefined ? {} : { uid: process.getuid() }),
136
- environment: process.env,
137
- });
138
- const targetConfirmation = storageRuntime?.identity.storageMigrationId === undefined
139
- ? null
140
- : createStorageTargetConfirmationHandler({
141
- backupsRoot: storageRuntime.local.backupsRoot,
142
- identity: storageRuntime.identity,
143
- profile: storageRuntime.installation.profile,
144
- completionTimeoutMs: 180_000,
145
- });
146
- const storageMigration = storageRuntime !== null
147
- ? {
148
- ready: false,
149
- statusOutboxPath: join(storageRuntime.local.stateRoot, "storage-status-outbox.json"),
150
- identity: storageRuntime.identity,
151
- ...(targetConfirmation === null ? {} : { confirm: targetConfirmation.handle }),
152
- ...(storageRuntime.identity.storageMigrationId === undefined ? {} : {
153
- replayStatus: async (send) => {
154
- const migrationId = storageRuntime.identity.storageMigrationId;
155
- const receipt = await readStorageMigrationReceipt(storageRuntime.local.backupsRoot, migrationId);
156
- if (receipt === null)
157
- throw new Error(`storage migration receipt not found: ${migrationId}`);
158
- for (const frame of storageMigrationStatusReplay(receipt, {
159
- migrationId,
160
- environment: storageRuntime.identity.storageEnvironment,
161
- workspaceId: storageRuntime.identity.storageWorkspaceId,
162
- profile: storageRuntime.installation.profile,
163
- }))
164
- send(frame);
165
- },
166
- }),
167
- }
168
- : migrationRuntime.ready
169
- ? {
170
- ready: true,
171
- statusOutboxPath: migrationRuntime.runtime.statusOutboxPath,
172
- identity: migrationRuntime.runtime.identity,
173
- controller: migrationRuntime.runtime.controller,
174
- attach: migrationRuntime.runtime.attach,
175
- bindServerIdentity: migrationRuntime.runtime.bindServerIdentity,
176
- replayStatus: migrationRuntime.runtime.replayStatus,
177
- }
178
- : undefined;
179
103
  await runWithDaemonInstallationLease(installationLease, async () => {
180
104
  const service = serve(config, {
181
105
  ...(values.profile === undefined ? {} : { profileName: values.profile }),
182
- ...(storageMigration === undefined ? {} : { storageMigration }),
183
106
  });
184
107
  await runServeLifecycle(service);
185
108
  });
@@ -2,10 +2,6 @@ import { posix, win32 } from "node:path";
2
2
  export const PROFILE_LAYOUT_VERSION = 1;
3
3
  const PROFILE_NAME = /^[a-z0-9][a-z0-9_-]{0,47}$/u;
4
4
  const LOCAL_INSTANCE_NAME = /^[a-z0-9][a-z0-9_-]{0,39}$/u;
5
- /** Accept the legacy `in` tag at the boundary, but never persist it in a new layout. */
6
- export function normalizeProfileEnvironment(value) {
7
- return value === "in" ? "production" : value;
8
- }
9
5
  export function validateLocalInstanceName(value) {
10
6
  const normalized = value.trim().toLowerCase();
11
7
  if (!LOCAL_INSTANCE_NAME.test(normalized) || normalized.includes("..")) {
@@ -14,21 +10,19 @@ export function validateLocalInstanceName(value) {
14
10
  return normalized;
15
11
  }
16
12
  export function profileForLocalInstance(environment, localInstanceName) {
17
- const canonicalEnvironment = normalizeProfileEnvironment(environment);
18
- const profile = `nw-${canonicalEnvironment}-${validateLocalInstanceName(localInstanceName)}`;
13
+ const profile = `nw-${environment}-${validateLocalInstanceName(localInstanceName)}`;
19
14
  if (!PROFILE_NAME.test(profile))
20
15
  throw new Error("derived daemon profile exceeds 48 characters");
21
16
  return profile;
22
17
  }
23
18
  export function profileLayout(input) {
24
- const environment = normalizeProfileEnvironment(input.environment);
25
19
  const localInstanceName = validateLocalInstanceName(input.localInstanceName);
26
- const profile = profileForLocalInstance(environment, localInstanceName);
20
+ const profile = profileForLocalInstance(input.environment, localInstanceName);
27
21
  const paths = input.platform === "win32" ? win32 : posix;
28
- const base = `${environment}-${localInstanceName}`;
22
+ const base = `${input.environment}-${localInstanceName}`;
29
23
  return Object.freeze({
30
24
  layoutVersion: PROFILE_LAYOUT_VERSION,
31
- environment,
25
+ environment: input.environment,
32
26
  localInstanceName,
33
27
  profile,
34
28
  daemonHome: paths.join(input.userHome, ".crew", `daemon-${base}`),
package/dist/runner.js CHANGED
@@ -105,7 +105,6 @@ export async function runAgent(config, input, onActivity = defaultPrint, onConso
105
105
  softTokens: config.sessionSoftTokens,
106
106
  maxTurns: config.sessionMaxTurns,
107
107
  },
108
- localMemoryV2Enabled: config.localMemoryV2Handles?.has(input.handle) === true,
109
108
  }, {
110
109
  onActivity,
111
110
  onConsole,
package/dist/serve.js CHANGED
@@ -51,7 +51,6 @@ import { handleRuntimeProbeFrame, probeRuntimeHealth } from "./runtime-health.js
51
51
  import { buildControlPlaneUrl } from "./control-plane-url.js";
52
52
  import { createServeMigrationController, handleMigrationControlMessage } from "./daemon-migration-wiring.js";
53
53
  import { createCodexHomeModeController } from "./codex-home-mode-controller.js";
54
- import { attachStorageMigrationRuntime, createServeStorageStatusDelivery, handleStorageMigrationControl, replayStorageMigrationStatus, storageMigrationCapabilities, } from "./storage-serve-adapter.js";
55
54
  export { buildControlPlaneUrl } from "./control-plane-url.js";
56
55
  // normalize.ts 的活动种类 → activity 枚举
57
56
  const ACTIVITY_MAP = {
@@ -176,36 +175,13 @@ export function serve(config, opts = {}) {
176
175
  });
177
176
  const machineHelloPublisher = createProjectSkillMachineHelloPublisher({
178
177
  status: projectSkillControllerReadiness.status,
179
- effective: (hello, status) => {
180
- const effective = withProjectSkillCapabilityStatus(hello, status);
181
- const configuredIdentity = opts.storageMigration?.identity;
182
- const currentIdentity = typeof configuredIdentity === "function"
183
- ? configuredIdentity()
184
- : configuredIdentity;
185
- return currentIdentity === undefined ? effective : { ...effective, ...currentIdentity };
186
- },
178
+ effective: withProjectSkillCapabilityStatus,
187
179
  onSent: (hello) => log(`📤 已上报机器信息: ${hello.hostname} · ${hello.os} · installed=[${hello.runtimes.join(",")}] · executable=[${hello.executionRuntimes.join(",")}] · agents=[${hello.agentHandles.join(",")}]`),
188
180
  });
189
181
  const initializedProjectSkills = initializedProjectSkillsReconciler(projectSkillControllerReadiness.ensure, projectSkillsReconciler);
190
- const configuredCapabilities = opts.machineInfo?.capabilities ?? daemonCapabilities(process.platform);
191
- const storageCapabilities = storageMigrationCapabilities(configuredCapabilities, opts.storageMigration);
192
- const storageStatusDelivery = createServeStorageStatusDelivery(opts.storageMigration, {
193
- onPersistenceError: (error) => dslog("daemon.storage_status_outbox_failed", "存储迁移状态 outbox 持久化失败", { level: "ERROR", error_message: error.message }),
194
- transmit: (frame) => { try {
195
- if (ws?.readyState === WebSocket.OPEN)
196
- ws.send(JSON.stringify(frame));
197
- }
198
- catch { /* retry */ } },
199
- });
200
- const storageMigrationRuntime = attachStorageMigrationRuntime(opts.storageMigration, {
201
- isBusy: () => executionRuns.size > 0 || legacyRuns.size > 0,
202
- waitForExecutionFrames: () => executionFrameQueue,
203
- flushLocalState: flushSlog,
204
- sendStatus: storageStatusDelivery.send,
205
- });
206
182
  const prepareProjectSkillCapabilities = createProjectSkillConnectionCapabilityProvider({
207
183
  platform: process.platform,
208
- baseCapabilities: storageCapabilities,
184
+ baseCapabilities: opts.machineInfo?.capabilities ?? daemonCapabilities(process.platform),
209
185
  additionalCapabilities: () => managedDaemonCapabilities(updateEligibility),
210
186
  probeRuntimeRootStorePrimitive: projectSkillRuntimeRootStartup.probeNativePublicationPrimitiveReadiness,
211
187
  initializeController: projectSkillControllerReadiness.ensure,
@@ -334,12 +310,6 @@ export function serve(config, opts = {}) {
334
310
  // 连上了才有机会把离线期间(断连原因/退出前)落盘的日志补传上去
335
311
  void drainSpool();
336
312
  // 上报本机信息 (hostname/os/daemon 版本/已装 runtimes)
337
- storageStatusDelivery.replayPending();
338
- const storageReplay = replayStorageMigrationStatus(opts.storageMigration, openedSocket, () => ws === openedSocket, (error) => {
339
- dslog("daemon.storage_status_replay_failed", "存储迁移状态重放失败", {
340
- level: "ERROR", error_message: error.message,
341
- });
342
- }, storageStatusDelivery.send);
343
313
  const { detectInstalled, detectExecutable = detectExecutionRuntimesWithSignal } = opts.machineInfo ?? {};
344
314
  const helloPromise = (opts.machineInfo?.collect ?? collectMachineHello)(config.agentsRoot, config.executionLimits, process.platform, {
345
315
  ...(detectInstalled ? { detectInstalled } : {}),
@@ -355,7 +325,6 @@ export function serve(config, opts = {}) {
355
325
  if (hello.executionProtocol === undefined)
356
326
  return [];
357
327
  const detected = await runtimeProbe.detect(hello.runtimes, detectExecutable);
358
- await storageReplay;
359
328
  if (ws === openedSocket && openedSocket.readyState === WebSocket.OPEN
360
329
  && detected.some((runtime) => !hello.executionRuntimes.includes(runtime))) {
361
330
  detectedExecutionRuntimes = detected;
@@ -370,8 +339,7 @@ export function serve(config, opts = {}) {
370
339
  return [];
371
340
  });
372
341
  void helloPromise
373
- .then(async (hello) => {
374
- await storageReplay;
342
+ .then((hello) => {
375
343
  if (ws !== openedSocket || openedSocket.readyState !== WebSocket.OPEN)
376
344
  return;
377
345
  detectedExecutionRuntimes = hello.executionRuntimes;
@@ -419,8 +387,6 @@ export function serve(config, opts = {}) {
419
387
  if (typeof decoded !== "object" || decoded === null)
420
388
  return;
421
389
  const rawType = "type" in decoded && typeof decoded.type === "string" ? decoded.type : "";
422
- if (storageStatusDelivery.acknowledge(decoded))
423
- return;
424
390
  if (rawType === "daemon:update" || rawType === "daemon:restart") {
425
391
  void updateController.handle(decoded).catch((error) => {
426
392
  dslog("daemon.control_failed", "daemon 控制操作处理失败", {
@@ -437,11 +403,6 @@ export function serve(config, opts = {}) {
437
403
  void codexHomeModeController.handle(decoded).catch((error) => dslog("daemon.codex_home_mode_failed", "Codex Home 模式切换失败", { level: "ERROR", error_message: error.message }));
438
404
  return;
439
405
  }
440
- if (handleStorageMigrationControl(opts.storageMigration, rawType, decoded, storageStatusDelivery.send, (kind, error) => {
441
- dslog(kind === "migration" ? "daemon.storage_migration_failed" : "daemon.storage_migration_confirmation_failed", kind === "migration" ? "存储迁移控制操作失败" : "存储迁移 hello 确认处理失败", { level: "ERROR", error_message: error.message });
442
- })) {
443
- return;
444
- }
445
406
  if (rawType.startsWith("execution:")) {
446
407
  const parsedExecution = ServerToDaemonExecutionFrameSchema.safeParse(decoded);
447
408
  if (!parsedExecution.success) {
@@ -503,14 +464,6 @@ export function serve(config, opts = {}) {
503
464
  return;
504
465
  }
505
466
  const spec = frame;
506
- if (storageMigrationRuntime.isAdmissionClosed()) {
507
- safeExecutionSend(ExecutionRejectedSchema.parse({
508
- type: "execution:rejected", protocolVersion: 1, executionId: spec.executionId,
509
- reason: "resource_limit", message: "Storage migration is quiescing this machine",
510
- at: new Date().toISOString(),
511
- }));
512
- return;
513
- }
514
467
  const workspaceRejection = projectWorkspaceCapabilityRejection(spec, capabilities.executionRunner, new Date().toISOString());
515
468
  if (workspaceRejection !== null)
516
469
  return void safeExecutionSend(workspaceRejection);
@@ -817,17 +770,6 @@ export function serve(config, opts = {}) {
817
770
  // ready 帧带 server 视角的 machineId/workspaceId → 作为后续所有日志的默认关联键
818
771
  const r = msg;
819
772
  setSlogDefaults({ machine_id: r.machineId, workspace_id: r.workspaceId });
820
- if (opts.storageMigration?.ready === true && typeof r.workspaceId === "string") {
821
- try {
822
- opts.storageMigration.bindServerIdentity?.({ workspaceId: r.workspaceId });
823
- machineHelloPublisher.republish();
824
- }
825
- catch (error) {
826
- dslog("daemon.storage_identity_bind_failed", "存储迁移 Workspace 身份绑定失败", {
827
- level: "ERROR", error_message: error.message,
828
- });
829
- }
830
- }
831
773
  // 旧 server 无此字段 → 空集合(daemon 保持全部旧行为)。
832
774
  serverCapabilities = new Set(Array.isArray(r.serverCapabilities)
833
775
  ? r.serverCapabilities.filter((c) => typeof c === "string")
@@ -926,8 +868,6 @@ export function serve(config, opts = {}) {
926
868
  }
927
869
  if (msg.type !== "agent:start")
928
870
  return; // ready/error 等忽略
929
- if (storageMigrationRuntime.isAdmissionClosed())
930
- return;
931
871
  // 任务键:scheduled run 用 runId(每次运行独立 cwd/work-log;overlap 由 server 端 skip_if_running 管,
932
872
  // daemon 的 running 去重只兜底"同一 run 重复投递");普通唤醒仍是 线程锚点 ?? 频道。
933
873
  const scheduled = msg.reason === "scheduled_job" && msg.scheduledRun
@@ -1343,7 +1283,6 @@ export function serve(config, opts = {}) {
1343
1283
  concurrencyTelemetry.stop();
1344
1284
  runtimeProbe.stop();
1345
1285
  completionRetransmitter.stop();
1346
- storageStatusDelivery.stop();
1347
1286
  const deadline = createShutdownDeadline(shutdownTimeoutMs);
1348
1287
  if (reconnectTimer !== null) {
1349
1288
  clearTimeout(reconnectTimer);