@nowcrew/daemon 0.6.60 → 0.6.64

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 (47) hide show
  1. package/README.md +9 -8
  2. package/dist/agent-ability/materializer.js +1 -20
  3. package/dist/automatic-install-target.js +10 -15
  4. package/dist/computer-cli.js +1 -10
  5. package/dist/computer-service.js +1 -41
  6. package/dist/config.js +0 -35
  7. package/dist/daemon-migration-controller.js +18 -16
  8. package/dist/execution-runner.js +13 -35
  9. package/dist/local-executor.js +1 -44
  10. package/dist/machine-info.js +2 -3
  11. package/dist/main.js +0 -83
  12. package/dist/managed-service-lifecycle.js +2 -47
  13. package/dist/managed-service-startup.js +0 -2
  14. package/dist/profile-layout.js +4 -10
  15. package/dist/runner.js +0 -1
  16. package/dist/serve.js +3 -64
  17. package/dist/workspace.js +1 -24
  18. package/package.json +10 -9
  19. package/dist/local-memory/index-budget.js +0 -74
  20. package/dist/local-memory/pending.js +0 -111
  21. package/dist/local-memory/runtime.js +0 -87
  22. package/dist/local-memory/types.js +0 -1
  23. package/dist/local-memory/write.js +0 -448
  24. package/dist/storage-config.js +0 -175
  25. package/dist/storage-control.js +0 -131
  26. package/dist/storage-copy.js +0 -444
  27. package/dist/storage-credential-guard.js +0 -21
  28. package/dist/storage-doctor.js +0 -175
  29. package/dist/storage-identity.js +0 -29
  30. package/dist/storage-layout.js +0 -143
  31. package/dist/storage-manifest.js +0 -440
  32. package/dist/storage-migration-controller.js +0 -417
  33. package/dist/storage-migration-manifest.js +0 -105
  34. package/dist/storage-migration-policy.js +0 -100
  35. package/dist/storage-runtime-controller.js +0 -313
  36. package/dist/storage-runtime-readiness.js +0 -184
  37. package/dist/storage-runtime.js +0 -39
  38. package/dist/storage-safe-fs.js +0 -293
  39. package/dist/storage-serve-adapter.js +0 -65
  40. package/dist/storage-status-delivery.js +0 -201
  41. package/dist/storage-status-replay.js +0 -125
  42. package/dist/storage-target-confirmation.js +0 -129
  43. package/dist/storage-worker-command.js +0 -27
  44. package/dist/storage-worker-executor.js +0 -383
  45. package/dist/storage-worker-handoff.js +0 -151
  46. package/dist/storage-worker-operation.js +0 -283
  47. package/dist/storage-worker-status.js +0 -105
package/README.md CHANGED
@@ -380,6 +380,7 @@ CREW_EXECUTION_MAX_STARTING_TOTAL=10
380
380
  CREW_EXECUTION_MAX_STARTING_PER_RUNTIME=10
381
381
  CREW_EXECUTION_START_GAP_MS=500
382
382
  CREW_EXECUTION_STARTUP_TIMEOUT_MS=120000
383
+ # Retained for compatibility; accepted executions wait indefinitely for a slot.
383
384
  CREW_EXECUTION_MAX_QUEUE_WAIT_MS=300000
384
385
  CREW_EXECUTION_MAX_FIRST_OUTPUT_WAIT_MS=120000
385
386
  CREW_EXECUTION_FIRST_OUTPUT_GRACE_MS=180000
@@ -391,14 +392,14 @@ protocol-v1 and legacy work.
391
392
  Runtime startup is a separate FIFO gate: by default up to ten Claude, Codex, or Kimi processes may be
392
393
  initializing at a time, and launches are spaced by 500ms. A process leaves the startup gate
393
394
  only after its runtime-specific ready event; startup timeout cancels the owned process tree.
