@nowcrew/daemon 0.6.67 → 0.6.68

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 (61) 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-protocol.js +1 -0
  10. package/dist/execution-runner.js +8 -5
  11. package/dist/local-execution-prompt.js +11 -15
  12. package/dist/local-executor.js +53 -93
  13. package/dist/machine-info.js +4 -3
  14. package/dist/main.js +0 -83
  15. package/dist/managed-service-lifecycle.js +2 -47
  16. package/dist/managed-service-startup.js +0 -2
  17. package/dist/profile-layout.js +4 -10
  18. package/dist/project-skills/controller.js +0 -2
  19. package/dist/prompt.js +1 -19
  20. package/dist/provider-env.js +21 -3
  21. package/dist/runner.js +2 -4
  22. package/dist/runtimes/codex-app-server-runner.js +23 -55
  23. package/dist/runtimes/codex-contract.js +5 -20
  24. package/dist/runtimes/codex-deepseek-config.js +17 -7
  25. package/dist/runtimes/codex.js +3 -4
  26. package/dist/self-training-workspace.js +15 -0
  27. package/dist/serve.js +3 -68
  28. package/dist/supervised-runtime.js +1 -3
  29. package/dist/workspace.js +14 -34
  30. package/package.json +1 -1
  31. package/dist/local-memory/index-budget.js +0 -74
  32. package/dist/local-memory/pending.js +0 -111
  33. package/dist/local-memory/runtime.js +0 -87
  34. package/dist/local-memory/types.js +0 -1
  35. package/dist/local-memory/write.js +0 -448
  36. package/dist/runtimes/codex-command-safety.js +0 -75
  37. package/dist/runtimes/codex-turn-retry.js +0 -69
  38. package/dist/storage-config.js +0 -175
  39. package/dist/storage-control.js +0 -193
  40. package/dist/storage-copy.js +0 -609
  41. package/dist/storage-credential-guard.js +0 -21
  42. package/dist/storage-doctor.js +0 -228
  43. package/dist/storage-identity.js +0 -29
  44. package/dist/storage-layout.js +0 -143
  45. package/dist/storage-manifest.js +0 -855
  46. package/dist/storage-migration-controller.js +0 -417
  47. package/dist/storage-migration-manifest.js +0 -105
  48. package/dist/storage-migration-policy.js +0 -100
  49. package/dist/storage-runtime-controller.js +0 -339
  50. package/dist/storage-runtime-readiness.js +0 -184
  51. package/dist/storage-runtime.js +0 -39
  52. package/dist/storage-safe-fs.js +0 -302
  53. package/dist/storage-serve-adapter.js +0 -65
  54. package/dist/storage-status-delivery.js +0 -209
  55. package/dist/storage-status-replay.js +0 -125
  56. package/dist/storage-target-confirmation.js +0 -129
  57. package/dist/storage-worker-command.js +0 -27
  58. package/dist/storage-worker-executor.js +0 -384
  59. package/dist/storage-worker-handoff.js +0 -151
  60. package/dist/storage-worker-operation.js +0 -284
  61. package/dist/storage-worker-status.js +0 -232
@@ -1,6 +1,7 @@
1
1
  import { createInterface } from "node:readline";
2
2
  import { readFile, rm, writeFile } from "node:fs/promises";
3
3
  import { delimiter, join } from "node:path";
4
+ import { cleanupSelfTrainingRunDir } from "./self-training-workspace.js";
4
5
  import { prepareWorkspace, rotateAgentSession, safeKey, } from "./workspace.js";
5
6
  import { CLAUDE_ADDITIONAL_DIRECTORY_INSTRUCTIONS_ENV, isClaudeAdditionalDirectoryInstructionsSupported, probeClaudeVersion, spawnClaude, } from "./runtimes/claude.js";
6
7
  import { spawnCodex } from "./runtimes/codex.js";
