@nowcrew/daemon 0.6.93-beta.1 → 0.6.93-beta.11

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 (43) hide show
  1. package/dist/agent-environment-execution.js +7 -2
  2. package/dist/execution-journal.js +1 -3
  3. package/dist/execution-protocol.js +1 -1
  4. package/dist/execution-runner.js +1 -3
  5. package/dist/local-execution-prompt.js +3 -8
  6. package/dist/local-executor.js +35 -64
  7. package/dist/local-memory/index-budget.js +3 -9
  8. package/dist/local-memory/inventory-cli.js +1 -23
  9. package/dist/local-memory/inventory-source.js +8 -15
  10. package/dist/local-memory/prompt.js +4 -20
  11. package/dist/local-memory/read-cli.js +77 -75
  12. package/dist/local-memory/rollout.js +8 -54
  13. package/dist/local-memory/runtime.js +13 -148
  14. package/dist/local-memory/write.js +19 -125
  15. package/dist/local-memory-diagnostics.js +2 -41
  16. package/dist/local-memory-telemetry.js +2 -19
  17. package/dist/machine-info.js +0 -3
  18. package/dist/memory-prune-diagnostics.js +4 -60
  19. package/dist/message-file-preview.js +1 -11
  20. package/dist/prompt.js +1 -5
  21. package/dist/runner.js +1 -1
  22. package/dist/serve.js +3 -36
  23. package/dist/token.js +0 -1
  24. package/dist/workspace-control-handler.js +3 -16
  25. package/dist/workspace.js +2 -10
  26. package/package.json +1 -1
  27. package/dist/local-memory/execution-workspace.js +0 -55
  28. package/dist/local-memory/full-index-search.js +0 -47
  29. package/dist/local-memory/initialization-identity.js +0 -23
  30. package/dist/local-memory/legacy-adapter.js +0 -233
  31. package/dist/local-memory/migration-access.js +0 -184
  32. package/dist/local-memory/migration-cli.js +0 -54
  33. package/dist/local-memory/migration-io.js +0 -199
  34. package/dist/local-memory/migration-plan.js +0 -283
  35. package/dist/local-memory/migration-schema.js +0 -55
  36. package/dist/local-memory/migration-store.js +0 -429
  37. package/dist/local-memory/migration-tool-entry.js +0 -21
  38. package/dist/local-memory/storage-format.js +0 -152
  39. package/dist/memory-migration-controller.js +0 -84
  40. package/dist/memory-migration-inspect.js +0 -75
  41. package/dist/memory-migration-journal.js +0 -14
  42. package/dist/memory-migration-protocol.js +0 -14
  43. package/dist/memory-migration-runtime.js +0 -305
@@ -86,8 +86,13 @@ export async function prepareEnvironmentExecutionSnapshot(input) {
86
86
  directory,
87
87
  prompt: `## Per-directory environment configuration\nThis execution uses environment revision ${parsed.data.revision}. `
88
88
  + `Directory layers are independent: Agent root is inherited by training and each repo; sibling repos do not inherit each other. `
89
- + `When running a command in a training or repository directory, use the immutable environment harness: `
90
- + `\`node ${JSON.stringify(join(directory, "run.mjs"))} --cwd <absolute-directory> -- <command> [arguments...]\`. `
89
+ + `The runtime starts in the task directory \`$CREW_TASK_DIR\`, which uses Agent-root variables. `
90
+ + `Training variables are available only when the harness \`--cwd\` points to \`$QA_RUNTIME_ROOT\` (the training directory); `
91
+ + `do not pass \`$PWD\` unless it is that directory. For a training command, use: `
92
+ + `\`node ${JSON.stringify(join(directory, "run.mjs"))} --cwd \"$QA_RUNTIME_ROOT\" -- <command> [arguments...]\`. `
93
+ + `For a repository command, pass that repository's absolute root to \`--cwd\`. `
94
+ + `Keep task outputs under \`$CREW_TASK_DIR\` and pass absolute values such as \`$CREW_TASK_DIR/outputs\` to --report-root, --output-dir, and --runtime-dir; `
95
+ + `do not let changing the harness cwd relocate task outputs. `
91
96
  + `Changing directory alone does not reload process variables. Do not source mutable .env files or print environment values.`,
92
97
  };
93
98
  }
@@ -8,7 +8,7 @@ import { provePosixProcessGroupStopped } from "./execution-posix-stop-proof.js";
8
8
  export { JournalLockedError, JournalLockCorruptionError } from "./execution-journal-lock.js";