394
- An accepted execution may wait up to five minutes for a local and host slot. After runtime readiness,
395
- the daemon waits up to two minutes for the first model event, with a three-minute grace window for
396
- slow providers. When one of these local liveness limits expires, the supervisor is stopped and the
397
- terminal result is written to the execution journal before it is reported, so the execution remains
398
- replayable instead of being silently dropped. Queue and first-output timeouts are retried locally at
399
- most twice (three total attempts). Each retry releases its current machine/host slot and reserves a
400
- new one, which puts it behind work already waiting in the queue. A third timeout is terminal and is
401
- not requeued; other failures are not automatically retried.
395
+ An accepted execution waits indefinitely for a local and host slot unless it is explicitly cancelled.
396
+ After runtime readiness, the daemon waits up to two minutes for the first model event, with a
397
+ three-minute grace window for slow providers. When the first-output liveness limit expires, the
398
+ supervisor is stopped and the terminal result is written to the execution journal before it is
399
+ reported, so the execution remains replayable instead of being silently dropped. First-output
400
+ timeouts are retried locally at most twice (three total attempts). Each retry releases its current
401
+ machine/host slot and reserves a new one, which puts it behind work already waiting in the queue. A
402
+ third timeout is terminal and is not requeued; other failures are not automatically retried.
402
403
 
403
404
  Local policy can reduce server-requested access and limits; it cannot grant more access than requested.
404
405
  Provider credentials and configured environment are prepared locally and never carried in execution