@@ -8,7 +9,7 @@ import { DEEPSEEK_CODEX_MODEL, DEEPSEEK_CODEX_REASONING_LEVELS, materializeDeepS
8
9
  import { materializeDefaultCodexHome } from "./runtimes/codex-home.js";
9
10
  import { materializePrivateCodexHome } from "./codex-home-launch.js";
10
11
  import { spawnKimi, KIMI_EFFORT_LEVELS } from "./runtimes/kimi.js";
11
- import { applyDeepSeekHarnessMachineEnv, applyProviderEnv, providerFingerprint, } from "./provider-env.js";
12
+ import { applyDeepSeekHarnessMachineEnv, applyProviderEnv, providerFingerprint, sanitizeEnvVars, } from "./provider-env.js";
12
13
  import { augmentedPath } from "./runtime-path.js";
13
14
  import { extractFinalText, extractRunMeta, normalizeEvent, parseLine, } from "./normalize.js";
14
15
  import { readSession, writeSession, pickResumeId, isNearBudget } from "./session.js";
@@ -31,7 +32,6 @@ import { diffMemoryPruneNotes, evaluateMemoryPrunePostcondition, inspectMemoryPr
31
32
  import { createLocalMemoryTelemetry, logLocalMemoryContextPrepareFailure, logLocalMemoryDiagnosticsFailure, } from "./local-memory-telemetry.js";
32
33
  import { CodexStartupStageParser } from "./codex-startup-stage.js";
33
34
  import { formatRuntimeStartupError } from "./runtime-startup-error.js";
34
- import { commitLocalMemoryResultAfterExit, initializeLocalMemoryV2ForWorkspace, isLocalMemoryV2Eligible, localMemoryResultPath as makeLocalMemoryResultPath, renderLocalMemoryResultInstructions, } from "./local-memory/runtime.js";
35
35
  function memoryPruneFileFactFields(fileFacts) {
36
36
  const fields = {};
37
37
  for (const [label, fact] of Object.entries(fileFacts)) {
@@ -78,7 +78,6 @@ const RUNTIME_RUNNING_CALLBACK_TIMEOUT_MS = 30_000;
78
78
  const MEMORY_PRUNE_DIAGNOSTICS_TIMEOUT_MS = 2_000;
79
79
  const LOCAL_MEMORY_DIAGNOSTICS_TIMEOUT_MS = 250;
80
80
  const CLAUDE_INSTRUCTION_WARNING = "claude_additional_directory_instructions_unverified";
81
- const CODEX_COLLABORATION_EXEC_WARNING = "codex_collaboration_disabled_on_exec";
82
81
  const ENV_KEY_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
83
82
  const RESERVED_ENV = new Set([
84
83
  "PATH",
@@ -98,14 +97,6 @@ function withoutClaudeInstructionFlag(env) {
98
97
  const warnRuntimeLocally = (code) => {
99
98
  process.stderr.write(`[runtime-warning] ${code}\n`);
100
99
  };
101
- export function sanitizeEnvVars(raw) {
102
- if (!raw)
103
- return {};
104
- return Object.fromEntries(Object.entries(raw).filter(([key, value]) => typeof value === "string"
105
- && ENV_KEY_RE.test(key)
106
- && !key.toUpperCase().startsWith("CREW_")
107
- && !RESERVED_ENV.has(key.toUpperCase())));
108
- }
109
100
  export function awaitExit(child) {
110
101
  return new Promise((resolve) => {
111
102
  let settled = false;
@@ -156,6 +147,7 @@ async function launchLegacyRuntime(request) {
156
147
  effectivePermission: request.effectivePermission,
157
148
  ...(request.model === undefined ? {} : { model: request.model }),
158
149
  ...(request.reasoning === undefined ? {} : { reasoning: request.reasoning }),
150
+ ...(request.networkAccess === undefined ? {} : { networkAccess: request.networkAccess }),
159
151
  };
160
152
  if (request.runtime === "claude") {
161
153
  const additionalDirectories = request.claudeAdditionalDirectories
@@ -245,6 +237,7 @@ export async function executeLocal(input, callbacks = {}, dependencies = {}) {
245
237
  async function executeLocalUnlocked(input, callbacks, dependencies) {
246
238
  const providerConfig = input.launch.providerConfig ?? {};
247
239
  const { runtime } = input;
240
+ const isolatedMaintenance = input.maintenancePurpose === "self_training";
248
241
  const isDeepSeekCodex = runtime.name === "codex" && providerConfig.provider === "deepseek";
249
242
  const currentModel = isDeepSeekCodex ? DEEPSEEK_CODEX_MODEL : runtime.model ?? null;
250
243
  const launchModel = isDeepSeekCodex ? DEEPSEEK_CODEX_MODEL : runtime.model;
@@ -269,6 +262,7 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
269
262
  ? {}
270
263
  : { sessionContextFingerprint: input.sessionContextFingerprint }),
271
264
  ...(input.launch.description ? { description: input.launch.description } : {}),
265
+ ...(isolatedMaintenance ? { skipLocalMemory: true } : {}),
272
266
  }), dependencies.cancellation);
273
267
  }
274
268
  catch (error) {
@@ -279,7 +273,6 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
279
273
  }, "workspace_prepare", error);
280
274
  throw error;
281
275
  }
282
- const localMemoryV2Initialized = await initializeLocalMemoryV2ForWorkspace(workspace, input.localMemoryV2Enabled === true);
283
276
  let materialized = null;
284
277
  let knownAttachmentDirectory = null;
285
278
  const startupReservationState = { current: null };
@@ -289,7 +282,6 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
289
282
  let memoryPruneFailurePhase = "diagnostics_before";
290
283
  let memoryPruneBeforeSnapshot = null;
291
284
  let memoryPruneSharedWriteKey = null;
292
- let localMemoryResultPath = null;
293
285
  let executionWorkspace = workspace;
294
286
  let localMemoryTelemetry = null;
295
287
  const inheritedEnv = { ...process.env };
@@ -299,12 +291,23 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
299
291
  }
300
292
  const sourceCodexHome = inheritedEnv.CODEX_HOME
301
293
  ?? (inheritedEnv.HOME === undefined ? undefined : join(inheritedEnv.HOME, ".codex"));
294
+ let materializedDeepSeekHome;
302
295
  try {
303
296
  if (isDeepSeekCodex && !providerConfig.providerApiKey) {
304
297
  throw new Error("DeepSeek API key is not configured for this Agent");
305
298
  }
306
299
  if (isDeepSeekCodex) {
307
- await awaitWithCancellation((dependencies.materializeDeepSeekCodexHome ?? materializeDeepSeekCodexHome)(workspace.homeDir), dependencies.cancellation);
300
+ const providerBaseUrl = providerConfig.providerBaseUrl
301
+ ? new URL(providerConfig.providerBaseUrl, input.launch.serverUrl).toString()
302
+ : undefined;
303
+ const materialize = dependencies.materializeDeepSeekCodexHome ?? materializeDeepSeekCodexHome;
304
+ const materializeOptions = {
305
+ ...(providerBaseUrl === undefined ? {} : { baseUrl: providerBaseUrl }),
306
+ ...(isolatedMaintenance ? { selfTraining: true } : {}),
307
+ };
308
+ materializedDeepSeekHome = await awaitWithCancellation(providerBaseUrl === undefined && !isolatedMaintenance
309
+ ? materialize(workspace.homeDir)
310
+ : materialize(workspace.homeDir, materializeOptions), dependencies.cancellation);
308
311
  }
309
312
  const supportsNativeResume = runtime.name === "claude"
310
313
  || (dependencies.launchRuntime !== undefined && runtimeCapability(runtime.name).nativeResume);
@@ -375,6 +378,7 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
375
378
  nearBudget,
376
379
  ...(input.projectContext === undefined ? {} : { projectContext: input.projectContext }),
377
380
  };
381
+ const systemPrompt = resolvePrompt(input.systemPrompt, promptContext);
378
382
  if (input.attachments && input.attachments.length > 0) {
379
383
  if (dependencies.cancellation?.isRequested())
380
384
  throw new RuntimeCancelledError();
@@ -408,52 +412,30 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
408
412
  const attachmentPlan = routeRuntimeAttachments(runtime.name, materialized?.attachments ?? []);
409
413
  const wakePrompt = `${resolvePrompt(input.wakePrompt, promptContext)}${attachmentPlan.promptSuffix}`;
410
414
  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
- try {
435
- localMemoryTelemetry = createLocalMemoryTelemetry({
436
- executionId: input.executionId,
437
- agentHandle: input.handle,
438
- ...(input.taskKey === undefined ? {} : { taskKey: input.taskKey }),
439
- runtime: runtime.name,
440
- stableProtocolRuntime: dependencies.launchRuntime !== undefined,
441
- agentDir: executionWorkspace.dir,
442
- runDir: executionWorkspace.runDir,
443
- isMemoryPrune: memoryPruneTraceId !== null,
444
- diagnosticsTimeoutMs: dependencies.localMemoryDiagnosticsTimeoutMs
445
- ?? LOCAL_MEMORY_DIAGNOSTICS_TIMEOUT_MS,
446
- });
447
- await localMemoryTelemetry.captureBefore();
448
- }
449
- catch (error) {
450
- localMemoryTelemetry = null;
451
- logLocalMemoryDiagnosticsFailure({
452
- executionId: input.executionId,
453
- agentHandle: input.handle,
454
- ...(input.taskKey === undefined ? {} : { taskKey: input.taskKey }),
455
- }, "initialize", error);
456
- }
415
+ if (!isolatedMaintenance)
416
+ try {
417
+ localMemoryTelemetry = createLocalMemoryTelemetry({
418
+ executionId: input.executionId,
419
+ agentHandle: input.handle,
420
+ ...(input.taskKey === undefined ? {} : { taskKey: input.taskKey }),
421
+ runtime: runtime.name,
422
+ stableProtocolRuntime: dependencies.launchRuntime !== undefined,
423
+ agentDir: executionWorkspace.dir,
424
+ runDir: executionWorkspace.runDir,
425
+ isMemoryPrune: memoryPruneTraceId !== null,
426
+ diagnosticsTimeoutMs: dependencies.localMemoryDiagnosticsTimeoutMs
427
+ ?? LOCAL_MEMORY_DIAGNOSTICS_TIMEOUT_MS,
428
+ });
429
+ await localMemoryTelemetry.captureBefore();
430
+ }
431
+ catch (error) {
432
+ localMemoryTelemetry = null;
433
+ logLocalMemoryDiagnosticsFailure({
434
+ executionId: input.executionId,
435
+ agentHandle: input.handle,
436
+ ...(input.taskKey === undefined ? {} : { taskKey: input.taskKey }),
437
+ }, "initialize", error);
438
+ }
457
439
  if (memoryPruneTraceId !== null) {
458
440
  memoryPruneSharedWriteKey = JSON.stringify([input.launch.agentsRoot, input.handle]);
459
441
  const activePruneCount = (activeMemoryPrunes.get(memoryPruneSharedWriteKey) ?? 0) + 1;
@@ -487,6 +469,7 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
487
469
  }
488
470
  }
489
471
  memoryPruneFailurePhase = "runtime_prepare";
472
+ const crewHome = isolatedMaintenance ? executionWorkspace.runDir : workspace.dir;
490
473
  const baseEnv = {
491
474
  ...inheritedEnv,
492
475
  ...sanitizeEnvVars(providerConfig.envVars),
@@ -495,10 +478,10 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
495
478
  CREW_SERVER_URL: input.launch.serverUrl,
496
479
  CREW_TOKEN: input.launch.token,
497
480
  CREW_CHANNEL: input.channelId,
498
- CREW_HOME: workspace.dir,
481
+ CREW_HOME: crewHome,
499
482
  // Expose only the training root; ability-specific workspace layout stays
500
483
  // owned by the training package rather than the NowWork daemon.
501
- QA_RUNTIME_ROOT: join(workspace.dir, "training"),
484
+ QA_RUNTIME_ROOT: join(crewHome, "training"),
502
485
  CREW_TASK_DIR: executionWorkspace.runDir,
503
486
  CREW_TASK_LOG: executionWorkspace.workLogPath,
504
487
  ...(input.wakeMessageId ? { CREW_WAKE_MESSAGE_ID: input.wakeMessageId } : {}),
@@ -514,7 +497,7 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
514
497
  };
515
498
  const providerEnv = applyProviderEnv(baseEnv, runtime.name, input.launch.codexHomeMode === undefined
516
499
  ? providerConfig
517
- : { ...providerConfig, codexHomeMode: input.launch.codexHomeMode }, workspace.homeDir);
500
+ : { ...providerConfig, codexHomeMode: input.launch.codexHomeMode }, workspace.homeDir, materializedDeepSeekHome);
518
501
  const childEnv = runtime.name === "deepseek-harness"
519
502
  ? applyDeepSeekHarnessMachineEnv(providerEnv, inheritedEnv)
520
503
  : providerEnv;
@@ -525,7 +508,7 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
525
508
  qa_runtime_root: childEnv.QA_RUNTIME_ROOT ?? null,
526
509
  qa_runtime_root_present: childEnv.QA_RUNTIME_ROOT !== undefined,
527
510
  inherited_qa_runtime_root_present: inheritedEnv.QA_RUNTIME_ROOT !== undefined,
528
- training_root: join(workspace.dir, "training"),
511
+ training_root: join(crewHome, "training"),
529
512
  project_context_present: input.projectContext !== undefined,
530
513
  project_ids: JSON.stringify(input.projectContext === undefined
531
514
  ? []
@@ -539,13 +522,7 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
539
522
  ability_release_id: input.abilityRelease?.releaseId,
540
523
  ability_root_commit: input.abilityRelease?.rootCommit,
541
524
  });
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
- });
525
+ const launchRuntime = dependencies.launchRuntime ?? launchLegacyRuntime;
549
526
  if (dependencies.cancellation?.isRequested())
550
527
  throw new RuntimeCancelledError();
551
528
  let activeProjectSkillRuntimeRoot;
@@ -614,7 +591,7 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
614
591
  }
