@nowcrew/daemon 0.6.93-beta.6 → 0.6.93
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/agent-environment-execution.js +7 -2
- package/dist/cloud-report-upload.js +10 -1
- package/dist/execution-journal.js +1 -3
- package/dist/execution-protocol.js +1 -1
- package/dist/execution-runner.js +1 -4
- package/dist/local-execution-prompt.js +3 -8
- package/dist/local-executor.js +37 -70
- package/dist/local-memory/index-budget.js +3 -9
- package/dist/local-memory/inventory-cli.js +7 -45
- package/dist/local-memory/inventory-source.js +8 -15
- package/dist/local-memory/prompt.js +4 -20
- package/dist/local-memory/read-cli.js +77 -75
- package/dist/local-memory/rollout.js +11 -73
- package/dist/local-memory/runtime.js +13 -176
- package/dist/local-memory/write.js +21 -135
- package/dist/local-memory-diagnostics.js +13 -94
- package/dist/local-memory-telemetry.js +2 -19
- package/dist/machine-info.js +0 -3
- package/dist/marketplace-protocol.js +1 -1
- package/dist/marketplace-remote.js +1 -1
- package/dist/marketplace-runner.js +1 -1
- package/dist/memory-prune-diagnostics.js +4 -60
- package/dist/message-file-preview.js +1 -11
- package/dist/prompt.js +2 -6
- package/dist/runner.js +1 -1
- package/dist/runtimes/claude-thread-runner.js +4 -25
- package/dist/serve.js +3 -38
- package/dist/token.js +0 -1
- package/dist/workspace-control-handler.js +3 -16
- package/dist/workspace.js +2 -13
- package/package.json +1 -1
- package/dist/local-memory/execution-workspace.js +0 -62
- package/dist/local-memory/full-index-search.js +0 -47
- package/dist/local-memory/initialization-identity.js +0 -36
- package/dist/local-memory/inventory-v2.js +0 -282
- package/dist/local-memory/legacy-adapter.js +0 -233
- package/dist/local-memory/migration-access.js +0 -196
- package/dist/local-memory/migration-cli.js +0 -54
- package/dist/local-memory/migration-io.js +0 -199
- package/dist/local-memory/migration-plan.js +0 -286
- package/dist/local-memory/migration-schema.js +0 -55
- package/dist/local-memory/migration-state-invariants.js +0 -52
- package/dist/local-memory/migration-store.js +0 -401
- package/dist/local-memory/migration-tool-entry.js +0 -21
- package/dist/local-memory/storage-format.js +0 -152
- package/dist/local-memory/writer-mode.js +0 -13
- package/dist/memory-migration-controller.js +0 -86
- package/dist/memory-migration-inspect.js +0 -108
- package/dist/memory-migration-journal.js +0 -14
- package/dist/memory-migration-protocol.js +0 -14
- 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
|
-
+ `
|
|
90
|
-
+
|
|
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
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import { homedir, hostname, platform } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
3
4
|
const ALIYUN_MARKER = /alibaba|aliyun|alicloud/iu;
|
|
4
5
|
function readMarker(path) {
|
|
5
6
|
try {
|
|
@@ -28,7 +29,15 @@ export function detectCloudProvider(env = process.env, platformName = platform()
|
|
|
28
29
|
}
|
|
29
30
|
export function findOssUploadScript(env = process.env, exists = existsSync) {
|
|
30
31
|
const explicit = env.CREW_OSS_UPLOAD_SCRIPT?.trim();
|
|
31
|
-
|
|
32
|
+
if (explicit)
|
|
33
|
+
return exists(explicit) ? explicit : undefined;
|
|
34
|
+
const home = env.HOME?.trim() || homedir();
|
|
35
|
+
const candidates = [
|
|
36
|
+
join(home, ".claude", "skills", "oss-upload", "scripts", "oss_upload.py"),
|
|
37
|
+
join(home, ".codex", "skills", "oss-upload", "scripts", "oss_upload.py"),
|
|
38
|
+
join(home, ".agents", "skills", "oss-upload", "scripts", "oss_upload.py"),
|
|
39
|
+
];
|
|
40
|
+
return candidates.find(exists);
|
|
32
41
|
}
|
|
33
42
|
export function cloudReportUploadFacts(env = process.env, platformName = platform(), hostName = hostname(), readMarkerValue, exists) {
|
|
34
43
|
const provider = detectCloudProvider(env, platformName, hostName, readMarkerValue);
|
|
@@ -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", "
|
|
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:
|
|
322
|
+
runtime: RuntimeSchema,
|
|
323
323
|
model: z.string().optional(),
|
|
324
324
|
resumed: z.boolean(),
|
|
325
325
|
finalText: z.string().optional(),
|
package/dist/execution-runner.js
CHANGED
|
@@ -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
|
|
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"
|
|
@@ -881,7 +879,6 @@ export async function runExecution(config, input, dependencies) {
|
|
|
881
879
|
serverUrl: config.serverUrl,
|
|
882
880
|
token: credential.token,
|
|
883
881
|
agentId: credential.agentId,
|
|
884
|
-
...(dependencies.controlIdentity === undefined ? {} : dependencies.controlIdentity),
|
|
885
882
|
agentsRoot: config.agentsRoot,
|
|
886
883
|
reposRoot: resolveReposRoot(config.agentsRoot, config.reposRoot),
|
|
887
884
|
...(config.qaReportRoot === undefined ? {} : { qaReportRoot: config.qaReportRoot }),
|
|
@@ -2,9 +2,8 @@
|
|
|
2
2
|
* 本机执行提示词的合成与预算裁剪。
|
|
3
3
|
*
|
|
4
4
|
* server 提示词是权威部分,绝不裁剪;项目工作区提示词是绑定执行的强制部分,放不下就直接失败;
|
|
5
|
-
*
|
|
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")
|
|
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");
|
package/dist/local-executor.js
CHANGED
|
@@ -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";
|
|
@@ -39,18 +39,13 @@ import { projectSkillRuntimeDirectories, } from "./project-skills/runtime-launch
|
|
|
39
39
|
import { logIdentityRosterRuntimePrepared } from "./identity-roster-telemetry.js";
|
|
40
40
|
import { diffMemoryPruneNotes, evaluateMemoryPrunePostcondition, inspectMemoryPruneFilesWithinDeadline, logMemoryPruneDiagnosticsFailure, memoryPruneSnapshotFields, parseMemoryPruneTraceId, } from "./memory-prune-diagnostics.js";
|
|
41
41
|
import { createLocalMemoryTelemetry, logLocalMemoryContextPrepareFailure, logLocalMemoryDiagnosticsFailure, } from "./local-memory-telemetry.js";
|
|
42
|
-
import { LOCAL_MEMORY_DIAGNOSTICS_TIMEOUT_MS } from "./local-memory-diagnostics.js";
|
|
43
42
|
import { CodexStartupStageParser } from "./codex-startup-stage.js";
|
|
44
43
|
import { formatRuntimeStartupError } from "./runtime-startup-error.js";
|
|
45
44
|
import { logRuntimeEnvironmentContract } from "./runtime-environment-telemetry.js";
|
|
46
|
-
import { commitLocalMemoryResultAfterExit, isLocalMemoryV2Eligible, localMemoryResultPath as makeLocalMemoryResultPath, } from "./local-memory/runtime.js";
|
|
45
|
+
import { commitLocalMemoryResultAfterExit, initializeLocalMemoryV2ForWorkspace, isLocalMemoryV2Eligible, localMemoryResultPath as makeLocalMemoryResultPath, } from "./local-memory/runtime.js";
|
|
47
46
|
import { prepareLocalMemoryPrompt } from "./local-memory/prompt.js";
|
|
48
|
-
import { reportLocalMemoryV2Deferred } from "./local-memory/rollout.js";
|
|
49
|
-
import { prepareExecutionMemoryWorkspace } from "./local-memory/execution-workspace.js";
|
|
47
|
+
import { reportLocalMemoryV2Deferred, resolveLocalMemoryV2Rollout } from "./local-memory/rollout.js";
|
|
50
48
|
import { localMemoryV2Environment } from "./config.js";
|
|
51
|
-
import { detectLocalMemoryStorageVersion } from "./local-memory/storage-format.js";
|
|
52
|
-
import { MEMORY_STATE_GUARD_CAPABILITY, MEMORY_STORAGE_V2_CAPABILITY } from "./memory-migration-protocol.js";
|
|
53
|
-
import { resolveLocalMemoryWriterMode } from "./local-memory/writer-mode.js";
|
|
54
49
|
export class RuntimeRunningCallbackTimeoutError extends Error {
|
|
55
50
|
constructor(runtime, timeoutMs) {
|
|
56
51
|
super(`${runtime} runtime-running callback timed out after ${timeoutMs}ms`);
|
|
@@ -64,6 +59,7 @@ const STDERR_TAIL_CAP = 2_000;
|
|
|
64
59
|
// the old failure mode where a callback could hold execution slots forever.
|
|
65
60
|
const RUNTIME_RUNNING_CALLBACK_TIMEOUT_MS = 30_000;
|
|
66
61
|
const MEMORY_PRUNE_DIAGNOSTICS_TIMEOUT_MS = 2_000;
|
|
62
|
+
const LOCAL_MEMORY_DIAGNOSTICS_TIMEOUT_MS = 250;
|
|
67
63
|
const CLAUDE_INSTRUCTION_WARNING = "claude_additional_directory_instructions_unverified";
|
|
68
64
|
const CODEX_COLLABORATION_EXEC_WARNING = "codex_collaboration_disabled_on_exec";
|
|
69
65
|
function withoutClaudeInstructionFlag(env) {
|
|
@@ -215,12 +211,37 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
215
211
|
: "high"
|
|
216
212
|
: runtime.reasoning;
|
|
217
213
|
const providerFp = providerFingerprint(runtime.name, providerConfig);
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
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 };
|
|
221
243
|
input = { ...input, localMemoryV2Enabled: memoryRollout.enabled };
|
|
222
|
-
const localMemoryV2Initialized =
|
|
223
|
-
let localMemoryWriterMode = resolveLocalMemoryWriterMode(workspace, memoryRollout);
|
|
244
|
+
const localMemoryV2Initialized = await initializeLocalMemoryV2ForWorkspace(workspace, memoryRollout.enabled);
|
|
224
245
|
let materialized = null;
|
|
225
246
|
let knownAttachmentDirectory = null;
|
|
226
247
|
const startupReservationState = { current: null };
|
|
@@ -370,16 +391,13 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
370
391
|
let baseWakePrompt = `${resolvePrompt(input.wakePrompt, promptContext)}${attachmentPlan.promptSuffix}`;
|
|
371
392
|
memoryPruneTraceId = isolatedMaintenance ? null : parseMemoryPruneTraceId(baseWakePrompt);
|
|
372
393
|
if (memoryRollout.deferred)
|
|
373
|
-
reportLocalMemoryV2Deferred(input.executionId, input.handle, memoryPruneTraceId !== null || input.maintenancePurpose === "memory_prune"
|
|
374
|
-
localMemoryWriterMode = resolveLocalMemoryWriterMode(executionWorkspace, memoryRollout);
|
|
375
|
-
if (memoryPruneTraceId !== null && localMemoryWriterMode === "blocked")
|
|
376
|
-
throw new Error(memoryRollout.failure === "inspection_failed" ? "local_memory_inspection_failed" : "local_memory_v2_requires_migration");
|
|
394
|
+
reportLocalMemoryV2Deferred(input.executionId, input.handle, memoryPruneTraceId !== null || input.maintenancePurpose === "memory_prune");
|
|
377
395
|
if (memoryPruneTraceId !== null) {
|
|
378
396
|
executionWorkspace = await resolveMemoryPruneWorkLog(executionWorkspace, input);
|
|
379
397
|
promptContext = { ...promptContext, workspace: executionWorkspace };
|
|
380
398
|
baseWakePrompt = `${resolvePrompt(input.wakePrompt, promptContext)}${attachmentPlan.promptSuffix}`;
|
|
381
399
|
}
|
|
382
|
-
if (
|
|
400
|
+
if (input.localMemoryV2Enabled === true && memoryPruneTraceId !== null
|
|
383
401
|
&& isLocalMemoryV2Eligible({
|
|
384
402
|
enabledForHandle: true,
|
|
385
403
|
agentHomePreexisted: workspace.agentHomePreexisted === true,
|
|
@@ -393,11 +411,8 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
393
411
|
agentDir: executionWorkspace.dir, executionId: input.executionId, agentHandle: input.handle,
|
|
394
412
|
enabled: input.localMemoryV2Enabled === true, query: baseWakePrompt,
|
|
395
413
|
resultPath: localMemoryResultPath, maxBytes: input.maxSystemPromptBytes,
|
|
396
|
-
|
|
397
|
-
strictRollout: !isolatedMaintenance && !memoryRollout.legacyFallback && (localMemoryV2Environment(input.launch.serverUrl) === "dev"
|
|
398
|
-
|| detectLocalMemoryStorageVersion(executionWorkspace.memory) === 2),
|
|
414
|
+
strictRollout: !isolatedMaintenance && localMemoryV2Environment(input.launch.serverUrl) === "dev",
|
|
399
415
|
deferred: memoryRollout.deferred,
|
|
400
|
-
...(memoryRollout.legacyFallback ? { legacyFallback: true } : {}),
|
|
401
416
|
renderBase: (reservedPromptBytes) => resolvePrompt(input.systemPrompt, { ...promptContext, reservedPromptBytes }),
|
|
402
417
|
});
|
|
403
418
|
localMemoryResultPath = memoryPrompt.resultPath;
|
|
@@ -899,10 +914,6 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
899
914
|
exitCode,
|
|
900
915
|
workLogPath: executionWorkspace.workLogPath,
|
|
901
916
|
expectedWorkLogSha256: workLogSha256(executionWorkspace.workLog),
|
|
902
|
-
...(localMemoryWriterMode === "v2" ? {
|
|
903
|
-
writerCapabilities: [MEMORY_STORAGE_V2_CAPABILITY, MEMORY_STATE_GUARD_CAPABILITY],
|
|
904
|
-
requiredStorageVersion: 2,
|
|
905
|
-
} : {}),
|
|
906
917
|
});
|
|
907
918
|
const committed = exitCode !== 0 ? await commit() : await withWorkLogCommit(workLogExecutionKey(input.launch.agentsRoot, input.handle, input.taskKey), commit, dependencies.cancellation);
|
|
908
919
|
if (committed.status === "failed" || committed.status === "missing") {
|
|
@@ -916,33 +927,6 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
916
927
|
});
|
|
917
928
|
throw new Error(`local_memory_commit_failed: ${reason}`);
|
|
918
929
|
}
|
|
919
|
-
if (committed.status === "applied") {
|
|
920
|
-
if (localMemoryWriterMode === "v2" && (committed.storageVersionBefore !== 2 || committed.storageVersionAfter !== 2
|
|
921
|
-
|| committed.committedState !== true)) {
|
|
922
|
-
throw new Error("local_memory_commit_failed: local_memory_v2_commit_state_invalid");
|
|
923
|
-
}
|
|
924
|
-
const boundedIds = (ids) => ({
|
|
925
|
-
value: (ids ?? []).slice(0, 100).join(","),
|
|
926
|
-
count: ids?.length ?? 0,
|
|
927
|
-
truncated: (ids?.length ?? 0) > 100,
|
|
928
|
-
});
|
|
929
|
-
const created = boundedIds(committed.createdIds);
|
|
930
|
-
const updated = boundedIds(committed.updatedIds);
|
|
931
|
-
const statusChanged = boundedIds(committed.statusChangedIds);
|
|
932
|
-
const pending = boundedIds(committed.pendingIds);
|
|
933
|
-
dslog("local_memory.commit_applied", "结构化本地记忆提交已验证", {
|
|
934
|
-
execution_id: input.executionId, agent_handle: input.handle, prune_trace_id: memoryPruneTraceId,
|
|
935
|
-
storage_version_before: committed.storageVersionBefore,
|
|
936
|
-
storage_version_after: committed.storageVersionAfter,
|
|
937
|
-
committed_state: committed.committedState,
|
|
938
|
-
writer_mode: committed.storageVersionAfter === 2 && committed.committedState ? "v2" : "legacy",
|
|
939
|
-
created_id_count: created.count, created_ids: created.value, created_ids_truncated: created.truncated,
|
|
940
|
-
updated_id_count: updated.count, updated_ids: updated.value, updated_ids_truncated: updated.truncated,
|
|
941
|
-
status_changed_id_count: statusChanged.count, status_changed_ids: statusChanged.value,
|
|
942
|
-
status_changed_ids_truncated: statusChanged.truncated,
|
|
943
|
-
pending_id_count: pending.count, pending_ids: pending.value, pending_ids_truncated: pending.truncated,
|
|
944
|
-
});
|
|
945
|
-
}
|
|
946
930
|
memoryPruneFailurePhase = "runtime_finalize";
|
|
947
931
|
}
|
|
948
932
|
const errorTail = [
|
|
@@ -1066,23 +1050,6 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
1066
1050
|
after_incomplete_file_count: noteDiff.after_manifest.incomplete_file_count,
|
|
1067
1051
|
changes: serializedChanges.json,
|
|
1068
1052
|
});
|
|
1069
|
-
const pendingDiff = diffMemoryPruneNotes(memoryPruneBeforeSnapshot.pending_manifest
|
|
1070
|
-
?? { files: [], total_count: 0, omitted_count: 0, truncated: false }, snapshot.pending_manifest
|
|
1071
|
-
?? { files: [], total_count: 0, omitted_count: 0, truncated: false });
|
|
1072
|
-
const serializedPendingChanges = boundedDiagnosticJsonArray(pendingDiff.changes);
|
|
1073
|
-
const pendingChangesTruncated = pendingDiff.truncated || serializedPendingChanges.truncated;
|
|
1074
|
-
dslog("memory_prune.pending_changed", "长期记忆 pending 候选变化已评估", {
|
|
1075
|
-
execution_id: input.executionId,
|
|
1076
|
-
agent_handle: input.handle,
|
|
1077
|
-
task_key: input.taskKey,
|
|
1078
|
-
prune_trace_id: memoryPruneTraceId,
|
|
1079
|
-
change_count: pendingDiff.change_count,
|
|
1080
|
-
changes_truncated: pendingChangesTruncated,
|
|
1081
|
-
evidence_complete: pendingDiff.evidence_complete && !pendingChangesTruncated,
|
|
1082
|
-
before_total_count: pendingDiff.before_manifest.total_count,
|
|
1083
|
-
after_total_count: pendingDiff.after_manifest.total_count,
|
|
1084
|
-
changes: serializedPendingChanges.json,
|
|
1085
|
-
});
|
|
1086
1053
|
const postcondition = evaluateMemoryPrunePostcondition(memoryPruneBeforeSnapshot, snapshot, {
|
|
1087
1054
|
executorCompleted: memoryPruneExecutorCompleted,
|
|
1088
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(
|
|
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
|
-
|
|
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,
|
|
@@ -2,9 +2,9 @@ const CommandSchema = z.object({
|
|
|
2
2
|
"agent-dir": z.string().min(1).refine(isAbsolute),
|
|
3
3
|
"dry-run": z.literal(true),
|
|
4
4
|
}).strict();
|
|
5
|
-
const USAGE = "
|
|
5
|
+
const USAGE = "只读记忆盘点与转换预演,不写文件,不支持 --apply。\n"
|
|
6
6
|
+ "pnpm --filter @nowcrew/daemon exec tsx src/local-memory/inventory-cli.ts --agent-dir <单个Agent绝对路径> --dry-run\n"
|
|
7
|
-
+ "报告包含原始记忆,需作为敏感本地资料保存;退出码0
|
|
7
|
+
+ "报告包含原始记忆,需作为敏感本地资料保存;退出码0仅代表盘点及格式预演无阻塞,不代表批准迁移。\n";
|
|
8
8
|
export async function runMemoryInventoryCommand(args, io) {
|
|
9
9
|
if (args.length === 1 && args[0] === "--help") {
|
|
10
10
|
io.out(USAGE);
|
|
@@ -24,55 +24,19 @@ export async function runMemoryInventoryCommand(args, io) {
|
|
|
24
24
|
try {
|
|
25
25
|
const scan = await scanMemorySources(command["agent-dir"]);
|
|
26
26
|
const scanComplete = scan.issues.length === 0;
|
|
27
|
-
const
|
|
28
|
-
let storageVersion = null;
|
|
29
|
-
let detectionIssue = null;
|
|
30
|
-
try {
|
|
31
|
-
storageVersion = detectLocalMemoryStorageVersion(memory);
|
|
32
|
-
}
|
|
33
|
-
catch (error) {
|
|
34
|
-
detectionIssue = {
|
|
35
|
-
code: error instanceof Error && /^[a-z0-9_]+$/u.test(error.message)
|
|
36
|
-
? error.message
|
|
37
|
-
: "local_memory_storage_version_invalid",
|
|
38
|
-
path: "MEMORY.md",
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
const v2 = storageVersion === 2
|
|
42
|
-
? await inspectV2MemoryInventory(scan.root, scan.sources, scanComplete)
|
|
43
|
-
: null;
|
|
44
|
-
const analysis = detectionIssue
|
|
45
|
-
? { entries: [], issues: [detectionIssue], preview: null, rawMemory: memory }
|
|
46
|
-
: v2?.analysis ?? previewMemoryInventory(scan.sources, scanComplete);
|
|
27
|
+
const analysis = previewMemoryInventory(scan.sources, scanComplete);
|
|
47
28
|
const complete = scanComplete && !analysis.issues.some((issue) => issue.code === "missing_detail");
|
|
48
29
|
const issues = [...scan.issues, ...analysis.issues];
|
|
49
30
|
const referenced = new Set(analysis.entries.map((entry) => entry.sourceFields.file));
|
|
50
|
-
const
|
|
51
|
-
const files = reportSources.map(({ path, bytes, sha256 }) => ({ path, bytes, sha256 }));
|
|
52
|
-
const orphanNotes = files.filter((file) => file.path.startsWith("notes/") && !referenced.has(file.path)).map((file) => file.path);
|
|
53
|
-
const indexedEntries = analysis.entries.filter((entry) => entry.format === "managed").length;
|
|
54
|
-
const legacyEntries = analysis.entries.length - indexedEntries;
|
|
31
|
+
const files = scan.sources.map(({ path, bytes, sha256 }) => ({ path, bytes, sha256 }));
|
|
55
32
|
const report = {
|
|
56
33
|
schemaVersion: 1, mode: "dry-run", agentDir: scan.root, applySupported: false,
|
|
57
34
|
complete, scanComplete, requiresHumanReview: true,
|
|
58
|
-
|
|
59
|
-
// A dry-run does not create a shadow index or server state. Existing
|
|
60
|
-
// schema1 cards therefore remain legacy; indexedEntries is only coverage
|
|
61
|
-
// information, not proof that the Agent entered shadow mode.
|
|
62
|
-
storageMode: storageVersion === 2 ? (v2?.writeReady ? "v2_write" : "repair_required")
|
|
63
|
-
: detectionIssue ? "repair_required" : "legacy",
|
|
64
|
-
contextMode: storageVersion === 2 ? (v2?.writeReady ? "bounded_preview" : "blocked")
|
|
65
|
-
: detectionIssue ? "blocked" : "legacy_context",
|
|
66
|
-
readerMode: storageVersion === 2 ? "v2" : detectionIssue ? "blocked" : "legacy",
|
|
67
|
-
detectedIndexVersion: storageVersion,
|
|
68
|
-
migrationCoverage: { detectedIndexVersion: storageVersion, storageVersion, indexedEntries, legacyEntries, unindexedFiles: orphanNotes.length,
|
|
69
|
-
totalEntries: analysis.entries.length, complete },
|
|
70
|
-
coverage: ["MEMORY.md", "notes/**", ...(storageVersion === 2 ? [".crew/memory/migration.v1.json"] : []),
|
|
71
|
-
".crew/memory/pending/**", "根目录的 .memory-result.v1-*.json"],
|
|
35
|
+
coverage: ["MEMORY.md", "notes/**", ".crew/memory/pending/**", "根目录的 .memory-result.v1-*.json"],
|
|
72
36
|
limitations: ["不扫描历史执行目录;result清单仅覆盖Agent根目录。", "两遍摘要比对不等同跨文件原子快照,正式迁移仍需停止写入并重新核对。"],
|
|
73
|
-
limits: inventoryLimits, totalBytes:
|
|
37
|
+
limits: inventoryLimits, totalBytes: scan.totalBytes, files, ...analysis, issues,
|
|
74
38
|
pending: files.filter((file) => file.path.startsWith(".crew/memory/pending/")).map((file) => file.path),
|
|
75
|
-
orphanNotes,
|
|
39
|
+
orphanNotes: files.filter((file) => file.path.startsWith("notes/") && !referenced.has(file.path)).map((file) => file.path),
|
|
76
40
|
resultFiles: files.filter((file) => file.path.startsWith(".memory-result.v1-")).map((file) => file.path),
|
|
77
41
|
};
|
|
78
42
|
io.out(JSON.stringify(report, null, 2) + "\n");
|
|
@@ -95,5 +59,3 @@ import { parseArgs } from "node:util";
|
|
|
95
59
|
import { z } from "zod";
|
|
96
60
|
import { inventoryLimits, scanMemorySources } from "./inventory-source.js";
|
|
97
61
|
import { previewMemoryInventory } from "./inventory-preview.js";
|
|
98
|
-
import { inspectV2MemoryInventory } from "./inventory-v2.js";
|
|
99
|
-
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
|
|
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
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
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
|
-
|
|
159
|
-
|
|
160
|
-
|
|
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
|
|
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.
|
|
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
|
|
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
|
-
|
|
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
|
// 强制基础指令与契约本身仍无法共存时,保留既有非结构化兼容回退,不裁基础指令。
|