@remnic/core 9.63.7 → 9.63.9

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 (153) hide show
  1. package/dist/access-admin-ops-surface.d.ts +3 -2
  2. package/dist/access-admin-ops-surface.js +11 -9
  3. package/dist/access-authorization-probe.d.ts +3 -2
  4. package/dist/access-authorization-probe.js +12 -10
  5. package/dist/access-boundary.d.ts +3 -2
  6. package/dist/access-boundary.js +11 -9
  7. package/dist/access-cli.js +46 -45
  8. package/dist/access-cli.js.map +1 -1
  9. package/dist/access-extraction-force-flush.d.ts +3 -2
  10. package/dist/access-extraction-force-flush.js +11 -9
  11. package/dist/access-http-lcm-compaction.d.ts +3 -2
  12. package/dist/access-http-lifecycle-flush.d.ts +3 -2
  13. package/dist/access-http-offline-stream.d.ts +3 -2
  14. package/dist/access-http-query.js +11 -9
  15. package/dist/access-http.d.ts +4 -3
  16. package/dist/access-http.js +24 -22
  17. package/dist/access-identity-continuity-surface.d.ts +2 -1
  18. package/dist/access-identity-continuity-surface.js +11 -9
  19. package/dist/access-lcm-surface.d.ts +3 -2
  20. package/dist/access-lcm-surface.js +11 -9
  21. package/dist/access-mcp-output-schemas.js +1 -1
  22. package/dist/access-mcp.d.ts +3 -2
  23. package/dist/access-mcp.js +19 -17
  24. package/dist/access-namespace-preflight.js +11 -9
  25. package/dist/access-observe-write-surface.d.ts +3 -2
  26. package/dist/access-observe-write-surface.js +11 -9
  27. package/dist/access-offline-manifest.d.ts +2 -1
  28. package/dist/access-offline-manifest.js +3 -3
  29. package/dist/access-operations-batch.js +13 -11
  30. package/dist/access-operations.d.ts +3 -2
  31. package/dist/access-operations.js +17 -15
  32. package/dist/access-recall-concurrency.d.ts +3 -2
  33. package/dist/access-recall-concurrency.js +11 -9
  34. package/dist/access-recall-response.d.ts +3 -2
  35. package/dist/access-recall-response.js +11 -9
  36. package/dist/access-recall-surface.d.ts +3 -2
  37. package/dist/access-recall-surface.js +11 -9
  38. package/dist/access-schema.js +5 -5
  39. package/dist/{access-service-CocW74uA.d.ts → access-service-DwfdjqkZ.d.ts} +1 -1
  40. package/dist/access-service-helpers.d.ts +3 -2
  41. package/dist/access-service.d.ts +3 -2
  42. package/dist/access-service.js +11 -9
  43. package/dist/access-surface-catalog.d.ts +3 -2
  44. package/dist/access-surface-catalog.js +13 -11
  45. package/dist/access-surface-catalog.js.map +1 -1
  46. package/dist/active-recall.js +3 -3
  47. package/dist/bootstrap.d.ts +2 -1
  48. package/dist/{capsule-crypto-7FJQINUR.js → capsule-crypto-YO5QJ6L3.js} +2 -2
  49. package/dist/chunk-2YA5N2BY.js +65 -0
  50. package/dist/chunk-2YA5N2BY.js.map +1 -0
  51. package/dist/{chunk-5S5DUJFJ.js → chunk-3IMYRX3W.js} +2 -2
  52. package/dist/{chunk-QIZOU3FN.js → chunk-5OOTHWGJ.js} +2 -2
  53. package/dist/{chunk-YMQKPSB2.js → chunk-5TF5STAV.js} +4 -4
  54. package/dist/{chunk-PMVPCZJZ.js → chunk-6433HV72.js} +2 -2
  55. package/dist/{chunk-WKJTMXME.js → chunk-BPQ7L27U.js} +44 -42
  56. package/dist/{chunk-WKJTMXME.js.map → chunk-BPQ7L27U.js.map} +1 -1
  57. package/dist/{chunk-2NLLXCJG.js → chunk-BXLOS5AJ.js} +2 -2
  58. package/dist/{chunk-IBZQQOM5.js → chunk-DGUVXXAU.js} +3 -3
  59. package/dist/{chunk-X7Y7WX73.js → chunk-DQEMWVMT.js} +1 -1
  60. package/dist/{chunk-HXZNTZ3Y.js → chunk-EDZNW3HE.js} +5 -5
  61. package/dist/{chunk-2L7DKNVE.js → chunk-FZEEWOI2.js} +4 -4
  62. package/dist/{chunk-XNJQZLAP.js → chunk-I2HTKOOP.js} +2 -2
  63. package/dist/{chunk-3S3PNR5H.js → chunk-KXWE62IH.js} +2 -2
  64. package/dist/{chunk-SYJW273H.js → chunk-LTYH2L6E.js} +12 -12
  65. package/dist/{chunk-YIMV2KYM.js → chunk-N7V3P4V5.js} +2 -2
  66. package/dist/{chunk-OOJCXJBN.js → chunk-O52TB5SL.js} +2 -2
  67. package/dist/{chunk-PZKWPXRF.js → chunk-PHKU6BZ2.js} +2 -2
  68. package/dist/{chunk-H4GQ2Y24.js → chunk-PQHNZ63P.js} +4 -4
  69. package/dist/{chunk-7ZAC2C2U.js → chunk-SY5JUJ2X.js} +35 -35
  70. package/dist/{chunk-YYQAKIDJ.js → chunk-VD2OT77P.js} +2 -2
  71. package/dist/chunk-VD2OT77P.js.map +1 -0
  72. package/dist/{chunk-KQAFEZQX.js → chunk-VDX2J7OX.js} +2 -2
  73. package/dist/{chunk-WZDTSBGQ.js → chunk-VYMICVRA.js} +2 -2
  74. package/dist/{chunk-XV6PUXJ5.js → chunk-WBPD6SBZ.js} +52 -59
  75. package/dist/chunk-WBPD6SBZ.js.map +1 -0
  76. package/dist/{chunk-3NZAVWU5.js → chunk-ZOAAY2OE.js} +2 -2
  77. package/dist/{chunk-PRDJMOST.js → chunk-ZVZHG7AI.js} +65 -15
  78. package/dist/chunk-ZVZHG7AI.js.map +1 -0
  79. package/dist/{chunk-IINVXO3U.js → chunk-ZWYXSF2T.js} +2 -2
  80. package/dist/{cli-BTNj5DzK.d.ts → cli-kcUqoV4E.d.ts} +2 -2
  81. package/dist/cli.d.ts +4 -3
  82. package/dist/cli.js +36 -34
  83. package/dist/config.js +3 -3
  84. package/dist/explicit-capture.d.ts +2 -1
  85. package/dist/external-wiki-access.d.ts +3 -2
  86. package/dist/external-wiki-access.js +12 -10
  87. package/dist/external-wiki-mcp-tools.d.ts +3 -2
  88. package/dist/{first-start-migration-Q2VB2E2D.js → first-start-migration-4NETATFF.js} +4 -4
  89. package/dist/included-memories.d.ts +11 -0
  90. package/dist/included-memories.js +14 -0
  91. package/dist/included-memories.js.map +1 -0
  92. package/dist/index.d.ts +6 -5
  93. package/dist/index.js +57 -56
  94. package/dist/lcm/index.js +3 -3
  95. package/dist/mcp-memory-inspector-app.d.ts +3 -2
  96. package/dist/offline-sync.js +2 -2
  97. package/dist/operator-toolkit.js +4 -4
  98. package/dist/{orchestrator-CSc9GfQv.d.ts → orchestrator-Bay_AofR.d.ts} +2 -3
  99. package/dist/orchestrator.d.ts +2 -1
  100. package/dist/orchestrator.js +46 -45
  101. package/dist/{public-http-BmPktTe3.d.ts → public-http-DscQtnCx.d.ts} +1 -1
  102. package/dist/recall-explain-renderer.d.ts +1 -0
  103. package/dist/recall-state.d.ts +6 -2
  104. package/dist/recall-state.js +6 -2
  105. package/dist/resume-bundles.js +4 -4
  106. package/dist/support-passport/index.d.ts +6 -5
  107. package/dist/support-passport/index.js +14 -12
  108. package/dist/transfer/backup.js +2 -2
  109. package/dist/transfer/capsule-export.js +2 -2
  110. package/dist/transfer/capsule-import.js +2 -2
  111. package/package.json +2 -2
  112. package/src/access-mcp-output-schemas.ts +1 -1
  113. package/src/access-recall-response.ts +4 -2
  114. package/src/access-recall-surface.ts +9 -9
  115. package/src/access-service-namespace.test.ts +9 -9
  116. package/src/access-service-raw-excerpt-read-gate.test.ts +1 -1
  117. package/src/included-memories.ts +88 -0
  118. package/src/orchestration/recall-internal-deps.ts +2 -4
  119. package/src/orchestration/recall-internal.ts +9 -13
  120. package/src/orchestration/recall-result-formatter-display-safe.test.ts +13 -16
  121. package/src/orchestration/recall-result-formatter.ts +32 -45
  122. package/src/orchestration/recall-section-coordinator.ts +20 -26
  123. package/src/orchestrator.ts +2 -4
  124. package/src/recall-explain-renderer.test.ts +4 -0
  125. package/src/recall-state.test.ts +108 -5
  126. package/src/recall-state.ts +85 -23
  127. package/src/recall-token-budget.test.ts +1 -1
  128. package/dist/chunk-PRDJMOST.js.map +0 -1
  129. package/dist/chunk-XV6PUXJ5.js.map +0 -1
  130. package/dist/chunk-YYQAKIDJ.js.map +0 -1
  131. /package/dist/{capsule-crypto-7FJQINUR.js.map → capsule-crypto-YO5QJ6L3.js.map} +0 -0
  132. /package/dist/{chunk-5S5DUJFJ.js.map → chunk-3IMYRX3W.js.map} +0 -0
  133. /package/dist/{chunk-QIZOU3FN.js.map → chunk-5OOTHWGJ.js.map} +0 -0
  134. /package/dist/{chunk-YMQKPSB2.js.map → chunk-5TF5STAV.js.map} +0 -0
  135. /package/dist/{chunk-PMVPCZJZ.js.map → chunk-6433HV72.js.map} +0 -0
  136. /package/dist/{chunk-2NLLXCJG.js.map → chunk-BXLOS5AJ.js.map} +0 -0
  137. /package/dist/{chunk-IBZQQOM5.js.map → chunk-DGUVXXAU.js.map} +0 -0
  138. /package/dist/{chunk-X7Y7WX73.js.map → chunk-DQEMWVMT.js.map} +0 -0
  139. /package/dist/{chunk-HXZNTZ3Y.js.map → chunk-EDZNW3HE.js.map} +0 -0
  140. /package/dist/{chunk-2L7DKNVE.js.map → chunk-FZEEWOI2.js.map} +0 -0
  141. /package/dist/{chunk-XNJQZLAP.js.map → chunk-I2HTKOOP.js.map} +0 -0
  142. /package/dist/{chunk-3S3PNR5H.js.map → chunk-KXWE62IH.js.map} +0 -0
  143. /package/dist/{chunk-SYJW273H.js.map → chunk-LTYH2L6E.js.map} +0 -0
  144. /package/dist/{chunk-YIMV2KYM.js.map → chunk-N7V3P4V5.js.map} +0 -0
  145. /package/dist/{chunk-OOJCXJBN.js.map → chunk-O52TB5SL.js.map} +0 -0
  146. /package/dist/{chunk-PZKWPXRF.js.map → chunk-PHKU6BZ2.js.map} +0 -0
  147. /package/dist/{chunk-H4GQ2Y24.js.map → chunk-PQHNZ63P.js.map} +0 -0
  148. /package/dist/{chunk-7ZAC2C2U.js.map → chunk-SY5JUJ2X.js.map} +0 -0
  149. /package/dist/{chunk-KQAFEZQX.js.map → chunk-VDX2J7OX.js.map} +0 -0
  150. /package/dist/{chunk-WZDTSBGQ.js.map → chunk-VYMICVRA.js.map} +0 -0
  151. /package/dist/{chunk-3NZAVWU5.js.map → chunk-ZOAAY2OE.js.map} +0 -0
  152. /package/dist/{chunk-IINVXO3U.js.map → chunk-ZWYXSF2T.js.map} +0 -0
  153. /package/dist/{first-start-migration-Q2VB2E2D.js.map → first-start-migration-4NETATFF.js.map} +0 -0