615
592
  else {
616
593
  runtimeEnv = withoutClaudeInstructionFlag(childEnv);
617
- warn(CLAUDE_INSTRUCTION_WARNING);
594
+ (dependencies.warnRuntime ?? warnRuntimeLocally)(CLAUDE_INSTRUCTION_WARNING);
618
595
  callbacks.onConsole?.({
619
596
  stream: "system",
620
597
  text: `[runtime-warning] ${CLAUDE_INSTRUCTION_WARNING}`,
@@ -638,9 +615,6 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
638
615
  ...(launchReasoning === undefined ? {} : { reasoning: launchReasoning }),
639
616
  ...(launchSessionId === null ? {} : { sessionId: launchSessionId }),
640
617
  resume: resuming,
641
- ...(runtime.codexCollaborationTools === undefined
642
- ? {}
643
- : { codexCollaborationTools: runtime.codexCollaborationTools }),
644
618
  ...(attachmentPlan.nativeImagePaths.length > 0
645
619
  ? { imagePaths: attachmentPlan.nativeImagePaths }
646
620
  : {}),
@@ -654,6 +628,7 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
654
628
  workspaceRoots: Object.freeze(input.projectContext.secondary.map((project) => project.root)),
655
629
  projectContext: input.projectContext,
656
630
  }),
631
+ ...(isolatedMaintenance && runtime.name === "codex" ? { networkAccess: true } : {}),
657
632
  };
