@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.
Files changed (136) hide show
  1. package/README.md +69 -6
  2. package/assets/skill/README.md +5 -1
  3. package/assets/skill/SKILL.md +98 -1
  4. package/assets/skill/cli-reference.md +140 -0
  5. package/assets/skill/examples.md +45 -0
  6. package/assets/skill/mcp-reference.md +62 -0
  7. package/assets/skill/recipes/capture-and-file.md +6 -0
  8. package/assets/skill/recipes/memory-file-decision.md +6 -0
  9. package/assets/skill/recipes/memory-supersede-fact.md +5 -0
  10. package/assets/skill/recipes/session-evidence-lookup.md +98 -0
  11. package/assets/spa-production.json.gz +0 -0
  12. package/browser-extension/artifacts/{gno-browser-clipper-v2.4.0.zip → gno-browser-clipper-v2.6.0.zip} +0 -0
  13. package/browser-extension/artifacts/gno-browser-clipper-v2.6.0.zip.sha256 +1 -0
  14. package/browser-extension/dist/manifest.json +1 -1
  15. package/package.json +2 -1
  16. package/spec/cli.md +358 -24
  17. package/spec/compiled-context.md +68 -0
  18. package/spec/mcp.md +223 -2
  19. package/spec/output-schemas/capture-receipt.schema.json +3 -0
  20. package/spec/output-schemas/compiled-context-check.schema.json +44 -0
  21. package/spec/output-schemas/compiled-context-file.schema.json +165 -0
  22. package/spec/output-schemas/compiled-context-preview.schema.json +142 -0
  23. package/spec/output-schemas/mcp-capture-result.schema.json +3 -0
  24. package/spec/output-schemas/memory-remember.schema.json +8 -2
  25. package/spec/output-schemas/request-status.schema.json +113 -0
  26. package/spec/output-schemas/sessions-automation-run.schema.json +46 -0
  27. package/spec/output-schemas/sessions-discovery.schema.json +38 -0
  28. package/spec/output-schemas/sessions-import-receipt.schema.json +156 -0
  29. package/spec/output-schemas/sessions-status.schema.json +432 -0
  30. package/src/app/compiled-context-files.ts +361 -0
  31. package/src/app/compiled-context.ts +240 -0
  32. package/src/app/context-surface.ts +7 -2
  33. package/src/cli/commands/ask.ts +14 -2
  34. package/src/cli/commands/audit.ts +4 -0
  35. package/src/cli/commands/capture.ts +55 -96
  36. package/src/cli/commands/context-compiled.ts +136 -0
  37. package/src/cli/commands/daemon.ts +41 -0
  38. package/src/cli/commands/ls.ts +3 -0
  39. package/src/cli/commands/memory.ts +12 -3
  40. package/src/cli/commands/request-status.ts +59 -0
  41. package/src/cli/commands/reset.ts +39 -5
  42. package/src/cli/commands/sessions.ts +713 -0
  43. package/src/cli/commands/shared.ts +14 -1
  44. package/src/cli/errors.ts +10 -3
  45. package/src/cli/program.ts +458 -1
  46. package/src/cli/session-binding.ts +49 -0
  47. package/src/config/types.ts +8 -0
  48. package/src/core/capture-publish.ts +239 -0
  49. package/src/core/capture-sync.ts +3 -0
  50. package/src/core/compiled-context.ts +254 -0
  51. package/src/core/context-budget.ts +2 -10
  52. package/src/core/file-lock.ts +22 -5
  53. package/src/core/folder-setup-planning.ts +2 -1
  54. package/src/core/memory-remember.ts +233 -122
  55. package/src/core/memory-types.ts +11 -0
  56. package/src/core/network-boundary-inventory.ts +16 -0
  57. package/src/core/request-receipts.ts +671 -0
  58. package/src/core/setup-receipt.ts +27 -20
  59. package/src/core/typed-metadata.ts +4 -0
  60. package/src/core/validation.ts +9 -2
  61. package/src/core/windows-private-path.ts +96 -0
  62. package/src/index.ts +11 -2
  63. package/src/ingestion/compiled-context.ts +15 -0
  64. package/src/ingestion/sync.ts +40 -8
  65. package/src/ingestion/walker.ts +5 -4
  66. package/src/llm/nodeLlamaCpp/simulator-install.ts +6 -2
  67. package/src/mcp/context.ts +8 -0
  68. package/src/mcp/http-egress.ts +15 -3
  69. package/src/mcp/http-transport.ts +2 -0
  70. package/src/mcp/retrieval-warnings.ts +24 -0
  71. package/src/mcp/tools/ask.ts +14 -1
  72. package/src/mcp/tools/capture.ts +87 -83
  73. package/src/mcp/tools/context.ts +46 -2
  74. package/src/mcp/tools/index.ts +119 -7
  75. package/src/mcp/tools/memory-remember.ts +7 -0
  76. package/src/mcp/tools/memory-shared.ts +7 -1
  77. package/src/mcp/tools/query.ts +3 -1
  78. package/src/mcp/tools/request-status.ts +73 -0
  79. package/src/mcp/tools/search.ts +3 -1
  80. package/src/mcp/tools/sessions.ts +208 -0
  81. package/src/mcp/tools/vsearch.ts +3 -1
  82. package/src/sdk/client.ts +252 -89
  83. package/src/sdk/index.ts +13 -0
  84. package/src/sdk/types.ts +82 -3
  85. package/src/serve/capture-service.ts +98 -32
  86. package/src/serve/compiled-context.ts +84 -0
  87. package/src/serve/config-sync.ts +3 -2
  88. package/src/serve/public/app.tsx +15 -1
  89. package/src/serve/public/components/CaptureModal.tsx +26 -8
  90. package/src/serve/public/components/sessions/AutomationPanel.tsx +800 -0
  91. package/src/serve/public/components/sessions/ImportReceipt.tsx +238 -0
  92. package/src/serve/public/components/sessions/SessionSearch.tsx +286 -0
  93. package/src/serve/public/components/sessions/SourcesPanel.tsx +541 -0
  94. package/src/serve/public/components/sessions/api.ts +40 -0
  95. package/src/serve/public/components/sessions/snippet.tsx +53 -0
  96. package/src/serve/public/globals.built.css +1 -1
  97. package/src/serve/public/hooks/use-api.ts +10 -2
  98. package/src/serve/public/lib/request-intent.ts +69 -0
  99. package/src/serve/public/lib/workspace-actions.ts +12 -1
  100. package/src/serve/public/lib/workspace-tabs.ts +4 -0
  101. package/src/serve/public/pages/CompiledContext.tsx +364 -0
  102. package/src/serve/public/pages/Dashboard.tsx +17 -0
  103. package/src/serve/public/pages/DocView.tsx +15 -1
  104. package/src/serve/public/pages/DocumentEditor.tsx +139 -96
  105. package/src/serve/public/pages/Sessions.tsx +350 -0
  106. package/src/serve/resident-runtime.ts +43 -3
  107. package/src/serve/routes/api.ts +476 -147
  108. package/src/serve/routes/sessions.ts +766 -0
  109. package/src/serve/security.ts +9 -0
  110. package/src/serve/server.ts +248 -3
  111. package/src/serve/session-automation.ts +146 -0
  112. package/src/serve/spa-production-build.ts +6 -5
  113. package/src/sessions/archive.ts +348 -0
  114. package/src/sessions/automation-state.ts +444 -0
  115. package/src/sessions/automation-status.ts +239 -0
  116. package/src/sessions/automation.ts +1169 -0
  117. package/src/sessions/binding.ts +105 -0
  118. package/src/sessions/claude-hook.ts +240 -0
  119. package/src/sessions/config.ts +176 -0
  120. package/src/sessions/format.ts +191 -0
  121. package/src/sessions/import-child-env.ts +8 -0
  122. package/src/sessions/import-child.ts +152 -0
  123. package/src/sessions/parsers/claude-code.ts +259 -0
  124. package/src/sessions/parsers/codex.ts +303 -0
  125. package/src/sessions/parsers/hermes.ts +248 -0
  126. package/src/sessions/parsers/openclaw.ts +496 -0
  127. package/src/sessions/parsers/shared.ts +184 -0
  128. package/src/sessions/sanitize.ts +222 -0
  129. package/src/sessions/service.ts +1533 -0
  130. package/src/sessions/setup.ts +477 -0
  131. package/src/sessions/sources.ts +518 -0
  132. package/src/sessions/state.ts +118 -0
  133. package/src/sessions/types.ts +457 -0
  134. package/src/store/sqlite/adapter.ts +68 -16
  135. package/src/store/sqlite/scoped-index.ts +9 -0
  136. 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 frontmatter: MemoryRecordFrontmatter = {
291
- recordId: buildMemoryRecordId({
292
- contentHash,
293
- createdAt,
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
- scopes,
298
- caller: identity.caller,
299
- session: identity.session,
300
- createdAt,
301
- contentHash,
302
- ...(source ? { source } : {}),
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 relPath = buildMemoryRecordRelPath(frontmatter);
305
- const absPath = join(collection.path, relPath);
306
- const lockWaitMs = memoryLockWaitMs(deps);
307
-
308
- let leased: RememberResult;
309
- try {
310
- leased = await withWriteLock(
311
- deps.lockPath,
312
- async () => {
313
- const supersedes: string[] = [];
314
- if (decision === "supersede") {
315
- supersedes.push(
316
- await verifyPredecessor(
317
- deps,
318
- collection.name,
319
- rawInput.predecessorUri as string,
320
- rawInput.predecessorHash as string
321
- )
322
- );
323
- } else {
324
- // The pre-lease check raced with any concurrent writer; decide
325
- // idempotency on the state visible under the lease.
326
- const existing = await findExactCurrent(deps, {
327
- text,
328
- collection: collection.name,
329
- scopes,
330
- contentHash,
331
- });
332
- if (existing) {
333
- return { outcome: "existing", record: existing, matching };
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
- await mkdir(dirname(absPath), { recursive: true });
337
- await atomicCreate(
338
- absPath,
339
- serializeMemoryRecord({ frontmatter, supersedes, text })
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
- // syncPaths (not syncFiles) so typed-edge projection errors surface.
342
- const syncResult = await (
343
- deps.syncService ?? defaultSyncService
344
- ).syncPaths(
345
- collection,
346
- store,
347
- [relPath],
348
- withContentTypeRules({ runUpdateCmd: false, gitPull: false }, config)
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 fileResult = syncResult.files?.[0];
351
- const doc = await store.getDocument(collection.name, relPath);
352
- const sync: MemorySyncState =
353
- fileResult?.status === "error" || !doc.ok || doc.value === null
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
- lockWaitMs
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) throw error;
415
- const message = error instanceof Error ? error.message : String(error);
416
- if (message.startsWith("LOCKED")) {
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
  }
@@ -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",