@@ -15,7 +15,7 @@ import { type BudgetDecision, toBudgetWarning } from "./cross-namespace-budget.j
15
15
  import { decideDisclosureEscalation } from "./recall-disclosure-escalation.js";
16
16
  import { type TagMatchMode, applyTagFilter, normalizeTags, parseTagMatch } from "./recall-tag-filter.js";
17
17
  import type { RecallDisclosure, RecallPlanMode } from "./types.js";
18
- import { displaySafeBudgetsApplied, displaySafeRecallSnapshot } from "./orchestration/recall-result-formatter.js";
18
+ import { displaySafeRecallSnapshot } from "./orchestration/recall-result-formatter.js";
19
19
  import {
20
20
  boundRecallContextComposition,
21
21
  composeRecallContext,
@@ -305,7 +305,9 @@ export async function assembleRecallResponse(
305
305
  fallbackUsed: snapshot?.fallbackUsed ?? false,
306
306
  sourcesUsed: snapshot?.sourcesUsed ?? [],
307
307
  disclosure,
308
- budgetsApplied: displaySafeBudgetsApplied(snapshot?.budgetsApplied, deps.orchestrator.config.memoryDir),
308
+ budgetsApplied: snapshot
309
+ ? displaySafeRecallSnapshot(snapshot, deps.orchestrator.config.memoryDir).budgetsApplied
310
+ : undefined,
309
311
  auditAnomalies,
310
312
  budgetWarning: toBudgetWarning(budgetDecision),
311
313
  latencyMs: snapshot?.latencyMs ?? (Date.now() - startedAt),
@@ -26,7 +26,7 @@ import { expandScopeProfileReadNamespaces, resolveScopeProfilePlan } from "./nam
26
26
  import type { Orchestrator, RecallInvocationOptions } from "./orchestrator.js";
27
27
  import { decideDisclosureEscalation } from "./recall-disclosure-escalation.js";
28
28
  import { assembleRecallResponse } from "./access-recall-response.js";
29
- import type { LastRecallSnapshot } from "./recall-state.js";
29
+ import { coerceIncludedMemories, type LastRecallSnapshot } from "./recall-state.js";
30
30
  import type { RecallContextComposition } from "./recall-context-composition.js";
31
31
  import { type TagMatchMode, applyTagFilter, normalizeTags, parseTagMatch } from "./recall-tag-filter.js";
32
32
  import { type RecallXraySnapshot, estimateRecallTokens } from "./recall-xray.js";
@@ -44,7 +44,6 @@ import {
44
44
  // Canonical copies (access-service.ts still carries private duplicates for
45
45
  // its remaining callers; dedupe tracked separately).
46
46
  import { qmdCollectionPathParts, qmdResultPathCandidates } from "./orchestration/qmd-result-resolver.js";
47
-
48
47
  export interface AccessRecallSurfaceDeps {
49
48
  readonly auditAdapter: AccessAuditAdapter | null;
50
49
  readonly budget: CrossNamespaceBudget;
@@ -214,7 +213,10 @@ export class AccessRecallSurface {
214
213
  rawExcerptsSuppressed?: boolean;
215
214
  }): Promise<EngramAccessRecallResponse> {
216
215
  const memoryIds = options.snapshot.results.map((result) => result.memoryId);
217
- const resultPaths = options.snapshot.results.map((result) => result.path);
216
+ const includedMemories = options.snapshot.results.map((result) => ({
217
+ id: result.memoryId,
218
+ path: result.path,
219
+ }));
218
220
  const namespace = options.snapshot.namespace
219
221
  ? this.deps.resolveNamespace(options.snapshot.namespace)
220
222
  : this.deps.orchestrator.config.defaultNamespace;
@@ -243,7 +245,7 @@ export class AccessRecallSurface {
243
245
  finalContextChars: options.snapshot.budget.used,
244
246
  },
245
247
  latencyMs: Date.now() - options.startedAt,
246
- resultPaths,
248
+ includedMemories,
247
249
  };
248
250
  const results = await this.deps.serializeRecallResults(
249
251
  snapshotForSerialization,
@@ -515,12 +517,10 @@ export class AccessRecallSurface {
515
517
  : null,
516
518
  );
517
519
  const rawExcerpts = rawExcerptsResult ?? undefined;
518
-
519
- const resultNamespaces = snapshot.resultNamespaces ?? [];
520
- for (let index = 0; index < (snapshot.resultPaths ?? []).length; index += 1) {
521
- const memoryPath = snapshot.resultPaths?.[index];
522
- const memoryNamespace = resultNamespaces[index];
520
+ for (const included of coerceIncludedMemories(snapshot)) {
521
+ const memoryPath = included.path;
523
522
  if (!memoryPath) continue;
523
+ const memoryNamespace = included.namespace;
524
524
  const seenKey = `${memoryNamespace ?? ""}\0${memoryPath}`;
525
525
  if (seen.has(seenKey)) continue;
526
526
  const resolved = await readResultPath(memoryPath, memoryNamespace);
@@ -259,7 +259,7 @@ test("last recall serialization resolves namespace collection-prefixed result pa
259
259
  queryLen: 4,
260
260
  memoryIds: [],
261
261
  namespace: "default",
262
- resultPaths: [`${collection}/archive/facts/2026-06-16/fact-001.md`],
262
+ includedMemories: [{ id: "", path: `${collection}/archive/facts/2026-06-16/fact-001.md` }],
263
263
  },
264
264
  "summary",
265
265
  );
@@ -355,7 +355,7 @@ test("last recall serialization does not fall back after namespace collection mi
355
355
  queryLen: 4,
356
356
  memoryIds: [],
357
357
  namespace: "default",
358
- resultPaths: [`${collection}/2026-06-16/fact-001.md`],
358
+ includedMemories: [{ id: "", path: `${collection}/2026-06-16/fact-001.md` }],
359
359
  },
360
360
  "summary",
361
361
  );
@@ -428,7 +428,7 @@ test("last recall serialization resolves cold collection paths through snapshot
428
428
  queryLen: 4,
429
429
  memoryIds: [],
430
430
  namespace: "default",
431
- resultPaths: ["test-memory-cold/facts/2026-06-16/fact-001.md"],
431
+ includedMemories: [{ id: "", path: "test-memory-cold/facts/2026-06-16/fact-001.md" }],
432
432
  },
433
433
  "summary",
434
434
  );
@@ -503,7 +503,7 @@ test("last recall serialization propagates locked namespace collection errors",
503
503
  queryLen: 4,
504
504
  memoryIds: [],
505
505
  namespace: "default",
506
- resultPaths: [`${collection}/2026-06-16/fact-001.md`],
506
+ includedMemories: [{ id: "", path: `${collection}/2026-06-16/fact-001.md` }],
507
507
  },
508
508
  "summary",
509
509
  ),
@@ -563,7 +563,7 @@ test("last recall serialization does not treat date paths as collection prefixes
563
563
  queryLen: 4,
564
564
  memoryIds: [],
565
565
  namespace: "default",
566
- resultPaths: ["2026-06-16/fact-001.md"],
566
+ includedMemories: [{ id: "", path: "2026-06-16/fact-001.md" }],
567
567
  },
568
568
  "summary",
569
569
  );
@@ -632,7 +632,7 @@ test("last recall serialization does not strip invalid collection prefixes", asy
632
632
  queryLen: 4,
633
633
  memoryIds: [],
634
634
  namespace: "default",
635
- resultPaths: ["test-memory--not-a-token/2026-06-16/fact-001.md"],
635
+ includedMemories: [{ id: "", path: "test-memory--not-a-token/2026-06-16/fact-001.md" }],
636
636
  },
637
637
  "summary",
638
638
  );