@@ -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({
@@ -82,14 +82,6 @@ function translatedError(error, td) {
82
82
  }
83
83
  return td(error.message);
84
84
  }
85
- function storageServiceEnvironment(env) {
86
- return {
87
- ...(env.NOWWORK_HOME === undefined ? {} : { NOWWORK_HOME: env.NOWWORK_HOME }),
88
- ...(env.NOWWORK_ENV_TAG === undefined ? {} : { NOWWORK_ENV_TAG: env.NOWWORK_ENV_TAG }),
89
- ...(env.NOWWORK_STORAGE_POLICY_PATH === undefined
90
- ? {} : { NOWWORK_STORAGE_POLICY_PATH: env.NOWWORK_STORAGE_POLICY_PATH }),
91
- };
92
- }
93
85
  export async function runComputerCommand(argv, overrides = {}) {
94
86
  const command = argv[0];
95
87
  if (!command || !COMPUTER_COMMANDS.has(command))
@@ -210,7 +202,6 @@ export async function runComputerCommand(argv, overrides = {}) {
210
202
  ...deps,
211
203
  profile: restartProfile.name,
212
204
  profileHome: home,
213
- environment: storageServiceEnvironment(deps.env),
214
205
  })
215
206
  : null;
216
207
  if (spec && restartProfile) {
@@ -281,7 +272,7 @@ export async function runComputerCommand(argv, overrides = {}) {
281
272
  throw new Error(td("Service lifecycle requires the built daemon entry (.js), not a TypeScript development entry"));
282
273
  }
283
274
  const spec = buildServiceSpec({
284
- ...deps, profile: name, profileHome: home, environment: storageServiceEnvironment(deps.env),
275
+ ...deps, profile: name, profileHome: home,
285
276
  });
286
277
  const installation = daemonGlobalInstallation(deps.entryPath, deps.platform);
287
278
  const managedRecord = installation !== null && spec.platform !== "win32"
@@ -53,7 +53,6 @@ export function windowsCommandArg(value) {
53
53
  }
54
54
  export function buildServiceSpec(input) {
55
55
  const { platform, profile, userHome } = input;
56
- const environment = input.environment ?? process.env;
57
56
  if (platform !== "darwin" && platform !== "linux" && platform !== "win32") {
58
57
  throw new Error(`Computer services are not supported on platform '${platform}'`);
59
58
  }
@@ -73,16 +72,6 @@ export function buildServiceSpec(input) {
73
72
  throw new Error("Cannot install launchd service without a numeric uid");
74
73
  const id = `com.nowcrew.daemon.${profile}${identitySuffix}`;
75
74
  const args = daemonCommand.map((arg) => ` <string>${xml(arg)}</string>`).join("\n");
76
- const environmentEntries = [
77
- ["NOWWORK_HOME", environment.NOWWORK_HOME],
78
- ["NOWWORK_ENV_TAG", environment.NOWWORK_ENV_TAG],
79
- ["NOWWORK_STORAGE_POLICY_PATH", environment.NOWWORK_STORAGE_POLICY_PATH],
80
- ].flatMap(([key, value]) => value === undefined
81
- ? []
82
- : [` <key>${key}</key><string>${xml(assertSingleLine(value, key))}</string>`]);
83
- const environmentBlock = environmentEntries.length === 0
84
- ? ""
85
- : ` <key>EnvironmentVariables</key>\n <dict>\n${environmentEntries.join("\n")}\n </dict>\n`;
86
75
  return {
87
76
  platform,
88
77
  profile,
@@ -90,7 +79,7 @@ export function buildServiceSpec(input) {
90
79
  descriptorPath: resolve(userHome, "Library", "LaunchAgents", `${id}.plist`),
91
80
  daemonCommand,
92
81
  managerDomain: `gui/${input.uid}`,
93
- descriptor: `<?xml version="1.0" encoding="UTF-8"?>\n<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n<plist version="1.0">\n<dict>\n <key>Label</key><string>${xml(id)}</string>\n <key>ProgramArguments</key>\n <array>\n${args}\n </array>\n${environmentBlock} <key>KeepAlive</key><true/>\n <key>ProcessType</key><string>Background</string>\n</dict>\n</plist>\n`,
82
+ descriptor: `<?xml version="1.0" encoding="UTF-8"?>\n<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n<plist version="1.0">\n<dict>\n <key>Label</key><string>${xml(id)}</string>\n <key>ProgramArguments</key>\n <array>\n${args}\n </array>\n <key>KeepAlive</key><true/>\n <key>ProcessType</key><string>Background</string>\n</dict>\n</plist>\n`,
94
83
  };
95
84
  }
96
85
  if (platform === "linux") {
@@ -277,35 +266,6 @@ export async function installService(spec, runner = systemCommandRunner) {
277
266
  throw error;
278
267
  }
279
268
  }
280
- export async function replaceInstalledServiceDescriptor(spec, previousDescriptor, runner = systemCommandRunner) {
281
- if (spec.platform === "win32" || spec.descriptorPath === null || spec.descriptor === null) {
282
- throw new Error("Installed service descriptor replacement requires launchd or systemd");
283
- }
284
- const current = await readServiceDescriptor(spec);
285
- if (current !== previousDescriptor) {
286
- throw new Error(`Installed descriptor for '${spec.id}' changed before replacement`);
287
- }
288
- const status = await serviceStatus(spec, runner);
289
- if (!status.installed)
290
- throw new Error(`Service '${spec.id}' is not installed`);
291
- if (status.running || status.loaded)
292
- await serviceAction(spec, "stop", runner);
293
- try {
294
- await writePrivateAtomic(spec.descriptorPath, spec.descriptor);
295
- if (spec.platform === "linux") {
296
- await checked(runner, "systemctl", ["--user", "daemon-reload"], "systemd reload");
297
- await checked(runner, "systemctl", ["--user", "enable", spec.id], "systemd enable");
298
- }
299
- if (status.running)
300
- await serviceAction(spec, "start", runner);
301
- }
302
- catch (error) {
303
- await writePrivateAtomic(spec.descriptorPath, previousDescriptor).catch(() => undefined);
304
- if (status.running)
305
- await serviceAction(spec, "start", runner).catch(() => undefined);
306
- throw error;
307
- }
308
- }
309
269
  export async function ensureServiceEnabled(spec, runner = systemCommandRunner) {
310
270
  if (spec.platform === "linux") {
311
271
  if (spec.descriptorPath === null || !await pathExists(spec.descriptorPath)) {
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;
@@ -299,12 +299,6 @@ class ExecutionCancelledError extends Error {
299
299
  this.name = "ExecutionCancelledError";
300
300
  }
301
301
  }
302
- class ExecutionQueueTimeoutError extends Error {
303
- constructor(timeoutMs) {
304
- super(`Execution queue wait exceeded ${timeoutMs}ms`);
305
- this.name = "ExecutionQueueTimeoutError";
306
- }
307
- }
308
302
  async function cancellable(promise, cancellation) {
309
303
  if (cancellation === undefined)
310
304
  return promise;
@@ -315,24 +309,9 @@ async function cancellable(promise, cancellation) {
315
309
  cancellation.requested.then(() => { throw new ExecutionCancelledError(); }),
316
310
  ]);
317
311
  }
318
- async function cancellableWithTimeout(promise, timeoutMs, cancellation, timeoutError) {
319
- let timer;
320
- try {
321
- return await Promise.race([
322
- cancellable(promise, cancellation),
323
- new Promise((_resolve, reject) => {
324
- timer = setTimeout(() => reject(timeoutError), timeoutMs);
325
- }),
326
- ]);
327
- }
328
- finally {
329
- if (timer !== undefined)
330
- clearTimeout(timer);
331
- }
332
- }
333
312
  function failedCompletion(spec, error, startedAt, finishedAt) {
334
313
  const message = error instanceof Error ? error.message : String(error);
335
- const errorCode = error instanceof ExecutionQueueTimeoutError || error instanceof HostReservationCancelledError
314
+ const errorCode = error instanceof HostReservationCancelledError
336
315
  ? "queue_timeout"
337
316
  : error instanceof ProjectContextUnavailableError
338
317
  ? error.code
@@ -519,7 +498,9 @@ export async function runExecution(config, input, dependencies) {
519
498
  });
520
499
  try {
521
500
  if (dependencies.slot !== undefined) {
522
- await cancellableWithTimeout(dependencies.slot.ready, config.executionLimits.maxQueueWaitMs, dependencies.cancellation, new ExecutionQueueTimeoutError(config.executionLimits.maxQueueWaitMs));
501
+ // Queueing is not a liveness failure. Keep the execution durable until its
502
+ // machine/host slot is granted or the server explicitly cancels it.
503
+ await cancellable(dependencies.slot.ready, dependencies.cancellation);
523
504
  }
524
505
  let projectContext;
525
506
  let sessionContextFingerprint;
@@ -562,17 +543,15 @@ export async function runExecution(config, input, dependencies) {
562
543
  if (cancel !== null && timeout === undefined && !dependencies.cancellation?.isRequested()) {
563
544
  const firstOutputDeadline = config.executionLimits.maxFirstOutputWaitMs
564
545
  + config.executionLimits.firstOutputGraceMs;
565
- if (effectiveTimeoutMs > firstOutputDeadline) {
566
- firstOutputTimer = setTimeout(() => {
567
- firstOutputTimedOut = true;
568
- try {
569
- void cancel().catch(rejectCancellationFailure);
570
- }
571
- catch (error) {
572
- rejectCancellationFailure(error);
573
- }
574
- }, firstOutputDeadline);
575
- }
546
+ firstOutputTimer = setTimeout(() => {
547
+ firstOutputTimedOut = true;
548
+ try {
549
+ void cancel().catch(rejectCancellationFailure);
550
+ }
551
+ catch (error) {
552
+ rejectCancellationFailure(error);
553
+ }
554
+ }, firstOutputDeadline);
576
555
  timeout = setTimeout(() => {
577
556
  timedOut = true;
578
557
  try {
@@ -847,7 +826,6 @@ export async function runExecution(config, input, dependencies) {
847
826
  softTokens: config.sessionSoftTokens,
848
827
  maxTurns: config.sessionMaxTurns,
849
828
  },
850
- localMemoryV2Enabled: config.localMemoryV2Handles?.has(spec.agent.handle) === true,
851
829
  };
852
830
  const result = await Promise.race([
853
831
  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({