@nowcrew/daemon 0.6.77-beta.5 → 0.6.77-beta.7

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.
package/dist/config.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /** daemon 配置:从 env 读取,解析 crew CLI 路径 (注入给 agent 的 wrapper 用)。 */
2
2
  import { fileURLToPath } from "node:url";
3
- import { dirname, join, resolve } from "node:path";
3
+ import { basename, dirname, join, resolve } from "node:path";
4
4
  import { homedir } from "node:os";
5
5
  import { createRequire } from "node:module";
6
6
  import { detectDaemonLang, translateDaemon } from "./i18n.js";
@@ -28,6 +28,23 @@ export const MIN_EXECUTION_EVENT_BYTES = 512;
28
28
  export const MAX_EXECUTION_TIMEOUT_MS = 2_147_483_647;
29
29
  export class ConfigError extends Error {
30
30
  }
31
+ /**
32
+ * Resolve the shared workspace repository root from the daemon's agent root.
33
+ *
34
+ * Standard NowWork storage keeps `agent-items`, `daemon`, `repos`, and the QA
35
+ * report hub as siblings. Legacy/standalone profiles may still point at an
36
+ * arbitrary agents directory, for which the historical `<agentsRoot>/repos`
37
+ * layout remains the compatible fallback.
38
+ */
39
+ export function resolveReposRoot(agentsRoot, configured) {
40
+ const explicit = configured?.trim();
41
+ if (explicit)
42
+ return resolve(explicit);
43
+ const resolvedAgentsRoot = resolve(agentsRoot);
44
+ return basename(resolvedAgentsRoot) === "agent-items"
45
+ ? join(dirname(resolvedAgentsRoot), "repos")
46
+ : join(resolvedAgentsRoot, "repos");
47
+ }
31
48
  function positiveIntegerEnv(env, field, fallback) {
32
49
  const raw = env[field];
33
50
  const value = raw === undefined ? fallback : Number(raw);
@@ -59,11 +76,11 @@ function defaultCliPath() {
59
76
  return resolve(here, "../../cli/dist/main.js");
60
77
  }
61
78
  }
62
- // 临时 dev 验收名单。测试完成后删除此常量,恢复未配置时默认关闭的行为。
79
+ // 临时 dev 验收名单。这里必须填写 Server 生成的 Agent handle,而不是显示名称或频道 ID。
80
+ // 测试完成后删除此常量,恢复未配置时默认关闭的行为。
63
81
  const TEMP_LOCAL_MEMORY_V2_TEST_HANDLES = [
64
- "memory-v2-test-a",
65
- "memory-v2-test-b",
66
- "memory-v2-test-c",
82
+ "94533bf3-b5b7-4a1e-9c41-6a1ab0a0e1ea", // memory-v2-test-a
83
+ "967ab9ce-724c-4f22-a664-2f855cd798af", // memory-v2-test-b
67
84
  ];
