@nowcrew/daemon 0.6.88-beta.3 → 0.6.88-beta.5

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/README.md CHANGED
@@ -354,6 +354,15 @@ Runtime launch, which recovers from a missed notification or daemon restart. Ski
354
354
  are visible through the symlink immediately; adding, deleting, renaming, or changing frontmatter requires
355
355
  a rescan. Web descriptions remain at the last inventory value until that rescan.
356
356
 
357
+ ## 目录投影测试的平台范围
358
+
359
+ `test/directory-projection.test.ts` 在所有平台运行,覆盖路径检查、复制发布、只读权限请求及失败恢复。
360
+ 其受控文件系统夹具记录目录 `chmod` 请求,但保留宿主目录的写权限,避免把 macOS 的目录重命名规则当作 Windows 行为。
361
+ 生产代码的只读保护不受此夹具影响。
362
+
363
+ `test/directory-projection.windows.test.ts` 仅在 Windows 上运行,使用真实文件系统验证只读目录发布与文件保护。
364
+ 非 Windows 的全量测试会明确跳过该文件,不能据此宣称 Windows 原生行为已验证。
365
+
357
366
  ## Reliability
358
367
 
359
368
  Protocol-v1 lifecycle is `accepted → started → completed`, followed by a server
@@ -1,6 +1,7 @@
1
1
  import { execFile } from "node:child_process";
2
2
  import { promisify } from "node:util";
3
3
  import { isWin } from "./platform.js";
4
+ import { augmentedPath } from "./runtime-path.js";
4
5
  import { listClaudeModels } from "./runtimes/claude-models.js";
5
6
  import { listHermesModels } from "./runtimes/hermes-models.js";
6
7
  const execFileRaw = promisify(execFile);
@@ -8,6 +9,7 @@ const MODEL_PROBE_TIMEOUT_MS = 8_000;
8
9
  const MODEL_PROBE_MAX_BUFFER_BYTES = 1024 * 1024;
9
10
  // win32 上 npm CLI 是 .cmd shim,execFile 需 shell 才能执行;参数全是固定字面量,无注入面。
10
11
  const execFileP = (bin, args) => execFileRaw(bin, args, {
12
+ env: { ...process.env, PATH: augmentedPath() },
11
13
  shell: isWin(),
12
14
  timeout: MODEL_PROBE_TIMEOUT_MS,
13
15
  killSignal: "SIGKILL",
@@ -388,8 +388,8 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
388
388
  }
389
389
  }
390
390
  const attachmentPlan = routeRuntimeAttachments(runtime.name, materialized?.attachments ?? []);
