@nowcrew/daemon 0.6.65 → 0.6.66

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/agent-ability/materializer.js +1 -20
  2. package/dist/agent-ability/resolver.js +10 -7
  3. package/dist/agent-ability/runtime.js +0 -3
  4. package/dist/automatic-install-target.js +10 -15
  5. package/dist/computer-cli.js +1 -10
  6. package/dist/computer-service.js +1 -41
  7. package/dist/config.js +0 -44
  8. package/dist/daemon-migration-controller.js +18 -16
  9. package/dist/execution-runner.js +1 -3
  10. package/dist/local-execution-prompt.js +9 -14
  11. package/dist/local-executor.js +3 -56
  12. package/dist/machine-info.js +2 -3
  13. package/dist/main.js +0 -83
  14. package/dist/managed-service-lifecycle.js +2 -47
  15. package/dist/managed-service-startup.js +0 -2
  16. package/dist/profile-layout.js +4 -10
  17. package/dist/project-skills/controller.js +0 -2
  18. package/dist/prompt.js +1 -19
  19. package/dist/runner.js +0 -3
  20. package/dist/runtimes/codex-app-server-runner.js +14 -54
  21. package/dist/runtimes/codex-contract.js +5 -20
  22. package/dist/runtimes/codex.js +0 -4
  23. package/dist/serve.js +3 -68
  24. package/dist/supervised-runtime.js +0 -3
  25. package/dist/workspace.js +1 -24
  26. package/package.json +9 -10
  27. package/dist/local-memory/index-budget.js +0 -74
  28. package/dist/local-memory/pending.js +0 -111
  29. package/dist/local-memory/runtime.js +0 -87
  30. package/dist/local-memory/types.js +0 -1
  31. package/dist/local-memory/write.js +0 -448
  32. package/dist/runtimes/codex-command-safety.js +0 -75
  33. package/dist/runtimes/codex-turn-retry.js +0 -69
  34. package/dist/storage-config.js +0 -175
  35. package/dist/storage-control.js +0 -193
  36. package/dist/storage-copy.js +0 -609
  37. package/dist/storage-credential-guard.js +0 -21
  38. package/dist/storage-doctor.js +0 -228
  39. package/dist/storage-identity.js +0 -29
  40. package/dist/storage-layout.js +0 -143
  41. package/dist/storage-manifest.js +0 -855
  42. package/dist/storage-migration-controller.js +0 -417
  43. package/dist/storage-migration-manifest.js +0 -105
  44. package/dist/storage-migration-policy.js +0 -100
  45. package/dist/storage-runtime-controller.js +0 -339
  46. package/dist/storage-runtime-readiness.js +0 -184
  47. package/dist/storage-runtime.js +0 -39
  48. package/dist/storage-safe-fs.js +0 -302
  49. package/dist/storage-serve-adapter.js +0 -65
  50. package/dist/storage-status-delivery.js +0 -209
  51. package/dist/storage-status-replay.js +0 -125
  52. package/dist/storage-target-confirmation.js +0 -129
  53. package/dist/storage-worker-command.js +0 -27
  54. package/dist/storage-worker-executor.js +0 -384
  55. package/dist/storage-worker-handoff.js +0 -151
  56. package/dist/storage-worker-operation.js +0 -284
  57. 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 });