68
85
  function isTemporaryDevServer(serverUrl) {
69
86
  try {
@@ -136,16 +153,19 @@ export function loadConfig(env = process.env) {
136
153
  });
137
154
  const agentsRoot = resolveAgentsRoot(env.CREW_AGENTS_ROOT, homedir());
138
155
  const configuredReposRoot = env.NOWWORK_REPOS_ROOT?.trim();
139
- const reposRoot = configuredReposRoot ? resolve(configuredReposRoot) : undefined;
156
+ const reposRoot = resolveReposRoot(agentsRoot, configuredReposRoot);
140
157
  const configuredQaReportRoot = env.QA_REPORT_HUB?.trim();
158
+ const defaultQaReportRoot = configuredReposRoot
159
+ ? join(dirname(reposRoot), "qa-report-server-v2")
160
+ : join(dirname(agentsRoot), "qa-report-server-v2");
141
161
  return {
142
162
  serverUrl,
143
163
  machineToken,
144
164
  agentsRoot,
145
- ...(reposRoot === undefined ? {} : { reposRoot }),
165
+ reposRoot,
146
166
  qaReportRoot: configuredQaReportRoot
147
167
  ? resolve(configuredQaReportRoot)
148
- : join(dirname(reposRoot ?? agentsRoot), "qa-report-server-v2"),
168
+ : defaultQaReportRoot,
149
169
  cliPath: env.CREW_CLI_PATH ?? defaultCliPath(),
150
170
  runtimeBin: env.CREW_RUNTIME ?? "claude",
151
171
  codexHomeMode: codexHomeModeEnv(env),
@@ -1,5 +1,6 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { dirname, join, resolve } from "node:path";
3
+ import { resolveReposRoot } from "./config.js";
3
4
  import { DaemonToServerExecutionFrameSchema, ExecutionCompletedSchema, ExecutionRejectedSchema, ExecutionStartSchema, } from "./execution-protocol.js";
4
5
  import { JournalConflictError } from "./execution-journal.js";
5
6
  import { boundExecutionFrame } from "./execution-event-limit.js";
@@ -524,7 +525,7 @@ export async function runExecution(config, input, dependencies) {
524
525
  if (dependencies.abilityRelease !== undefined) {
525
526
  await cancellable(dependencies.abilityRelease.apply(config.agentsRoot, spec.agent.handle, spec.agent.abilityRelease), dependencies.cancellation);
526
527
  }
527
- const reposRoot = config.reposRoot ?? join(config.agentsRoot, "repos");
528
+ const reposRoot = resolveReposRoot(config.agentsRoot, config.reposRoot);
528
529
  const preparedProjectDependencies = await cancellable(dependencies.projectDependencies.prepare({
529
530
  trainingRoot: join(config.agentsRoot, spec.agent.handle, "training"),
530
531
  reposRoot,
@@ -863,7 +864,7 @@ export async function runExecution(config, input, dependencies) {
863
864
  token: credential.token,
864
865
  agentId: credential.agentId,
865
866
  agentsRoot: config.agentsRoot,
866
- ...(config.reposRoot === undefined ? {} : { reposRoot: config.reposRoot }),
867
+ reposRoot: resolveReposRoot(config.agentsRoot, config.reposRoot),
867
868
  ...(config.qaReportRoot === undefined ? {} : { qaReportRoot: config.qaReportRoot }),
868
869
  cliPath: config.cliPath,
869
870
  providerConfig,
@@ -466,6 +466,11 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
466
466
  CREW_TOKEN: input.launch.token,
467
467
  CREW_CHANNEL: input.channelId,
468
468
  CREW_HOME: workspace.dir,
469
+ // Keep the daemon-selected storage root authoritative for runtimes.
470
+ // In profile-backed NowWork sessions the daemon config may differ from
471
+ // the daemon process environment, so inheriting CREW_AGENTS_ROOT is not
472
+ // sufficient for QA/project diagnostics.
473
+ CREW_AGENTS_ROOT: input.launch.agentsRoot,
469
474
  ...(input.launch.reposRoot === undefined ? {} : { NOWWORK_REPOS_ROOT: input.launch.reposRoot }),
470
475
  ...(input.launch.qaReportRoot === undefined ? {} : { QA_REPORT_HUB: input.launch.qaReportRoot }),
471
476
  // Expose only the training root; ability-specific workspace layout stays
package/dist/main.js CHANGED
File without changes
@@ -16,12 +16,18 @@ export async function initializeAfterProjectSkillLeaseProtection(input) {
16
16
  /**
17
17
  * Runs only after execution-journal restart reconciliation has settled. A lease without a terminal
18
18
  * journal fact remains durable and prevents the caller from marking Project Skill v2 ready.
19
+ *
20
+ * 重连时本进程仍在跑的 execution 合法持有 lease(`liveExecutionIds`):既不释放,也不算 unresolved。
21
+ * 否则一次网络抖动就会让 daemon 在整条连接上摘掉 Project Skills 能力(incident: 2026-09-02,
22
+ * 绑定项目的 Agent 全部 execution_spec_incompatible)。
19
23
  */
20
24
  export async function recoverProjectSkillRuntimeRoots(input) {
21
25
  for (const agentRoot of input.agentRoots) {
22
26
  await input.roots.recover(agentRoot);
23
27
  let unresolvedLease = false;
24
28
  await input.leases.recover(agentRoot, async (executionId) => {
29
+ if (input.liveExecutionIds?.has(executionId))
30
+ return false;
25
31
  const entry = await input.journal.get(executionId);
26
32
  const releasable = entry?.state === "completed" || entry?.state === "interrupted";
27
33
  if (!releasable)
@@ -22,13 +22,14 @@ export function createProjectSkillRuntimeRootStartup() {
22
22
  leases,
23
23
  initialize,
24
24
  }),
25
- recover: async (agentsRoot, journal, protectedExecutionIds) => {
25
+ recover: async (agentsRoot, journal, protectedExecutionIds, liveExecutionIds) => {
26
26
  await recoverProjectSkillRuntimeRoots({
27
27
  agentRoots: await agentRoots(agentsRoot),
28
28
  journal,
29
29
  roots,
30
30
  leases,
31
31
  gc,
32
+ ...(liveExecutionIds === undefined ? {} : { liveExecutionIds }),
32
33
  currentRootId: async (agentRoot) => (await readAppliedProjectSkillManifest(agentRoot))?.rootId ?? null,
33
34
  afterLeaseRecovery: async () => {
34
35
  const remaining = await initializeAfterProjectSkillLeaseProtection({
@@ -1,6 +1,29 @@
1
1
  import { createExecutionJournal } from "../execution-journal.js";
2
2
  import { reconcileExecutionJournal } from "../execution-recovery.js";
3
3
  import { dslog, flushSlog } from "../slog.js";
4
+ /**
5
+ * 每次连接 preflight 的 Runtime root / lease 恢复。重连时本进程仍在跑的 execution 合法持有 lease,
6
+ * 必须当作存活而非孤儿;失败也必须留痕,否则整条连接静默摘掉 Project Skills 能力
7
+ * (incident: 2026-09-02,绑定项目的 Agent 全部 execution_spec_incompatible)。
8
+ */
9
+ export function createProjectSkillReconnectRecovery(startup, agentsRoot, journal, protectedExecutionIds, liveExecutionIds) {
10
+ return async () => {
11
+ const live = liveExecutionIds();
12
+ try {
13
+ await startup.recover(agentsRoot, journal(), protectedExecutionIds, live);
14
+ return true;
15
+ }
16
+ catch (error) {
17
+ dslog("project_skills.preflight_recovery_failed", "Project Skill runtime root 恢复失败,本连接不宣告 Project Skills 能力", {
18
+ level: "ERROR",
19
+ error_code: error.code ?? "unknown",
20
+ error_message: error.message,
21
+ live_execution_count: live.size,
22
+ });
23
+ return false;
24
+ }
25
+ };
26
+ }
4
27
  export async function initializeProjectSkillProtectedExecutionJournal(input) {
5
28
  return input.startup.initializeAfterLeaseProtection(input.config.agentsRoot, async (snapshot) => {
6
29
  input.protectedExecutionIds.clear();
package/dist/runner.js CHANGED
@@ -1,6 +1,7 @@
1
1
  /** Legacy agent orchestration around the business-neutral local executor. */
2
2
  import { randomUUID } from "node:crypto";
3
3
  import { join } from "node:path";
4
+ import { resolveReposRoot } from "./config.js";
4
5
  import { mintAgentToken } from "./token.js";
5
6
  import { buildSystemPrompt, buildWakePrompt, capMemoryForInject, capWorkLogForInject } from "./prompt.js";
6
7
  import { deliverScheduledReport, } from "./scheduled-report.js";
@@ -84,7 +85,7 @@ export async function runAgent(config, input, onActivity = defaultPrint, onConso
84
85
  token: credential.token,
85
86
  agentId: credential.agentId,
86
87
  agentsRoot: config.agentsRoot,
87
- ...(config.reposRoot === undefined ? {} : { reposRoot: config.reposRoot }),
88
+ reposRoot: resolveReposRoot(config.agentsRoot, config.reposRoot),
88
89
  ...(config.qaReportRoot === undefined ? {} : { qaReportRoot: config.qaReportRoot }),
89
90
  cliPath: config.cliPath,
90
91
  providerConfig,
package/dist/serve.js CHANGED
@@ -42,7 +42,7 @@ import { PROJECT_SKILLS_CAPABILITY } from "./project-skills/types.js";
42
42
  import { createAgentProjectionCoordinator } from "./project-skills/agent-projection-coordinator.js";
43
43
  import { createProjectSkillRuntimeRootStartup, } from "./project-skills/runtime-root-startup.js";
44
44
  import { createProjectSkillControllerReadiness, withProjectSkillCapabilityStatus } from "./project-skills/capability.js";
45
- import { initializeProjectSkillProtectedExecutionJournal } from "./project-skills/serve-startup.js";
45
+ import { createProjectSkillReconnectRecovery, initializeProjectSkillProtectedExecutionJournal } from "./project-skills/serve-startup.js";
46
46
  import { createProjectSkillConnectionCapabilityProvider, createProjectSkillMachineHelloPublisher } from "./project-skills/serve-capability.js";
47
47
  import { createAgentAbilityRuntime } from "./agent-ability/runtime.js";
48
48
  import { createProjectSkillsReconciler, } from "./project-skills/reconciler.js";
@@ -215,9 +215,7 @@ export function serve(config, opts = {}) {
215
215
  additionalCapabilities: () => managedDaemonCapabilities(updateEligibility),
216
216
  probeRuntimeRootStorePrimitive: projectSkillRuntimeRootStartup.probeNativePublicationPrimitiveReadiness,
217
217
  initializeController: projectSkillControllerReadiness.ensure,
218
- recoverRuntimeRootsAndLeases: async () => projectSkillRuntimeRootStartup
219
- .recover(config.agentsRoot, executionJournal, protectedExecutionIds)
220
- .then(() => true, () => false),
218
+ recoverRuntimeRootsAndLeases: createProjectSkillReconnectRecovery(projectSkillRuntimeRootStartup, config.agentsRoot, () => executionJournal, protectedExecutionIds, () => new Set(executionRuns.keys())),
221
219
  applyReadiness: projectSkillControllerReadiness.applyPreflight,
222
220
  });
223
221
  const safeExecutionSend = (frame) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nowcrew/daemon",
3
- "version": "0.6.77-beta.5",
3
+ "version": "0.6.77-beta.7",
4
4
  "type": "module",
5
5
  "description": "crew daemon — 运行在用户机器:拉起/管理 agent 进程,注入 crew CLI,归一化 runtime 事件",
6
6
  "license": "Apache-2.0",
@@ -16,6 +16,14 @@
16
16
  "publishConfig": {
17
17
  "access": "public"
18
18
  },
19
+ "scripts": {
20
+ "daemon": "pnpm --filter @nowcrew/cli build && tsx src/main.ts",
21
+ "build": "tsc -p tsconfig.json",
22
+ "codex-home:migrate": "tsx src/runtimes/codex-home-migration-cli.ts",
23
+ "prepublishOnly": "pnpm build && node ../scripts/daemon-release-artifact.mjs --strict-registry",
24
+ "test": "vitest run",
25
+ "typecheck": "tsc --noEmit"
26
+ },
19
27
  "dependencies": {
20
28
  "@agentclientprotocol/sdk": "1.2.1",
21
29
  "@nowcrew/cli": "0.4.13",
@@ -36,12 +44,5 @@
36
44
  "tsx": "^4.19.0",
37
45
  "typescript": "^5.6.0",
38
46
  "vitest": "^2.1.0"
39
- },
40
- "scripts": {
41
- "daemon": "pnpm --filter @nowcrew/cli build && tsx src/main.ts",
42
- "build": "tsc -p tsconfig.json",
43
- "codex-home:migrate": "tsx src/runtimes/codex-home-migration-cli.ts",
44
- "test": "vitest run",
45
- "typecheck": "tsc --noEmit"
46
47
  }
47
- }
48
+ }