@gmickel/gno 2.4.0 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +69 -6
- package/assets/skill/README.md +5 -1
- package/assets/skill/SKILL.md +98 -1
- package/assets/skill/cli-reference.md +140 -0
- package/assets/skill/examples.md +45 -0
- package/assets/skill/mcp-reference.md +62 -0
- package/assets/skill/recipes/capture-and-file.md +6 -0
- package/assets/skill/recipes/memory-file-decision.md +6 -0
- package/assets/skill/recipes/memory-supersede-fact.md +5 -0
- package/assets/skill/recipes/session-evidence-lookup.md +98 -0
- package/assets/spa-production.json.gz +0 -0
- package/browser-extension/artifacts/{gno-browser-clipper-v2.4.0.zip → gno-browser-clipper-v2.6.0.zip} +0 -0
- package/browser-extension/artifacts/gno-browser-clipper-v2.6.0.zip.sha256 +1 -0
- package/browser-extension/dist/manifest.json +1 -1
- package/package.json +2 -1
- package/spec/cli.md +358 -24
- package/spec/compiled-context.md +68 -0
- package/spec/mcp.md +223 -2
- package/spec/output-schemas/capture-receipt.schema.json +3 -0
- package/spec/output-schemas/compiled-context-check.schema.json +44 -0
- package/spec/output-schemas/compiled-context-file.schema.json +165 -0
- package/spec/output-schemas/compiled-context-preview.schema.json +142 -0
- package/spec/output-schemas/mcp-capture-result.schema.json +3 -0
- package/spec/output-schemas/memory-remember.schema.json +8 -2
- package/spec/output-schemas/request-status.schema.json +113 -0
- package/spec/output-schemas/sessions-automation-run.schema.json +46 -0
- package/spec/output-schemas/sessions-discovery.schema.json +38 -0
- package/spec/output-schemas/sessions-import-receipt.schema.json +156 -0
- package/spec/output-schemas/sessions-status.schema.json +432 -0
- package/src/app/compiled-context-files.ts +361 -0
- package/src/app/compiled-context.ts +240 -0
- package/src/app/context-surface.ts +7 -2
- package/src/cli/commands/ask.ts +14 -2
- package/src/cli/commands/audit.ts +4 -0
- package/src/cli/commands/capture.ts +55 -96
- package/src/cli/commands/context-compiled.ts +136 -0
- package/src/cli/commands/daemon.ts +41 -0
- package/src/cli/commands/ls.ts +3 -0
- package/src/cli/commands/memory.ts +12 -3
- package/src/cli/commands/request-status.ts +59 -0
- package/src/cli/commands/reset.ts +39 -5
- package/src/cli/commands/sessions.ts +713 -0
- package/src/cli/commands/shared.ts +14 -1
- package/src/cli/errors.ts +10 -3
- package/src/cli/program.ts +458 -1
- package/src/cli/session-binding.ts +49 -0
- package/src/config/types.ts +8 -0
- package/src/core/capture-publish.ts +239 -0
- package/src/core/capture-sync.ts +3 -0
- package/src/core/compiled-context.ts +254 -0
- package/src/core/context-budget.ts +2 -10
- package/src/core/file-lock.ts +22 -5
- package/src/core/folder-setup-planning.ts +2 -1
- package/src/core/memory-remember.ts +233 -122
- package/src/core/memory-types.ts +11 -0
- package/src/core/network-boundary-inventory.ts +16 -0
- package/src/core/request-receipts.ts +671 -0
- package/src/core/setup-receipt.ts +27 -20
- package/src/core/typed-metadata.ts +4 -0
- package/src/core/validation.ts +9 -2
- package/src/core/windows-private-path.ts +96 -0
- package/src/index.ts +11 -2
- package/src/ingestion/compiled-context.ts +15 -0
- package/src/ingestion/sync.ts +40 -8
- package/src/ingestion/walker.ts +5 -4
- package/src/llm/nodeLlamaCpp/simulator-install.ts +6 -2
- package/src/mcp/context.ts +8 -0
- package/src/mcp/http-egress.ts +15 -3
- package/src/mcp/http-transport.ts +2 -0
- package/src/mcp/retrieval-warnings.ts +24 -0
- package/src/mcp/tools/ask.ts +14 -1
- package/src/mcp/tools/capture.ts +87 -83
- package/src/mcp/tools/context.ts +46 -2
- package/src/mcp/tools/index.ts +119 -7
- package/src/mcp/tools/memory-remember.ts +7 -0
- package/src/mcp/tools/memory-shared.ts +7 -1
- package/src/mcp/tools/query.ts +3 -1
- package/src/mcp/tools/request-status.ts +73 -0
- package/src/mcp/tools/search.ts +3 -1
- package/src/mcp/tools/sessions.ts +208 -0
- package/src/mcp/tools/vsearch.ts +3 -1
- package/src/sdk/client.ts +252 -89
- package/src/sdk/index.ts +13 -0
- package/src/sdk/types.ts +82 -3
- package/src/serve/capture-service.ts +98 -32
- package/src/serve/compiled-context.ts +84 -0
- package/src/serve/config-sync.ts +3 -2
- package/src/serve/public/app.tsx +15 -1
- package/src/serve/public/components/CaptureModal.tsx +26 -8
- package/src/serve/public/components/sessions/AutomationPanel.tsx +800 -0
- package/src/serve/public/components/sessions/ImportReceipt.tsx +238 -0
- package/src/serve/public/components/sessions/SessionSearch.tsx +286 -0
- package/src/serve/public/components/sessions/SourcesPanel.tsx +541 -0
- package/src/serve/public/components/sessions/api.ts +40 -0
- package/src/serve/public/components/sessions/snippet.tsx +53 -0
- package/src/serve/public/globals.built.css +1 -1
- package/src/serve/public/hooks/use-api.ts +10 -2
- package/src/serve/public/lib/request-intent.ts +69 -0
- package/src/serve/public/lib/workspace-actions.ts +12 -1
- package/src/serve/public/lib/workspace-tabs.ts +4 -0
- package/src/serve/public/pages/CompiledContext.tsx +364 -0
- package/src/serve/public/pages/Dashboard.tsx +17 -0
- package/src/serve/public/pages/DocView.tsx +15 -1
- package/src/serve/public/pages/DocumentEditor.tsx +139 -96
- package/src/serve/public/pages/Sessions.tsx +350 -0
- package/src/serve/resident-runtime.ts +43 -3
- package/src/serve/routes/api.ts +476 -147
- package/src/serve/routes/sessions.ts +766 -0
- package/src/serve/security.ts +9 -0
- package/src/serve/server.ts +248 -3
- package/src/serve/session-automation.ts +146 -0
- package/src/serve/spa-production-build.ts +6 -5
- package/src/sessions/archive.ts +348 -0
- package/src/sessions/automation-state.ts +444 -0
- package/src/sessions/automation-status.ts +239 -0
- package/src/sessions/automation.ts +1169 -0
- package/src/sessions/binding.ts +105 -0
- package/src/sessions/claude-hook.ts +240 -0
- package/src/sessions/config.ts +176 -0
- package/src/sessions/format.ts +191 -0
- package/src/sessions/import-child-env.ts +8 -0
- package/src/sessions/import-child.ts +152 -0
- package/src/sessions/parsers/claude-code.ts +259 -0
- package/src/sessions/parsers/codex.ts +303 -0
- package/src/sessions/parsers/hermes.ts +248 -0
- package/src/sessions/parsers/openclaw.ts +496 -0
- package/src/sessions/parsers/shared.ts +184 -0
- package/src/sessions/sanitize.ts +222 -0
- package/src/sessions/service.ts +1533 -0
- package/src/sessions/setup.ts +477 -0
- package/src/sessions/sources.ts +518 -0
- package/src/sessions/state.ts +118 -0
- package/src/sessions/types.ts +457 -0
- package/src/store/sqlite/adapter.ts +68 -16
- package/src/store/sqlite/scoped-index.ts +9 -0
- package/browser-extension/artifacts/gno-browser-clipper-v2.4.0.zip.sha256 +0 -1
|
@@ -56,6 +56,13 @@ import {
|
|
|
56
56
|
MEMORY_SEMANTIC_LIKELY_THRESHOLD,
|
|
57
57
|
MemoryError,
|
|
58
58
|
} from "./memory-types";
|
|
59
|
+
import {
|
|
60
|
+
isLeaseBusy,
|
|
61
|
+
RequestReceiptError,
|
|
62
|
+
requestDigest,
|
|
63
|
+
runRequestedWrite,
|
|
64
|
+
validateRequestId,
|
|
65
|
+
} from "./request-receipts";
|
|
59
66
|
|
|
60
67
|
export interface CandidateQuery {
|
|
61
68
|
text: string;
|
|
@@ -249,6 +256,12 @@ async function supersedesEdgeProjected(
|
|
|
249
256
|
// remember
|
|
250
257
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
251
258
|
|
|
259
|
+
interface RememberPlan {
|
|
260
|
+
frontmatter: MemoryRecordFrontmatter;
|
|
261
|
+
supersedes: string[];
|
|
262
|
+
relPath: string;
|
|
263
|
+
}
|
|
264
|
+
|
|
252
265
|
export async function rememberFact(
|
|
253
266
|
deps: MemoryServiceDeps,
|
|
254
267
|
rawInput: RememberInput
|
|
@@ -270,14 +283,26 @@ export async function rememberFact(
|
|
|
270
283
|
);
|
|
271
284
|
}
|
|
272
285
|
}
|
|
286
|
+
const requestId =
|
|
287
|
+
rawInput.requestId === undefined
|
|
288
|
+
? undefined
|
|
289
|
+
: validateRequestId(rawInput.requestId);
|
|
290
|
+
if (requestId !== undefined && decision === undefined) {
|
|
291
|
+
throw new RequestReceiptError(
|
|
292
|
+
"REQUEST_ID_INVALID",
|
|
293
|
+
"requestId applies only to writes: pass decision add or supersede."
|
|
294
|
+
);
|
|
295
|
+
}
|
|
273
296
|
|
|
274
297
|
const { candidates, matching } = await findMemoryCandidates(deps, {
|
|
275
298
|
text,
|
|
276
299
|
collection: collection.name,
|
|
277
300
|
scopes,
|
|
278
301
|
});
|
|
302
|
+
// With a request ID the exact-duplicate decision is made (and recorded)
|
|
303
|
+
// under the lease, so a later retry replays it instead of re-deciding.
|
|
279
304
|
const exact = candidates.find((candidate) => candidate.match === "exact");
|
|
280
|
-
if (exact && decision !== "supersede") {
|
|
305
|
+
if (exact && decision !== "supersede" && requestId === undefined) {
|
|
281
306
|
const { similarity: _similarity, match: _match, ...record } = exact;
|
|
282
307
|
return { outcome: "existing", record, matching };
|
|
283
308
|
}
|
|
@@ -285,135 +310,222 @@ export async function rememberFact(
|
|
|
285
310
|
return { outcome: "candidates", candidates, matching };
|
|
286
311
|
}
|
|
287
312
|
|
|
288
|
-
const createdAt = memoryNow(deps).toISOString();
|
|
289
313
|
const source = rawInput.source?.trim() || undefined;
|
|
290
|
-
const
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
314
|
+
const absPathOf = (plan: RememberPlan) => join(collection.path, plan.relPath);
|
|
315
|
+
|
|
316
|
+
/** Under the lease: decide against current state; never writes. */
|
|
317
|
+
const prepare = async (): Promise<
|
|
318
|
+
{ plan: RememberPlan } | { result: RememberResult }
|
|
319
|
+
> => {
|
|
320
|
+
const supersedes: string[] = [];
|
|
321
|
+
if (decision === "supersede") {
|
|
322
|
+
supersedes.push(
|
|
323
|
+
await verifyPredecessor(
|
|
324
|
+
deps,
|
|
325
|
+
collection.name,
|
|
326
|
+
rawInput.predecessorUri as string,
|
|
327
|
+
rawInput.predecessorHash as string
|
|
328
|
+
)
|
|
329
|
+
);
|
|
330
|
+
} else {
|
|
331
|
+
// The pre-lease check raced with any concurrent writer; decide
|
|
332
|
+
// idempotency on the state visible under the lease.
|
|
333
|
+
const existing = await findExactCurrent(deps, {
|
|
334
|
+
text,
|
|
335
|
+
collection: collection.name,
|
|
336
|
+
scopes,
|
|
337
|
+
contentHash,
|
|
338
|
+
});
|
|
339
|
+
if (existing) {
|
|
340
|
+
return { result: { outcome: "existing", record: existing, matching } };
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
const createdAt = memoryNow(deps).toISOString();
|
|
344
|
+
const frontmatter: MemoryRecordFrontmatter = {
|
|
345
|
+
recordId: buildMemoryRecordId({
|
|
346
|
+
contentHash,
|
|
347
|
+
createdAt,
|
|
348
|
+
caller: identity.caller,
|
|
349
|
+
session: identity.session,
|
|
350
|
+
}),
|
|
351
|
+
scopes,
|
|
294
352
|
caller: identity.caller,
|
|
295
353
|
session: identity.session,
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
354
|
+
createdAt,
|
|
355
|
+
contentHash,
|
|
356
|
+
...(source ? { source } : {}),
|
|
357
|
+
};
|
|
358
|
+
return {
|
|
359
|
+
plan: {
|
|
360
|
+
frontmatter,
|
|
361
|
+
supersedes,
|
|
362
|
+
relPath: buildMemoryRecordRelPath(frontmatter),
|
|
363
|
+
},
|
|
364
|
+
};
|
|
303
365
|
};
|
|
304
|
-
const
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
366
|
+
const serialize = (plan: RememberPlan): string =>
|
|
367
|
+
serializeMemoryRecord({
|
|
368
|
+
frontmatter: plan.frontmatter,
|
|
369
|
+
supersedes: plan.supersedes,
|
|
370
|
+
text,
|
|
371
|
+
});
|
|
372
|
+
const publish = async (plan: RememberPlan): Promise<void> => {
|
|
373
|
+
await mkdir(dirname(absPathOf(plan)), { recursive: true });
|
|
374
|
+
await atomicCreate(absPathOf(plan), serialize(plan));
|
|
375
|
+
};
|
|
376
|
+
/** Under the lease: lexical sync, projection check, result. */
|
|
377
|
+
const finish = async (plan: RememberPlan): Promise<RememberResult> => {
|
|
378
|
+
const { relPath, supersedes, frontmatter } = plan;
|
|
379
|
+
const absPath = absPathOf(plan);
|
|
380
|
+
// syncPaths (not syncFiles) so typed-edge projection errors surface.
|
|
381
|
+
const syncResult = await (deps.syncService ?? defaultSyncService).syncPaths(
|
|
382
|
+
collection,
|
|
383
|
+
store,
|
|
384
|
+
[relPath],
|
|
385
|
+
withContentTypeRules({ runUpdateCmd: false, gitPull: false }, config)
|
|
386
|
+
);
|
|
387
|
+
const fileResult = syncResult.files?.[0];
|
|
388
|
+
const doc = await store.getDocument(collection.name, relPath);
|
|
389
|
+
const sync: MemorySyncState =
|
|
390
|
+
fileResult?.status === "error" || !doc.ok || doc.value === null
|
|
391
|
+
? {
|
|
392
|
+
status: "failed",
|
|
393
|
+
error:
|
|
394
|
+
fileResult?.errorMessage ??
|
|
395
|
+
fileResult?.errorCode ??
|
|
396
|
+
"memory record was written but is not retrievable yet",
|
|
334
397
|
}
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
398
|
+
: { status: "completed" };
|
|
399
|
+
if (sync.status === "failed") {
|
|
400
|
+
throw new MemoryError(
|
|
401
|
+
"MEMORY_SYNC_FAILED",
|
|
402
|
+
`Memory record written to ${absPath} but lexical sync failed: ${sync.error}. Run gno update to retry indexing.`
|
|
403
|
+
);
|
|
404
|
+
}
|
|
405
|
+
const written = (doc as { value: DocumentRow }).value;
|
|
406
|
+
const projectionErrors = syncResult.errors
|
|
407
|
+
.map((error) => `${error.relPath}: ${error.message}`)
|
|
408
|
+
.join("; ");
|
|
409
|
+
if (decision === "supersede") {
|
|
410
|
+
// The write is only a supersession once the edge is projected;
|
|
411
|
+
// until then the predecessor still reads as current.
|
|
412
|
+
const projected =
|
|
413
|
+
projectionErrors.length === 0 &&
|
|
414
|
+
(await supersedesEdgeProjected(deps, written.id, supersedes));
|
|
415
|
+
if (!projected) {
|
|
416
|
+
throw new MemoryError(
|
|
417
|
+
"MEMORY_SUPERSEDE_PROJECTION_FAILED",
|
|
418
|
+
`Successor written to ${absPath} but its supersedes edge did not project${projectionErrors ? ` (${projectionErrors})` : ""}; the predecessor still reads as current. Run gno update to retry the projection.`
|
|
340
419
|
);
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
420
|
+
}
|
|
421
|
+
} else if (projectionErrors.length > 0) {
|
|
422
|
+
throw new MemoryError(
|
|
423
|
+
"MEMORY_SYNC_FAILED",
|
|
424
|
+
`Memory record written to ${absPath} but typed-edge projection failed: ${projectionErrors}. Run gno update to retry indexing.`
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
const record: MemoryFact = {
|
|
428
|
+
uri: written.uri,
|
|
429
|
+
docid: written.docid,
|
|
430
|
+
recordId: frontmatter.recordId,
|
|
431
|
+
text,
|
|
432
|
+
scopes: frontmatter.scopes,
|
|
433
|
+
caller: frontmatter.caller,
|
|
434
|
+
session: frontmatter.session,
|
|
435
|
+
createdAt: frontmatter.createdAt,
|
|
436
|
+
contentHash,
|
|
437
|
+
supersedes,
|
|
438
|
+
...(frontmatter.source ? { source: frontmatter.source } : {}),
|
|
439
|
+
};
|
|
440
|
+
return {
|
|
441
|
+
outcome: decision === "supersede" ? "superseded" : "added",
|
|
442
|
+
record,
|
|
443
|
+
absPath,
|
|
444
|
+
sync,
|
|
445
|
+
matching,
|
|
446
|
+
};
|
|
447
|
+
};
|
|
448
|
+
|
|
449
|
+
const lockWaitMs = memoryLockWaitMs(deps);
|
|
450
|
+
try {
|
|
451
|
+
if (requestId === undefined) {
|
|
452
|
+
return await withWriteLock(
|
|
453
|
+
deps.lockPath,
|
|
454
|
+
async () => {
|
|
455
|
+
const prepared = await prepare();
|
|
456
|
+
if ("result" in prepared) return prepared.result;
|
|
457
|
+
await publish(prepared.plan);
|
|
458
|
+
return finish(prepared.plan);
|
|
459
|
+
},
|
|
460
|
+
lockWaitMs
|
|
461
|
+
);
|
|
462
|
+
}
|
|
463
|
+
if (!deps.requests) {
|
|
464
|
+
throw new RequestReceiptError(
|
|
465
|
+
"REQUEST_LEDGER_UNAVAILABLE",
|
|
466
|
+
"This memory service has no request ledger; retry without requestId."
|
|
467
|
+
);
|
|
468
|
+
}
|
|
469
|
+
const outcome = await runRequestedWrite<RememberPlan, RememberResult>({
|
|
470
|
+
ledgerPath: deps.requests.ledgerPath,
|
|
471
|
+
namespace: deps.requests.namespace,
|
|
472
|
+
requestId,
|
|
473
|
+
operation: "remember",
|
|
474
|
+
// Caller/session are provenance, not intent: a retry may reconnect
|
|
475
|
+
// under a new session and must still match.
|
|
476
|
+
digest: requestDigest("remember", {
|
|
477
|
+
collection: collection.name,
|
|
478
|
+
text,
|
|
479
|
+
scopes,
|
|
480
|
+
decision,
|
|
481
|
+
predecessorUri: rawInput.predecessorUri?.trim(),
|
|
482
|
+
predecessorHash: rawInput.predecessorHash,
|
|
483
|
+
source,
|
|
484
|
+
}),
|
|
485
|
+
lockPath: deps.lockPath,
|
|
486
|
+
lockWaitMs,
|
|
487
|
+
checkpoint: deps.requests.checkpoint,
|
|
488
|
+
prepare: async () => {
|
|
489
|
+
const prepared = await prepare();
|
|
490
|
+
if ("result" in prepared) return prepared;
|
|
491
|
+
return { plan: prepared.plan, publish: () => publish(prepared.plan) };
|
|
492
|
+
},
|
|
493
|
+
inspect: async (plan) => {
|
|
494
|
+
const file = Bun.file(absPathOf(plan));
|
|
495
|
+
if (!(await file.exists())) return "absent";
|
|
496
|
+
if ((await file.text()) !== serialize(plan)) return "unexpected";
|
|
497
|
+
// Another successor may have superseded the predecessor while this
|
|
498
|
+
// one sat unprojected: finishing would leave two successors.
|
|
499
|
+
const [predecessorUri] = plan.supersedes;
|
|
500
|
+
if (!predecessorUri) return "published";
|
|
501
|
+
const predecessor = await store.getDocumentByUri(predecessorUri);
|
|
502
|
+
if (!predecessor.ok || !predecessor.value) return "unexpected";
|
|
503
|
+
const successors = await store.getEdgeBacklinksForDoc(
|
|
504
|
+
predecessor.value.id,
|
|
505
|
+
{ edgeType: MEMORY_SUPERSEDES_EDGE }
|
|
349
506
|
);
|
|
350
|
-
const
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
status: "failed",
|
|
356
|
-
error:
|
|
357
|
-
fileResult?.errorMessage ??
|
|
358
|
-
fileResult?.errorCode ??
|
|
359
|
-
"memory record was written but is not retrievable yet",
|
|
360
|
-
}
|
|
361
|
-
: { status: "completed" };
|
|
362
|
-
if (sync.status === "failed") {
|
|
363
|
-
throw new MemoryError(
|
|
364
|
-
"MEMORY_SYNC_FAILED",
|
|
365
|
-
`Memory record written to ${absPath} but lexical sync failed: ${sync.error}. Run gno update to retry indexing.`
|
|
366
|
-
);
|
|
367
|
-
}
|
|
368
|
-
const written = (doc as { value: DocumentRow }).value;
|
|
369
|
-
const projectionErrors = syncResult.errors
|
|
370
|
-
.map((error) => `${error.relPath}: ${error.message}`)
|
|
371
|
-
.join("; ");
|
|
372
|
-
if (decision === "supersede") {
|
|
373
|
-
// The write is only a supersession once the edge is projected;
|
|
374
|
-
// until then the predecessor still reads as current.
|
|
375
|
-
const projected =
|
|
376
|
-
projectionErrors.length === 0 &&
|
|
377
|
-
(await supersedesEdgeProjected(deps, written.id, supersedes));
|
|
378
|
-
if (!projected) {
|
|
379
|
-
throw new MemoryError(
|
|
380
|
-
"MEMORY_SUPERSEDE_PROJECTION_FAILED",
|
|
381
|
-
`Successor written to ${absPath} but its supersedes edge did not project${projectionErrors ? ` (${projectionErrors})` : ""}; the predecessor still reads as current. Run gno update to retry the projection.`
|
|
382
|
-
);
|
|
383
|
-
}
|
|
384
|
-
} else if (projectionErrors.length > 0) {
|
|
385
|
-
throw new MemoryError(
|
|
386
|
-
"MEMORY_SYNC_FAILED",
|
|
387
|
-
`Memory record written to ${absPath} but typed-edge projection failed: ${projectionErrors}. Run gno update to retry indexing.`
|
|
388
|
-
);
|
|
389
|
-
}
|
|
390
|
-
const record: MemoryFact = {
|
|
391
|
-
uri: written.uri,
|
|
392
|
-
docid: written.docid,
|
|
393
|
-
recordId: frontmatter.recordId,
|
|
394
|
-
text,
|
|
395
|
-
scopes,
|
|
396
|
-
caller: identity.caller,
|
|
397
|
-
session: identity.session,
|
|
398
|
-
createdAt,
|
|
399
|
-
contentHash,
|
|
400
|
-
supersedes,
|
|
401
|
-
...(source ? { source } : {}),
|
|
402
|
-
};
|
|
403
|
-
return {
|
|
404
|
-
outcome: decision === "supersede" ? "superseded" : "added",
|
|
405
|
-
record,
|
|
406
|
-
absPath,
|
|
407
|
-
sync,
|
|
408
|
-
matching,
|
|
409
|
-
};
|
|
507
|
+
const ownUri = `gno://${collection.name}/${plan.relPath}`;
|
|
508
|
+
return successors.ok &&
|
|
509
|
+
successors.value.every((edge) => edge.sourceUri === ownUri)
|
|
510
|
+
? "published"
|
|
511
|
+
: "unexpected";
|
|
410
512
|
},
|
|
411
|
-
|
|
412
|
-
|
|
513
|
+
finish,
|
|
514
|
+
resultRef: (result) =>
|
|
515
|
+
result.outcome === "candidates"
|
|
516
|
+
? {}
|
|
517
|
+
: {
|
|
518
|
+
uri: result.record.uri,
|
|
519
|
+
docid: result.record.docid,
|
|
520
|
+
contentHash: result.record.contentHash,
|
|
521
|
+
},
|
|
522
|
+
});
|
|
523
|
+
return { ...outcome.result, request: outcome.request } as RememberResult;
|
|
413
524
|
} catch (error) {
|
|
414
|
-
if (error instanceof MemoryError
|
|
415
|
-
|
|
416
|
-
|
|
525
|
+
if (error instanceof MemoryError || error instanceof RequestReceiptError) {
|
|
526
|
+
throw error;
|
|
527
|
+
}
|
|
528
|
+
if (isLeaseBusy(error)) {
|
|
417
529
|
throw new MemoryError(
|
|
418
530
|
"MEMORY_WRITE_LEASE_BUSY",
|
|
419
531
|
`Could not acquire the shared write lease at ${deps.lockPath} within ${lockWaitMs}ms: another write holds it. The memory service takes the lease itself; callers must not pre-hold it.`
|
|
@@ -421,5 +533,4 @@ export async function rememberFact(
|
|
|
421
533
|
}
|
|
422
534
|
throw error;
|
|
423
535
|
}
|
|
424
|
-
return leased;
|
|
425
536
|
}
|
package/src/core/memory-types.ts
CHANGED
|
@@ -15,6 +15,7 @@ import type { EmbeddingPort } from "../llm/types";
|
|
|
15
15
|
import type { StorePort } from "../store/types";
|
|
16
16
|
import type { VectorIndexPort } from "../store/vector/types";
|
|
17
17
|
import type { EgressLineage } from "./egress-provenance";
|
|
18
|
+
import type { RequestCheckpoint, RequestReceiptInfo } from "./request-receipts";
|
|
18
19
|
|
|
19
20
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
20
21
|
// Binding defaults (tunable here, documented in docs/MEMORY.md)
|
|
@@ -106,6 +107,8 @@ export interface RememberInput extends MemoryIdentity {
|
|
|
106
107
|
/** Declared origins; any gno:// origin is fenced. */
|
|
107
108
|
derivedFrom?: string[];
|
|
108
109
|
source?: string;
|
|
110
|
+
/** Opt-in retry identity for add/supersede; see docs/MEMORY.md. */
|
|
111
|
+
requestId?: string;
|
|
109
112
|
}
|
|
110
113
|
|
|
111
114
|
export interface MemoryFact {
|
|
@@ -149,6 +152,7 @@ export type RememberResult =
|
|
|
149
152
|
outcome: "existing";
|
|
150
153
|
record: MemoryFact;
|
|
151
154
|
matching: MemoryMatchDiagnostics;
|
|
155
|
+
request?: RequestReceiptInfo;
|
|
152
156
|
}
|
|
153
157
|
| {
|
|
154
158
|
outcome: "candidates";
|
|
@@ -161,6 +165,7 @@ export type RememberResult =
|
|
|
161
165
|
absPath: string;
|
|
162
166
|
sync: MemorySyncState;
|
|
163
167
|
matching: MemoryMatchDiagnostics;
|
|
168
|
+
request?: RequestReceiptInfo;
|
|
164
169
|
};
|
|
165
170
|
|
|
166
171
|
export interface RecallInput extends MemoryIdentity {
|
|
@@ -208,4 +213,10 @@ export interface MemoryServiceDeps {
|
|
|
208
213
|
/** Must surface typed-edge projection errors (`syncPaths`, not `syncFiles`). */
|
|
209
214
|
syncService?: Pick<typeof defaultSyncService, "syncPaths">;
|
|
210
215
|
now?: () => Date;
|
|
216
|
+
/** Request ledger + caller namespace; required to honour `requestId`. */
|
|
217
|
+
requests?: {
|
|
218
|
+
ledgerPath: string;
|
|
219
|
+
namespace: string;
|
|
220
|
+
checkpoint?: (stage: RequestCheckpoint) => Promise<void> | void;
|
|
221
|
+
};
|
|
211
222
|
}
|
|
@@ -77,6 +77,14 @@ export const NETWORK_BOUNDARY_INVENTORY = [
|
|
|
77
77
|
action: null,
|
|
78
78
|
enforcement: "no_collection_data",
|
|
79
79
|
},
|
|
80
|
+
{
|
|
81
|
+
id: "windows-private-path-acl",
|
|
82
|
+
key: "src/core/windows-private-path.ts::child_process#1",
|
|
83
|
+
path: "src/core/windows-private-path.ts",
|
|
84
|
+
primitive: "child_process",
|
|
85
|
+
action: null,
|
|
86
|
+
enforcement: "local_process_only",
|
|
87
|
+
},
|
|
80
88
|
{
|
|
81
89
|
id: "local-file-operations",
|
|
82
90
|
key: "src/core/file-ops.ts::child_process#1",
|
|
@@ -289,6 +297,14 @@ export const NETWORK_BOUNDARY_INVENTORY = [
|
|
|
289
297
|
action: "serve",
|
|
290
298
|
enforcement: "local_process_only",
|
|
291
299
|
},
|
|
300
|
+
{
|
|
301
|
+
id: "session-import-process",
|
|
302
|
+
key: "src/sessions/import-child.ts::child_process#1",
|
|
303
|
+
path: "src/sessions/import-child.ts",
|
|
304
|
+
primitive: "child_process",
|
|
305
|
+
action: null,
|
|
306
|
+
enforcement: "local_process_only",
|
|
307
|
+
},
|
|
292
308
|
{
|
|
293
309
|
id: "http-mcp-tools",
|
|
294
310
|
key: "logical::http-mcp-tools",
|