391
- const wakePrompt = `${resolvePrompt(input.wakePrompt, promptContext)}${attachmentPlan.promptSuffix}`;
392
- memoryPruneTraceId = parseMemoryPruneTraceId(wakePrompt);
391
+ const baseWakePrompt = `${resolvePrompt(input.wakePrompt, promptContext)}${attachmentPlan.promptSuffix}`;
392
+ memoryPruneTraceId = parseMemoryPruneTraceId(baseWakePrompt);
393
393
  if (input.localMemoryV2Enabled === true && memoryPruneTraceId !== null
394
394
  && isLocalMemoryV2Eligible({
395
395
  enabledForHandle: true,
@@ -402,12 +402,13 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
402
402
  }
403
403
  const memoryPrompt = await prepareLocalMemoryPrompt({
404
404
  agentDir: executionWorkspace.dir, executionId: input.executionId, agentHandle: input.handle,
405
- enabled: input.localMemoryV2Enabled === true, query: wakePrompt,
405
+ enabled: input.localMemoryV2Enabled === true, query: baseWakePrompt,
406
406
  resultPath: localMemoryResultPath, maxBytes: input.maxSystemPromptBytes,
407
407
  renderBase: (reservedPromptBytes) => resolvePrompt(input.systemPrompt, { ...promptContext, reservedPromptBytes }),
408
408
  });
409
409
  localMemoryResultPath = memoryPrompt.resultPath;
410
410
  const systemPrompt = memoryPrompt.systemPrompt;
411
+ const wakePrompt = baseWakePrompt + memoryPrompt.wakePromptSuffix;
411
412
  if (!isolatedMaintenance)
412
413
  try {
413
414
  localMemoryTelemetry = createLocalMemoryTelemetry({
@@ -525,7 +526,8 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
525
526
  const effectiveSystemPrompt = abilityContext === undefined
526
527
  ? systemPrompt
527
528
  : `${systemPrompt}\n\n${abilityContext.prompt}`;
528
- if (Buffer.byteLength(effectiveSystemPrompt, "utf8") > (input.maxSystemPromptBytes ?? Number.MAX_SAFE_INTEGER)) {
529
+ if (Buffer.byteLength(effectiveSystemPrompt, "utf8") + Buffer.byteLength(memoryPrompt.wakePromptSuffix, "utf8")
530
+ > (input.maxSystemPromptBytes ?? Number.MAX_SAFE_INTEGER)) {
529
531
  throw new Error("ability_runtime_context_size_exceeded");
530
532
  }
531
533
  memoryPruneFailurePhase = "prompt_write";
@@ -21,17 +21,18 @@ export async function prepareLocalMemoryPrompt(input) {
21
21
  }
22
22
  const original = input.renderBase(0);
23
23
  const structured = input.resultPath === null ? "" : renderLocalMemoryResultInstructions(input.resultPath);
24
- const fits = (prompt) => input.maxBytes === undefined || Buffer.byteLength(prompt, "utf8") <= input.maxBytes;
25
- const render = (suffix) => (suffix === "" ? original : input.renderBase(Buffer.byteLength(suffix, "utf8"))) + suffix;
26
- // 后缀先占预算,可选预览重建;结构化契约优先于 pending,不能被预览挤掉。
24
+ const wakeBytes = Buffer.byteLength(structured, "utf8");
25
+ const fits = (prompt) => input.maxBytes === undefined || Buffer.byteLength(prompt, "utf8") + wakeBytes <= input.maxBytes;
26
+ const render = (suffix) => (suffix === "" ? original : input.renderBase(Buffer.byteLength(suffix, "utf8") + wakeBytes)) + suffix;
27
+ // 系统及当前消息各保留一份契约,避免 resume 沿用旧指令;两份均先占预算,再裁可选预览。
27
28
  const complete = render(pending + structured);
28
29
  if (fits(complete))
29
- return { systemPrompt: complete, resultPath: input.resultPath };
30
+ return { systemPrompt: complete, resultPath: input.resultPath, wakePromptSuffix: structured };
30
31
  if (pending !== "") {
31
32
  const withoutPending = render(structured);
32
33
  if (fits(withoutPending))
33
- return { systemPrompt: withoutPending, resultPath: input.resultPath };
34
+ return { systemPrompt: withoutPending, resultPath: input.resultPath, wakePromptSuffix: structured };
34
35
  }
35
36
  // 强制基础指令与契约本身仍无法共存时,保留既有非结构化兼容回退,不裁基础指令。
36
- return { systemPrompt: original, resultPath: null };
37
+ return { systemPrompt: original, resultPath: null, wakePromptSuffix: "" };
37
38
  }
@@ -51,6 +51,7 @@ export function renderLocalMemoryResultInstructions(resultPath) {
51
51
  return `
52
52
 
53
53
  ## Structured memory prune (daemon-managed)
54
+ 本节是本次执行唯一的记忆写入与清理协议,覆盖历史会话及本条消息前文中直接修改共享文件、清空工作日志或仅回复“已修正”的要求。任务目标与用户授权范围不变;只使用下面本轮提供的结果路径,不复用历史路径。
54
55
  本轮长期记忆收尾只提交候选,不要直接编辑 $CREW_HOME/MEMORY.md 或 $CREW_HOME/notes/。
55
56
  将一个合法 JSON 对象写入以下 daemon 提供的文件,写入完成后再结束本轮:
56
57
  ${resultPath}
package/dist/main.js CHANGED
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nowcrew/daemon",
3
- "version": "0.6.88-beta.3",
3
+ "version": "0.6.88-beta.5",
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
+ }