@nowcrew/daemon 0.6.19 → 0.6.21

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 (40) hide show
  1. package/dist/atomic-no-replace-rename.js +91 -0
  2. package/dist/completion-retransmitter-logging.js +16 -0
  3. package/dist/completion-retransmitter.js +39 -4
  4. package/dist/control-plane-url.js +4 -2
  5. package/dist/directory-projection-publication.js +105 -0
  6. package/dist/directory-projection.js +20 -4
  7. package/dist/execution-journal.js +40 -4
  8. package/dist/execution-posix-stop-proof.js +82 -0
  9. package/dist/execution-runner.js +68 -8
  10. package/dist/local-executor.js +67 -52
  11. package/dist/machine-info.js +8 -5
  12. package/dist/project-skills/capability.js +109 -0
  13. package/dist/project-skills/controller-convergence.js +57 -0
  14. package/dist/project-skills/controller.js +80 -24
  15. package/dist/project-skills/initialized-reconciler.js +4 -4
  16. package/dist/project-skills/projection-state-domain.js +19 -2
  17. package/dist/project-skills/projection-state-store.js +3 -2
  18. package/dist/project-skills/projection-state-transaction.js +5 -1
  19. package/dist/project-skills/projection-state.js +1 -1
  20. package/dist/project-skills/reconciler.js +275 -102
  21. package/dist/project-skills/runtime-launch.js +102 -0
  22. package/dist/project-skills/runtime-root-bootstrap.js +47 -0
  23. package/dist/project-skills/runtime-root-domain.js +268 -0
  24. package/dist/project-skills/runtime-root-gc.js +293 -0
  25. package/dist/project-skills/runtime-root-lease-artifact.js +46 -0
  26. package/dist/project-skills/runtime-root-leases.js +487 -0
  27. package/dist/project-skills/runtime-root-source-identity.js +60 -0
  28. package/dist/project-skills/runtime-root-startup.js +49 -0
  29. package/dist/project-skills/runtime-root-state-artifact-domain.js +143 -0
  30. package/dist/project-skills/runtime-root-state-index.js +356 -0
  31. package/dist/project-skills/runtime-root-store.js +722 -0
  32. package/dist/project-skills/serve-capability.js +28 -0
  33. package/dist/project-skills/serve-startup.js +22 -0
  34. package/dist/project-skills/types.js +1 -0
  35. package/dist/runtimes/codex-home-migration-cli.js +26 -0
  36. package/dist/runtimes/codex-home-migration.js +112 -0
  37. package/dist/runtimes/codex-home.js +200 -17
  38. package/dist/serve.js +60 -79
  39. package/dist/supervised-runtime.js +1 -5
  40. package/package.json +2 -1
package/dist/serve.js CHANGED
@@ -8,7 +8,7 @@ import { randomUUID } from "node:crypto";
8
8
  import { initSlog, dslog, setSlogDefaults, drainSpool, flushSlog } from "./slog.js";
9
9
  import { mergeRunAgentResults, reportScheduledStartFailure, runAgent } from "./runner.js";
10
10
  import { buildOriginDecisionRetryPrompt, buildScheduledPrompt } from "./prompt.js";
11
- import { collectMachineHello, cliVersion, daemonCapabilityBindings, daemonVersion, detectExecutionRuntimesWithSignal, } from "./machine-info.js";
11
+ import { collectMachineHello, cliVersion, daemonCapabilities, daemonVersion, detectExecutionRuntimesWithSignal, } from "./machine-info.js";
12
12
  import { conservativeExecutionRuntimes, createRuntimeProbeCoordinator, } from "./runtime-probe.js";
13
13
  import { listWorkspace, readWorkspaceFile } from "./workspace-fs.js";
14
14
  import { listSkills } from "./skills.js";
@@ -18,16 +18,15 @@ import { normalizeScheduledContext } from "./scheduled-report.js";
18
18
  import { formatDaemonLogLine } from "./log-format.js";
19
19
  import { reportAgentRunComplete } from "./scheduled-run-report.js";