9
9
  const ExecutionIdSchema = z.string().uuid();
10
10
  const TimestampSchema = z.string().datetime({ offset: true });
11
- const RuntimeSchema = z.enum(["claude", "codex", "kimi", "hermes", "opencode", "deepseek-harness", "memory_migration", "pi"]);
11
+ const RuntimeSchema = z.enum(["claude", "codex", "kimi", "hermes", "opencode", "deepseek-harness", "pi"]);
12
12
  const RUNTIME_READY_SUFFIX = ".runtime-ready";
13
13
  const RawJournalEntrySchema = z.object({
14
14
  executionId: ExecutionIdSchema,
@@ -523,8 +523,6 @@ export function createExecutionJournal(agentsRoot, options = {}) {
523
523
  }
524
524
  };
525
525
  const pruneInternal = async () => {
526
- if (options.retainCompleted)
527
- return;
528
526
  const protectedExecutionIds = new Set(options.protectedExecutionIds?.() ?? []);
529
527
  const entries = await readAll();
530
528
  const acknowledged = entries
@@ -319,7 +319,7 @@ const RawExecutionCompletedSchema = z.object({
319
319
  providerErrorCategory: ProviderErrorCategorySchema.optional(),
320
320
  providerHttpStatus: ProviderHttpStatusSchema.optional(),
321
321
  providerErrorCode: ProviderErrorCodeSchema.optional(),
322
- runtime: z.union([RuntimeSchema, z.literal("memory_migration")]),
322
+ runtime: RuntimeSchema,
323
323
  model: z.string().optional(),
324
324
  resumed: z.boolean(),
325
325
  finalText: z.string().optional(),
@@ -1,7 +1,6 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { dirname, join, resolve } from "node:path";
3
3
  import { isLocalMemoryV2Enabled, localMemoryV2Environment, resolveReposRoot } from "./config.js";
4
- import { detectLocalMemoryStorageVersion } from "./local-memory/storage-format.js";
5
4
  import { DaemonToServerExecutionFrameSchema, ExecutionCompletedSchema, ExecutionRejectedSchema, ExecutionStartSchema, } from "./execution-protocol.js";
6
5
  import { JournalConflictError } from "./execution-journal.js";
7
6
  import { boundExecutionFrame } from "./execution-event-limit.js";
@@ -839,9 +838,8 @@ export async function runExecution(config, input, dependencies) {
839
838
  const localMemoryGuardInBase = localMemoryV2Environment(config.serverUrl) === "custom";
840
839
  const boundedSystemPrompt = (context) => {
841
840
  const enabled = context.workspace.localMemoryV2Enabled ?? localMemoryV2Enabled;
842
- const systemPromptWithLocalFacts = withLocalExecutionFacts(`${spec.instructions.systemPrompt}${enabled && localMemoryGuardInBase && detectLocalMemoryStorageVersion(context.workspace.memory) !== 2 ? renderLocalMemoryV2WriteGuard() : ""}`, baseSystemPromptBudget, {
841
+ const systemPromptWithLocalFacts = withLocalExecutionFacts(`${spec.instructions.systemPrompt}${enabled && localMemoryGuardInBase ? renderLocalMemoryV2WriteGuard() : ""}`, baseSystemPromptBudget, {
843
842
  ...(selfTraining ? { isolateLocalMemory: true } : {}),
844
- memoryQuery: spec.instructions.wakePrompt,
845
843
  collaborationTools: config.codexCollaborationTools && spec.runtime.name === "codex",
846
844
  });
847
845
  const base = typeof systemPromptWithLocalFacts === "string"
@@ -2,9 +2,8 @@
2
2
  * 本机执行提示词的合成与预算裁剪。
3
3
  *
4
4
  * server 提示词是权威部分,绝不裁剪;项目工作区提示词是绑定执行的强制部分,放不下就直接失败;
5
- * 本机事实与旧格式预览可按预算省略;schema2身份和置顶规则必须保留,放不下则拒绝执行。
5
+ * 只有本机事实(CREW_HOME / work-log / MEMORY 注入)是可选增强,按整段丢弃或按字节截断。
6
6
  */
7
- import { detectLocalMemoryStorageVersion } from "./local-memory/storage-format.js";
8
7
  import { buildCollaborationToolsPrompt, buildRuntimeWorkspacePrompt, capMemoryForInject, capWorkLogForInject, } from "./prompt.js";
9
8
  const trainingProjectJson = (project) => JSON.stringify({
10
9
  repositoryKey: project.repositoryKey,
@@ -58,14 +57,10 @@ export function withLocalExecutionFacts(serverPrompt, maxBytes, options = {}) {
58
57
  }
59
58
  const remaining = maxBytes - requiredBytes - Math.max(0, reservedPromptBytes);
60
59
  const factsBytes = Buffer.byteLength(localFacts, "utf8");
61
- if (factsBytes > remaining) {
62
- if (!options.isolateLocalMemory && detectLocalMemoryStorageVersion(workspace.memory) === 2) {
63
- throw new Error("local_memory_identity_budget_exceeded");
64
- }
60
+ if (factsBytes > remaining)
65
61
  return `${serverPrompt}${collaborationPrompt}${mandatoryWorkspacePrompt}`;
66
- }
67
62
  const memoryHeading = "\n\n## Injected MEMORY.md (bounded local context)\n";
68
- const memory = options.isolateLocalMemory ? "" : capMemoryForInject(workspace.memory, undefined, remaining - factsBytes - Buffer.byteLength(memoryHeading, "utf8"), options.memoryQuery);
63
+ const memory = options.isolateLocalMemory ? "" : capMemoryForInject(workspace.memory, undefined, remaining - factsBytes - Buffer.byteLength(memoryHeading, "utf8"));
69
64
  const memorySection = memory === "" ? "" : memoryHeading + memory;
70
65
  const workLogSection = workLog === "" ? "" : `\n\n## Injected work-log (bounded local context)\n${workLog}`;
71
66
  const workLogBudget = remaining - factsBytes - Buffer.byteLength(memorySection, "utf8");
@@ -6,7 +6,7 @@ import { createInterface } from "node:readline";
6
6
  import { readFile, rm, writeFile } from "node:fs/promises";
7
7
  import { join } from "node:path";
8
8
  import { cleanupSelfTrainingRunDir } from "./self-training-workspace.js";
9
- import { rotateAgentSession, safeKey, } from "./workspace.js";
9
+ import { prepareWorkspace, rotateAgentSession, safeKey, } from "./workspace.js";
10
10
  import { CLAUDE_ADDITIONAL_DIRECTORY_INSTRUCTIONS_ENV, isClaudeAdditionalDirectoryInstructionsSupported, probeClaudeVersion, spawnClaude, } from "./runtimes/claude.js";
11
11
  import { spawnCodex } from "./runtimes/codex.js";
12
12
  import { DEEPSEEK_CODEX_MODEL, DEEPSEEK_CODEX_REASONING_LEVELS, materializeDeepSeekCodexHome, } from "./runtimes/codex-deepseek-config.js";
@@ -42,13 +42,10 @@ import { createLocalMemoryTelemetry, logLocalMemoryContextPrepareFailure, logLoc
42
42
  import { CodexStartupStageParser } from "./codex-startup-stage.js";
43
43
  import { formatRuntimeStartupError } from "./runtime-startup-error.js";
44
44
  import { logRuntimeEnvironmentContract } from "./runtime-environment-telemetry.js";
45
- import { commitLocalMemoryResultAfterExit, isLocalMemoryV2Eligible, localMemoryResultPath as makeLocalMemoryResultPath, } from "./local-memory/runtime.js";
45
+ import { commitLocalMemoryResultAfterExit, initializeLocalMemoryV2ForWorkspace, isLocalMemoryV2Eligible, localMemoryResultPath as makeLocalMemoryResultPath, } from "./local-memory/runtime.js";
46
46
  import { prepareLocalMemoryPrompt } from "./local-memory/prompt.js";
47
- import { reportLocalMemoryV2Deferred } from "./local-memory/rollout.js";
48
- import { prepareExecutionMemoryWorkspace } from "./local-memory/execution-workspace.js";
47
+ import { reportLocalMemoryV2Deferred, resolveLocalMemoryV2Rollout } from "./local-memory/rollout.js";
49
48
  import { localMemoryV2Environment } from "./config.js";
50
- import { detectLocalMemoryStorageVersion } from "./local-memory/storage-format.js";
51
- import { MEMORY_STATE_GUARD_CAPABILITY, MEMORY_STORAGE_V2_CAPABILITY } from "./memory-migration-protocol.js";
52
49
  export class RuntimeRunningCallbackTimeoutError extends Error {
53
50
  constructor(runtime, timeoutMs) {
54
51
  super(`${runtime} runtime-running callback timed out after ${timeoutMs}ms`);
@@ -214,11 +211,37 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
214
211
  : "high"
215
212
  : runtime.reasoning;
216
213
  const providerFp = providerFingerprint(runtime.name, providerConfig);
217
- const preparedMemory = await prepareExecutionMemoryWorkspace(input, isolatedMaintenance, dependencies.cancellation);
218
- let workspace = preparedMemory.workspace;
219
- const memoryRollout = preparedMemory.mode;
214
+ let workspace;
215
+ try {
216
+ workspace = await awaitWithCancellation(prepareWorkspace({
217
+ agentsRoot: input.launch.agentsRoot,
218
+ handle: input.handle,
219
+ cliPath: input.launch.cliPath,
220
+ executionId: input.executionId,
221
+ ...(input.keyMode === undefined ? {} : { keyMode: input.keyMode }),
222
+ ...(input.taskKey === undefined ? {} : { taskKey: input.taskKey }),
223
+ ...(input.resumeKey === undefined ? {} : { resumeKey: input.resumeKey }),
224
+ ...(input.sessionContextFingerprint === undefined
225
+ ? {}
226
+ : { sessionContextFingerprint: input.sessionContextFingerprint }),
227
+ ...(input.launch.description ? { description: input.launch.description } : {}),
228
+ ...(isolatedMaintenance ? { skipLocalMemory: true } : {}),
229
+ }), dependencies.cancellation);
230
+ }
231
+ catch (error) {
232
+ logLocalMemoryContextPrepareFailure({
233
+ executionId: input.executionId,
234
+ agentHandle: input.handle,
235
+ ...(input.taskKey === undefined ? {} : { taskKey: input.taskKey }),
236
+ }, "workspace_prepare", error);
237
+ throw error;
238
+ }
239
+ const memoryRollout = await resolveLocalMemoryV2Rollout({ workspace, handle: input.handle,
240
+ serverUrl: input.launch.serverUrl, requested: input.localMemoryV2Enabled === true,
241
+ selfTraining: isolatedMaintenance, ...(input.launch.description ? { description: input.launch.description } : {}) });
242
+ workspace = { ...workspace, localMemoryV2Enabled: memoryRollout.enabled };
220
243
  input = { ...input, localMemoryV2Enabled: memoryRollout.enabled };
221
- const localMemoryV2Initialized = preparedMemory.initialized;
244
+ const localMemoryV2Initialized = await initializeLocalMemoryV2ForWorkspace(workspace, memoryRollout.enabled);
222
245
  let materialized = null;
223
246
  let knownAttachmentDirectory = null;
224
247
  const startupReservationState = { current: null };
@@ -368,7 +391,7 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
368
391
  let baseWakePrompt = `${resolvePrompt(input.wakePrompt, promptContext)}${attachmentPlan.promptSuffix}`;
369
392
  memoryPruneTraceId = isolatedMaintenance ? null : parseMemoryPruneTraceId(baseWakePrompt);
370
393
  if (memoryRollout.deferred)
371
- reportLocalMemoryV2Deferred(input.executionId, input.handle, memoryPruneTraceId !== null || input.maintenancePurpose === "memory_prune", memoryRollout.legacyFallback);
394
+ reportLocalMemoryV2Deferred(input.executionId, input.handle, memoryPruneTraceId !== null || input.maintenancePurpose === "memory_prune");
372
395
  if (memoryPruneTraceId !== null) {
373
396
  executionWorkspace = await resolveMemoryPruneWorkLog(executionWorkspace, input);
374
397
  promptContext = { ...promptContext, workspace: executionWorkspace };
@@ -388,11 +411,8 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
388
411
  agentDir: executionWorkspace.dir, executionId: input.executionId, agentHandle: input.handle,
389
412
  enabled: input.localMemoryV2Enabled === true, query: baseWakePrompt,
390
413
  resultPath: localMemoryResultPath, maxBytes: input.maxSystemPromptBytes,
391
- storageVersion: detectLocalMemoryStorageVersion(executionWorkspace.memory),
392
- strictRollout: !isolatedMaintenance && !memoryRollout.legacyFallback && (localMemoryV2Environment(input.launch.serverUrl) === "dev"
393
- || detectLocalMemoryStorageVersion(executionWorkspace.memory) === 2),
414
+ strictRollout: !isolatedMaintenance && localMemoryV2Environment(input.launch.serverUrl) === "dev",
394
415
  deferred: memoryRollout.deferred,
395
- ...(memoryRollout.legacyFallback ? { legacyFallback: true } : {}),
396
416
  renderBase: (reservedPromptBytes) => resolvePrompt(input.systemPrompt, { ...promptContext, reservedPromptBytes }),
397
417
  });
398
418
  localMemoryResultPath = memoryPrompt.resultPath;
@@ -894,10 +914,6 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
894
914
  exitCode,
895
915
  workLogPath: executionWorkspace.workLogPath,
896
916
  expectedWorkLogSha256: workLogSha256(executionWorkspace.workLog),
897
- ...(input.localMemoryV2Enabled === true ? {
898
- writerCapabilities: [MEMORY_STORAGE_V2_CAPABILITY, MEMORY_STATE_GUARD_CAPABILITY],
899
- requiredStorageVersion: 2,
900
- } : {}),
901
917
  });
902
918
  const committed = exitCode !== 0 ? await commit() : await withWorkLogCommit(workLogExecutionKey(input.launch.agentsRoot, input.handle, input.taskKey), commit, dependencies.cancellation);
903
919
  if (committed.status === "failed" || committed.status === "missing") {
@@ -911,34 +927,6 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
911
927
  });
912
928
  throw new Error(`local_memory_commit_failed: ${reason}`);
913
929
  }
914
- if (committed.status === "applied") {
915
- if (input.localMemoryV2Enabled === true
916
- && (committed.storageVersionBefore !== 2 || committed.storageVersionAfter !== 2
917
- || committed.committedState !== true)) {
918
- throw new Error("local_memory_commit_failed: local_memory_v2_commit_state_invalid");
919
- }
920
- const boundedIds = (ids) => ({
921
- value: (ids ?? []).slice(0, 100).join(","),
922
- count: ids?.length ?? 0,
923
- truncated: (ids?.length ?? 0) > 100,
924
- });
925
- const created = boundedIds(committed.createdIds);
926
- const updated = boundedIds(committed.updatedIds);
927
- const statusChanged = boundedIds(committed.statusChangedIds);
928
- const pending = boundedIds(committed.pendingIds);
929
- dslog("local_memory.commit_applied", "结构化本地记忆提交已验证", {
930
- execution_id: input.executionId, agent_handle: input.handle, prune_trace_id: memoryPruneTraceId,
931
- storage_version_before: committed.storageVersionBefore,
932
- storage_version_after: committed.storageVersionAfter,
933
- committed_state: committed.committedState,
934
- writer_mode: committed.storageVersionAfter === 2 && committed.committedState ? "v2" : "legacy",
935
- created_id_count: created.count, created_ids: created.value, created_ids_truncated: created.truncated,
936
- updated_id_count: updated.count, updated_ids: updated.value, updated_ids_truncated: updated.truncated,
937
- status_changed_id_count: statusChanged.count, status_changed_ids: statusChanged.value,
938
- status_changed_ids_truncated: statusChanged.truncated,
939
- pending_id_count: pending.count, pending_ids: pending.value, pending_ids_truncated: pending.truncated,
940
- });
941
- }
942
930
  memoryPruneFailurePhase = "runtime_finalize";
943
931
  }
944
932
  const errorTail = [
@@ -1062,23 +1050,6 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
1062
1050
  after_incomplete_file_count: noteDiff.after_manifest.incomplete_file_count,
1063
1051
  changes: serializedChanges.json,
1064
1052
  });
1065
- const pendingDiff = diffMemoryPruneNotes(memoryPruneBeforeSnapshot.pending_manifest
1066
- ?? { files: [], total_count: 0, omitted_count: 0, truncated: false }, snapshot.pending_manifest
1067
- ?? { files: [], total_count: 0, omitted_count: 0, truncated: false });
1068
- const serializedPendingChanges = boundedDiagnosticJsonArray(pendingDiff.changes);
1069
- const pendingChangesTruncated = pendingDiff.truncated || serializedPendingChanges.truncated;
1070
- dslog("memory_prune.pending_changed", "长期记忆 pending 候选变化已评估", {
1071
- execution_id: input.executionId,
1072
- agent_handle: input.handle,
1073
- task_key: input.taskKey,
1074
- prune_trace_id: memoryPruneTraceId,
1075
- change_count: pendingDiff.change_count,
1076
- changes_truncated: pendingChangesTruncated,
1077
- evidence_complete: pendingDiff.evidence_complete && !pendingChangesTruncated,
1078
- before_total_count: pendingDiff.before_manifest.total_count,
1079
- after_total_count: pendingDiff.after_manifest.total_count,
1080
- changes: serializedPendingChanges.json,
1081
- });
1082
1053
  const postcondition = evaluateMemoryPrunePostcondition(memoryPruneBeforeSnapshot, snapshot, {
1083
1054
  executorCompleted: memoryPruneExecutorCompleted,
1084
1055
  ...(memoryPruneRuntimeExitCode === undefined
@@ -50,17 +50,14 @@ export function selectLocalMemoryIndexEntries(entries, budget, options = {}) {
50
50
  .map((entry) => entry.id);
51
51
  const protectedIds = options.protectedIds ?? new Set();
52
52
  const protectedEntries = projectable.filter((entry) => protectedIds.has(entry.id)).sort(compareEntries);
53
- const candidates = projectable.filter((entry) => !protectedIds.has(entry.id)).sort((left, right) => (options.relevance?.(right) ?? 0) - (options.relevance?.(left) ?? 0) || compareEntries(left, right));
53
+ const candidates = projectable.filter((entry) => !protectedIds.has(entry.id)).sort(compareEntries);
54
54
  const selected = [];
55
55
  let renderedLength = 0;
56
- let renderedBytes = 0;
57
56
  for (const entry of protectedEntries) {
58
57
  selected.push(entry);
59
58
  renderedLength += cardLength(entry, selected.length > 1);
60
- renderedBytes += Buffer.byteLength(renderLocalMemoryIndexCard(entry), "utf8") + (selected.length > 1 ? 1 : 0);
61
59
  }
62
- if (selected.length > budget.entryCap || renderedLength > budget.indexCodePointCap
63
- || renderedBytes > (budget.indexByteCap ?? Infinity)) {
60
+ if (selected.length > budget.entryCap || renderedLength > budget.indexCodePointCap) {
64
61
  return {
65
62
  entries: selected,
66
63
  omittedIds: [...omittedIds, ...candidates.map((entry) => entry.id)],
@@ -68,15 +65,12 @@ export function selectLocalMemoryIndexEntries(entries, budget, options = {}) {
68
65
  };
69
66
  }
70
67
  for (const entry of candidates) {
71
- const bytes = Buffer.byteLength(renderLocalMemoryIndexCard(entry), "utf8") + (selected.length > 0 ? 1 : 0);
72
- if (selected.length >= budget.entryCap || renderedLength + cardLength(entry, selected.length > 0) > budget.indexCodePointCap
73
- || renderedBytes + bytes > (budget.indexByteCap ?? Infinity)) {
68
+ if (selected.length >= budget.entryCap || renderedLength + cardLength(entry, selected.length > 0) > budget.indexCodePointCap) {
74
69
  omittedIds.push(entry.id);
75
70
  continue;
76
71
  }
77
72
  selected.push(entry);
78
73
  renderedLength += cardLength(entry, selected.length > 1);
79
- renderedBytes += bytes;
80
74
  }
81
75
  return {
82
76
  entries: selected,
@@ -29,35 +29,14 @@ export async function runMemoryInventoryCommand(args, io) {
29
29
  const issues = [...scan.issues, ...analysis.issues];
30
30
  const referenced = new Set(analysis.entries.map((entry) => entry.sourceFields.file));
31
31
  const files = scan.sources.map(({ path, bytes, sha256 }) => ({ path, bytes, sha256 }));
32
- const orphanNotes = files.filter((file) => file.path.startsWith("notes/") && !referenced.has(file.path)).map((file) => file.path);
33
- const storageVersion = (() => {
34
- try {
35
- return detectLocalMemoryStorageVersion(scan.sources.find((source) => source.path === "MEMORY.md")?.content ?? "");
36
- }
37
- catch {
38
- return null;
39
- }
40
- })();
41
- const indexedEntries = analysis.entries.filter((entry) => entry.format === "managed").length;
42
- const legacyEntries = analysis.entries.length - indexedEntries;
43
32
  const report = {
44
33
  schemaVersion: 1, mode: "dry-run", agentDir: scan.root, applySupported: false,
45
34
  complete, scanComplete, requiresHumanReview: true,
46
- // 只读派生观测,不改变资格判断,也不新增持久化状态。
47
- // A dry-run does not create a shadow index or server state. Existing
48
- // schema1 cards therefore remain legacy; indexedEntries is only coverage
49
- // information, not proof that the Agent entered shadow mode.
50
- storageMode: storageVersion === 2 ? "v2_write" : "legacy",
51
- contextMode: storageVersion === 2 ? "bounded_preview" : "legacy_context",
52
- readerMode: storageVersion === 2 ? "v2" : "legacy",
53
- detectedIndexVersion: storageVersion,
54
- migrationCoverage: { detectedIndexVersion: storageVersion, storageVersion, indexedEntries, legacyEntries, unindexedFiles: orphanNotes.length,
55
- totalEntries: analysis.entries.length, complete },
56
35
  coverage: ["MEMORY.md", "notes/**", ".crew/memory/pending/**", "根目录的 .memory-result.v1-*.json"],
57
36
  limitations: ["不扫描历史执行目录;result清单仅覆盖Agent根目录。", "两遍摘要比对不等同跨文件原子快照,正式迁移仍需停止写入并重新核对。"],
58
37
  limits: inventoryLimits, totalBytes: scan.totalBytes, files, ...analysis, issues,
59
38
  pending: files.filter((file) => file.path.startsWith(".crew/memory/pending/")).map((file) => file.path),
60
- orphanNotes,
39
+ orphanNotes: files.filter((file) => file.path.startsWith("notes/") && !referenced.has(file.path)).map((file) => file.path),
61
40
  resultFiles: files.filter((file) => file.path.startsWith(".memory-result.v1-")).map((file) => file.path),
62
41
  };
63
42
  io.out(JSON.stringify(report, null, 2) + "\n");
@@ -80,4 +59,3 @@ import { parseArgs } from "node:util";
80
59
  import { z } from "zod";
81
60
  import { inventoryLimits, scanMemorySources } from "./inventory-source.js";
82
61
  import { previewMemoryInventory } from "./inventory-preview.js";
83
- import { detectLocalMemoryStorageVersion } from "./storage-format.js";
@@ -73,8 +73,7 @@ export async function readMemoryInventorySource(root, file, budget) {
73
73
  await handle.close();
74
74
  }
75
75
  }
76
- export async function scanMemorySources(agentDir, options = {}) {
77
- const includeOperationalFiles = options.includeOperationalFiles ?? true;
76
+ export async function scanMemorySources(agentDir) {
78
77
  const root = await realpath(agentDir);
79
78
  if ((await lstat(agentDir)).isSymbolicLink())
80
79
  throw new Error("agent_root_symlink");
@@ -125,14 +124,10 @@ export async function scanMemorySources(agentDir, options = {}) {
125
124
  }
126
125
  await visit("MEMORY.md");
127
126
  await visit("notes", true);
128
- const resultNames = includeOperationalFiles
129
- ? (await readdir(root)).filter((name) => /^\.memory-result\.v1-.*\.json$/u.test(name)).sort()
130
- : [];
131
- if (includeOperationalFiles) {
132
- await visit(".crew/memory/pending", true);
133
- for (const name of resultNames)
134
- await visit(name);
135
- }
127
+ await visit(".crew/memory/pending", true);
128
+ const resultNames = (await readdir(root)).filter((name) => /^\.memory-result\.v1-.*\.json$/u.test(name)).sort();
129
+ for (const name of resultNames)
130
+ await visit(name);
136
131
  // 第二遍比对摘要和目录清单,发现扫描期间的变化就拒绝完整性结论;这不是跨文件事务锁。
137
132
  for (const source of sources) {
138
133
  try {
@@ -155,11 +150,9 @@ export async function scanMemorySources(agentDir, options = {}) {
155
150
  addIssue("source_changed", file);
156
151
  }
157
152
  }
158
- if (includeOperationalFiles) {
159
- const currentResults = (await readdir(root)).filter((name) => /^\.memory-result\.v1-.*\.json$/u.test(name)).sort();
160
- if (JSON.stringify(currentResults) !== JSON.stringify(resultNames))
161
- addIssue("source_changed", ".memory-result.v1-*.json");
162
- }
153
+ const currentResults = (await readdir(root)).filter((name) => /^\.memory-result\.v1-.*\.json$/u.test(name)).sort();
154
+ if (JSON.stringify(currentResults) !== JSON.stringify(resultNames))
155
+ addIssue("source_changed", ".memory-result.v1-*.json");
163
156
  if (await realpath(agentDir) !== root)
164
157
  addIssue("source_changed", "agent_root");
165
158
  return { root, sources: sources.sort((a, b) => a.path.localeCompare(b.path)), issues, totalBytes };
@@ -1,7 +1,7 @@
1
1
  import { dslog } from "../slog.js";
2
2
  import { renderPendingMemoryContext, searchPendingMemoryCandidates } from "./pending.js";
3
3
  import { renderLocalMemoryResultInstructions, renderLocalMemoryV2WriteGuard } from "./runtime.js";
4
- import { LOCAL_MEMORY_V2_DEFERRED_GUIDANCE, LOCAL_MEMORY_LEGACY_GUIDANCE } from "./rollout.js";
4
+ import { LOCAL_MEMORY_V2_DEFERRED_GUIDANCE } from "./rollout.js";
5
5
  const PENDING_READ_GUIDANCE = "\n\nV2新增资料预览未命中不代表没有资料;先查有效索引,必要时用 crew-memory search-pending --query '关键词',按 nextCursor 续查;用 read-pending --id ID 分页读正文。容量暂存资料不能覆盖现行规则,未应用修改不作为记忆。用 crew-memory --help 查看参数。\n";
6
6
  export async function prepareLocalMemoryPrompt(input) {
7
7
  let pending = "";
@@ -22,11 +22,10 @@ export async function prepareLocalMemoryPrompt(input) {
22
22
  }
23
23
  }
24
24
  const original = input.renderBase(0);
25
- const guard = input.legacyFallback ? LOCAL_MEMORY_LEGACY_GUIDANCE : input.deferred ? LOCAL_MEMORY_V2_DEFERRED_GUIDANCE
25
+ const guard = input.deferred ? LOCAL_MEMORY_V2_DEFERRED_GUIDANCE
26
26
  : input.strictRollout && input.enabled && input.resultPath === null ? renderLocalMemoryV2WriteGuard() : "";
27
- const structured = input.resultPath === null ? "" : renderLocalMemoryResultInstructions(input.resultPath, input.storageVersion);
27
+ const structured = input.resultPath === null ? "" : renderLocalMemoryResultInstructions(input.resultPath);
28
28
  const guidance = input.enabled ? PENDING_READ_GUIDANCE : "";
29
- let requiredMemoryBudgetError;
30
29
  // 短命令指引放在当前轮,独立于可舍弃预览;收尾契约仍保持系统与当前轮两份。
31
30
  for (const choice of [
32
31
  { suffix: pending + guard + structured, wake: guard + structured + guidance },
@@ -34,26 +33,11 @@ export async function prepareLocalMemoryPrompt(input) {
34
33
  { suffix: guard + structured, wake: guard + structured },
35
34
  ]) {
36
35
  const reserved = Buffer.byteLength(choice.suffix + choice.wake, "utf8");
37
- let base;
38
- try {
39
- base = reserved === 0 ? original : input.renderBase(reserved);
40
- }
41
- catch (error) {
42
- // 可选候选先让出预算;最后仍不能容纳身份/置顶时必须拒绝,不能省略必需记忆。
43
- if (input.storageVersion === 2 && error instanceof Error
44
- && ["local_memory_identity_budget_exceeded", "local_memory_pinned_budget_exceeded"].includes(error.message)) {
45
- requiredMemoryBudgetError = error;
46
- continue;
47
- }
48
- throw error;
49
- }
50
- const systemPrompt = base + choice.suffix;
36
+ const systemPrompt = (reserved === 0 ? original : input.renderBase(reserved)) + choice.suffix;
51
37
  if (input.maxBytes === undefined || Buffer.byteLength(systemPrompt + choice.wake, "utf8") <= input.maxBytes) {
52
38
  return { systemPrompt, resultPath: input.resultPath, wakePromptSuffix: choice.wake };
53
39
  }
54
40
  }
55
- if (requiredMemoryBudgetError)
56
- throw requiredMemoryBudgetError;
57
41
  if (input.strictRollout)
58
42
  throw new Error("local_memory_v2_prompt_over_budget");
59
43
  // 强制基础指令与契约本身仍无法共存时,保留既有非结构化兼容回退,不裁基础指令。