658
633
  return launchRequest;
659
634
  };
@@ -927,24 +902,6 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
927
902
  const { exitCode, spawnError, terminationSignal } = runtimeExit;
928
903
  localMemoryTelemetry?.markRuntimeExited(exitCode);
929
904
  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
905
  const errorTail = [
949
906
  stderrTail.trim(),
950
907
  spawnError === undefined
@@ -1118,5 +1075,8 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
1118
1075
  const detail = error instanceof Error ? error.message : String(error);
1119
1076
  process.stderr.write(`[execution] failed to remove prompt ${workspace.systemPromptPath}: ${detail}\n`);
1120
1077
  }
1078
+ if (isolatedMaintenance) {
1079
+ await cleanupSelfTrainingRunDir(join(workspace.dir, "tasks"), executionWorkspace.runDir);
1080
+ }
1121
1081
  }
1122
1082
  }
@@ -24,6 +24,7 @@ export { executableRuntimes } from "./runtime-capabilities.js";
24
24
  const execFileP = promisify(execFile);
25
25
  export const PROJECT_WORKSPACES_CAPABILITY = "project_workspaces_v1";
26
26
  export const TRAINING_PROJECT_DEPENDENCIES_CAPABILITY = "training_project_dependencies_v1";
27
+ export const SELF_TRAINING_EXECUTION_CAPABILITY = "self_training_execution_v1";
27
28
  export const NATIVE_PROJECT_WORKSPACE_READINESS = Object.freeze({
28
29
  resolverReady: true,
29
30
  nativeRuntimeAdaptersReady: true,
@@ -39,6 +40,7 @@ export const DAEMON_CAPABILITIES = Object.freeze([
39
40
  "execution_machine_queue_v1",
40
41
  "execution_agent_memory_policy_v1",
41
42
  "execution_agent_memory_mapping_v1",
43
+ SELF_TRAINING_EXECUTION_CAPABILITY,
42
44
  "project_skills_v1",
43
45
  PROJECT_WORKSPACES_CAPABILITY,
44
46
  TRAINING_PROJECT_DEPENDENCIES_CAPABILITY,
@@ -191,11 +193,10 @@ export async function collectMachineHello(agentsRoot, executionLimits, runtimePl
191
193
  profileName: dependencies.profileName,
192
194
  daemonHome: dependencies.daemonHome ?? daemonHome(),
193
195
  agentsRoot,
194
- ...(dependencies.profileName.match(/^nw-(?:production|in|dev)-([a-z0-9][a-z0-9_-]{0,39})$/u)?.[1] === undefined
196
+ ...(dependencies.profileName.match(/^nw-(?:in|dev)-([a-z0-9][a-z0-9_-]{0,39})$/u)?.[1] === undefined
195
197
  ? {}
196
- : { localInstanceName: dependencies.profileName.match(/^nw-(?:production|in|dev)-([a-z0-9][a-z0-9_-]{0,39})$/u)[1] }),
198
+ : { localInstanceName: dependencies.profileName.match(/^nw-(?:in|dev)-([a-z0-9][a-z0-9_-]{0,39})$/u)[1] }),
197
199
  }),
198
- ...(dependencies.storageIdentity ?? {}),
199
200
  ...(backend.supported ? {
200
201
  executionProtocol: EXECUTION_PROTOCOL,
201
202
  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());
@@ -85,12 +71,6 @@ async function main() {
85
71
  agentsRoot: resolveAgentsRoot(profile.agentsRoot, homedir(), process.platform),
86
72
  nodePath: process.execPath,
87
73
  entryPath: builtDaemonEntry(),
88
- environment: {
89
- ...(process.env.NOWWORK_HOME === undefined ? {} : { NOWWORK_HOME: process.env.NOWWORK_HOME }),
90
- ...(process.env.NOWWORK_ENV_TAG === undefined ? {} : { NOWWORK_ENV_TAG: process.env.NOWWORK_ENV_TAG }),
91
- ...(process.env.NOWWORK_STORAGE_POLICY_PATH === undefined
92
- ? {} : { NOWWORK_STORAGE_POLICY_PATH: process.env.NOWWORK_STORAGE_POLICY_PATH }),
93
- },
94
74
  });
95
75
  }
96
76
  applyProfileToEnv(profile, process.env);
@@ -120,72 +100,9 @@ async function main() {
120
100
  const installationLease = installation === null
121
101
  ? null
122
102
  : await acquireDaemonInstallationLease(installation.npmPrefix);
123
- const storageRuntime = values.profile === undefined
124
- ? null
125
- : await loadStorageRuntime({
126
- profile: values.profile,
127
- serverUrl: config.serverUrl,
128
- userHome: homedir(),
129
- platform: process.platform,
130
- environment: process.env,
131
- });
132
- const migrationRuntime = values.profile === undefined || activeDaemonHome === undefined || storageRuntime !== null
133
- ? { ready: false, reason: "not_legacy_source" }
134
- : await probeStorageMigrationRuntime({
135
- profile: values.profile,
136
- serverUrl: config.serverUrl,
137
- currentDaemonHome: activeDaemonHome,
138
- currentAgentsRoot: config.agentsRoot,
139
- userHome: homedir(),
140
- platform: process.platform,
141
- ...(process.getuid === undefined ? {} : { uid: process.getuid() }),
142
- environment: process.env,
143
- });
144
- const targetConfirmation = storageRuntime?.identity.storageMigrationId === undefined
145
- ? null
146
- : createStorageTargetConfirmationHandler({
147
- backupsRoot: storageRuntime.local.backupsRoot,
148
- identity: storageRuntime.identity,
149
- profile: storageRuntime.installation.profile,
150
- completionTimeoutMs: 180_000,
151
- });
152
- const storageMigration = storageRuntime !== null
153
- ? {
154
- ready: false,
155
- statusOutboxPath: join(storageRuntime.local.stateRoot, "storage-status-outbox.json"),
156
- identity: storageRuntime.identity,
157
- ...(targetConfirmation === null ? {} : { confirm: targetConfirmation.handle }),
158
- ...(storageRuntime.identity.storageMigrationId === undefined ? {} : {
159
- replayStatus: async (send) => {
160
- const migrationId = storageRuntime.identity.storageMigrationId;
161
- const receipt = await readStorageMigrationReceipt(storageRuntime.local.backupsRoot, migrationId);
162
- if (receipt === null)
163
- throw new Error(`storage migration receipt not found: ${migrationId}`);
164
- for (const frame of storageMigrationStatusReplay(receipt, {
165
- migrationId,
166
- environment: storageRuntime.identity.storageEnvironment,
167
- workspaceId: storageRuntime.identity.storageWorkspaceId,
168
- profile: storageRuntime.installation.profile,
169
- }))
170
- send(frame);
171
- },
172
- }),
173
- }
174
- : migrationRuntime.ready
175
- ? {
176
- ready: true,
177
- statusOutboxPath: migrationRuntime.runtime.statusOutboxPath,
178
- identity: migrationRuntime.runtime.identity,
179
- controller: migrationRuntime.runtime.controller,
180
- attach: migrationRuntime.runtime.attach,
181
- bindServerIdentity: migrationRuntime.runtime.bindServerIdentity,
182
- replayStatus: migrationRuntime.runtime.replayStatus,
183
- }
184
- : undefined;
185
103
  await runWithDaemonInstallationLease(installationLease, async () => {
186
104
  const service = serve(config, {
187
105
  ...(values.profile === undefined ? {} : { profileName: values.profile }),
188
- ...(storageMigration === undefined ? {} : { storageMigration }),
189
106
  });
190
107
  await runServeLifecycle(service);
191
108
  });
@@ -1,6 +1,6 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { acquireDaemonInstallationLease } from "./daemon-installation-lease.js";
3
- import { installService, ensureServiceEnabled, readServiceDescriptor, serviceAction, replaceInstalledServiceDescriptor, uninstallService, } from "./computer-service.js";
3
+ import { installService, ensureServiceEnabled, readServiceDescriptor, serviceAction, uninstallService, } from "./computer-service.js";
4
4
  import { ManagedServiceConflictError, ManagedServiceRegistryError, assertRegistryCoversDescriptors, listManagedServiceDescriptorPaths, managedServiceIdentityMatches, managedServiceConflict, normalizeManagedServiceRecord, serviceDescriptorSha256, withManagedServiceRegistryLock, } from "./managed-service-registry.js";
5
5
  function requirePortableManagedSpec(spec) {
6
6
  if ((spec.platform !== "darwin" && spec.platform !== "linux")
@@ -35,17 +35,6 @@ function sameManagedIdentity(left, right) {
35
35
  const { generation: _generation, createdAt: _createdAt, ...expected } = right;
36
36
  return managedServiceIdentityMatches(left, expected);
37
37
  }
38
- function sameManagedResource(left, right) {
39
- const { generation: _leftGeneration, createdAt: _leftCreatedAt, descriptorSha256: _leftHash, ...leftResource } = left;
40
- const { generation: _rightGeneration, createdAt: _rightCreatedAt, descriptorSha256: _rightHash, ...rightResource } = right;
41
- return JSON.stringify(leftResource) === JSON.stringify(rightResource);
42
- }
43
- function descriptorDiffIsStorageEnvironmentOnly(previous, next) {
44
- if (!next.includes("\n <key>EnvironmentVariables</key>\n"))
45
- return false;
46
- const strip = (descriptor) => descriptor.replace(/\n <key>EnvironmentVariables<\/key>\n <dict>\n(?: <key>(?:NOWWORK_HOME|NOWWORK_ENV_TAG|NOWWORK_STORAGE_POLICY_PATH)<\/key><string>[^<]*<\/string>\n)+ <\/dict>\n/u, "\n");
47
- return strip(previous) === strip(next);
48
- }
49
38
  function assertRecordDescribesSpec(input) {
50
39
  requirePortableManagedSpec(input.spec);
51
40
  const { spec, record } = input;
@@ -62,20 +51,8 @@ function assertRecordDescribesSpec(input) {
62
51
  }
63
52
  }
64
53
  export async function installManagedService(input) {
54
+ assertRecordDescribesSpec(input);
65
55
  requirePortableManagedSpec(input.spec);
66
- const requestedDescriptor = input.spec.descriptor;
67
- const expectedDescriptorHash = serviceDescriptorSha256(input.spec.descriptor);
68
- const expectedPath = normalizeManagedServiceRecord({
69
- ...input.record,
70
- descriptorPath: input.spec.descriptorPath,
71
- descriptorSha256: expectedDescriptorHash,
72
- }).descriptorPath;
73
- if (input.record.platform !== input.spec.platform
74
- || input.record.serviceId !== input.spec.id
75
- || input.record.profile !== input.spec.profile
76
- || input.record.descriptorPath !== expectedPath) {
77
- throw new ManagedServiceRegistryError(`Managed service record does not describe '${input.spec.id}'`);
78
- }
79
56
  const acquire = input.acquireInstallationLease ?? acquireDaemonInstallationLease;
80
57
  await withManagedServiceRegistryLock(input.registry ?? {}, async ({ services, replace }) => {
81
58
  const exact = services.find((record) => sameManagedIdentity(record, input.record));
@@ -99,28 +76,6 @@ export async function installManagedService(input) {
99
76
  }
100
77
  return;
101
78
  }
102
- const registered = services.find((record) => record.serviceId === input.record.serviceId
103
- && record.profile === input.record.profile
104
- && record.daemonHome === input.record.daemonHome);
105
- if (registered !== undefined && sameManagedResource(registered, input.record)) {
106
- const current = await readServiceDescriptor(input.spec);
107
- if (current === null || serviceDescriptorSha256(current) !== registered.descriptorSha256) {
108
- throw new ManagedServiceRegistryError(`Installed descriptor for '${input.spec.id}' does not match its registry record`);
109
- }
110
- if (!descriptorDiffIsStorageEnvironmentOnly(current, requestedDescriptor)) {
111
- throw new ManagedServiceRegistryError(`Installed descriptor for '${input.spec.id}' conflicts with the requested identity`);
112
- }
113
- await replaceInstalledServiceDescriptor(input.spec, current, input.runner);
114
- try {
115
- await replace(services.map((record) => record === registered ? input.record : record));
116
- }
117
- catch (error) {
118
- const rollbackSpec = { ...input.spec, descriptor: current };
119
- await replaceInstalledServiceDescriptor(rollbackSpec, requestedDescriptor, input.runner).catch(() => undefined);
120
- throw error;
121
- }
122
- return;
123
- }
124
79
  const conflict = managedServiceConflict(services, input.record);
125
80
  if (conflict !== null) {
126
81
  throw new ManagedServiceConflictError(conflict.resource, input.record, conflict.owner);
@@ -14,7 +14,6 @@ export async function assertManagedServiceStartup(input) {
14
14
  nodePath: input.nodePath,
15
15
  entryPath: input.entryPath,
16
16
  profileHome: input.daemonHome,
17
- ...(input.environment === undefined ? {} : { environment: input.environment }),
18
17
  });
19
18
  const readRegistry = input.readManagedServices
20
19
  ?? (() => readManagedServiceRegistry({ userHome: input.userHome }));
@@ -43,7 +42,6 @@ export async function assertManagedServiceStartup(input) {
43
42
  nodePath: input.nodePath,
44
43
  entryPath: input.entryPath,
45
44
  profileHome: input.daemonHome,
46
- ...(input.environment === undefined ? {} : { environment: input.environment }),
47
45
  legacyServiceId: true,
48
46
  });
49
47
  const legacyDescriptor = await readDescriptor(legacySpec);
@@ -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}`),
@@ -183,8 +183,6 @@ export function createProjectSkillsController(deps) {
183
183
  const parsed = ProjectCommandSchema.safeParse(input);
184
184
  if (!parsed.success)
185
185
  return { ok: false, error: "invalid_project_command" };
186
- if (deps.isAdmissionClosed?.() === true)
187
- return { ok: false, error: "storage_migration_quiescing" };
188
186
  try {
189
187
  assertCurrentEpoch(epochToken);
190
188
  }
package/dist/prompt.js CHANGED
@@ -305,7 +305,7 @@ ${memoryTaskReadRule}
305
305
  - **跨任务协调看实时清单**:用 \`crew task list --mine\` 查你当前所有任务及其状态(谁在排队、谁在评审)。例:你一次只能测一个页面时,据此判断"正在测 #1、#2 排队",在 #2 的工作日志里标注"等 #1 完成"。
306
306
  - 需要更多背景时再 \`Read $CREW_HOME/notes/<topic>.md\`;不要一次把所有笔记读进上下文。
307
307
 
308
- ${communicationSection}${ctx.collaborationToolsEnabled === true ? buildCollaborationToolsPrompt() : ""}
308
+ ${communicationSection}
309
309
 
310
310
  CLI 成功时打印人类可读文本到 stdout;失败时 stderr 给出错误,并用**退出码**告诉你下一步:
311
311
  - \`4\` = 任务已被他人认领 / 不可认领 → 停手,别抢,转做别的。
@@ -385,21 +385,3 @@ export function buildOriginDecisionRetryPrompt(channelId, threadId) {
385
385
  "普通内部消息不算外部回复决策;--send-draft 也不是外部回复开关。",
386
386
  ].join("\n");
387
387
  }
388
- /**
389
- * Codex 原生 collaboration.* 工具的用法与边界(spec 2d;2026-08-28 对 Codex 0.149.1/0.150.1 实测)。
390
- * 只在 daemon 开关打开且 runtime 为 codex 时注入;是本机 runtime 能力事实,不是产品策略。
391
- * 契约来自实测,不编造 Codex 未声明的返回字段。
392
- */
393
- export function buildCollaborationToolsPrompt() {
394
- return `
395
-
396
- ## Codex 原生协作工具
397
- 本机 Codex 已开启 \`collaboration.*\` 子 agent 工具。它们完全在 Codex 进程内运行:子 agent 的工作对 NowWork 的频道、线程、任务、execution 记录和按 agent 的 token 统计**都不可见**,token 并入你本轮的用量。只用于你自己会汇总的内部交叉核对、并行调研;绝不用于人类或其它 NowWork 成员需要观察、审计或接手的事。
398
- - **\`collaboration.spawn_agent\`** —— 必填 \`task_name\`(仅小写字母/数字/下划线)、\`message\`;可选 \`fork_turns\`(\`"none"\`|\`"all"\`|\`"3"\` 这样的轮数,默认 \`"all"\`)、\`model\`、\`reasoning_effort\`。立即启动独立 turn,你不阻塞;子 agent 跑完后 idle 而非销毁。新 agent 用 **canonical 任务名路径**引用,如 \`/root/api_review\`。含 root 最多 4 个活跃 agent。
399
- - **\`collaboration.followup_task\`** —— 必填 \`target\`(canonical 路径,不是 UUID)、\`message\`。异步投递并把 idle 的目标唤醒进新 turn,可重复。
400
- - **\`collaboration.send_message\`** —— 必填 \`target\`、\`message\`。只入队,**不**启动 turn;要触发工作用 followup_task。
401
- - **\`collaboration.list_agents\`** —— 可选 \`path_prefix\`。只读,列出 live agents。
402
- - **\`collaboration.wait_agent\`** —— 可选 \`timeout_ms\`(10000–3600000,默认 30000)。阻塞你直到任一子 agent 有更新、有新输入或超时。
403
- - **没有任何返回值有保证的 schema**:不要去解析返回值里的 \`agent_id\`/\`id\`/\`accepted\` 之类字段;用 \`list_agents\` 和 \`wait_agent\` 获取状态。
404
- - **不要调用 \`collaboration.interrupt_agent\`**:它会把你自己这轮 turn 一起中断(2026-08-10 线上事故),中断后本轮结果全部丢失且不会重跑。让子 agent 自然跑完或直接忽略其结果。`;
405
- }