@nowcrew/daemon 0.6.99 → 0.6.100-beta.1
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/cloud-report-upload.js +1 -10
- package/dist/computer-main.js +0 -0
- package/dist/console.js +17 -2
- package/dist/execution-journal.js +3 -1
- package/dist/execution-protocol.js +1 -1
- package/dist/execution-runner.js +4 -1
- package/dist/local-execution-prompt.js +8 -3
- package/dist/local-executor.js +78 -55
- package/dist/local-memory/commit-telemetry.js +30 -0
- package/dist/local-memory/execution-workspace.js +62 -0
- package/dist/local-memory/full-index-search.js +47 -0
- package/dist/local-memory/index-budget.js +9 -3
- package/dist/local-memory/initialization-identity.js +36 -0
- package/dist/local-memory/inventory-cli.js +52 -8
- package/dist/local-memory/inventory-source.js +15 -8
- package/dist/local-memory/inventory-v2.js +362 -0
- package/dist/local-memory/legacy-adapter.js +233 -0
- package/dist/local-memory/migration-access.js +242 -0
- package/dist/local-memory/migration-cli.js +54 -0
- package/dist/local-memory/migration-io.js +199 -0
- package/dist/local-memory/migration-pending.js +9 -0
- package/dist/local-memory/migration-plan.js +432 -0
- package/dist/local-memory/migration-schema.js +55 -0
- package/dist/local-memory/migration-state-invariants.js +65 -0
- package/dist/local-memory/migration-store.js +414 -0
- package/dist/local-memory/migration-tool-entry.js +21 -0
- package/dist/local-memory/pending-query.js +5 -2
- package/dist/local-memory/pending-schema.js +1 -1
- package/dist/local-memory/pending.js +16 -2
- package/dist/local-memory/prompt.js +21 -5
- package/dist/local-memory/read-cli.js +98 -76
- package/dist/local-memory/release-artifact-guard.js +55 -0
- package/dist/local-memory/rollout.js +79 -11
- package/dist/local-memory/runtime.js +191 -17
- package/dist/local-memory/source-path.js +13 -0
- package/dist/local-memory/storage-format.js +249 -0
- package/dist/local-memory/write.js +190 -21
- package/dist/local-memory/writer-mode.js +13 -0
- package/dist/local-memory-diagnostics.js +96 -15
- package/dist/local-memory-telemetry.js +20 -3
- package/dist/machine-info.js +6 -1
- package/dist/main.js +0 -0
- package/dist/marketplace-controller.js +21 -8
- package/dist/marketplace-maintenance-bridge.js +79 -0
- package/dist/marketplace-maintenance-protocol.js +40 -0
- package/dist/marketplace-protocol.js +1 -1
- package/dist/marketplace-remote.js +1 -1
- package/dist/marketplace-runner.js +1 -1
- package/dist/marketplace-serve.js +4 -2
- package/dist/memory-migration-controller.js +89 -0
- package/dist/memory-migration-inspect.js +108 -0
- package/dist/memory-migration-journal.js +14 -0
- package/dist/memory-migration-protocol.js +14 -0
- package/dist/memory-migration-runtime.js +305 -0
- package/dist/memory-prune-diagnostics.js +74 -4
- package/dist/message-file-preview.js +11 -1
- package/dist/prompt.js +9 -17
- package/dist/runner.js +1 -1
- package/dist/runtimes/claude-thread-runner.js +25 -4
- package/dist/runtimes/pi-runner.js +1 -0
- package/dist/runtimes/pi.js +54 -20
- package/dist/serve.js +39 -4
- package/dist/token.js +1 -0
- package/dist/workspace-control-handler.js +16 -3
- package/dist/workspace.js +13 -2
- package/package.json +9 -10
|
@@ -6,6 +6,8 @@ import { dslog } from "../slog.js";
|
|
|
6
6
|
import { LocalMemoryChangeSchema, LocalMemorySingleLineText, validateLocalMemoryChange, } from "./candidate.js";
|
|
7
7
|
import { LOCAL_MEMORY_INDEX_CODE_POINT_CAP, LOCAL_MEMORY_INDEX_ENTRY_CAP, LOCAL_MEMORY_TOTAL_CODE_POINT_CAP, renderGeneratedIndex, selectLocalMemoryIndexEntries, } from "./index-budget.js";
|
|
8
8
|
import { persistPendingMemoryCandidate, readPendingMemoryCandidates, removePendingMemoryCandidate, } from "./pending.js";
|
|
9
|
+
import { assertLocalMemoryStorageSize, detectLocalMemoryStorageVersion, LOCAL_MEMORY_GENERATED_INDEX_PATTERN, LOCAL_MEMORY_NOTE_BYTE_CAP, LOCAL_MEMORY_READ_BYTE_CAP, parseFullMemoryIndex, replaceFullMemoryIndex, selectLocalMemoryPreview, } from "./storage-format.js";
|
|
10
|
+
import { assertMemoryInventoryHealth, assertMemoryWriterAllowed, withMemoryAccess } from "./migration-access.js";
|
|
9
11
|
const StatusChangeSchema = z.object({
|
|
10
12
|
memoryId: LocalMemorySingleLineText,
|
|
11
13
|
expectedRevision: z.number().int().positive(),
|
|
@@ -20,6 +22,7 @@ const ResultSchema = z.object({
|
|
|
20
22
|
outcome: z.enum(["no_change", "proposed", "conflict", "failed"]),
|
|
21
23
|
changes: z.array(LocalMemoryChangeSchema).max(50),
|
|
22
24
|
statusChanges: z.array(StatusChangeSchema).max(50),
|
|
25
|
+
workLogDisposition: z.enum(["clear", "preserve"]).optional(),
|
|
23
26
|
}).strict();
|
|
24
27
|
const GENERATED_INDEX_PATTERN = /<!--\s*NOWWORK:MEMORY_INDEX:BEGIN\s+schema=1\s*-->[\s\S]*?<!--\s*NOWWORK:MEMORY_INDEX:END\s*-->/u;
|
|
25
28
|
const INDEX_HEADING = /^##\s+Knowledge Index\s*$/mu;
|
|
@@ -90,12 +93,14 @@ function parseCards(section) {
|
|
|
90
93
|
});
|
|
91
94
|
}
|
|
92
95
|
export function parseLocalMemoryIndex(memory) {
|
|
96
|
+
if (detectLocalMemoryStorageVersion(memory) === 2)
|
|
97
|
+
return parseFullMemoryIndex(memory);
|
|
93
98
|
const section = indexSection(memory);
|
|
94
99
|
const generated = GENERATED_INDEX_PATTERN.exec(section)?.[0];
|
|
95
100
|
return parseCards(generated ?? section);
|
|
96
101
|
}
|
|
97
102
|
function parseUnmarkedMemoryIndex(memory) {
|
|
98
|
-
const section = indexSection(memory).replace(
|
|
103
|
+
const section = indexSection(memory).replace(LOCAL_MEMORY_GENERATED_INDEX_PATTERN, "");
|
|
99
104
|
return parseCards(section);
|
|
100
105
|
}
|
|
101
106
|
function replaceGeneratedIndex(memory, entries) {
|
|
@@ -122,11 +127,36 @@ function noteSection(id, revision, title, body) {
|
|
|
122
127
|
}
|
|
123
128
|
function notePattern(id) {
|
|
124
129
|
const escaped = id.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
|
|
125
|
-
return new RegExp(`<!--\\s*NOWWORK:MEMORY_ENTRY:BEGIN\\s+id=${escaped}[^>]*-->[\\s\\S]*?<!--\\s*NOWWORK:MEMORY_ENTRY:END\\s+id=${escaped}\\s*-->\\s*`, "u");
|
|
130
|
+
return new RegExp(`<!--\\s*NOWWORK:MEMORY_ENTRY:BEGIN\\s+id=${escaped}(?=\\s|-->)[^>]*-->[\\s\\S]*?<!--\\s*NOWWORK:MEMORY_ENTRY:END\\s+id=${escaped}\\s*-->\\s*`, "u");
|
|
131
|
+
}
|
|
132
|
+
function uniqueNoteSection(content, id) {
|
|
133
|
+
const escaped = id.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
|
|
134
|
+
const begins = [...content.matchAll(new RegExp(`<!--\\s*NOWWORK:MEMORY_ENTRY:BEGIN\\s+id=${escaped}(?=\\s|-->)`, "gu"))];
|
|
135
|
+
const ends = [...content.matchAll(new RegExp(`<!--\\s*NOWWORK:MEMORY_ENTRY:END\\s+id=${escaped}\\s*-->`, "gu"))];
|
|
136
|
+
if (begins.length === 0 && ends.length === 0)
|
|
137
|
+
return null;
|
|
138
|
+
const section = notePattern(id).exec(content)?.[0];
|
|
139
|
+
if (begins.length !== 1 || ends.length !== 1 || section === undefined
|
|
140
|
+
|| (section.match(/NOWWORK:MEMORY_ENTRY:(?:BEGIN|END)/gu) ?? []).length !== 2) {
|
|
141
|
+
throw new Error("local_memory_note_anchor_invalid");
|
|
142
|
+
}
|
|
143
|
+
return section;
|
|
126
144
|
}
|
|
127
145
|
function replaceNote(content, id, replacement) {
|
|
128
146
|
const pattern = notePattern(id);
|
|
129
|
-
return
|
|
147
|
+
return uniqueNoteSection(content, id) !== null ? content.replace(pattern, () => replacement) : null;
|
|
148
|
+
}
|
|
149
|
+
function updateNoteRevision(content, id, revision) {
|
|
150
|
+
const section = uniqueNoteSection(content, id);
|
|
151
|
+
const beginning = `<!-- NOWWORK:MEMORY_ENTRY:BEGIN id=${id} `;
|
|
152
|
+
const ending = `<!-- NOWWORK:MEMORY_ENTRY:END id=${id} -->`;
|
|
153
|
+
if (section === null || content.split(beginning).length !== 2 || content.split(ending).length !== 2) {
|
|
154
|
+
throw new Error("local_memory_note_anchor_missing");
|
|
155
|
+
}
|
|
156
|
+
const updated = section.replace(/^(<!--\s*NOWWORK:MEMORY_ENTRY:BEGIN\s+id=[A-Za-z0-9_-]+\s+revision=)[1-9][0-9]*(\s*-->)/u, (_match, prefix, suffix) => `${prefix}${revision}${suffix}`);
|
|
157
|
+
if (!updated.startsWith(`${beginning}revision=${revision} -->`))
|
|
158
|
+
throw new Error("local_memory_note_anchor_missing");
|
|
159
|
+
return content.replace(notePattern(id), () => updated);
|
|
130
160
|
}
|
|
131
161
|
function validateCandidate(result) {
|
|
132
162
|
if (result.outcome === "no_change" && (result.changes.length > 0 || result.statusChanges.length > 0)) {
|
|
@@ -180,6 +210,25 @@ function stableId(executionId, index, change) {
|
|
|
180
210
|
const input = [executionId, String(index), change.operation, change.title, change.summary, change.detailBody].join("\0");
|
|
181
211
|
return `mem_${createHash("sha256").update(input, "utf8").digest("hex").slice(0, 12)}`;
|
|
182
212
|
}
|
|
213
|
+
function migrationPendingId(change) {
|
|
214
|
+
// Keep the digest independent of object insertion order for callers that
|
|
215
|
+
// reach applyLocalMemoryResult directly instead of the JSON/Zod boundary.
|
|
216
|
+
const canonicalChange = {
|
|
217
|
+
operation: change.operation,
|
|
218
|
+
memoryId: change.memoryId,
|
|
219
|
+
baseRevision: change.baseRevision,
|
|
220
|
+
supersedesMemoryId: change.supersedesMemoryId,
|
|
221
|
+
title: change.title,
|
|
222
|
+
kind: change.kind,
|
|
223
|
+
summary: change.summary,
|
|
224
|
+
triggers: change.triggers,
|
|
225
|
+
priority: change.priority,
|
|
226
|
+
detailCategory: change.detailCategory,
|
|
227
|
+
detailBody: change.detailBody,
|
|
228
|
+
reviewAfter: change.reviewAfter,
|
|
229
|
+
};
|
|
230
|
+
return `pending-migration-${createHash("sha256").update(JSON.stringify(canonicalChange), "utf8").digest("hex").slice(0, 12)}`;
|
|
231
|
+
}
|
|
183
232
|
function normalize(value) {
|
|
184
233
|
return value.normalize("NFKC").toLocaleLowerCase().replace(/\s+/gu, " ").trim();
|
|
185
234
|
}
|
|
@@ -199,14 +248,14 @@ function detailCategoryFromFile(file) {
|
|
|
199
248
|
return DETAIL_CATEGORIES.has(category) ? category : null;
|
|
200
249
|
}
|
|
201
250
|
function readNoteBody(content, id) {
|
|
202
|
-
const
|
|
203
|
-
if (
|
|
251
|
+
const section = uniqueNoteSection(content, id);
|
|
252
|
+
if (section === null)
|
|
204
253
|
return null;
|
|
205
|
-
const bodyStart =
|
|
206
|
-
const bodyEnd =
|
|
254
|
+
const bodyStart = section.indexOf("\n\n");
|
|
255
|
+
const bodyEnd = section.indexOf(`\n<!-- NOWWORK:MEMORY_ENTRY:END id=${id}`, bodyStart + 2);
|
|
207
256
|
if (bodyStart < 0 || bodyEnd < 0)
|
|
208
257
|
return null;
|
|
209
|
-
return
|
|
258
|
+
return section.slice(bodyStart + 2, bodyEnd).trim();
|
|
210
259
|
}
|
|
211
260
|
function archivedChange(entry, detailBody) {
|
|
212
261
|
const detailCategory = detailCategoryFromFile(entry.file);
|
|
@@ -230,6 +279,34 @@ function archivedChange(entry, detailBody) {
|
|
|
230
279
|
function emptyResult() {
|
|
231
280
|
return { createdIds: [], updatedIds: [], statusChangedIds: [], deferredIds: [], pendingIds: [] };
|
|
232
281
|
}
|
|
282
|
+
async function stageMigrationDeferredPending(input) {
|
|
283
|
+
if (input.result.outcome !== "proposed")
|
|
284
|
+
return emptyResult();
|
|
285
|
+
if (input.result.statusChanges.length > 0 || input.result.changes.some((change) => change.operation !== "create")) {
|
|
286
|
+
throw new Error("local_memory_migration_pending_create_only");
|
|
287
|
+
}
|
|
288
|
+
const executionId = input.executionId ?? "unknown";
|
|
289
|
+
const pending = input.result.changes.map((change) => ({
|
|
290
|
+
schemaVersion: 1,
|
|
291
|
+
pendingId: migrationPendingId(change),
|
|
292
|
+
createdAt: new Date().toISOString(),
|
|
293
|
+
reason: "legacy_requires_migration",
|
|
294
|
+
source: { executionId, pruneTraceId: input.pruneTraceId ?? null },
|
|
295
|
+
candidate: change,
|
|
296
|
+
}));
|
|
297
|
+
await assertMemoryAccessDirectory(input.agentDir);
|
|
298
|
+
await withMemoryAccess(input.agentDir, async () => {
|
|
299
|
+
const memory = await readFile(join(input.agentDir, "MEMORY.md"), "utf8").catch(() => null);
|
|
300
|
+
if (memory === null || detectLocalMemoryStorageVersion(memory) !== 1) {
|
|
301
|
+
throw new Error("local_memory_migration_pending_storage_invalid");
|
|
302
|
+
}
|
|
303
|
+
for (const candidate of pending) {
|
|
304
|
+
await persistPendingMemoryCandidate({ agentDir: input.agentDir, candidate });
|
|
305
|
+
}
|
|
306
|
+
}, { pendingOnly: true });
|
|
307
|
+
const pendingIds = [...new Set(pending.map((candidate) => candidate.pendingId))];
|
|
308
|
+
return { createdIds: [], updatedIds: [], statusChangedIds: [], deferredIds: pendingIds, pendingIds };
|
|
309
|
+
}
|
|
233
310
|
function isWithin(root, candidate) {
|
|
234
311
|
const path = relative(root, candidate);
|
|
235
312
|
return path === "" || (path !== ".." && !path.startsWith(`..${sep}`));
|
|
@@ -244,12 +321,29 @@ async function assertWritablePaths(agentDir, memoryPath) {
|
|
|
244
321
|
if (!memoryStat.isFile() || memoryStat.isSymbolicLink())
|
|
245
322
|
throw new Error("local_memory_memory_path_invalid");
|
|
246
323
|
}
|
|
324
|
+
async function assertMemoryAccessDirectory(agentDir) {
|
|
325
|
+
try {
|
|
326
|
+
const stat = await lstat(join(agentDir, ".crew/memory"));
|
|
327
|
+
if (!stat.isDirectory() || stat.isSymbolicLink())
|
|
328
|
+
throw new Error("local_memory_pending_path_invalid");
|
|
329
|
+
}
|
|
330
|
+
catch (error) {
|
|
331
|
+
if (error.code === "ENOENT")
|
|
332
|
+
return;
|
|
333
|
+
throw error;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
247
336
|
async function readNote(path) {
|
|
248
337
|
try {
|
|
249
338
|
const stat = await lstat(path);
|
|
250
339
|
if (!stat.isFile() || stat.isSymbolicLink())
|
|
251
340
|
throw new Error("local_memory_note_path_invalid");
|
|
252
|
-
|
|
341
|
+
if (stat.size > LOCAL_MEMORY_NOTE_BYTE_CAP)
|
|
342
|
+
throw new Error("local_memory_note_byte_limit");
|
|
343
|
+
const content = await readFile(path, "utf8");
|
|
344
|
+
if (Buffer.byteLength(content, "utf8") > LOCAL_MEMORY_NOTE_BYTE_CAP)
|
|
345
|
+
throw new Error("local_memory_note_byte_limit");
|
|
346
|
+
return content;
|
|
253
347
|
}
|
|
254
348
|
catch (error) {
|
|
255
349
|
if (error.code === "ENOENT")
|
|
@@ -269,6 +363,11 @@ async function atomicWrite(path, content) {
|
|
|
269
363
|
}
|
|
270
364
|
}
|
|
271
365
|
function projectIndex(memory, entries, protectedIds) {
|
|
366
|
+
if (detectLocalMemoryStorageVersion(memory) === 2) {
|
|
367
|
+
const updated = replaceFullMemoryIndex(memory, entries);
|
|
368
|
+
selectLocalMemoryPreview(updated);
|
|
369
|
+
return { memory: updated, omittedIds: [], reason: null };
|
|
370
|
+
}
|
|
272
371
|
const empty = replaceGeneratedIndex(memory, []);
|
|
273
372
|
if (codePointLength(empty) > LOCAL_MEMORY_TOTAL_CODE_POINT_CAP)
|
|
274
373
|
throw new Error("local_memory_memory_over_budget");
|
|
@@ -306,12 +405,29 @@ async function withApplyLease(agentDir, operation) {
|
|
|
306
405
|
}
|
|
307
406
|
export async function applyLocalMemoryResult(input) {
|
|
308
407
|
validateCandidate(input.result);
|
|
309
|
-
if (input.
|
|
408
|
+
if (input.migrationDeferredPending)
|
|
409
|
+
return stageMigrationDeferredPending(input);
|
|
410
|
+
if (input.result.outcome !== "proposed" && input.requiredStorageVersion !== 2)
|
|
310
411
|
return emptyResult();
|
|
311
412
|
const memoryPath = join(input.agentDir, "MEMORY.md");
|
|
312
|
-
|
|
313
|
-
|
|
413
|
+
if (input.result.outcome === "proposed")
|
|
414
|
+
await assertWritablePaths(input.agentDir, memoryPath);
|
|
415
|
+
await assertMemoryAccessDirectory(input.agentDir);
|
|
416
|
+
return withApplyLease(input.agentDir, async () => withMemoryAccess(input.agentDir, async () => {
|
|
417
|
+
await assertMemoryWriterAllowed(input.agentDir, input.writerCapabilities ?? []);
|
|
418
|
+
const memoryStat = await lstat(memoryPath);
|
|
419
|
+
if (memoryStat.size > 16 * 1024 * 1024)
|
|
420
|
+
throw new Error("local_memory_storage_byte_limit");
|
|
314
421
|
const memory = await readFile(memoryPath, "utf8");
|
|
422
|
+
assertLocalMemoryStorageSize(memory);
|
|
423
|
+
const fullStorage = detectLocalMemoryStorageVersion(memory) === 2;
|
|
424
|
+
if (input.requiredStorageVersion === 2 && !fullStorage) {
|
|
425
|
+
throw new Error("local_memory_v2_state_invalid_before_commit");
|
|
426
|
+
}
|
|
427
|
+
// A V2 no-op is still a commit decision. Return only after the access
|
|
428
|
+
// lock and committed-state writer gate have validated the same snapshot.
|
|
429
|
+
if (input.result.outcome !== "proposed")
|
|
430
|
+
return emptyResult();
|
|
315
431
|
if (parseUnmarkedMemoryIndex(memory).length > 0)
|
|
316
432
|
throw new Error("local_memory_legacy_index_requires_migration");
|
|
317
433
|
let entries = [...parseLocalMemoryIndex(memory)];
|
|
@@ -342,6 +458,26 @@ export async function applyLocalMemoryResult(input) {
|
|
|
342
458
|
const pendingCandidates = [];
|
|
343
459
|
const resolvedPendingIds = new Set();
|
|
344
460
|
const noteUpdates = new Map();
|
|
461
|
+
const noteCache = new Map();
|
|
462
|
+
let totalReadBytes = Buffer.byteLength(memory, "utf8");
|
|
463
|
+
const readStoredNote = async (path) => {
|
|
464
|
+
const cached = noteUpdates.get(path) ?? noteCache.get(path);
|
|
465
|
+
if (cached !== undefined)
|
|
466
|
+
return cached;
|
|
467
|
+
const stat = await lstat(path).catch((error) => {
|
|
468
|
+
if (error.code === "ENOENT")
|
|
469
|
+
return null;
|
|
470
|
+
throw error;
|
|
471
|
+
});
|
|
472
|
+
if (stat !== null && totalReadBytes + stat.size > LOCAL_MEMORY_READ_BYTE_CAP)
|
|
473
|
+
throw new Error("local_memory_read_byte_limit");
|
|
474
|
+
const content = await readNote(path);
|
|
475
|
+
totalReadBytes += Buffer.byteLength(content, "utf8");
|
|
476
|
+
if (totalReadBytes > LOCAL_MEMORY_READ_BYTE_CAP)
|
|
477
|
+
throw new Error("local_memory_read_byte_limit");
|
|
478
|
+
noteCache.set(path, content);
|
|
479
|
+
return content;
|
|
480
|
+
};
|
|
345
481
|
let allowActiveEviction = false;
|
|
346
482
|
const randomId = input.randomId ?? randomUUID;
|
|
347
483
|
const executionId = input.executionId ?? "unknown";
|
|
@@ -383,10 +519,21 @@ export async function applyLocalMemoryResult(input) {
|
|
|
383
519
|
if (existingCreate !== undefined && !matchesCreateCandidate(existingCreate, change)) {
|
|
384
520
|
throw new Error("local_memory_duplicate_candidate");
|
|
385
521
|
}
|
|
386
|
-
if (existingCreate === undefined && entries.some((entry) =>
|
|
387
|
-
|| normalize(entry.summary) === normalize(change.summary))) {
|
|
522
|
+
if (existingCreate === undefined && entries.some((entry) => (!fullStorage || entry.status === "active" || entry.status === "pinned")
|
|
523
|
+
&& (normalize(entry.title) === normalize(change.title) || normalize(entry.summary) === normalize(change.summary)))) {
|
|
388
524
|
throw new Error("local_memory_duplicate_candidate");
|
|
389
525
|
}
|
|
526
|
+
if (existingCreate !== undefined) {
|
|
527
|
+
if (existingCreate.revision !== 1 || existingCreate.status !== "active") {
|
|
528
|
+
throw new Error("local_memory_create_replay_conflict");
|
|
529
|
+
}
|
|
530
|
+
const stored = await readStoredNote(resolve(input.agentDir, existingCreate.file));
|
|
531
|
+
if (uniqueNoteSection(stored, id)?.trimEnd() !== noteSection(id, 1, change.title, change.detailBody).trimEnd()) {
|
|
532
|
+
throw new Error("local_memory_create_replay_conflict");
|
|
533
|
+
}
|
|
534
|
+
// A proven duplicate is a no-op; it must never rewrite a newer body or status.
|
|
535
|
+
continue;
|
|
536
|
+
}
|
|
390
537
|
}
|
|
391
538
|
const revision = existingCreate?.revision ?? (current === undefined ? 1 : current.revision + 1);
|
|
392
539
|
const entry = existingCreate ?? entryFromChange(change, id, revision, current?.status ?? "active");
|
|
@@ -400,7 +547,7 @@ export async function applyLocalMemoryResult(input) {
|
|
|
400
547
|
: item),
|
|
401
548
|
entry,
|
|
402
549
|
];
|
|
403
|
-
const allowEvictionForChange = change.operation === "create" && change.priority === "high";
|
|
550
|
+
const allowEvictionForChange = !fullStorage && change.operation === "create" && change.priority === "high";
|
|
404
551
|
const protectedIds = new Set(allowEvictionForChange ? pinnedIds(entries) : activeOrPinnedIds(entries));
|
|
405
552
|
if (allowEvictionForChange)
|
|
406
553
|
allowActiveEviction = true;
|
|
@@ -417,7 +564,7 @@ export async function applyLocalMemoryResult(input) {
|
|
|
417
564
|
const notePath = resolve(input.agentDir, entry.file);
|
|
418
565
|
if (!isWithin(resolve(input.agentDir, "notes"), notePath))
|
|
419
566
|
throw new Error("local_memory_note_path_invalid");
|
|
420
|
-
const previous =
|
|
567
|
+
const previous = await readStoredNote(notePath);
|
|
421
568
|
const replacement = noteSection(id, revision, change.title, change.detailBody);
|
|
422
569
|
const existingSection = replaceNote(previous, id, replacement);
|
|
423
570
|
const next = existingSection ?? (current === undefined
|
|
@@ -435,7 +582,7 @@ export async function applyLocalMemoryResult(input) {
|
|
|
435
582
|
// Pending create candidates are durable overflow, not dead ends. Promote only
|
|
436
583
|
// when the current catalog has room; supersede/update candidates stay pending
|
|
437
584
|
// until a fresh revision-aware decision can safely replay them.
|
|
438
|
-
const pendingCandidatesOnDisk = await readPendingMemoryCandidates({ agentDir: input.agentDir }).catch((error) => {
|
|
585
|
+
const pendingCandidatesOnDisk = fullStorage ? [] : await readPendingMemoryCandidates({ agentDir: input.agentDir }).catch((error) => {
|
|
439
586
|
if (!(error instanceof Error) || error.message !== "memory_pending_directory_over_budget")
|
|
440
587
|
throw error;
|
|
441
588
|
// 自动提升是可选工作,目录查询预算不应阻断本轮已验证的更新或腾位操作。
|
|
@@ -446,6 +593,10 @@ export async function applyLocalMemoryResult(input) {
|
|
|
446
593
|
return [];
|
|
447
594
|
});
|
|
448
595
|
for (const pending of pendingCandidatesOnDisk) {
|
|
596
|
+
// Migration-deferred candidates require reviewed import; only capacity
|
|
597
|
+
// overflow is eligible for the existing automatic promotion path.
|
|
598
|
+
if (pending.reason !== "index_over_budget")
|
|
599
|
+
continue;
|
|
449
600
|
const change = pending.candidate;
|
|
450
601
|
if (change.operation !== "create")
|
|
451
602
|
continue;
|
|
@@ -460,7 +611,7 @@ export async function applyLocalMemoryResult(input) {
|
|
|
460
611
|
|| normalize(entry.summary) === normalize(change.summary)));
|
|
461
612
|
if (duplicate !== undefined) {
|
|
462
613
|
const duplicatePath = resolve(input.agentDir, duplicate.file);
|
|
463
|
-
const duplicateBody = readNoteBody(
|
|
614
|
+
const duplicateBody = readNoteBody(await readStoredNote(duplicatePath), duplicate.id);
|
|
464
615
|
if (duplicateBody === change.detailBody
|
|
465
616
|
|| duplicateBody === unwrappedNoteBody(change.title, change.detailBody).trim()) {
|
|
466
617
|
resolvedPendingIds.add(pending.pendingId);
|
|
@@ -478,7 +629,7 @@ export async function applyLocalMemoryResult(input) {
|
|
|
478
629
|
const notePath = resolve(input.agentDir, entry.file);
|
|
479
630
|
if (!isWithin(resolve(input.agentDir, "notes"), notePath))
|
|
480
631
|
throw new Error("local_memory_note_path_invalid");
|
|
481
|
-
const previous =
|
|
632
|
+
const previous = await readStoredNote(notePath);
|
|
482
633
|
const replacement = noteSection(id, 1, change.title, change.detailBody);
|
|
483
634
|
const existingSection = replaceNote(previous, id, replacement);
|
|
484
635
|
const next = existingSection ?? `${previous.trimEnd()}${previous.trimEnd() ? "\n\n" : ""}${replacement}`;
|
|
@@ -487,6 +638,17 @@ export async function applyLocalMemoryResult(input) {
|
|
|
487
638
|
createdIds.push(id);
|
|
488
639
|
resolvedPendingIds.add(pending.pendingId);
|
|
489
640
|
}
|
|
641
|
+
if (fullStorage) {
|
|
642
|
+
const changedMetadataIds = new Set([
|
|
643
|
+
...statusChangedIds,
|
|
644
|
+
...input.result.changes.flatMap(change => change.supersedesMemoryId === null ? [] : [change.supersedesMemoryId]),
|
|
645
|
+
]);
|
|
646
|
+
for (const id of changedMetadataIds) {
|
|
647
|
+
const entry = entries.find(item => item.id === id);
|
|
648
|
+
const notePath = resolve(input.agentDir, entry.file);
|
|
649
|
+
noteUpdates.set(notePath, updateNoteRevision(await readStoredNote(notePath), id, entry.revision));
|
|
650
|
+
}
|
|
651
|
+
}
|
|
490
652
|
let finalProjection = projectIndex(memory, entries, allowActiveEviction ? pinnedIds(entries) : activeOrPinnedIds(entries));
|
|
491
653
|
if (finalProjection.reason !== null
|
|
492
654
|
&& (!allowActiveEviction || finalProjection.reason === "index_over_budget_pinned")) {
|
|
@@ -498,7 +660,7 @@ export async function applyLocalMemoryResult(input) {
|
|
|
498
660
|
.filter((entry) => entry?.status === "active");
|
|
499
661
|
for (const entry of omittedActiveEntries) {
|
|
500
662
|
const notePath = resolve(input.agentDir, entry.file);
|
|
501
|
-
const content =
|
|
663
|
+
const content = await readStoredNote(notePath);
|
|
502
664
|
const detailBody = readNoteBody(content, entry.id);
|
|
503
665
|
const change = detailBody === null ? null : archivedChange(entry, detailBody);
|
|
504
666
|
if (change === null)
|
|
@@ -519,6 +681,13 @@ export async function applyLocalMemoryResult(input) {
|
|
|
519
681
|
if (finalProjection.reason !== null)
|
|
520
682
|
throw new Error(finalProjection.reason);
|
|
521
683
|
}
|
|
684
|
+
// Complete every resource check before the first persistent write.
|
|
685
|
+
assertLocalMemoryStorageSize(finalProjection.memory);
|
|
686
|
+
for (const content of noteUpdates.values()) {
|
|
687
|
+
if (Buffer.byteLength(content, "utf8") > LOCAL_MEMORY_NOTE_BYTE_CAP)
|
|
688
|
+
throw new Error("local_memory_note_byte_limit");
|
|
689
|
+
}
|
|
690
|
+
await assertMemoryInventoryHealth(input.agentDir);
|
|
522
691
|
for (const candidate of pendingCandidates)
|
|
523
692
|
await persistPendingMemoryCandidate({ agentDir: input.agentDir, candidate });
|
|
524
693
|
for (const [path, content] of noteUpdates)
|
|
@@ -535,5 +704,5 @@ export async function applyLocalMemoryResult(input) {
|
|
|
535
704
|
deferredIds,
|
|
536
705
|
pendingIds: deferredIds,
|
|
537
706
|
};
|
|
538
|
-
});
|
|
707
|
+
}));
|
|
539
708
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { detectLocalMemoryStorageVersion } from "./storage-format.js";
|
|
2
|
+
/** Resolve format and rollout into one writer decision before a prune result is requested. */
|
|
3
|
+
export function resolveLocalMemoryWriterMode(workspace, rollout) {
|
|
4
|
+
if (rollout.legacyFallback)
|
|
5
|
+
return "legacy";
|
|
6
|
+
if (!rollout.enabled)
|
|
7
|
+
return rollout.deferred ? "blocked" : "legacy";
|
|
8
|
+
// Production workspaces always report this fact. Historical partial unit
|
|
9
|
+
// callers retain their already-resolved rollout witness.
|
|
10
|
+
if (workspace.localMemoryV2Initialized === undefined)
|
|
11
|
+
return "v2";
|
|
12
|
+
return detectLocalMemoryStorageVersion(workspace.memory) === 2 ? "v2" : "blocked";
|
|
13
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
import { readFile, stat } from "node:fs/promises";
|
|
2
|
+
import { lstat, readFile, readdir, stat } from "node:fs/promises";
|
|
3
3
|
import { isAbsolute, join, normalize, relative, resolve } from "node:path";
|
|
4
4
|
import { parseArgs } from "node:util";
|
|
5
5
|
import { capMemoryForInject, MEMORY_INJECT_CAP } from "./prompt.js";
|
|
@@ -8,6 +8,7 @@ const INJECTION_MARKERS = [
|
|
|
8
8
|
"## [注入] 你的 MEMORY.md(索引,只读参考)\n",
|
|
9
9
|
];
|
|
10
10
|
const LOCAL_MEMORY_HASH_CAP_BYTES = 10 * 1024 * 1024;
|
|
11
|
+
export const LOCAL_MEMORY_DIAGNOSTICS_TIMEOUT_MS = 2_000;
|
|
11
12
|
function errorCode(error) {
|
|
12
13
|
const code = error?.code;
|
|
13
14
|
return typeof code === "string" ? code : error instanceof Error ? error.name : "unknown";
|
|
@@ -39,30 +40,75 @@ async function inspectFile(path, signal) {
|
|
|
39
40
|
return { ...base, error_code: errorCode(error) };
|
|
40
41
|
}
|
|
41
42
|
}
|
|
42
|
-
|
|
43
|
+
async function inspectPendingDirectory(path, signal) {
|
|
44
|
+
let metadata;
|
|
45
|
+
try {
|
|
46
|
+
metadata = await lstat(path);
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
const code = errorCode(error);
|
|
50
|
+
return code === "ENOENT" ? { exists: false, entry_count: 0 } : { exists: false, error_code: code };
|
|
51
|
+
}
|
|
52
|
+
const base = { exists: true, mtime_ms: metadata.mtimeMs };
|
|
53
|
+
if (metadata.isSymbolicLink() || !metadata.isDirectory())
|
|
54
|
+
return { ...base, hash_skipped_reason: "not_regular_file" };
|
|
55
|
+
try {
|
|
56
|
+
const names = (await readdir(path, { withFileTypes: true }))
|
|
57
|
+
.filter(entry => entry.name.endsWith(".json"))
|
|
58
|
+
.sort((left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : 0);
|
|
59
|
+
if (names.length > 128)
|
|
60
|
+
return { ...base, entry_count: names.length, error_code: "entry_limit" };
|
|
61
|
+
const hash = createHash("sha256");
|
|
62
|
+
let size = 0;
|
|
63
|
+
for (const entry of names) {
|
|
64
|
+
if (signal.aborted)
|
|
65
|
+
throw signal.reason;
|
|
66
|
+
if (!entry.isFile() || entry.isSymbolicLink())
|
|
67
|
+
return { ...base, entry_count: names.length, error_code: "invalid_entry" };
|
|
68
|
+
const fact = await inspectFile(join(path, entry.name), signal);
|
|
69
|
+
if (!fact.exists || fact.sha256 === undefined || fact.error_code !== undefined
|
|
70
|
+
|| fact.hash_skipped_reason !== undefined)
|
|
71
|
+
return { ...base, entry_count: names.length, error_code: "incomplete_entry" };
|
|
72
|
+
size += fact.size_bytes ?? 0;
|
|
73
|
+
hash.update(entry.name).update("\0").update(fact.sha256).update("\0");
|
|
74
|
+
}
|
|
75
|
+
return { ...base, size_bytes: size, entry_count: names.length, sha256: hash.digest("hex") };
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
return { ...base, error_code: errorCode(error) };
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
export async function withinLocalMemoryDiagnosticsDeadline(operation, timeoutMs) {
|
|
43
82
|
const controller = new AbortController();
|
|
44
83
|
let timer;
|
|
45
|
-
const inspection = Promise.all([
|
|
46
|
-
inspectFile(join(homeDir, "MEMORY.md"), controller.signal),
|
|
47
|
-
inspectFile(join(homeDir, "notes", "lessons.md"), controller.signal),
|
|
48
|
-
]).then(([memory, lessons]) => ({ memory, lessons }));
|
|
49
84
|
const timeout = new Promise((_resolve, reject) => {
|
|
50
85
|
timer = setTimeout(() => {
|
|
51
|
-
|
|
52
|
-
reject(Object.assign(new Error("local memory diagnostics timed out"), {
|
|
86
|
+
const error = Object.assign(new Error("local memory diagnostics timed out"), {
|
|
53
87
|
code: "diagnostics_timeout",
|
|
54
|
-
})
|
|
88
|
+
});
|
|
89
|
+
controller.abort(error);
|
|
90
|
+
reject(error);
|
|
55
91
|
}, Math.max(1, timeoutMs));
|
|
56
92
|
timer.unref?.();
|
|
57
93
|
});
|
|
58
94
|
try {
|
|
59
|
-
return await Promise.race([
|
|
95
|
+
return await Promise.race([operation(controller.signal), timeout]);
|
|
60
96
|
}
|
|
61
97
|
finally {
|
|
62
98
|
if (timer !== undefined)
|
|
63
99
|
clearTimeout(timer);
|
|
64
100
|
}
|
|
65
101
|
}
|
|
102
|
+
export async function inspectLocalMemoryFilesWithinDeadline(homeDir, timeoutMs) {
|
|
103
|
+
return withinLocalMemoryDiagnosticsDeadline(async (signal) => {
|
|
104
|
+
const [memory, lessons, pending] = await Promise.all([
|
|
105
|
+
inspectFile(join(homeDir, "MEMORY.md"), signal),
|
|
106
|
+
inspectFile(join(homeDir, "notes", "lessons.md"), signal),
|
|
107
|
+
inspectPendingDirectory(join(homeDir, ".crew", "memory", "pending"), signal),
|
|
108
|
+
]);
|
|
109
|
+
return { memory, lessons, pending };
|
|
110
|
+
}, timeoutMs);
|
|
111
|
+
}
|
|
66
112
|
export function localMemoryContentFacts(content, maxScanBytes = LOCAL_MEMORY_HASH_CAP_BYTES) {
|
|
67
113
|
const sizeBytes = Buffer.byteLength(content, "utf8");
|
|
68
114
|
if (sizeBytes > Math.max(0, maxScanBytes)) {
|
|
@@ -76,12 +122,12 @@ export function localMemoryContentFacts(content, maxScanBytes = LOCAL_MEMORY_HAS
|
|
|
76
122
|
lessons_reference_count: content.match(/\bnotes[\\/]lessons\.md\b/gi)?.length ?? 0,
|
|
77
123
|
};
|
|
78
124
|
}
|
|
79
|
-
export function analyzeLocalMemoryInjection(systemPrompt, sourceMemory) {
|
|
125
|
+
export function analyzeLocalMemoryInjection(systemPrompt, sourceMemory, query = "") {
|
|
80
126
|
const sourceBytes = Buffer.byteLength(sourceMemory, "utf8");
|
|
81
127
|
if (sourceMemory.length === 0) {
|
|
82
128
|
return { state: "empty_source", source_bytes: 0, bounded_bytes: 0, injected_bytes: 0 };
|
|
83
129
|
}
|
|
84
|
-
const bounded = capMemoryForInject(sourceMemory);
|
|
130
|
+
const bounded = capMemoryForInject(sourceMemory, undefined, Number.POSITIVE_INFINITY, query);
|
|
85
131
|
const marker = INJECTION_MARKERS.find((candidate) => systemPrompt.includes(candidate));
|
|
86
132
|
if (marker === undefined) {
|
|
87
133
|
return {
|
|
@@ -217,15 +263,50 @@ function crewMemoryReadAccess(command, paths) {
|
|
|
217
263
|
return null;
|
|
218
264
|
}
|
|
219
265
|
}
|
|
266
|
+
/**
|
|
267
|
+
* Decode only the shell wrapper shape emitted by supported runtimes. The body must be one complete
|
|
268
|
+
* quoted argument. Double-quoted bodies may escape only a quote or backslash; shell expansion and
|
|
269
|
+
* concatenated quoting stay unknown so telemetry never guesses what the shell actually executed.
|
|
270
|
+
*/
|
|
271
|
+
function unwrapLiteralShellCommand(command) {
|
|
272
|
+
const trimmed = command.trim();
|
|
273
|
+
const wrapper = /^\/bin\/(?:(?:zsh|bash)\s+-lc|sh\s+-c)\s+([\s\S]+)$/u.exec(trimmed);
|
|
274
|
+
if (wrapper === null)
|
|
275
|
+
return trimmed;
|
|
276
|
+
const quoted = wrapper[1];
|
|
277
|
+
const quote = quoted[0];
|
|
278
|
+
if ((quote !== "'" && quote !== '"') || quoted.at(-1) !== quote || quoted.length < 2)
|
|
279
|
+
return null;
|
|
280
|
+
const body = quoted.slice(1, -1);
|
|
281
|
+
if (quote === "'")
|
|
282
|
+
return body.includes("'") ? null : body;
|
|
283
|
+
let decoded = "";
|
|
284
|
+
for (let index = 0; index < body.length; index += 1) {
|
|
285
|
+
const character = body[index];
|
|
286
|
+
if (character === '"' || character === "$" || character === "`")
|
|
287
|
+
return null;
|
|
288
|
+
if (character !== "\\") {
|
|
289
|
+
decoded += character;
|
|
290
|
+
continue;
|
|
291
|
+
}
|
|
292
|
+
const escaped = body[index + 1];
|
|
293
|
+
if (escaped !== '"' && escaped !== "\\")
|
|
294
|
+
return null;
|
|
295
|
+
decoded += escaped;
|
|
296
|
+
index += 1;
|
|
297
|
+
}
|
|
298
|
+
return decoded;
|
|
299
|
+
}
|
|
220
300
|
function commandAccesses(command, paths) {
|
|
221
|
-
|
|
301
|
+
const unwrapped = unwrapLiteralShellCommand(command);
|
|
302
|
+
if (unwrapped === null || /[;|&\n]/.test(unwrapped))
|
|
222
303
|
return [];
|
|
223
304
|
const accesses = new Map();
|
|
224
|
-
const memoryRead = crewMemoryReadAccess(
|
|
305
|
+
const memoryRead = crewMemoryReadAccess(unwrapped, paths);
|
|
225
306
|
if (memoryRead !== null) {
|
|
226
307
|
accesses.set(`${memoryRead.target}:${memoryRead.relative_path ?? ""}:read`, memoryRead);
|
|
227
308
|
}
|
|
228
|
-
const expanded =
|
|
309
|
+
const expanded = unwrapped
|
|
229
310
|
.replaceAll("${CREW_HOME}", paths.homeDir)
|
|
230
311
|
.replaceAll("$CREW_HOME", paths.homeDir)
|
|
231
312
|
.replace(/["']/g, "");
|
|
@@ -38,6 +38,7 @@ export function logLocalMemoryDiagnosticsFailure(identity, phase, error) {
|
|
|
38
38
|
function snapshotFields(snapshot) {
|
|
39
39
|
if (snapshot === null)
|
|
40
40
|
return {};
|
|
41
|
+
const pending = snapshot.pending ?? { exists: false, entry_count: 0 };
|
|
41
42
|
return {
|
|
42
43
|
memory_exists: snapshot.memory.exists,
|
|
43
44
|
memory_size_bytes: snapshot.memory.size_bytes,
|
|
@@ -54,6 +55,12 @@ function snapshotFields(snapshot) {
|
|
|
54
55
|
lessons_hash_skipped_reason: snapshot.lessons.hash_skipped_reason,
|
|
55
56
|
lessons_error_code: snapshot.lessons.error_code,
|
|
56
57
|
lessons_markdown_heading_count: snapshot.lessons.markdown_heading_count,
|
|
58
|
+
pending_exists: pending.exists,
|
|
59
|
+
pending_size_bytes: pending.size_bytes,
|
|
60
|
+
pending_mtime_ms: pending.mtime_ms,
|
|
61
|
+
pending_sha256: pending.sha256,
|
|
62
|
+
pending_error_code: pending.error_code,
|
|
63
|
+
pending_entry_count: pending.entry_count,
|
|
57
64
|
};
|
|
58
65
|
}
|
|
59
66
|
function promptDelivery(runtime, stableProtocolRuntime) {
|
|
@@ -103,7 +110,7 @@ export function createLocalMemoryTelemetry(options) {
|
|
|
103
110
|
contextPrepared(context) {
|
|
104
111
|
try {
|
|
105
112
|
const sourceFacts = localMemoryContentFacts(context.memory);
|
|
106
|
-
const injectionFacts = analyzeLocalMemoryInjection(context.systemPrompt, context.memory);
|
|
113
|
+
const injectionFacts = analyzeLocalMemoryInjection(context.systemPrompt, context.memory, context.memoryQuery);
|
|
107
114
|
safeLog("local_memory.context_prepared", "本地记忆上下文已准备", {
|
|
108
115
|
execution_id: options.executionId,
|
|
109
116
|
agent_handle: options.agentHandle,
|
|
@@ -183,10 +190,13 @@ export function createLocalMemoryTelemetry(options) {
|
|
|
183
190
|
const lessonsChanged = beforeSnapshot !== null && afterSnapshot !== null
|
|
184
191
|
? localMemoryFileChanged(beforeSnapshot.lessons, afterSnapshot.lessons)
|
|
185
192
|
: undefined;
|
|
193
|
+
const pendingChanged = beforeSnapshot !== null && afterSnapshot !== null
|
|
194
|
+
? localMemoryFileChanged(beforeSnapshot.pending ?? { exists: false }, afterSnapshot.pending ?? { exists: false })
|
|
195
|
+
: undefined;
|
|
186
196
|
const accessSummary = accessObserver.summary();
|
|
187
|
-
const sharedFilesChanged = memoryChanged === undefined || lessonsChanged === undefined
|
|
197
|
+
const sharedFilesChanged = memoryChanged === undefined || lessonsChanged === undefined || pendingChanged === undefined
|
|
188
198
|
? undefined
|
|
189
|
-
: memoryChanged || lessonsChanged;
|
|
199
|
+
: memoryChanged || lessonsChanged || pendingChanged;
|
|
190
200
|
safeLog("local_memory.execution_observed", "本地记忆执行观察已完成", {
|
|
191
201
|
execution_id: options.executionId,
|
|
192
202
|
agent_handle: options.agentHandle,
|
|
@@ -200,6 +210,7 @@ export function createLocalMemoryTelemetry(options) {
|
|
|
200
210
|
executor_outcome: executorCompleted ? "succeeded" : "failed",
|
|
201
211
|
memory_changed: memoryChanged,
|
|
202
212
|
lessons_changed: lessonsChanged,
|
|
213
|
+
pending_changed: pendingChanged,
|
|
203
214
|
shared_files_changed_during_execution: sharedFilesChanged,
|
|
204
215
|
unexpected_shared_write: options.isMemoryPrune
|
|
205
216
|
? undefined
|
|
@@ -224,6 +235,12 @@ export function createLocalMemoryTelemetry(options) {
|
|
|
224
235
|
lessons_after_hash_skipped_reason: afterSnapshot.lessons.hash_skipped_reason,
|
|
225
236
|
lessons_after_error_code: afterSnapshot.lessons.error_code,
|
|
226
237
|
lessons_after_markdown_heading_count: afterSnapshot.lessons.markdown_heading_count,
|
|
238
|
+
pending_after_exists: afterSnapshot.pending?.exists ?? false,
|
|
239
|
+
pending_after_size_bytes: afterSnapshot.pending?.size_bytes,
|
|
240
|
+
pending_after_mtime_ms: afterSnapshot.pending?.mtime_ms,
|
|
241
|
+
pending_after_sha256: afterSnapshot.pending?.sha256,
|
|
242
|
+
pending_after_error_code: afterSnapshot.pending?.error_code,
|
|
243
|
+
pending_after_entry_count: afterSnapshot.pending?.entry_count ?? 0,
|
|
227
244
|
}),
|
|
228
245
|
});
|
|
229
246
|
},
|
package/dist/machine-info.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { MAINTENANCE_CAPABILITY } from "./marketplace-maintenance-protocol.js";
|
|
1
2
|
import { retryHarnessProbe } from "./runtimes/harness-probe-retry.js";
|
|
2
3
|
import { MARKETPLACE_CAPABILITY } from "./marketplace-protocol.js";
|
|
3
4
|
/**
|
|
@@ -25,6 +26,7 @@ import { RUNTIME_HEALTH_PROBE_CAPABILITY } from "./runtime-health.js";
|
|
|
25
26
|
import { probeDeepSeekHarnessAcp, deepSeekHarnessLauncher } from "./runtimes/deepseek-harness.js";
|
|
26
27
|
import { daemonHome } from "./computer-profile.js";
|
|
27
28
|
import { daemonReleaseProvenance } from "./release-provenance.js";
|
|
29
|
+
import { MEMORY_MIGRATION_CAPABILITY, MEMORY_STORAGE_V2_CAPABILITY, MEMORY_STATE_GUARD_CAPABILITY } from "./memory-migration-protocol.js";
|
|
28
30
|
export { executableRuntimes } from "./runtime-capabilities.js";
|
|
29
31
|
const execFileP = promisify(execFile);
|
|
30
32
|
export const PROJECT_WORKSPACES_CAPABILITY = "project_workspaces_v1";
|
|
@@ -40,6 +42,8 @@ export const NATIVE_PROJECT_WORKSPACE_READINESS = Object.freeze({
|
|
|
40
42
|
});
|
|
41
43
|
export const DAEMON_CAPABILITIES = Object.freeze([
|
|
42
44
|
MARKETPLACE_CAPABILITY,
|
|
45
|
+
MEMORY_MIGRATION_CAPABILITY, MEMORY_STORAGE_V2_CAPABILITY, MEMORY_STATE_GUARD_CAPABILITY,
|
|
46
|
+
MAINTENANCE_CAPABILITY,
|
|
43
47
|
"scheduled_job_v1",
|
|
44
48
|
"reply_origin_v1",
|
|
45
49
|
"origin_decision_v1",
|
|
@@ -72,9 +76,10 @@ export const daemonCapabilities = (runtimePlatform = process.platform, readiness
|
|
|
72
76
|
const nativeWorkspaceReady = nativeWorkspacePlatform
|
|
73
77
|
&& readiness.resolverReady
|
|
74
78
|
&& readiness.nativeRuntimeAdaptersReady;
|
|
75
|
-
return Object.freeze(DAEMON_CAPABILITIES.filter((capability) => ((capability !== MARKETPLACE_CAPABILITY || nativeWorkspacePlatform)
|
|
79
|
+
return Object.freeze(DAEMON_CAPABILITIES.filter((capability) => (((capability !== MARKETPLACE_CAPABILITY && capability !== MAINTENANCE_CAPABILITY) || nativeWorkspacePlatform)
|
|
76
80
|
&& (capability !== PROJECT_WORKSPACES_CAPABILITY || nativeWorkspaceReady)
|
|
77
81
|
&& (capability !== "message_file_preview_v1" || nativeWorkspacePlatform)
|
|
82
|
+
&& (nativeWorkspacePlatform || ![MEMORY_MIGRATION_CAPABILITY, MEMORY_STORAGE_V2_CAPABILITY, MEMORY_STATE_GUARD_CAPABILITY].includes(capability))
|
|
78
83
|
&& (nativeWorkspacePlatform
|
|
79
84
|
|| (capability !== "project_skills_v1"
|
|
80
85
|
&& capability !== "project_skill_projection_v2"
|
package/dist/main.js
CHANGED
|
File without changes
|