20
20
  import { runWithOriginDecisionGuard } from "./origin-decision.js";
21
- import { createExecutionJournal } from "./execution-journal.js";
22
21
  import { createExecutionTelemetryJournal } from "./execution-telemetry-journal.js";
23
22
  import { ExecutionRejectedSchema, ExecutionSnapshotSchema, LegacyAgentStartSchema, ServerToDaemonExecutionFrameSchema, } from "./execution-protocol.js";
24
23
  import { hashExecutionSpec, projectWorkspaceCapabilityRejection, runExecution, } from "./execution-runner.js";
25
24
  import { awaitWithCancellation, createRuntimeCancellation, RuntimeCancelledError, } from "./runtime-cancellation.js";
26
25
  import { createShutdownDeadline, readTestShutdownConfiguration } from "./shutdown-deadline.js";
27
26
  import { closeWebSocketWithinDeadline } from "./websocket-shutdown.js";
28
- import { reconcileExecutionJournal } from "./execution-recovery.js";
29
27
  import { createSharedSlotManager } from "./shared-execution-slots.js";
30
28
  import { createCompletionRetransmitter } from "./completion-retransmitter.js";
29
+ import { completionRetransmitterOptions } from "./completion-retransmitter-logging.js";
31
30
  import { createAgentMemoryBridge } from "./agent-memory/bridge.js";
32
31
  import { createRuntimeStartupGate } from "./runtime-startup-gate.js";
33
32
  import { createHostExecutionCoordinator, hostCoordinatedSlotManager, hostCoordinatedStartupGate, } from "./host-execution-coordinator.js";
@@ -39,6 +38,10 @@ import { createProjectRegistry } from "./project-skills/registry.js";
39
38
  import { createProjectSkillsController, } from "./project-skills/controller.js";
40
39
  import { PROJECT_SKILLS_CAPABILITY } from "./project-skills/types.js";
41
40
  import { createAgentProjectionCoordinator } from "./project-skills/agent-projection-coordinator.js";
41
+ import { createProjectSkillRuntimeRootStartup, } from "./project-skills/runtime-root-startup.js";
42
+ import { createProjectSkillControllerReadiness, withProjectSkillCapabilityStatus } from "./project-skills/capability.js";
43
+ import { initializeProjectSkillProtectedExecutionJournal } from "./project-skills/serve-startup.js";
44
+ import { createProjectSkillConnectionCapabilityProvider, createProjectSkillMachineHelloPublisher } from "./project-skills/serve-capability.js";
42
45
  import { createAgentAbilityRuntime } from "./agent-ability/runtime.js";
43
46
  import { createProjectSkillsReconciler, } from "./project-skills/reconciler.js";
44
47
  import { initializedProjectSkillsReconciler } from "./project-skills/initialized-reconciler.js";
@@ -65,7 +68,9 @@ export function serve(config, opts = {}) {
65
68
  const createWebSocket = opts.createWebSocket ?? ((url) => new WebSocket(url));
66
69
  let reconnectTimer = null;
67
70
  let stopPromise = null;
68
- const executionJournal = opts.execution?.journal ?? createExecutionJournal(config.agentsRoot);
71
+ let executionJournal;
72
+ const injectedExecutionJournal = opts.execution?.journal;
73
+ const protectedExecutionIds = new Set();
69
74
  const agentMemory = opts.execution?.dependencies?.agentMemory
70
75
  ?? (config.agentMemory === null ? undefined : createAgentMemoryBridge(config.agentMemory));
71
76
  const executionTelemetry = createExecutionTelemetryJournal(config.agentsRoot);
@@ -126,6 +131,7 @@ export function serve(config, opts = {}) {
126
131
  agentsRoot: config.agentsRoot,
127
132
  coordinator: projectionCoordinator,
128
133
  scannedProjects: () => projectSkillsController.scannedProjects(),
134
+ protectedExecutionIds,
129
135
  });