@@ -9,8 +9,8 @@ import { abilityWorkspaceProjectionPath, DaemonAbilityLockSchema, DaemonAbilityM
9
9
  import { PROJECT_MANIFEST_SOURCE_PATH, ProjectDependencyManifestSchema, } from "../project-dependency-manifest.js";
10
10
  const runFile = promisify(execFile);
11
11
  const MAX_FILES = 5_000;
12
- const MAX_TOTAL_BYTES = 25 * 1024 * 1024;
13
- const MAX_FILE_BYTES = 2 * 1024 * 1024;
12
+ export const AGENT_ABILITY_MAX_TOTAL_BYTES = 200 * 1024 * 1024;
13
+ export const AGENT_ABILITY_MAX_FILE_BYTES = 100 * 1024 * 1024;
14
14
  const MAX_INSTRUCTIONS_BYTES = 96 * 1024;
15
15
  const MAX_MEMORY_BYTES = 64 * 1024;
16
16
  const AUTHORING_ONLY_ROOTS = new Set([".claude"]);
@@ -114,10 +114,11 @@ export const collectAbilityFiles = async (root) => {
114
114
  if (info.isDirectory())
115
115
  await visit(absolute);
116
116
  else if (info.isFile()) {
117
- if (info.size > MAX_FILE_BYTES)
117
+ if (info.size > AGENT_ABILITY_MAX_FILE_BYTES) {
118
118
  throw new Error(`ability_file_size_exceeded:${relative(root, absolute)}`);
119
+ }
119
120
  totalBytes += info.size;
120
- if (totalBytes > MAX_TOTAL_BYTES)
121
+ if (totalBytes > AGENT_ABILITY_MAX_TOTAL_BYTES)
121
122
  throw new Error("ability_total_size_exceeded");
122
123
  if (files.length >= MAX_FILES)
123
124
  throw new Error("ability_file_count_exceeded");
@@ -251,7 +252,7 @@ export const validateAbilityDependencyDeclarations = (manifest, lock) => {
251
252
  throw new Error("ability_lock_has_undeclared_dependency");
252
253
  }
253
254
  };
254
- const scan = (files, manifest) => {
255
+ export const scanAbilityFiles = (files, manifest) => {
255
256
  const findings = [];
256
257
  const secrets = [
257
258
  /-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----/u,
@@ -259,7 +260,9 @@ const scan = (files, manifest) => {
259
260
  /\bAK(?:ID|IA)[A-Z0-9]{12,}\b/u,
260
261
  ];
261
262
  const executable = new Set([".exe", ".dll", ".dylib", ".so", ".bin", ".class", ".jar"]);
262
- const allowedBinaryDocuments = new Set([".pdf", ".png", ".jpg", ".jpeg", ".gif", ".webp", ".xlsx", ".xls", ".docx", ".pptx"]);
263
+ const allowedBinaryDocuments = new Set([
264
+ ".pdf", ".png", ".jpg", ".jpeg", ".gif", ".webp", ".xlsx", ".xls", ".docx", ".pptx", ".ttf", ".otf",
265
+ ]);
263
266
  for (const file of files) {
264
267
  let text;
265
268
  try {
@@ -382,7 +385,7 @@ export async function resolveAbilityRelease(agentsRoot, command) {
382
385
  }
383
386
  const externalPaths = await verifyExternalDependencies(cacheRoot, resolved.root, lock);
384
387
  files = await collectAbilityFiles(resolved.root);
385
- const security = scan(files, manifest);
388
+ const security = scanAbilityFiles(files, manifest);
386
389
  const kinds = new Set(lock.assets.map((asset) => asset.kind));
387
390
  const memory = [];
388
391
  for (const [layer, mounts] of [["release-policy", manifest.spec.memory.policy], ["release-seed", manifest.spec.memory.seeds]]) {
@@ -32,9 +32,6 @@ export function createAgentAbilityRuntime(agentsRoot, options = {}) {
32
32
  else if (type === "agent:ability:resolve") {
33
33
  result = await controller.handle(input);
34
34
  }
35
- else if (options.isAdmissionClosed?.() === true) {
36
- result = { ok: false, error: "storage_migration_quiescing" };
37
- }
38
35
  else {
39
36
  const parsed = AbilityApplyCommandSchema.safeParse(input);
40
37
  if (!parsed.success)
@@ -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;
@@ -102,14 +68,6 @@ function codexHomeModeEnv(env) {
102
68
  return "private";
103
69
  throw new ConfigError("CREW_CODEX_HOME_MODE must be shared or private");
104
70
  }
105
- function codexCollaborationToolsEnv(env) {
106
- const value = (env.CREW_CODEX_COLLABORATION_TOOLS ?? "").trim().toLowerCase();
107
- if (value === "" || value === "0" || value === "false" || value === "off")
108
- return false;
109
- if (value === "1" || value === "true" || value === "on")
110
- return true;
111
- throw new ConfigError("CREW_CODEX_COLLABORATION_TOOLS must be one of 1/true/on/0/false/off");
112
- }
113
71
  export function loadConfig(env = process.env) {
114
72
  const serverUrl = (env.CREW_SERVER_URL ?? DEFAULT_SERVER_URL).replace(/\/+$/, "");
115
73
  const machineToken = env.CREW_MACHINE_TOKEN ?? "";
@@ -141,7 +99,6 @@ export function loadConfig(env = process.env) {
141
99
  cliPath: env.CREW_CLI_PATH ?? defaultCliPath(),
142
100
  runtimeBin: env.CREW_RUNTIME ?? "claude",
143
101
  codexHomeMode: codexHomeModeEnv(env),
144
- codexCollaborationTools: codexCollaborationToolsEnv(env),
145
102
  dangerous: env.CREW_RUNTIME_SAFE !== "1", // 默认开启 (headless agent 在自有 workspace 内运行)
146
103
  resume: env.CREW_RESUME !== "off" && env.CREW_RESUME !== "0", // 默认开启;一键回退现状用 CREW_RESUME=off
147
104
  resumeWarmMs: env.CREW_RESUME_WARM_MS != null ? Number(env.CREW_RESUME_WARM_MS) : 3_600_000, // 默认 1h
@@ -151,7 +108,6 @@ export function loadConfig(env = process.env) {
151
108
  sessionMaxTurns: env.CREW_SESSION_MAX_TURNS != null ? Number(env.CREW_SESSION_MAX_TURNS) : 30,
152
109
  productName: env.CREW_PRODUCT_NAME ?? "nowwork",
153
110
  agentMemory: loadAgentMemoryConfig(env),
154
- localMemoryV2Handles: localMemoryV2Handles(env, serverUrl),
155
111
  executionLimits,
156
112
  };
157
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;
@@ -775,7 +775,7 @@ export async function runExecution(config, input, dependencies) {
775
775
  const baseSystemPromptBudget = spec.agent.abilityRelease === undefined
776
776
  ? systemPromptBudget
777
777
  : Math.max(0, Math.min(systemPromptBudget, Math.max(32 * 1024, systemPromptBudget - 168 * 1024)));
778
- const systemPromptWithLocalFacts = withLocalExecutionFacts(spec.instructions.systemPrompt, baseSystemPromptBudget, { collaborationTools: config.codexCollaborationTools && spec.runtime.name === "codex" });
778
+ const systemPromptWithLocalFacts = withLocalExecutionFacts(spec.instructions.systemPrompt, baseSystemPromptBudget);
779
779
  const boundedSystemPrompt = (context) => appendAgentMemoryContext(typeof systemPromptWithLocalFacts === "string"
780
780
  ? systemPromptWithLocalFacts
781
781
  : systemPromptWithLocalFacts(context), recalledMemory, baseSystemPromptBudget);
@@ -801,7 +801,6 @@ export async function runExecution(config, input, dependencies) {
801
801
  name: spec.runtime.name,
802
802
  ...(spec.runtime.model === undefined ? {} : { model: spec.runtime.model }),
803
803
  ...(spec.runtime.reasoning === undefined ? {} : { reasoning: spec.runtime.reasoning }),
804
- codexCollaborationTools: config.codexCollaborationTools,
805
804
  },
806
805
  effectivePermission: permission,
807
806
  captureFinal: spec.reporting.captureFinal,
@@ -827,7 +826,6 @@ export async function runExecution(config, input, dependencies) {
827
826
  softTokens: config.sessionSoftTokens,
828
827
  maxTurns: config.sessionMaxTurns,
829
828
  },
830
- localMemoryV2Enabled: config.localMemoryV2Handles?.has(spec.agent.handle) === true,
831
829
  };
832
830
  const result = await Promise.race([
833
831
  execute(localInput, callbacks, localDependencies),
@@ -4,7 +4,7 @@
4
4
  * server 提示词是权威部分,绝不裁剪;项目工作区提示词是绑定执行的强制部分,放不下就直接失败;
5
5
  * 只有本机事实(CREW_HOME / work-log / MEMORY 注入)是可选增强,按整段丢弃或按字节截断。
6
6
  */
7
- import { buildCollaborationToolsPrompt, buildRuntimeWorkspacePrompt, capMemoryForInject, capWorkLogForInject, } from "./prompt.js";
7
+ import { buildRuntimeWorkspacePrompt, capMemoryForInject, capWorkLogForInject, } from "./prompt.js";
8
8
  export function truncateUtf8(value, maxBytes) {
9
9
  if (maxBytes <= 0)
10
10
  return "";
@@ -30,11 +30,10 @@ function takeCompletePromptSections(sections, maxBytes) {
30
30
  }, { value: "", bytes: 0, full: true }).value;
31
31
  }
32
32
  /** v1 server instructions opt in to daemon-owned paths and bounded local workspace context. */
33
- export function withLocalExecutionFacts(serverPrompt, maxBytes, options = {}) {
33
+ export function withLocalExecutionFacts(serverPrompt, maxBytes) {
34
34
  return ({ workspace, resuming, projectContext }) => {
35
35
  const memory = capMemoryForInject(workspace.memory);
36
36
  const workLog = resuming ? "" : capWorkLogForInject(workspace.workLog);
37
- const collaborationPrompt = options.collaborationTools === true ? buildCollaborationToolsPrompt() : "";
38
37
  const mandatoryWorkspacePrompt = buildRuntimeWorkspacePrompt(projectContext);
39
38
  const localFactSections = [`\n\n## Local execution facts
40
39
  - $CREW_HOME: ${workspace.dir}
@@ -42,21 +41,17 @@ export function withLocalExecutionFacts(serverPrompt, maxBytes, options = {}) {
42
41
  ...(memory ? [`\n\n## Injected MEMORY.md (bounded local context)\n${memory}`] : []),
43
42
  ...(workLog ? [`\n\n## Injected work-log (bounded local context)\n${workLog}`] : []),
44
43
  ];
45
- const serverPromptBytes = Buffer.byteLength(serverPrompt, "utf8");
46
- const collaborationBytes = Buffer.byteLength(collaborationPrompt, "utf8");
47
- const mandatoryBytes = Buffer.byteLength(mandatoryWorkspacePrompt, "utf8");
48
- const requiredBytes = serverPromptBytes + collaborationBytes + mandatoryBytes;
49
- // 必需预算与 projectContext 无关:协作段落永远参与;无协作时与旧实现逐字节等价。
50
- if (collaborationPrompt !== "" && requiredBytes > maxBytes) {
51
- throw new Error("collaboration_tools_prompt_budget_exceeded");
52
- }
44
+ const localFacts = localFactSections.join("");
53
45
  if (mandatoryWorkspacePrompt !== "") {
46
+ const serverPromptBytes = Buffer.byteLength(serverPrompt, "utf8");
47
+ const mandatoryBytes = Buffer.byteLength(mandatoryWorkspacePrompt, "utf8");
48
+ const requiredBytes = serverPromptBytes + mandatoryBytes;
54
49
  if (requiredBytes > maxBytes)
55
50
  throw new Error("project_workspace_prompt_budget_exceeded");
56
51
  const optionalFacts = takeCompletePromptSections(localFactSections, maxBytes - requiredBytes);
57
- return `${serverPrompt}${collaborationPrompt}${optionalFacts}${mandatoryWorkspacePrompt}`;
52
+ return `${serverPrompt}${optionalFacts}${mandatoryWorkspacePrompt}`;
58
53
  }
59
- const remaining = maxBytes - requiredBytes;
60
- return `${serverPrompt}${collaborationPrompt}${truncateUtf8(localFactSections.join(""), remaining)}`;
54
+ const remaining = maxBytes - Buffer.byteLength(serverPrompt, "utf8");
55
+ return `${serverPrompt}${truncateUtf8(localFacts, remaining)}`;
61
56
  };
62
57
  }
@@ -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)) {
@@ -78,7 +77,6 @@ const RUNTIME_RUNNING_CALLBACK_TIMEOUT_MS = 30_000;
78
77
  const MEMORY_PRUNE_DIAGNOSTICS_TIMEOUT_MS = 2_000;
79
78
  const LOCAL_MEMORY_DIAGNOSTICS_TIMEOUT_MS = 250;
80
79
  const CLAUDE_INSTRUCTION_WARNING = "claude_additional_directory_instructions_unverified";
81
- const CODEX_COLLABORATION_EXEC_WARNING = "codex_collaboration_disabled_on_exec";
82
80
  const ENV_KEY_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
83
81
  const RESERVED_ENV = new Set([
84
82
  "PATH",
@@ -279,7 +277,6 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
279
277
  }, "workspace_prepare", error);
280
278
  throw error;
281
279
  }
282
- const localMemoryV2Initialized = await initializeLocalMemoryV2ForWorkspace(workspace, input.localMemoryV2Enabled === true);
283
280
  let materialized = null;
284
281
  let knownAttachmentDirectory = null;
285
282
  const startupReservationState = { current: null };
@@ -289,7 +286,6 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
289
286
  let memoryPruneFailurePhase = "diagnostics_before";
290
287
  let memoryPruneBeforeSnapshot = null;
291
288
  let memoryPruneSharedWriteKey = null;
292
- let localMemoryResultPath = null;
293
289
  let executionWorkspace = workspace;
294
290
  let localMemoryTelemetry = null;
295
291
  const inheritedEnv = { ...process.env };
@@ -375,6 +371,7 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
375
371
  nearBudget,
376
372
  ...(input.projectContext === undefined ? {} : { projectContext: input.projectContext }),
377
373
  };
374
+ const systemPrompt = resolvePrompt(input.systemPrompt, promptContext);
378
375
  if (input.attachments && input.attachments.length > 0) {
379
376
  if (dependencies.cancellation?.isRequested())
380
377
  throw new RuntimeCancelledError();
@@ -408,29 +405,6 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
408
405
  const attachmentPlan = routeRuntimeAttachments(runtime.name, materialized?.attachments ?? []);
409
406
  const wakePrompt = `${resolvePrompt(input.wakePrompt, promptContext)}${attachmentPlan.promptSuffix}`;
410
407
  memoryPruneTraceId = parseMemoryPruneTraceId(wakePrompt);
411
- if (input.localMemoryV2Enabled === true && memoryPruneTraceId !== null
412
- && isLocalMemoryV2Eligible({
413
- enabledForHandle: true,
414
- agentHomePreexisted: workspace.agentHomePreexisted === true,
415
- memorySeedCreated: executionWorkspace.memorySeedCreated === true && (localMemoryV2Initialized || workspace.localMemoryV2Initialized !== false),
416
- localMemoryV2Initialized,
417
- memoryPrune: true,
418
- })) {
419
- localMemoryResultPath = makeLocalMemoryResultPath(executionWorkspace.runDir, input.executionId);
420
- }
421
- const baseSystemPrompt = resolvePrompt(input.systemPrompt, promptContext);
422
- const structuredMemoryInstructions = localMemoryResultPath === null
423
- ? null
424
- : renderLocalMemoryResultInstructions(localMemoryResultPath);
425
- const structuredPromptFits = structuredMemoryInstructions === null
426
- || input.maxSystemPromptBytes === undefined
427
- || Buffer.byteLength(baseSystemPrompt, "utf8") + Buffer.byteLength(structuredMemoryInstructions, "utf8")
428
- <= input.maxSystemPromptBytes;
429
- if (!structuredPromptFits)
430
- localMemoryResultPath = null;
431
- const systemPrompt = localMemoryResultPath === null
432
- ? baseSystemPrompt
433
- : `${baseSystemPrompt}${structuredMemoryInstructions ?? ""}`;
434
408
  try {
435
409
  localMemoryTelemetry = createLocalMemoryTelemetry({
436
410
  executionId: input.executionId,
@@ -539,13 +513,7 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
539
513
  ability_release_id: input.abilityRelease?.releaseId,
540
514
  ability_root_commit: input.abilityRelease?.rootCommit,
541
515
  });
542
- const warn = dependencies.warnRuntime ?? warnRuntimeLocally;
543
- const launchRuntime = dependencies.launchRuntime ?? (async (launchReq) => {
544
- if (launchReq.runtime === "codex" && launchReq.codexCollaborationTools === true) {
545
- warn(CODEX_COLLABORATION_EXEC_WARNING);
546
- }
547
- return launchLegacyRuntime(launchReq);
548
- });
516
+ const launchRuntime = dependencies.launchRuntime ?? launchLegacyRuntime;
549
517
  if (dependencies.cancellation?.isRequested())
550
518
  throw new RuntimeCancelledError();
551
519
  let activeProjectSkillRuntimeRoot;
@@ -614,7 +582,7 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
614
582
  }
615
583
  else {
616
584
  runtimeEnv = withoutClaudeInstructionFlag(childEnv);
617
- warn(CLAUDE_INSTRUCTION_WARNING);
585
+ (dependencies.warnRuntime ?? warnRuntimeLocally)(CLAUDE_INSTRUCTION_WARNING);
618
586
  callbacks.onConsole?.({
619
587
  stream: "system",
620
588
  text: `[runtime-warning] ${CLAUDE_INSTRUCTION_WARNING}`,
@@ -638,9 +606,6 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
638
606
  ...(launchReasoning === undefined ? {} : { reasoning: launchReasoning }),
639
607
  ...(launchSessionId === null ? {} : { sessionId: launchSessionId }),
640
608
  resume: resuming,
641
- ...(runtime.codexCollaborationTools === undefined
642
- ? {}
643
- : { codexCollaborationTools: runtime.codexCollaborationTools }),
644
609
  ...(attachmentPlan.nativeImagePaths.length > 0
645
610
  ? { imagePaths: attachmentPlan.nativeImagePaths }
646
611
  : {}),
@@ -927,24 +892,6 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
927
892
  const { exitCode, spawnError, terminationSignal } = runtimeExit;
928
893
  localMemoryTelemetry?.markRuntimeExited(exitCode);
929
894
  memoryPruneRuntimeExitCode = exitCode;
930
- if (localMemoryResultPath !== null) {
931
- const committed = await commitLocalMemoryResultAfterExit({
932
- resultPath: localMemoryResultPath,
933
- agentDir: executionWorkspace.dir,
934
- executionId: input.executionId,
935
- pruneTraceId: memoryPruneTraceId,
936
- exitCode,
937
- });
938
- if (committed.status === "failed") {
939
- dslog("local_memory.commit_failed", "结构化本地记忆候选未落地,保留结果文件", {
940
- level: "WARN",
941
- execution_id: input.executionId,
942
- agent_handle: input.handle,
943
- error_code: committed.error,
944
- result_path: localMemoryResultPath,
945
- });
946
- }
947
- }
948
895
  const errorTail = [
949
896
  stderrTail.trim(),
950
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({