@@ -733,7 +733,7 @@ test("last recall serialization preserves absolute paths from readable namespace
733
733
  queryLen: 4,
734
734
  memoryIds: [],
735
735
  namespace: "team",
736
- resultPaths: [sharedMemoryPath],
736
+ includedMemories: [{ id: "", path: sharedMemoryPath }],
737
737
  },
738
738
  "summary",
739
739
  );
@@ -817,7 +817,7 @@ test("last recall serialization preserves absolute paths from dynamic namespace
817
817
  queryLen: 4,
818
818
  memoryIds: [],
819
819
  namespace: "team",
820
- resultPaths: [dynamicMemoryPath],
820
+ includedMemories: [{ id: "", path: dynamicMemoryPath }],
821
821
  },
822
822
  "summary",
823
823
  );
@@ -905,7 +905,7 @@ test("last recall serialization rejects traversing collection paths", async () =
905
905
  queryLen: 4,
906
906
  memoryIds: [],
907
907
  namespace: "default",
908
- resultPaths: [`${collection}/../../facts/2026-06-16/fact-001.md`],
908
+ includedMemories: [{ id: "", path: `${collection}/../../facts/2026-06-16/fact-001.md` }],
909
909
  },
910
910
  "summary",
911
911
  );
@@ -92,7 +92,7 @@ function makeRawExcerptProbe(options: {
92
92
  memoryIds: [],
93
93
  namespace: options.snapshotNamespace,
94
94
  recallNamespaces: [options.snapshotNamespace],
95
- resultPaths: [],
95
+ includedMemories: [],
96
96
  };
97
97
 
98
98
  const storage = {
@@ -0,0 +1,88 @@
1
+ export interface IncludedMemory {
2
+ id: string;
3
+ path: string;
4
+ namespace?: string;
5
+ }
6
+
7
+ type LegacyIncludedMemorySource = {
8
+ includedMemories?: unknown;
9
+ memoryIds?: unknown;
10
+ resultPaths?: unknown;
11
+ resultNamespaces?: unknown;
12
+ budgetsApplied?: unknown;
13
+ };
14
+
15
+ function legacyBudgetView(raw: LegacyIncludedMemorySource): {
16
+ includedMemoryIds?: unknown;
17
+ includedMemoryPaths?: unknown;
18
+ includedMemoryNamespaces?: unknown;
19
+ } {
20
+ const budgets = raw.budgetsApplied;
21
+ if (!budgets || typeof budgets !== "object") return {};
22
+ const record = budgets as Record<string, unknown>;
23
+ return {
24
+ includedMemoryIds: record.includedMemoryIds,
25
+ includedMemoryPaths: record.includedMemoryPaths,
26
+ includedMemoryNamespaces: record.includedMemoryNamespaces,
27
+ };
28
+ }
29
+
30
+ function asStringList(value: unknown): string[] {
31
+ return Array.isArray(value) ? value.map((item) => (typeof item === "string" ? item : "")) : [];
32
+ }
33
+
34
+ function asOptionalStringList(value: unknown): Array<string | undefined> {
35
+ return Array.isArray(value)
36
+ ? value.map((item) => (typeof item === "string" ? item : undefined))
37
+ : [];
38
+ }
39
+
40
+ function normalizeIncludedMemory(value: unknown): IncludedMemory | undefined {
41
+ if (!value || typeof value !== "object") return undefined;
42
+ const record = value as { id?: unknown; path?: unknown; namespace?: unknown };
43
+ const id = typeof record.id === "string" ? record.id : "";
44
+ const path = typeof record.path === "string" ? record.path : "";
45
+ if (!id && !path) return undefined;
46
+ const namespace = typeof record.namespace === "string" ? record.namespace : undefined;
47
+ return namespace ? { id, path, namespace } : { id, path };
48
+ }
49
+
50
+ export function coerceIncludedMemories(raw: object): IncludedMemory[] {
51
+ const source = raw as LegacyIncludedMemorySource;
52
+ if (Array.isArray(source.includedMemories)) {
53
+ return source.includedMemories
54
+ .map(normalizeIncludedMemory)
55
+ .filter((item): item is IncludedMemory => item !== undefined);
56
+ }
57
+
58
+ const budgets = legacyBudgetView(source);
59
+ const ids = asStringList(source.memoryIds ?? budgets.includedMemoryIds);
60
+ const paths = asStringList(source.resultPaths ?? budgets.includedMemoryPaths);
61
+ const namespaces = asOptionalStringList(
62
+ source.resultNamespaces ?? budgets.includedMemoryNamespaces,
63
+ );
64
+ const count = Math.max(ids.length, paths.length);
65
+ const memories: IncludedMemory[] = [];
66
+ for (let index = 0; index < count; index += 1) {
67
+ const id = ids[index] ?? "";
68
+ const path = paths[index] ?? "";
69
+ if (!id && !path) continue;
70
+ const namespace = namespaces[index];
71
+ memories.push(namespace ? { id, path, namespace } : { id, path });
72
+ }
73
+ return memories;
74
+ }
75
+
76
+ export function includedMemoryIds(memories: readonly IncludedMemory[]): string[] {
77
+ return memories.map((memory) => memory.id);
78
+ }
79
+
80
+ export function includedMemoryPaths(memories: readonly IncludedMemory[]): string[] {
81
+ return memories.map((memory) => memory.path);
82
+ }
83
+
84
+ export function includedMemoryNamespaces(
85
+ memories: readonly IncludedMemory[],
86
+ ): Array<string | undefined> {
87
+ return memories.map((memory) => memory.namespace);
88
+ }
@@ -20,7 +20,7 @@ import type { ObjectiveStateSearchResult } from "../objective-state.js";
20
20
  import type { IntentDebugSnapshot, QmdRecallSnapshot, QueryAwarePrefilter } from "../orchestrator.js";
21
21
  import type { CorpusReadOptions } from "../corpus-read-cancellation.js";
22
22
  import type { ProfilingCollector } from "../profiling.js";
23
- import type { GraphRecallExpandedEntry, LastRecallBudgetSummary, LastRecallStore, RecallHandleHistoryStore } from "../recall-state.js";
23
+ import type { GraphRecallExpandedEntry, IncludedMemory, LastRecallBudgetSummary, LastRecallStore, RecallHandleHistoryStore } from "../recall-state.js";
24
24
  import type { RecallXraySnapshot } from "../recall-xray.js";
25
25
  import type { RerankCache } from "../rerank.js";
26
26
  import type { SearchBackend, SearchDegradation, SearchQueryOptions } from "../search/port.js";
@@ -115,6 +115,7 @@ export interface RecallInternalDeps {
115
115
  omittedIds: string[];
116
116
  truncated: boolean;
117
117
  finalChars: number;
118
+ includedMemories: IncludedMemory[];
118
119
  includedMemoryIds: string[];
119
120
  includedMemoryPaths: string[];
120
121
  includedMemoryNamespaces: Array<string | undefined>;
@@ -168,9 +169,6 @@ export interface RecallInternalDeps {
168
169
  truncated?: boolean;
169
170
  includedSections?: string[];
170
171
  omittedSections?: string[];
171
- includedMemoryIds?: string[];
172
- includedMemoryPaths?: string[];
173
- includedMemoryNamespaces?: Array<string | undefined>;
174
172
  omittedMemoryIds?: string[];
175
173
  }): LastRecallBudgetSummary;
176
174
  buildQueryAwarePrefilter(
@@ -622,7 +622,7 @@ export class RecallInternalCoordinator {
622
622
  qmdHybridFetchLimit,
623
623
  }),
624
624
  latencyMs: Date.now() - recallStart,
625
- resultPaths: [],
625
+ includedMemories: [],
626
626
  policyVersion,
627
627
  appendImpression: this.deps.config.recordEmptyRecallImpressions,
628
628
  identityInjection: {
@@ -4944,14 +4944,14 @@ export class RecallInternalCoordinator {
4944
4944
  const assembledRecall = this.deps.assembleRecallSections(
4945
4945
  sectionBuckets, contextBudgetForFooter(recallBudgetChars, curiosityFooter),
4946
4946
  );
4947
- recalledMemoryIds = assembledRecall.includedMemoryIds;
4948
- recalledMemoryPaths = assembledRecall.includedMemoryPaths;
4949
- recalledMemoryNamespaces = assembledRecall.includedMemoryNamespaces;
4950
- recalledMemoryCount = assembledRecall.includedMemoryIds.length;
4947
+ recalledMemoryIds = assembledRecall.includedMemories.map((memory) => memory.id);
4948
+ recalledMemoryPaths = assembledRecall.includedMemories.map((memory) => memory.path);
4949
+ recalledMemoryNamespaces = assembledRecall.includedMemories.map((memory) => memory.namespace);
4950
+ recalledMemoryCount = assembledRecall.includedMemories.length;
4951
4951
  this.deps.trackMemoryAccess(
4952
- assembledRecall.includedMemoryIds,
4953
- assembledRecall.includedMemoryPaths,
4954
- assembledRecall.includedMemoryNamespaces,
4952
+ recalledMemoryIds,
4953
+ recalledMemoryPaths,
4954
+ recalledMemoryNamespaces,
4955
4955
  );
4956
4956
  const recalledContext = assembledRecall.sections.join("\n\n---\n\n");
4957
4957
  const composition = boundRecallContextComposition({
@@ -4987,9 +4987,6 @@ export class RecallInternalCoordinator {
4987
4987
  truncated: assembledRecall.truncated || compositionTruncated || curiosityFooterTruncated,
4988
4988
  includedSections,
4989
4989
  omittedSections,
4990
- includedMemoryIds: assembledRecall.includedMemoryIds,
4991
- includedMemoryPaths: assembledRecall.includedMemoryPaths,
4992
- includedMemoryNamespaces: assembledRecall.includedMemoryNamespaces,
4993
4990
  omittedMemoryIds: assembledRecall.omittedMemoryIds,
4994
4991
  });
4995
4992
  // X-ray capture (issue #570 PR 1). Only fires when the caller
@@ -5241,8 +5238,7 @@ export class RecallInternalCoordinator {
5241
5238
  sourcesUsed,
5242
5239
  budgetsApplied,
5243
5240
  latencyMs: Date.now() - recallStart,
5244
- resultPaths: recalledMemoryPaths,
5245
- resultNamespaces: recalledMemoryNamespaces,
5241
+ includedMemories: assembledRecall.includedMemories,
5246
5242
  policyVersion,
5247
5243
  appendImpression:
5248
5244
  recalledMemoryIds.length > 0 ||
@@ -2,19 +2,17 @@ import test from "node:test";
2
2
  import assert from "node:assert/strict";
3
3
  import { displaySafeRecallSnapshot } from "./recall-result-formatter.js";
4
4
 
5
- test("displaySafeRecallSnapshot relativizes result, budget, and tier-anchor paths without mutating input (#2077)", () => {
5
+ test("displaySafeRecallSnapshot relativizes included-memory and tier-anchor paths without mutating input (#2077)", () => {
6
6
  const memoryDir = "/srv/memory";
7
7
  const flatAbs = `${memoryDir}/facts/2026-07-19/b.md`;
8
8
  const nsResultAbs = `${memoryDir}/namespaces/tok-team/facts/2026-07-19/r.md`;
9
- // An anchor that is NOT among the returned results — no authoritative owner.
9
+ // An anchor that is NOT among the included memories — no authoritative owner.
10
10
  const unmatchedAnchorAbs = `${memoryDir}/namespaces/ns-616c706861/facts/2026-07-19/a.md`;
11
11
  const snapshot = {
12
- resultPaths: [flatAbs, nsResultAbs],
13
- resultNamespaces: [undefined, "team-alpha"],
14
- budgetsApplied: {
15
- includedMemoryPaths: [flatAbs, nsResultAbs],
16
- includedMemoryNamespaces: [undefined, "team-alpha"],
17
- },
12
+ includedMemories: [
13
+ { id: "mem-b", path: flatAbs },
14
+ { id: "mem-r", path: nsResultAbs, namespace: "team-alpha" },
15
+ ],
18
16
  tierExplain: {
19
17
  tier: "direct-answer",
20
18
  tierReason: "single high-confidence hit",
@@ -31,21 +29,20 @@ test("displaySafeRecallSnapshot relativizes result, budget, and tier-anchor path
31
29
 
32
30
  const safe = displaySafeRecallSnapshot(snapshot, memoryDir);
33
31
 
34
- assert.deepEqual(safe.resultPaths, ["facts/2026-07-19/b.md", "team-alpha/facts/2026-07-19/r.md"]);
35
- assert.deepEqual(safe.budgetsApplied.includedMemoryPaths, [
36
- "facts/2026-07-19/b.md",
37
- "team-alpha/facts/2026-07-19/r.md",
32
+ assert.deepEqual(safe.includedMemories, [
33
+ { id: "mem-b", path: "facts/2026-07-19/b.md" },
34
+ { id: "mem-r", path: "team-alpha/facts/2026-07-19/r.md", namespace: "team-alpha" },
38
35
  ]);
39
36
  assert.deepEqual(safe.tierExplain.sourceAnchors, [
40
- // Anchor coincides with a namespaced result → reuses its authoritative namespace.
37
+ // Anchor coincides with a namespaced included memory → reuses its authoritative namespace.
41
38
  { path: "team-alpha/facts/2026-07-19/r.md", lineRange: [1, 2] },
42
- // Anchor not among the results → truthful storage-relative segment, never a decode.
39
+ // Anchor not among the included memories → truthful storage-relative segment, never a decode.
43
40
  { path: "namespaces/ns-616c706861/facts/2026-07-19/a.md" },
44
- // Anchor coincides with a default-namespace result → memoryDir-relative.
41
+ // Anchor coincides with a default-namespace included memory → memoryDir-relative.
45
42
  { path: "facts/2026-07-19/b.md" },
46
43
  ]);
47
44
 
48
45
  // The input snapshot must not be mutated — the live cache keeps absolute paths.
49
46
  assert.equal(snapshot.tierExplain.sourceAnchors[0].path, nsResultAbs);
50
- assert.equal(snapshot.resultPaths[1], nsResultAbs);
47
+ assert.equal(snapshot.includedMemories[1].path, nsResultAbs);
51
48
  });
@@ -33,6 +33,7 @@ import type { WorkProductLedgerSearchResult } from "../work-product-ledger.js";
33
33
  import { trustResultFor, type TrustStageResultItem } from "../trust-score-stage.js";
34
34
  import { CONNECTOR_ID_PATTERN, CONNECTOR_LABEL_MAX_LENGTH } from "../connectors/label.js";
35
35
  import { isValidConnectorId } from "../connectors/index.js";
36
+ import { coerceIncludedMemories, type IncludedMemory } from "../included-memories.js";
36
37
  import type {
37
38
  ContinuityIncidentRecord,
38
39
  IdentityInjectionMode,
@@ -112,43 +113,26 @@ export function displayResultPath(
112
113
  : resultPath;
113
114
  }
114
115
 
115
- /**
116
- * Return a copy of budget metadata with `includedMemoryPaths` rendered
117
- * memoryDir-relative, so recall/last-recall API callers never receive operator
118
- * filesystem paths via budget output even though the internal snapshot keeps
119
- * absolute paths for tracking/x-ray (#2020). Other fields are unchanged.
120
- */
121
- export function displaySafeBudgetsApplied<
122
- T extends {
123
- includedMemoryPaths?: string[];
124
- includedMemoryNamespaces?: Array<string | undefined>;
125
- },
126
- >(budgetsApplied: T | undefined, memoryDir: string): T | undefined {
127
- if (!budgetsApplied?.includedMemoryPaths) return budgetsApplied;
128
- return {
129
- ...budgetsApplied,
130
- includedMemoryPaths: budgetsApplied.includedMemoryPaths.map((p, i) =>
131
- displayResultPath(p, memoryDir, budgetsApplied.includedMemoryNamespaces?.[i]),
132
- ),
133
- };
134
- }
135
-
136
116
  /**
137
117
  * Return a display-safe copy of a recall snapshot for the `includeDebug=true`
138
- * surface (#2077). `resultPaths` and `budgetsApplied.includedMemoryPaths` are
139
- * rendered namespace-relative from their aligned namespace metadata; a
140
- * `tierExplain.sourceAnchors[]` entry reuses that SAME authoritative metadata
141
- * when its absolute path matches a result/included path, and otherwise falls
142
- * back to the memoryDir-relative on-disk form. Either way the debug flag never
143
- * leaks absolute operator paths, and an anchor is never attributed to a decoded
144
- * (guessed) namespace an anchor carries no owner of its own. Returns a
145
- * shallow copy; the input snapshot is never mutated.
118
+ * surface (#2077). `includedMemories[].path` is rendered namespace-relative
119
+ * from each memory's own namespace the same rendering `resultPaths` received
120
+ * before the struct consolidation; a `tierExplain.sourceAnchors[]` entry
121
+ * reuses that SAME authoritative metadata when its absolute path matches an
122
+ * included memory's path, and otherwise falls back to the memoryDir-relative
123
+ * on-disk form. Either way the debug flag never leaks absolute operator
124
+ * paths, and an anchor is never attributed to a decoded (guessed) namespace —
125
+ * an anchor carries no owner of its own. Returns a shallow copy; the input
126
+ * snapshot is never mutated.
146
127
  */
147
128
  export function displaySafeRecallSnapshot<
148
129
  T extends {
130
+ includedMemories?: IncludedMemory[];
131
+ memoryIds?: string[];
149
132
  resultPaths?: string[];
150
133
  resultNamespaces?: Array<string | undefined>;
151
134
  budgetsApplied?: {
135
+ includedMemoryIds?: string[];
152
136
  includedMemoryPaths?: string[];
153
137
  includedMemoryNamespaces?: Array<string | undefined>;
154
138
  };
@@ -157,21 +141,14 @@ export function displaySafeRecallSnapshot<
157
141
  };
158
142
  },
159
143
  >(snapshot: T, memoryDir: string): T {
160
- const resultPaths = snapshot.resultPaths?.map((p, i) =>
161
- displayResultPath(p, memoryDir, snapshot.resultNamespaces?.[i]),
162
- );
163
- // Authoritative absolute-path -> namespace map recorded at capture time, so a
164
- // tier anchor that coincides with a returned result renders under the SAME
165
- // namespace as that result instead of the raw storage segment (#2077).
144
+ const includedMemories = coerceIncludedMemories(snapshot).map((memory) => ({
145
+ ...memory,
146
+ path: displayResultPath(memory.path, memoryDir, memory.namespace),
147
+ }));
166
148
  const namespaceByPath = new Map<string, string | undefined>();
167
- snapshot.resultPaths?.forEach((p, i) => {
168
- if (!namespaceByPath.has(p)) namespaceByPath.set(p, snapshot.resultNamespaces?.[i]);
169
- });
170
- snapshot.budgetsApplied?.includedMemoryPaths?.forEach((p, i) => {
171
- if (!namespaceByPath.has(p)) {
172
- namespaceByPath.set(p, snapshot.budgetsApplied?.includedMemoryNamespaces?.[i]);
173
- }
174
- });
149
+ for (const memory of coerceIncludedMemories(snapshot)) {
150
+ if (!namespaceByPath.has(memory.path)) namespaceByPath.set(memory.path, memory.namespace);
151
+ }
175
152
  const tierExplain =
176
153
  snapshot.tierExplain?.sourceAnchors
177
154
  ? {
@@ -182,11 +159,21 @@ export function displaySafeRecallSnapshot<
182
159
  })),
183
160
  }
184
161
  : undefined;
162
+ const resultPaths = includedMemories.map((memory) => memory.path);
163
+ const resultNamespaces = includedMemories.map((memory) => memory.namespace);
185
164
  return {
186
165
  ...snapshot,
187
- ...(resultPaths ? { resultPaths } : {}),
166
+ includedMemories,
167
+ resultPaths,
168
+ resultNamespaces,
188
169
  ...(snapshot.budgetsApplied
189
- ? { budgetsApplied: displaySafeBudgetsApplied(snapshot.budgetsApplied, memoryDir) }
170
+ ? {
171
+ budgetsApplied: {
172
+ ...snapshot.budgetsApplied,
173
+ includedMemoryPaths: resultPaths,
174
+ includedMemoryNamespaces: resultNamespaces,
175
+ },
176
+ }
190
177
  : {}),
191
178
  ...(tierExplain ? { tierExplain } : {}),
192
179
  };
@@ -17,7 +17,7 @@
17
17
  * TierMigrationCoordinator accessor pattern.
18
18
  */
19
19
 
20
- import type { LastRecallBudgetSummary } from "../recall-state.js";
20
+ import type { IncludedMemory, LastRecallBudgetSummary } from "../recall-state.js";
21
21
  import { estimateTokenCount } from "../token-estimate.js";
22
22
  import { graphemeUnits, truncateGraphemeSafe } from "../whitespace.js";
23
23
  import type { PluginConfig, RecallSectionConfig } from "../types.js";
@@ -273,6 +273,7 @@ export class RecallSectionCoordinator {
273
273
  omittedIds: string[];
274
274
  truncated: boolean;
275
275
  finalChars: number;
276
+ includedMemories: IncludedMemory[];
276
277
  includedMemoryIds: string[];
277
278
  includedMemoryPaths: string[];
278
279
  includedMemoryNamespaces: Array<string | undefined>;
@@ -340,6 +341,7 @@ export class RecallSectionCoordinator {
340
341
  omittedIds: orderedSections.map((entry) => entry.id),
341
342
  truncated: orderedSections.length > 0,
342
343
  finalChars: 0,
344
+ includedMemories: [],
343
345
  includedMemoryIds: [],
344
346
  includedMemoryPaths: [],
345
347
  includedMemoryNamespaces: [],
@@ -372,9 +374,7 @@ export class RecallSectionCoordinator {
372
374
  break;
373
375
  }
374
376
  }
375
- const includedMemoryIds: string[] = [];
376
- const includedMemoryPaths: string[] = [];
377
- const includedMemoryNamespaces: Array<string | undefined> = [];
377
+ const includedMemories: IncludedMemory[] = [];
378
378
  let usedChars = 0;
379
379
  let usedTokens = 0;
380
380
  let truncated = false;
@@ -492,12 +492,12 @@ export class RecallSectionCoordinator {
492
492
  rendered = chunk.content;
493
493
  includedLeadingContent = false;
494
494
  includedAtomicCount = 1;
495
- // Push id/path/namespace together so the parallel tracking arrays
496
- // never misalign when a chunk carries only one of them (#2020).
497
495
  if (chunk.memoryId && chunk.memoryPath) {
498
- includedMemoryIds.push(chunk.memoryId);
499
- includedMemoryPaths.push(chunk.memoryPath);
500
- includedMemoryNamespaces.push(chunk.memoryNamespace);
496
+ includedMemories.push(
497
+ chunk.memoryNamespace
498
+ ? { id: chunk.memoryId, path: chunk.memoryPath, namespace: chunk.memoryNamespace }
499
+ : { id: chunk.memoryId, path: chunk.memoryPath },
500
+ );
501
501
  }
502
502
  continue;
503
503
  }
@@ -522,9 +522,11 @@ export class RecallSectionCoordinator {
522
522
  if (chunk.atomic) {
523
523
  includedAtomicCount += 1;
524
524
  if (chunk.memoryId && chunk.memoryPath) {
525
- includedMemoryIds.push(chunk.memoryId);
526
- includedMemoryPaths.push(chunk.memoryPath);
527
- includedMemoryNamespaces.push(chunk.memoryNamespace);
525
+ includedMemories.push(
526
+ chunk.memoryNamespace
527
+ ? { id: chunk.memoryId, path: chunk.memoryPath, namespace: chunk.memoryNamespace }
528
+ : { id: chunk.memoryId, path: chunk.memoryPath },
529
+ );
528
530
  }
529
531
  } else {
530
532
  includedPostAtomicContent = true;
@@ -574,16 +576,14 @@ export class RecallSectionCoordinator {
574
576
  omittedIds,
575
577
  truncated,
576
578
  finalChars: usedChars,
577
- includedMemoryIds,
578
- includedMemoryPaths,
579
- includedMemoryNamespaces,
579
+ includedMemories,
580
+ includedMemoryIds: includedMemories.map((memory) => memory.id),
581
+ includedMemoryPaths: includedMemories.map((memory) => memory.path),
582
+ includedMemoryNamespaces: includedMemories.map((memory) => memory.namespace),
580
583
  omittedMemoryIds: (() => {
581
- // Key by (namespace, id): the same memory id can exist in more than one
582
- // namespace, so an id-only subtraction would hide a budget-dropped copy
583
- // when another namespace's copy was included (#2020).
584
584
  const includedKeys = new Set(
585
- includedMemoryIds.map(
586
- (id, i) => `${includedMemoryNamespaces[i] ?? ""}\u0000${id}`,
585
+ includedMemories.map(
586
+ (memory) => `${memory.namespace ?? ""}\u0000${memory.id}`,
587
587
  ),
588
588
  );
589
589
  return candidateMemoryChunks
@@ -604,9 +604,6 @@ export class RecallSectionCoordinator {
604
604
  finalContextChars?: number;
605
605
  truncated?: boolean;
606
606
  includedSections?: string[];
607
- includedMemoryIds?: string[];
608
- includedMemoryPaths?: string[];
609
- includedMemoryNamespaces?: Array<string | undefined>;
610
607
  omittedMemoryIds?: string[];
611
608
  omittedSections?: string[];
612
609
  }): LastRecallBudgetSummary {
@@ -619,9 +616,6 @@ export class RecallSectionCoordinator {
619
616
  qmdHybridFetchLimit: options.qmdHybridFetchLimit,
620
617
  finalContextChars: options.finalContextChars,
621
618
  truncated: options.truncated,
622
- includedMemoryIds: [...(options.includedMemoryIds ?? [])],
623
- includedMemoryPaths: [...(options.includedMemoryPaths ?? [])],
624
- includedMemoryNamespaces: [...(options.includedMemoryNamespaces ?? [])],
625
619
  omittedMemoryIds: [...(options.omittedMemoryIds ?? [])],
626
620
  includedSections: [...(options.includedSections ?? [])],
627
621
  omittedSections: [...(options.omittedSections ?? [])],
@@ -260,6 +260,7 @@ import { NegativeExampleStore } from "./negative.js";
260
260
  import {
261
261
  LastRecallStore,
262
262
  RecallHandleHistoryStore,
263
+ type IncludedMemory,
263
264
  type LastRecallBudgetSummary,
264
265
  TierMigrationStatusStore,
265
266
  clampGraphRecallExpandedEntries,
@@ -2716,7 +2717,7 @@ export class Orchestrator {
2716
2717
  includedIds: string[];
2717
2718
  omittedIds: string[];
2718
2719
  truncated: boolean;
2719
- finalChars: number;
2720
+ includedMemories: IncludedMemory[];
2720
2721
  includedMemoryIds: string[];
2721
2722
  includedMemoryPaths: string[];
2722
2723
  includedMemoryNamespaces: Array<string | undefined>;
@@ -3542,9 +3543,6 @@ export class Orchestrator {
3542
3543
  finalContextChars?: number;
3543
3544
  truncated?: boolean;
3544
3545
  includedSections?: string[];
3545
- includedMemoryIds?: string[];
3546
- includedMemoryPaths?: string[];
3547
- includedMemoryNamespaces?: Array<string | undefined>;
3548
3546
  omittedMemoryIds?: string[];
3549
3547
  omittedSections?: string[];
3550
3548
  }): LastRecallBudgetSummary {
@@ -17,6 +17,10 @@ function legacySnapshot(
17
17
  queryHash: "deadbeef",
18
18
  queryLen: 21,
19
19
  memoryIds: ["mem-1", "mem-2"],
20
+ includedMemories: [
21
+ { id: "mem-1", path: "" },
22
+ { id: "mem-2", path: "" },
23
+ ],
20
24
  source: "hot_qmd",
21
25
  sourcesUsed: ["hot_qmd", "memories"],
22
26
  namespace: "workspace-a",