130
136
  projectSkillsController = opts.projectSkills?.controller ?? createProjectSkillsController({
131
137
  registry: createProjectRegistry(config.agentsRoot),
@@ -139,53 +145,34 @@ export function serve(config, opts = {}) {
139
145
  catch { /* 下一次 ready 或项目操作会重新发送完整快照 */ }
140
146
  },
141
147
  reconcile: (handle, bindings) => projectSkillsReconciler.reconcile(handle, bindings),
142
- ensureSnapshot: (handle, snapshot) => projectSkillsReconciler.ensureSnapshot(handle, snapshot),
148
+ ensureSnapshot: (handle, snapshot, options) => projectSkillsReconciler.ensureSnapshot(handle, snapshot, options),
143
149
  });
144
- let projectSkillsStatus = "initializing";
145
- let projectSkillsInitialization = null;
146
- let latestMachineHello = null;
147
- let latestHelloSocket = null;
148
- const effectiveMachineHello = (hello) => ({
149
- ...hello,
150
- projectSkillsStatus,
151
- capabilities: projectSkillsStatus === "ready"
152
- ? hello.capabilities
153
- : hello.capabilities.filter((capability) => capability !== PROJECT_SKILLS_CAPABILITY),
150
+ const projectSkillRuntimeRootStartup = opts.projectSkills?.runtimeRootStartup
151
+ ?? createProjectSkillRuntimeRootStartup();
152
+ const projectSkillControllerReadiness = createProjectSkillControllerReadiness({
153
+ initialize: () => projectSkillsController.initialize(),
154
+ onUnavailable: (error) => dslog("project_skills.registry_failed", "本机项目注册表读取失败", {
155
+ level: "ERROR", error_code: error.code ?? "project_registry_corrupt",
156
+ }),
157
+ onSettled: () => machineHelloPublisher.republish(),
158
+ });
159
+ const machineHelloPublisher = createProjectSkillMachineHelloPublisher({
160
+ status: projectSkillControllerReadiness.status,
161
+ effective: withProjectSkillCapabilityStatus,
162
+ onSent: (hello) => log(`📤 已上报机器信息: ${hello.hostname} · ${hello.os} · installed=[${hello.runtimes.join(",")}] · executable=[${hello.executionRuntimes.join(",")}] · agents=[${hello.agentHandles.join(",")}]`),
163
+ });
164
+ const initializedProjectSkills = initializedProjectSkillsReconciler(projectSkillControllerReadiness.ensure, projectSkillsReconciler);
165
+ const prepareProjectSkillCapabilities = createProjectSkillConnectionCapabilityProvider({
166
+ platform: process.platform,
167
+ baseCapabilities: opts.machineInfo?.capabilities ?? daemonCapabilities(process.platform),
168
+ additionalCapabilities: () => managedDaemonCapabilities(updateEligibility),
169
+ probeRuntimeRootStorePrimitive: projectSkillRuntimeRootStartup.probeNativePublicationPrimitiveReadiness,
170
+ initializeController: projectSkillControllerReadiness.ensure,
171
+ recoverRuntimeRootsAndLeases: async () => projectSkillRuntimeRootStartup
172
+ .recover(config.agentsRoot, executionJournal, protectedExecutionIds)
173
+ .then(() => true, () => false),
174
+ applyReadiness: projectSkillControllerReadiness.applyPreflight,
154
175
  });
155
- const sendEffectiveMachineHello = () => {
156
- if (latestMachineHello === null || latestHelloSocket?.readyState !== WebSocket.OPEN)
157
- return;
158
- const hello = effectiveMachineHello(latestMachineHello);
159
- try {
160
- latestHelloSocket.send(JSON.stringify(hello));
161
- log(`📤 已上报机器信息: ${hello.hostname} · ${hello.os} · installed=[${hello.runtimes.join(",")}] · executable=[${hello.executionRuntimes.join(",")}] · agents=[${hello.agentHandles.join(",")}]`);
162
- }
163
- catch { /* 非 OPEN,忽略 */ }
164
- };
165
- const ensureProjectSkillsInitialized = () => {
166
- if (projectSkillsStatus === "ready")
167
- return Promise.resolve(true);
168
- if (projectSkillsInitialization !== null)
169
- return projectSkillsInitialization;
170
- projectSkillsStatus = "initializing";
171
- const attempt = projectSkillsController.initialize().then(() => {
172
- projectSkillsStatus = "ready";
173
- return true;
174
- }, (error) => {
175
- projectSkillsStatus = "unavailable";
176
- dslog("project_skills.registry_failed", "本机项目注册表读取失败", {
177
- level: "ERROR", error_code: error.code ?? "project_registry_corrupt",
178
- });
179
- return false;
180
- }).finally(() => {
181
- if (projectSkillsInitialization === attempt)
182
- projectSkillsInitialization = null;
183
- sendEffectiveMachineHello();
184
- });
185
- projectSkillsInitialization = attempt;
186
- return attempt;
187
- };
188
- const initializedProjectSkills = initializedProjectSkillsReconciler(ensureProjectSkillsInitialized, projectSkillsReconciler);
189
176
  const safeExecutionSend = (frame) => {
190
177
  try {
191
178
  if (ws?.readyState !== WebSocket.OPEN)
@@ -199,12 +186,7 @@ export function serve(config, opts = {}) {
199
186
  };
200
187
  const completionRetransmitter = createCompletionRetransmitter({
201
188
  send: safeExecutionSend,
202
- ...(opts.execution?.completionRetryDelaysMs === undefined ? {} : {
203
- retryDelaysMs: opts.execution.completionRetryDelaysMs,
204
- }),
205
- onAttempt: ({ kind, executionId, attempt, nextDelayMs }) => {
206
- dslog(kind === "sent" ? "execution.completion_sent" : "execution.completion_retried", kind === "sent" ? "execution completion 已发送" : "execution completion 未确认,已重传", { execution_id: executionId, attempt, next_delay_ms: nextDelayMs });
207
- },
189
+ ...completionRetransmitterOptions(opts.execution),
208
190
  });
209
191
  const reportExecutionFrame = async (frame) => {
210
192
  if (frame.type === "execution:activity" || frame.type === "execution:console") {
@@ -283,7 +265,6 @@ export function serve(config, opts = {}) {
283
265
  reservation.release();
284
266
  };
285
267
  let connectedAt = 0; // 本次 WS 连接建立时刻(断开日志算在线时长用)
286
- const capabilities = daemonCapabilityBindings(process.platform, opts.machineInfo?.capabilities);
287
268
  initSlog(config.serverUrl, config.machineToken, { daemonVersion: daemonVersion(), cliVersion: cliVersion(), ...(opts.profileName === undefined ? {} : { profileName: opts.profileName }), agentsRoot: config.agentsRoot });
288
269
  dslog("daemon.start", "daemon 常驻模式启动", { server_url: config.serverUrl, runtime: config.runtimeBin });
289
270
  // 并行调度:同一 agent 可并行处理多个【不同任务】(线程/频道),每任务隔离 cwd+work-log。
@@ -293,12 +274,17 @@ export function serve(config, opts = {}) {
293
274
  const activeExecutionTaskKeys = new Map();
294
275
  const legacyTaskTails = new Map();
295
276
  const log = (s) => process.stdout.write(formatDaemonLogLine(s) + "\n");
296
- function connect() {
277
+ async function connect() {
278
+ if (stopped)
279
+ return;
280
+ const capabilities = await prepareProjectSkillCapabilities();
297
281
  if (stopped)
298
282
  return;
299
- const wsUrl = buildControlPlaneUrl(config.serverUrl, config.machineToken, process.platform, undefined, projectSkillsStatus === "ready", capabilities.controlPlaneUrl);
283
+ const wsUrl = (opts.buildControlPlaneUrl ?? buildControlPlaneUrl)(config.serverUrl, config.machineToken, process.platform, undefined, projectSkillControllerReadiness.status() === "ready", capabilities.controlPlaneUrl);
300
284
  ws = createWebSocket(wsUrl);
285
+ let connectionEpochToken;
301
286
  ws.on("open", () => {
287
+ connectionEpochToken = projectSkillsController.beginConnectionEpoch();
302
288
  const openedSocket = ws;
303
289
  backoff = 1000;
304
290
  connectedAt = Date.now();
@@ -308,12 +294,11 @@ export function serve(config, opts = {}) {
308
294
  void drainSpool();
309
295
  // 上报本机信息 (hostname/os/daemon 版本/已装 runtimes)
310
296
  const { detectInstalled, detectExecutable = detectExecutionRuntimesWithSignal } = opts.machineInfo ?? {};
311
- const helloPromise = collectMachineHello(config.agentsRoot, config.executionLimits, process.platform, {
297
+ const helloPromise = (opts.machineInfo?.collect ?? collectMachineHello)(config.agentsRoot, config.executionLimits, process.platform, {
312
298
  ...(detectInstalled ? { detectInstalled } : {}),
313
299
  ...(opts.profileName === undefined ? {} : { profileName: opts.profileName }),
314
300
  // First hello must not wait for third-party handshakes; optional transports arrive in the refresh.
315
301
  detectExecutable: async (installed) => conservativeExecutionRuntimes(installed),
316
- additionalCapabilities: () => managedDaemonCapabilities(updateEligibility),
317
302
  capabilities: capabilities.machineHello,
318
303
  });
319
304
  runtimeFacts = helloPromise
@@ -324,9 +309,7 @@ export function serve(config, opts = {}) {
324
309
  if (ws === openedSocket && openedSocket.readyState === WebSocket.OPEN
325
310
  && detected.some((runtime) => !hello.executionRuntimes.includes(runtime))) {
326
311
  detectedExecutionRuntimes = detected;
327
- latestMachineHello = { ...hello, executionRuntimes: [...detected] };
328
- latestHelloSocket = openedSocket;
329
- sendEffectiveMachineHello();
312
+ machineHelloPublisher.publish({ ...hello, executionRuntimes: [...detected] }, openedSocket);
330
313
  }
331
314
  return detected;
332
315
  })
@@ -341,9 +324,7 @@ export function serve(config, opts = {}) {
341
324
  if (ws !== openedSocket || openedSocket.readyState !== WebSocket.OPEN)
342
325
  return;
343
326
  detectedExecutionRuntimes = hello.executionRuntimes;
344
- latestMachineHello = hello;
345
- latestHelloSocket = openedSocket;
346
- sendEffectiveMachineHello();
327
+ machineHelloPublisher.publish(hello, openedSocket);
347
328
  })
348
329
  .catch(() => { });
349
330
  opts.onOpen?.(ws);
@@ -678,7 +659,7 @@ export function serve(config, opts = {}) {
678
659
  ? r.serverCapabilities.filter((c) => typeof c === "string")
679
660
  : []);
680
661
  if (serverCapabilities.has(PROJECT_SKILLS_CAPABILITY)) {
681
- if (await ensureProjectSkillsInitialized())
662
+ if (await projectSkillControllerReadiness.ensure())
682
663
  await projectSkillsController.publishCurrent();
683
664
  }
684
665
  return;
@@ -700,11 +681,11 @@ export function serve(config, opts = {}) {
700
681
  if (typeof request.reqId !== "string")
701
682
  return;
702
683
  const result = serverCapabilities.has(PROJECT_SKILLS_CAPABILITY)
703
- ? await projectSkillsController.handle(msg)
684
+ ? await projectSkillsController.handle(msg, connectionEpochToken)
704
685
  : { ok: false, error: "capability_unavailable" };
705
686
  if (result.ok && (msg.type === "project:remove"
706
687
  || msg.type === "project:rescan")) {
707
- void ensureProjectSkillsInitialized();
688
+ void projectSkillControllerReadiness.ensure();
708
689
  }
709
690
  try {
710
691
  ws?.send(JSON.stringify({ type: "fs:result", reqId: request.reqId, ...result }));
@@ -1130,7 +1111,7 @@ export function serve(config, opts = {}) {
1130
1111
  void flushSlog();
1131
1112
  reconnectTimer = setTimeout(() => {
1132
1113
  reconnectTimer = null;
1133
- connect();
1114
+ void connect();
1134
1115
  }, backoff);
1135
1116
  backoff = Math.min(backoff * 2, maxBackoff);
1136
1117
  });
@@ -1140,16 +1121,14 @@ export function serve(config, opts = {}) {
1140
1121
  });
1141
1122
  }
1142
1123
  const ready = (async () => {
1143
- await reconcileExecutionJournal(executionJournal, {
1144
- agentsRoot: config.agentsRoot,
1145
- serverUrl: config.serverUrl,
1124
+ executionJournal = await initializeProjectSkillProtectedExecutionJournal({
1125
+ config,
1146
1126
  ...(opts.profileName === undefined ? {} : { profileName: opts.profileName }),
1147
- log: dslog,
1148
- flush: flushSlog,
1149
- writeStderr: (line) => process.stderr.write(line),
1127
+ startup: projectSkillRuntimeRootStartup,
1128
+ protectedExecutionIds,
1129
+ ...(injectedExecutionJournal === undefined ? {} : { injectedJournal: injectedExecutionJournal }),
1150
1130
  });
1151
- void ensureProjectSkillsInitialized();
1152
- connect();
1131
+ await connect();
1153
1132
  })();
1154
1133
  const stop = () => {
1155
1134
  if (stopPromise !== null)
@@ -1180,7 +1159,9 @@ export function serve(config, opts = {}) {
1180
1159
  run.controller.request();
1181
1160
  await deadline.waitFor(Promise.all(pending));
1182
1161
  await deadline.waitFor(hostCoordinator.drain());
1183
- await executionJournal.close({ signal: deadline.signal });
1162
+ if (executionJournal !== undefined) {
1163
+ await executionJournal.close({ signal: deadline.signal });
1164
+ }
1184
1165
  }
1185
1166
  finally {
1186
1167
  deadline.dispose();
@@ -1,5 +1,4 @@
1
1
  import { fileURLToPath } from "node:url";
2
- import { join } from "node:path";
3
2
  import { startDormantSupervisor, } from "./execution-supervisor.js";
4
3
  import { buildClaudeArgs } from "./runtimes/claude.js";
5
4
  import { DEEPSEEK_HARNESS_CONFIG_ENV, deepSeekHarnessConfigPath, } from "./runtimes/deepseek-harness.js";
@@ -13,10 +12,7 @@ export function supervisorLaunch(request) {
13
12
  ...(request.reasoning === undefined ? {} : { reasoning: request.reasoning }),
14
13
  };
15
14
  if (request.runtime === "claude") {
16
- const additionalDirectories = request.claudeAdditionalDirectories
17
- ?? (request.agentRoot === undefined
18
- ? undefined
19
- : [join(request.agentRoot, ".crew", "claude-skills"), request.agentRoot]);
15
+ const additionalDirectories = request.claudeAdditionalDirectories;
20
16
  return {
21
17
  command: request.bin,
22
18
  args: buildClaudeArgs({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nowcrew/daemon",
3
- "version": "0.6.19",
3
+ "version": "0.6.21",
4
4
  "type": "module",
5
5
  "description": "crew daemon — 运行在用户机器:拉起/管理 agent 进程,注入 crew CLI,归一化 runtime 事件",
6
6
  "license": "Apache-2.0",
@@ -38,6 +38,7 @@
38
38
  "scripts": {
39
39
  "daemon": "pnpm --filter @nowcrew/cli build && tsx src/main.ts",
40
40
  "build": "tsc -p tsconfig.json",
41
+ "codex-home:migrate": "tsx src/runtimes/codex-home-migration-cli.ts",
41
42
  "test": "vitest run",
42
43
  "typecheck": "tsc --noEmit"
43
44
  }