@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
package/dist/workspace.js CHANGED
@@ -8,7 +8,7 @@
8
8
  *
9
9
  * spawn runtime 时把 <ws>/.crew 置于 PATH 最前,于是 agent 的 `crew ...` 命中 wrapper。
10
10
  */
11
- import { mkdir, writeFile, readFile, chmod, access, lstat } from "node:fs/promises";
11
+ import { mkdir, writeFile, readFile, chmod, access } from "node:fs/promises";
12
12
  import { createHash, randomUUID } from "node:crypto";
13
13
  import { dirname, join } from "node:path";
14
14
  import { dslog } from "./slog.js";
@@ -34,37 +34,17 @@ async function exists(p) {
34
34
  return false;
35
35
  }
36
36
  }
37
- async function hasLocalMemoryV2Marker(agentDir) {
38
- const crewDir = join(agentDir, ".crew");
39
- const memoryDir = join(crewDir, "memory");
40
- const markerPath = join(agentDir, ".crew", "memory", "v2-enabled");
41
- try {
42
- const crewStat = await lstat(crewDir);
43
- const memoryStat = await lstat(memoryDir);
44
- if (!crewStat.isDirectory() || crewStat.isSymbolicLink()
45
- || !memoryStat.isDirectory() || memoryStat.isSymbolicLink())
46
- return false;
47
- const stat = await lstat(markerPath);
48
- if (!stat.isFile() || stat.isSymbolicLink())
49
- return false;
50
- return (await readFile(markerPath, "utf8")) === "1\n";
51
- }
52
- catch {
53
- return false;
54
- }
55
- }
56
37
  export async function prepareWorkspace(input) {
57
38
  const dir = join(input.agentsRoot, input.handle);
58
- const agentHomePreexisted = await exists(dir);
59
39
  const crewDir = join(dir, ".crew");
60
40
  await mkdir(crewDir, { recursive: true });
61
41
  // 预先创建明细目录,让收尾流程可以按功能创建 notes/<topic>.md。
62
- await mkdir(join(dir, "notes"), { recursive: true });
42
+ if (!input.skipLocalMemory)
43
+ await mkdir(join(dir, "notes"), { recursive: true });
63
44
  // MEMORY.md:首次创建"持久身份 + 知识索引"骨架,之后由 agent 自己维护
64
45
  const memoryPath = join(dir, "MEMORY.md");
65
- const memorySeedCreated = !(await exists(memoryPath));
66
- const localMemoryV2Initialized = await hasLocalMemoryV2Marker(dir);
67
- if (memorySeedCreated) {
46
+ const memorySeedCreated = !input.skipLocalMemory && !(await exists(memoryPath));
47
+ if (!input.skipLocalMemory && memorySeedCreated) {
68
48
  try {
69
49
  await writeFile(memoryPath, memorySeed(input.handle, input.description), "utf8");
70
50
  }
@@ -73,13 +53,15 @@ export async function prepareWorkspace(input) {
73
53
  throw error;
74
54
  }
75
55
  }
76
- let memory;
77
- try {
78
- memory = await readFile(memoryPath, "utf8");
79
- }
80
- catch (error) {
81
- logMemoryPrepareFailure(input, "memory_read", error);
82
- throw error;
56
+ let memory = "";
57
+ if (!input.skipLocalMemory) {
58
+ try {
59
+ memory = await readFile(memoryPath, "utf8");
60
+ }
61
+ catch (error) {
62
+ logMemoryPrepareFailure(input, "memory_read", error);
63
+ throw error;
64
+ }
83
65
  }
84
66
  // 系统提示词按 execution 隔离;省略 identity 的兼容调用每次使用随机路径。
85
67
  const promptDir = join(crewDir, "prompts");
@@ -145,9 +127,7 @@ export async function prepareWorkspace(input) {
145
127
  crewDir,
146
128
  systemPromptPath,
147
129
  memory,
148
- agentHomePreexisted,
149
130
  memorySeedCreated,
150
- localMemoryV2Initialized,
151
131
  homeDir,
152
132
  runDir,
153
133
  workLogPath,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nowcrew/daemon",
3
- "version": "0.6.67",
3
+ "version": "0.6.68",
4
4
  "type": "module",
5
5
  "description": "crew daemon — 运行在用户机器:拉起/管理 agent 进程,注入 crew CLI,归一化 runtime 事件",
6
6
  "license": "Apache-2.0",
@@ -1,74 +0,0 @@
1
- export const LOCAL_MEMORY_TOTAL_CODE_POINT_CAP = 6_000;
2
- export const LOCAL_MEMORY_INDEX_CODE_POINT_CAP = 3_000;
3
- export const LOCAL_MEMORY_INDEX_ENTRY_CAP = 20;
4
- const PROJECTABLE_STATUSES = new Set(["pinned", "active"]);
5
- function codePointLength(value) {
6
- return Array.from(value).length;
7
- }
8
- export function renderLocalMemoryIndexCard(entry) {
9
- return `### ${entry.title}\n`
10
- + `- id: ${entry.id}\n`
11
- + `- file: ${entry.file}\n`
12
- + `- type: ${entry.type}\n`
13
- + `- status: ${entry.status}\n`
14
- + `- summary: ${entry.summary}\n`
15
- + `- triggers: ${entry.triggers.join(", ")}\n`
16
- + `- priority: ${entry.priority}\n`
17
- + `- revision: ${entry.revision}\n`
18
- + `- anchor: ${entry.anchor ?? ""}\n`
19
- + `- updated: ${entry.updated}\n`
20
- + (entry.reviewAfter === null ? "" : `- review_after: ${entry.reviewAfter}\n`);
21
- }
22
- function compareEntries(left, right) {
23
- const statusRank = (status) => status === "pinned" ? 0 : 1;
24
- const priorityRank = (priority) => (priority === "high" ? 0 : priority === "medium" ? 1 : 2);
25
- return statusRank(left.status) - statusRank(right.status)
26
- || priorityRank(left.priority) - priorityRank(right.priority)
27
- || right.updated.localeCompare(left.updated)
28
- || left.id.localeCompare(right.id);
29
- }
30
- function cardLength(entry, hasPrevious) {
31
- return codePointLength(renderLocalMemoryIndexCard(entry)) + (hasPrevious ? 1 : 0);
32
- }
33
- function overBudgetReason(entries) {
34
- return entries.some((entry) => entry.status === "pinned")
35
- ? "index_over_budget_pinned"
36
- : "index_over_budget_active";
37
- }
38
- export function selectLocalMemoryIndexEntries(entries, budget, options = {}) {
39
- const projectable = entries.filter((entry) => PROJECTABLE_STATUSES.has(entry.status));
40
- const omittedIds = entries
41
- .filter((entry) => !PROJECTABLE_STATUSES.has(entry.status))
42
- .map((entry) => entry.id);
43
- const protectedIds = options.protectedIds ?? new Set();
44
- const protectedEntries = projectable.filter((entry) => protectedIds.has(entry.id)).sort(compareEntries);
45
- const candidates = projectable.filter((entry) => !protectedIds.has(entry.id)).sort(compareEntries);
46
- const selected = [];
47
- let renderedLength = 0;
48
- for (const entry of protectedEntries) {
49
- selected.push(entry);
50
- renderedLength += cardLength(entry, selected.length > 1);
51
- }
52
- if (selected.length > budget.entryCap || renderedLength > budget.indexCodePointCap) {
53
- return {
54
- entries: selected,
55
- omittedIds: [...omittedIds, ...candidates.map((entry) => entry.id)],
56
- reason: overBudgetReason(selected),
57
- };
58
- }
59
- for (const entry of candidates) {
60
- if (selected.length >= budget.entryCap || renderedLength + cardLength(entry, selected.length > 0) > budget.indexCodePointCap) {
61
- omittedIds.push(entry.id);
62
- continue;
63
- }
64
- selected.push(entry);
65
- renderedLength += cardLength(entry, selected.length > 1);
66
- }
67
- return {
68
- entries: selected,
69
- omittedIds,
70
- reason: omittedIds.length === entries.filter((item) => !PROJECTABLE_STATUSES.has(item.status)).length
71
- ? null
72
- : "index_over_budget",
73
- };
74
- }
@@ -1,111 +0,0 @@
1
- import { randomUUID } from "node:crypto";
2
- import { lstat, mkdir, readdir, realpath, rename, rm, writeFile, readFile } from "node:fs/promises";
3
- import { basename, dirname, join, relative, resolve, sep } from "node:path";
4
- import { z } from "zod";
5
- const CandidateSchema = z.object({
6
- operation: z.enum(["create", "update", "supersede"]),
7
- memoryId: z.string().nullable(),
8
- baseRevision: z.number().int().nonnegative().nullable(),
9
- supersedesMemoryId: z.string().nullable(),
10
- title: z.string().min(1),
11
- kind: z.enum(["procedural", "semantic", "episodic", "preference"]),
12
- summary: z.string().min(1),
13
- triggers: z.array(z.string()).min(1),
14
- priority: z.enum(["high", "medium", "low"]),
15
- detailCategory: z.enum(["preferences", "project-rules", "testing", "debugging", "git", "incidents", "lessons"]),
16
- detailBody: z.string().min(1),
17
- reviewAfter: z.string().nullable(),
18
- }).strict();
19
- const PendingSchema = z.object({
20
- schemaVersion: z.literal(1),
21
- pendingId: z.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]*$/u),
22
- createdAt: z.string().min(1),
23
- reason: z.literal("index_over_budget"),
24
- source: z.object({
25
- executionId: z.string().min(1),
26
- pruneTraceId: z.string().min(1).nullable(),
27
- }).strict(),
28
- candidate: CandidateSchema,
29
- }).strict();
30
- function within(root, candidate) {
31
- const path = relative(root, candidate);
32
- return path === "" || (path !== ".." && !path.startsWith(`..${sep}`));
33
- }
34
- async function pendingDirectory(agentDir) {
35
- const agentRoot = await realpath(agentDir);
36
- const memoryRoot = resolve(agentRoot, ".crew", "memory");
37
- await mkdir(memoryRoot, { recursive: true });
38
- const memoryStat = await lstat(memoryRoot);
39
- if (!memoryStat.isDirectory() || memoryStat.isSymbolicLink())
40
- throw new Error("local_memory_pending_path_invalid");
41
- const resolvedMemoryRoot = await realpath(memoryRoot);
42
- if (resolvedMemoryRoot !== memoryRoot || !within(agentRoot, resolvedMemoryRoot)) {
43
- throw new Error("local_memory_pending_path_invalid");
44
- }
45
- const directory = join(memoryRoot, "pending");
46
- try {
47
- const stat = await lstat(directory);
48
- if (!stat.isDirectory() || stat.isSymbolicLink())
49
- throw new Error("local_memory_pending_path_invalid");
50
- }
51
- catch (error) {
52
- if (error.code !== "ENOENT")
53
- throw error;
54
- await mkdir(directory);
55
- }
56
- const resolvedDirectory = await realpath(directory);
57
- if (resolvedDirectory !== directory || !within(agentRoot, resolvedDirectory)) {
58
- throw new Error("local_memory_pending_path_invalid");
59
- }
60
- return directory;
61
- }
62
- async function assertRegularFile(path) {
63
- try {
64
- const stat = await lstat(path);
65
- if (!stat.isFile() || stat.isSymbolicLink())
66
- throw new Error("local_memory_pending_path_invalid");
67
- }
68
- catch (error) {
69
- if (error.code !== "ENOENT")
70
- throw error;
71
- }
72
- }
73
- async function atomicWrite(path, content) {
74
- const temporary = join(dirname(path), `.${basename(path)}.tmp-${randomUUID()}`);
75
- try {
76
- await writeFile(temporary, content, { encoding: "utf8", mode: 0o600, flag: "wx" });
77
- await rename(temporary, path);
78
- }
79
- catch (error) {
80
- await rm(temporary, { force: true }).catch(() => undefined);
81
- throw error;
82
- }
83
- }
84
- export async function persistPendingMemoryCandidate(input) {
85
- const parsed = PendingSchema.safeParse(input.candidate);
86
- if (!parsed.success)
87
- throw new Error("local_memory_pending_invalid");
88
- const directory = await pendingDirectory(input.agentDir);
89
- const target = join(directory, `${parsed.data.pendingId}.json`);
90
- await assertRegularFile(target);
91
- await atomicWrite(target, `${JSON.stringify(parsed.data, null, 2)}\n`);
92
- return target;
93
- }
94
- export async function readPendingMemoryCandidates(input) {
95
- const directory = await pendingDirectory(input.agentDir);
96
- const names = (await readdir(directory)).filter((name) => name.endsWith(".json")).sort();
97
- const result = [];
98
- for (const name of names) {
99
- const path = join(directory, name);
100
- try {
101
- await assertRegularFile(path);
102
- const parsed = PendingSchema.safeParse(JSON.parse(await readFile(path, "utf8")));
103
- if (parsed.success && `${parsed.data.pendingId}.json` === name)
104
- result.push(parsed.data);
105
- }
106
- catch {
107
- // A damaged pending item is diagnosable and must not hide other candidates.
108
- }
109
- }
110
- return result;
111
- }
@@ -1,87 +0,0 @@
1
- import { createHash } from "node:crypto";
2
- import { lstat, mkdir, readFile, rm, writeFile } from "node:fs/promises";
3
- import { join } from "node:path";
4
- import { applyLocalMemoryResult, parseLocalMemoryResult } from "./write.js";
5
- export function isLocalMemoryV2Eligible(input) {
6
- return input.enabledForHandle
7
- && (input.localMemoryV2Initialized || (!input.agentHomePreexisted && input.memorySeedCreated))
8
- && input.memoryPrune;
9
- }
10
- const LOCAL_MEMORY_V2_MARKER = "v2-enabled";
11
- export async function initializeLocalMemoryV2(agentDir) {
12
- const crewDir = join(agentDir, ".crew");
13
- const memoryDir = join(crewDir, "memory");
14
- await mkdir(crewDir, { recursive: true });
15
- const crewStat = await lstat(crewDir);
16
- if (!crewStat.isDirectory() || crewStat.isSymbolicLink())
17
- throw new Error("local_memory_marker_path_invalid");
18
- await mkdir(memoryDir, { recursive: true });
19
- const memoryStat = await lstat(memoryDir);
20
- if (!memoryStat.isDirectory() || memoryStat.isSymbolicLink())
21
- throw new Error("local_memory_marker_path_invalid");
22
- try {
23
- await writeFile(join(memoryDir, LOCAL_MEMORY_V2_MARKER), "1\n", { encoding: "utf8", mode: 0o600, flag: "wx" });
24
- }
25
- catch (error) {
26
- if (error.code !== "EEXIST")
27
- throw error;
28
- }
29
- }
30
- export async function initializeLocalMemoryV2ForWorkspace(workspace, enabledForHandle) {
31
- const initialized = workspace.localMemoryV2Initialized === true;
32
- if (!enabledForHandle || initialized || workspace.localMemoryV2Initialized !== false
33
- || workspace.agentHomePreexisted === true || workspace.memorySeedCreated !== true)
34
- return initialized;
35
- try {
36
- await initializeLocalMemoryV2(workspace.dir);
37
- return true;
38
- }
39
- catch {
40
- return false;
41
- }
42
- }
43
- export function localMemoryResultPath(runDir, executionId) {
44
- const suffix = createHash("sha256").update(executionId, "utf8").digest("hex").slice(0, 32);
45
- return join(runDir, `.memory-result.v1-${suffix}.json`);
46
- }
47
- export function renderLocalMemoryResultInstructions(resultPath) {
48
- return `
49
-
50
- ## Structured memory prune (daemon-managed)
51
- 本轮长期记忆收尾只提交候选,不要直接编辑 $CREW_HOME/MEMORY.md 或 $CREW_HOME/notes/。
52
- 将一个合法 JSON 对象写入以下 daemon 提供的文件,写入完成后再结束本轮:
53
- ${resultPath}
54
-
55
- JSON 顶层必须包含:
56
- {"schemaVersion":1,"purpose":"memory_prune","baseCatalogRevision":0,"outcome":"no_change","changes":[],"statusChanges":[]}
57
- 每个 changes 项使用 operation=create|update|supersede、title、kind、summary、triggers、priority、detailCategory、detailBody、reviewAfter;create 的 memoryId/baseRevision/supersedesMemoryId 必须为 null,update/supersede 按现有索引填写版本和 id。
58
- 只提交可复用、与本次任务明确相关的少量候选;不要写入凭证、token、隐私或内部控制标记。daemon 会校验、去重、限制 MEMORY.md 大小,超出索引容量的候选会保存在 pending 中等待后续治理。
59
- `;
60
- }
61
- export async function commitLocalMemoryResultAfterExit(input) {
62
- if (input.exitCode !== 0)
63
- return { status: "skipped" };
64
- let raw;
65
- try {
66
- raw = await readFile(input.resultPath, "utf8");
67
- }
68
- catch (error) {
69
- if (error.code === "ENOENT")
70
- return { status: "missing" };
71
- return { status: "failed", error: error instanceof Error ? error.message : "read_failed" };
72
- }
73
- try {
74
- const result = parseLocalMemoryResult(raw);
75
- await applyLocalMemoryResult({
76
- agentDir: input.agentDir,
77
- result,
78
- executionId: input.executionId,
79
- pruneTraceId: input.pruneTraceId,
80
- });
81
- await rm(input.resultPath, { force: true });
82
- return { status: "applied" };
83
- }
84
- catch (error) {
85
- return { status: "failed", error: error instanceof Error ? error.message : "apply_failed" };
86
- }
87
- }
@@ -1 +0,0 @@
1
- export {};