@fgv/ts-agent-memory 5.1.0-49 → 5.1.0-50

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 (164) hide show
  1. package/dist/packlets/index/memoryIndex.js +23 -16
  2. package/dist/packlets/index/memoryIndex.js.map +1 -1
  3. package/dist/packlets/ingest/orchestrator.js +13 -1
  4. package/dist/packlets/ingest/orchestrator.js.map +1 -1
  5. package/dist/packlets/retrieve/linkTraversalRetriever.js +12 -26
  6. package/dist/packlets/retrieve/linkTraversalRetriever.js.map +1 -1
  7. package/dist/packlets/retrieve/recencyRetriever.js +7 -7
  8. package/dist/packlets/retrieve/recencyRetriever.js.map +1 -1
  9. package/dist/packlets/retrieve/retriever.js +91 -10
  10. package/dist/packlets/retrieve/retriever.js.map +1 -1
  11. package/dist/packlets/retrieve/semanticRetriever.js +16 -16
  12. package/dist/packlets/retrieve/semanticRetriever.js.map +1 -1
  13. package/dist/packlets/retrieve/structuredFilterRetriever.js +7 -7
  14. package/dist/packlets/retrieve/structuredFilterRetriever.js.map +1 -1
  15. package/dist/packlets/retrieve/tagRetriever.js +7 -7
  16. package/dist/packlets/retrieve/tagRetriever.js.map +1 -1
  17. package/dist/packlets/retrieve/temporalRetrievers.js +23 -20
  18. package/dist/packlets/retrieve/temporalRetrievers.js.map +1 -1
  19. package/dist/packlets/store/coverage.js +6 -0
  20. package/dist/packlets/store/coverage.js.map +1 -0
  21. package/dist/packlets/store/fileTreeMemoryStore.js +221 -79
  22. package/dist/packlets/store/fileTreeMemoryStore.js.map +1 -1
  23. package/dist/packlets/store/index.js +4 -0
  24. package/dist/packlets/store/index.js.map +1 -1
  25. package/dist/packlets/store/listSelection.js +36 -0
  26. package/dist/packlets/store/listSelection.js.map +1 -0
  27. package/dist/packlets/store/memoryStore.js +6 -0
  28. package/dist/packlets/store/memoryStore.js.map +1 -0
  29. package/dist/packlets/store/reconcile.js +6 -0
  30. package/dist/packlets/store/reconcile.js.map +1 -0
  31. package/dist/packlets/store/storeCoverage.js +102 -0
  32. package/dist/packlets/store/storeCoverage.js.map +1 -0
  33. package/dist/packlets/store/storeReconcile.js +122 -0
  34. package/dist/packlets/store/storeReconcile.js.map +1 -0
  35. package/dist/packlets/store/vectorMaintenance.js +116 -8
  36. package/dist/packlets/store/vectorMaintenance.js.map +1 -1
  37. package/dist/packlets/store/vectorRecordSource.js +44 -0
  38. package/dist/packlets/store/vectorRecordSource.js.map +1 -0
  39. package/dist/packlets/tools/memoryTools.js +25 -2
  40. package/dist/packlets/tools/memoryTools.js.map +1 -1
  41. package/dist/packlets/types/envelope.js +25 -0
  42. package/dist/packlets/types/envelope.js.map +1 -1
  43. package/dist/packlets/types/index.js +1 -0
  44. package/dist/packlets/types/index.js.map +1 -1
  45. package/dist/packlets/types/recordResolver.js +6 -0
  46. package/dist/packlets/types/recordResolver.js.map +1 -0
  47. package/dist/packlets/types/temporal.js.map +1 -1
  48. package/dist/packlets/vector/inMemoryCosineIndex.js +39 -18
  49. package/dist/packlets/vector/inMemoryCosineIndex.js.map +1 -1
  50. package/dist/packlets/vector/inMemoryFragmentCosineIndex.js +67 -12
  51. package/dist/packlets/vector/inMemoryFragmentCosineIndex.js.map +1 -1
  52. package/dist/packlets/vector/rebuildHelpers.js +38 -0
  53. package/dist/packlets/vector/rebuildHelpers.js.map +1 -0
  54. package/dist/packlets/vector/vectorIndex.js.map +1 -1
  55. package/dist/ts-agent-memory.d.ts +1035 -106
  56. package/lib/packlets/index/memoryIndex.d.ts +118 -27
  57. package/lib/packlets/index/memoryIndex.d.ts.map +1 -1
  58. package/lib/packlets/index/memoryIndex.js +23 -16
  59. package/lib/packlets/index/memoryIndex.js.map +1 -1
  60. package/lib/packlets/ingest/orchestrator.d.ts.map +1 -1
  61. package/lib/packlets/ingest/orchestrator.js +13 -1
  62. package/lib/packlets/ingest/orchestrator.js.map +1 -1
  63. package/lib/packlets/retrieve/linkTraversalRetriever.d.ts +3 -10
  64. package/lib/packlets/retrieve/linkTraversalRetriever.d.ts.map +1 -1
  65. package/lib/packlets/retrieve/linkTraversalRetriever.js +11 -25
  66. package/lib/packlets/retrieve/linkTraversalRetriever.js.map +1 -1
  67. package/lib/packlets/retrieve/recencyRetriever.d.ts +3 -3
  68. package/lib/packlets/retrieve/recencyRetriever.d.ts.map +1 -1
  69. package/lib/packlets/retrieve/recencyRetriever.js +6 -6
  70. package/lib/packlets/retrieve/recencyRetriever.js.map +1 -1
  71. package/lib/packlets/retrieve/retriever.d.ts +88 -7
  72. package/lib/packlets/retrieve/retriever.d.ts.map +1 -1
  73. package/lib/packlets/retrieve/retriever.js +94 -9
  74. package/lib/packlets/retrieve/retriever.js.map +1 -1
  75. package/lib/packlets/retrieve/semanticRetriever.d.ts +3 -5
  76. package/lib/packlets/retrieve/semanticRetriever.d.ts.map +1 -1
  77. package/lib/packlets/retrieve/semanticRetriever.js +15 -15
  78. package/lib/packlets/retrieve/semanticRetriever.js.map +1 -1
  79. package/lib/packlets/retrieve/structuredFilterRetriever.d.ts +3 -3
  80. package/lib/packlets/retrieve/structuredFilterRetriever.d.ts.map +1 -1
  81. package/lib/packlets/retrieve/structuredFilterRetriever.js +6 -6
  82. package/lib/packlets/retrieve/structuredFilterRetriever.js.map +1 -1
  83. package/lib/packlets/retrieve/tagRetriever.d.ts +3 -3
  84. package/lib/packlets/retrieve/tagRetriever.d.ts.map +1 -1
  85. package/lib/packlets/retrieve/tagRetriever.js +6 -6
  86. package/lib/packlets/retrieve/tagRetriever.js.map +1 -1
  87. package/lib/packlets/retrieve/temporalRetrievers.d.ts +7 -5
  88. package/lib/packlets/retrieve/temporalRetrievers.d.ts.map +1 -1
  89. package/lib/packlets/retrieve/temporalRetrievers.js +22 -19
  90. package/lib/packlets/retrieve/temporalRetrievers.js.map +1 -1
  91. package/lib/packlets/store/coverage.d.ts +102 -0
  92. package/lib/packlets/store/coverage.d.ts.map +1 -0
  93. package/lib/packlets/store/coverage.js +7 -0
  94. package/lib/packlets/store/coverage.js.map +1 -0
  95. package/lib/packlets/store/fileTreeMemoryStore.d.ts +53 -166
  96. package/lib/packlets/store/fileTreeMemoryStore.d.ts.map +1 -1
  97. package/lib/packlets/store/fileTreeMemoryStore.js +221 -79
  98. package/lib/packlets/store/fileTreeMemoryStore.js.map +1 -1
  99. package/lib/packlets/store/index.d.ts +4 -0
  100. package/lib/packlets/store/index.d.ts.map +1 -1
  101. package/lib/packlets/store/index.js +4 -0
  102. package/lib/packlets/store/index.js.map +1 -1
  103. package/lib/packlets/store/listSelection.d.ts +101 -0
  104. package/lib/packlets/store/listSelection.d.ts.map +1 -0
  105. package/lib/packlets/store/listSelection.js +40 -0
  106. package/lib/packlets/store/listSelection.js.map +1 -0
  107. package/lib/packlets/store/memoryStore.d.ts +237 -0
  108. package/lib/packlets/store/memoryStore.d.ts.map +1 -0
  109. package/lib/packlets/store/memoryStore.js +7 -0
  110. package/lib/packlets/store/memoryStore.js.map +1 -0
  111. package/lib/packlets/store/reconcile.d.ts +82 -0
  112. package/lib/packlets/store/reconcile.d.ts.map +1 -0
  113. package/lib/packlets/store/reconcile.js +7 -0
  114. package/lib/packlets/store/reconcile.js.map +1 -0
  115. package/lib/packlets/store/storeCoverage.d.ts +45 -0
  116. package/lib/packlets/store/storeCoverage.d.ts.map +1 -0
  117. package/lib/packlets/store/storeCoverage.js +105 -0
  118. package/lib/packlets/store/storeCoverage.js.map +1 -0
  119. package/lib/packlets/store/storeReconcile.d.ts +41 -0
  120. package/lib/packlets/store/storeReconcile.d.ts.map +1 -0
  121. package/lib/packlets/store/storeReconcile.js +125 -0
  122. package/lib/packlets/store/storeReconcile.js.map +1 -0
  123. package/lib/packlets/store/vectorMaintenance.d.ts +74 -0
  124. package/lib/packlets/store/vectorMaintenance.d.ts.map +1 -1
  125. package/lib/packlets/store/vectorMaintenance.js +117 -8
  126. package/lib/packlets/store/vectorMaintenance.js.map +1 -1
  127. package/lib/packlets/store/vectorRecordSource.d.ts +36 -0
  128. package/lib/packlets/store/vectorRecordSource.d.ts.map +1 -0
  129. package/lib/packlets/store/vectorRecordSource.js +47 -0
  130. package/lib/packlets/store/vectorRecordSource.js.map +1 -0
  131. package/lib/packlets/tools/memoryTools.d.ts.map +1 -1
  132. package/lib/packlets/tools/memoryTools.js +25 -2
  133. package/lib/packlets/tools/memoryTools.js.map +1 -1
  134. package/lib/packlets/types/envelope.d.ts +24 -2
  135. package/lib/packlets/types/envelope.d.ts.map +1 -1
  136. package/lib/packlets/types/envelope.js +26 -0
  137. package/lib/packlets/types/envelope.js.map +1 -1
  138. package/lib/packlets/types/index.d.ts +1 -0
  139. package/lib/packlets/types/index.d.ts.map +1 -1
  140. package/lib/packlets/types/index.js +1 -0
  141. package/lib/packlets/types/index.js.map +1 -1
  142. package/lib/packlets/types/recordResolver.d.ts +39 -0
  143. package/lib/packlets/types/recordResolver.d.ts.map +1 -0
  144. package/lib/packlets/types/recordResolver.js +7 -0
  145. package/lib/packlets/types/recordResolver.js.map +1 -0
  146. package/lib/packlets/types/temporal.d.ts +26 -6
  147. package/lib/packlets/types/temporal.d.ts.map +1 -1
  148. package/lib/packlets/types/temporal.js.map +1 -1
  149. package/lib/packlets/vector/inMemoryCosineIndex.d.ts +9 -2
  150. package/lib/packlets/vector/inMemoryCosineIndex.d.ts.map +1 -1
  151. package/lib/packlets/vector/inMemoryCosineIndex.js +40 -19
  152. package/lib/packlets/vector/inMemoryCosineIndex.js.map +1 -1
  153. package/lib/packlets/vector/inMemoryFragmentCosineIndex.d.ts +6 -3
  154. package/lib/packlets/vector/inMemoryFragmentCosineIndex.d.ts.map +1 -1
  155. package/lib/packlets/vector/inMemoryFragmentCosineIndex.js +66 -11
  156. package/lib/packlets/vector/inMemoryFragmentCosineIndex.js.map +1 -1
  157. package/lib/packlets/vector/rebuildHelpers.d.ts +30 -0
  158. package/lib/packlets/vector/rebuildHelpers.d.ts.map +1 -0
  159. package/lib/packlets/vector/rebuildHelpers.js +42 -0
  160. package/lib/packlets/vector/rebuildHelpers.js.map +1 -0
  161. package/lib/packlets/vector/vectorIndex.d.ts +270 -15
  162. package/lib/packlets/vector/vectorIndex.d.ts.map +1 -1
  163. package/lib/packlets/vector/vectorIndex.js.map +1 -1
  164. package/package.json +7 -7
@@ -6,6 +6,10 @@ import { succeed } from '@fgv/ts-utils';
6
6
  import { edgeTargetKey } from '../types';
7
7
  /** Shared empty key set for `byKind` / `byTag` misses (avoids per-call allocation). */
8
8
  const EMPTY_KEY_SET = new Set();
9
+ /** Project the write-side whole-record form onto the held/read entry form. */
10
+ function toEntry(entry) {
11
+ return { scope: entry.scope, envelope: entry.record.envelope };
12
+ }
9
13
  /**
10
14
  * Default in-memory {@link IMemoryIndex}. Maintains the derived `byKind` /
11
15
  * `byTag` / `byRecency` / `backlinks` views incrementally; a `'put'` for an
@@ -51,7 +55,9 @@ export class MemoryIndex {
51
55
  // changes kind/tags/links cannot strand a stale reference.
52
56
  this._remove(key);
53
57
  if (op === 'put') {
54
- this._add(entry);
58
+ // Projected on the way in: the caller's record is used for its envelope and
59
+ // the body is not retained.
60
+ this._add(toEntry(entry));
55
61
  }
56
62
  return succeed(entry);
57
63
  }
@@ -59,6 +65,10 @@ export class MemoryIndex {
59
65
  entries() {
60
66
  return Array.from(this._byKey.values());
61
67
  }
68
+ /** {@inheritDoc IMemoryIndex.get} */
69
+ get(target) {
70
+ return this._byKey.get(edgeTargetKey(target));
71
+ }
62
72
  /** {@inheritDoc IMemoryIndex.byKind} */
63
73
  byKind(kind) {
64
74
  var _a;
@@ -88,14 +98,7 @@ export class MemoryIndex {
88
98
  * records sort deterministically).
89
99
  */
90
100
  _recencyOrdered(keys) {
91
- const records = [];
92
- for (const key of keys) {
93
- const entry = this._byKey.get(key);
94
- if (entry !== undefined) {
95
- records.push(entry.record);
96
- }
97
- }
98
- return records.sort((a, b) => {
101
+ return MemoryIndex._resolve(this._byKey, keys).sort((a, b) => {
99
102
  const byUpdated = b.envelope.updated - a.envelope.updated;
100
103
  return byUpdated !== 0 ? byUpdated : b.envelope.seq - a.envelope.seq;
101
104
  });
@@ -109,14 +112,18 @@ export class MemoryIndex {
109
112
  * approach; the sort is over the in-memory index, never a filesystem walk.
110
113
  */
111
114
  _rankOrdered(keys) {
112
- const records = [];
115
+ return MemoryIndex._resolve(this._byKey, keys).sort(MemoryIndex._compareByRank);
116
+ }
117
+ /** Resolve composite keys to their entries, skipping any that are absent. */
118
+ static _resolve(byKey, keys) {
119
+ const entries = [];
113
120
  for (const key of keys) {
114
- const entry = this._byKey.get(key);
121
+ const entry = byKey.get(key);
115
122
  if (entry !== undefined) {
116
- records.push(entry.record);
123
+ entries.push(entry);
117
124
  }
118
125
  }
119
- return records.sort(MemoryIndex._compareByRank);
126
+ return entries;
120
127
  }
121
128
  /**
122
129
  * Rank-descending comparator with an absent-`rank`-last rule and a recency
@@ -143,8 +150,8 @@ export class MemoryIndex {
143
150
  }
144
151
  /** Insert an entry and register all its derived associations. */
145
152
  _add(entry) {
146
- const key = MemoryIndex._keyOf(entry.scope, entry.record.envelope.id);
147
- const envelope = entry.record.envelope;
153
+ const key = MemoryIndex._keyOf(entry.scope, entry.envelope.id);
154
+ const envelope = entry.envelope;
148
155
  this._byKey.set(key, entry);
149
156
  this._addToSetMap(this._byKind, envelope.kind, key);
150
157
  for (const tag of envelope.tags) {
@@ -160,7 +167,7 @@ export class MemoryIndex {
160
167
  if (entry === undefined) {
161
168
  return;
162
169
  }
163
- const envelope = entry.record.envelope;
170
+ const envelope = entry.envelope;
164
171
  this._byKey.delete(key);
165
172
  this._removeFromSetMap(this._byKind, envelope.kind, key);
166
173
  for (const tag of envelope.tags) {
@@ -1 +1 @@
1
- {"version":3,"file":"memoryIndex.js","sourceRoot":"","sources":["../../../src/packlets/index/memoryIndex.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright (c) 2026 Erik Fortune\n * SPDX-License-Identifier: MIT\n */\n\nimport { Result, succeed } from '@fgv/ts-utils';\nimport { IEdgeTarget, IMemoryRecord, Kind, MemoryId, MemoryScopeKey, Tag, edgeTargetKey } from '../types';\n\n/**\n * The mutation a {@link IMemoryIndex.patch | patch} applies: a record was\n * written (`'put'`) or removed (`'delete'`).\n * @public\n */\nexport type MemoryIndexPatchOp = 'put' | 'delete';\n\n/** Shared empty key set for `byKind` / `byTag` misses (avoids per-call allocation). */\nconst EMPTY_KEY_SET: ReadonlySet<string> = new Set<string>();\n\n/**\n * A memory record paired with the {@link MemoryScopeKey | scope} it lives\n * under. The scope is not carried on the {@link IMemoryEnvelope} (it is derived\n * from the entity id by the codec), so the store threads it alongside the\n * record when patching or rebuilding the index. The `(scope, id)` pair is the\n * index's primary key, keeping records distinct across scopes that reuse a\n * filename stem (e.g. `turn-0` under different conversations in Phase C).\n * @public\n */\nexport interface IIndexedMemoryRecord {\n /** The scope the record is stored under. */\n readonly scope: MemoryScopeKey;\n /** The memory record itself. */\n readonly record: IMemoryRecord<unknown>;\n}\n\n/**\n * The derived, in-memory secondary indexes the store maintains over its\n * records. Never the source of truth — the FileTree is. The index is fully\n * rebuildable from a walk of the store ({@link IMemoryIndex.rebuild}) and is\n * patched incrementally on every write ({@link IMemoryIndex.patch}).\n *\n * @remarks\n * B1 builds the maps; link-traversal BFS over {@link IMemoryIndex.backlinks}\n * is B2. The accessors return records (not bare ids) so the B2 retrievers can\n * consume them directly.\n * @public\n */\nexport interface IMemoryIndex {\n /**\n * Replace the entire index from a full set of records (a store walk).\n * @returns The number of records indexed.\n */\n rebuild(entries: ReadonlyArray<IIndexedMemoryRecord>): Result<number>;\n\n /**\n * Apply a single incremental change. `'put'` inserts or replaces the entry\n * at its `(scope, id)` key (removing any prior associations first); `'delete'`\n * removes it.\n * @returns The entry that was applied.\n */\n patch(op: MemoryIndexPatchOp, entry: IIndexedMemoryRecord): Result<IIndexedMemoryRecord>;\n\n /** Every indexed entry (scope + record). Primary read surface for the store. */\n entries(): ReadonlyArray<IIndexedMemoryRecord>;\n\n /** Records of the given kind, in recency order (most-recently-updated first). */\n byKind(kind: Kind): ReadonlyArray<IMemoryRecord<unknown>>;\n\n /** Records carrying the given tag, in recency order. */\n byTag(tag: Tag): ReadonlyArray<IMemoryRecord<unknown>>;\n\n /** All records in recency order (most-recently-updated first). */\n byRecency(): ReadonlyArray<IMemoryRecord<unknown>>;\n\n /**\n * All records ordered by store-computed {@link IMemoryEnvelope.rank} descending,\n * with recency (most-recently-updated, then `seq`) as a tiebreak. Records with\n * an absent `rank` sort LAST (after every ranked record), then by recency among\n * themselves. Serves a bounded top-M ({@link IMemoryEnvelope.rank}-ordered) page\n * from the in-memory index with no full-vault (filesystem) scan.\n */\n byRank(): ReadonlyArray<IMemoryRecord<unknown>>;\n\n /**\n * The scope-qualified sources of records whose `links` point AT `target`\n * (inbound edges), keyed on the target's `(scope, id)` address. The seed map\n * for B2 link-traversal; results are {@link IEdgeTarget}s so a caller can feed\n * them straight back in as further traversal seeds.\n */\n backlinks(target: IEdgeTarget): ReadonlyArray<IEdgeTarget>;\n}\n\n/**\n * Default in-memory {@link IMemoryIndex}. Maintains the derived `byKind` /\n * `byTag` / `byRecency` / `backlinks` views incrementally; a `'put'` for an\n * already-indexed key first removes the prior entry's associations so a changed\n * kind / tag / link set never leaves a stale reference behind.\n * @public\n */\nexport class MemoryIndex implements IMemoryIndex {\n /** Primary store: `(scope, id)` composite key → indexed entry. */\n private readonly _byKey: Map<string, IIndexedMemoryRecord>;\n /** kind → set of composite keys. */\n private readonly _byKind: Map<Kind, Set<string>>;\n /** tag → set of composite keys. */\n private readonly _byTag: Map<Tag, Set<string>>;\n /**\n * canonical target key (`edgeTargetKey`) → (source composite key → source\n * {@link IEdgeTarget}). The OUTER map is keyed on the scope-qualified target's\n * canonical `(scope, id)` string — NOT the target's bare id — so an edge to\n * `turn-3` in one conversation is tracked separately from `turn-3` in another.\n * The INNER map is keyed by the source's `(scope, id)` composite so two distinct\n * source records that share an id across scopes are tracked independently and\n * removing one never drops the other's edge.\n */\n private readonly _backlinks: Map<string, Map<string, IEdgeTarget>>;\n\n private constructor() {\n this._byKey = new Map<string, IIndexedMemoryRecord>();\n this._byKind = new Map<Kind, Set<string>>();\n this._byTag = new Map<Tag, Set<string>>();\n this._backlinks = new Map<string, Map<string, IEdgeTarget>>();\n }\n\n /** Family-convention factory. */\n public static create(): Result<MemoryIndex> {\n return succeed(new MemoryIndex());\n }\n\n /**\n * The composite primary key for an entry: scope + id, NUL-separated. NUL\n * is excluded from both components (scope segments pass\n * `assertPortableFilenameStem`; `MemoryId` is portable-filename-safe), so it\n * is a collision-proof separator across every scope/id pair the codecs produce.\n */\n private static _keyOf(scope: MemoryScopeKey, id: MemoryId): string {\n return edgeTargetKey({ scope, id });\n }\n\n /** {@inheritDoc IMemoryIndex.rebuild} */\n public rebuild(entries: ReadonlyArray<IIndexedMemoryRecord>): Result<number> {\n this._byKey.clear();\n this._byKind.clear();\n this._byTag.clear();\n this._backlinks.clear();\n for (const entry of entries) {\n this._add(entry);\n }\n return succeed(this._byKey.size);\n }\n\n /** {@inheritDoc IMemoryIndex.patch} */\n public patch(op: MemoryIndexPatchOp, entry: IIndexedMemoryRecord): Result<IIndexedMemoryRecord> {\n const key: string = MemoryIndex._keyOf(entry.scope, entry.record.envelope.id);\n // Always drop any prior associations for this key first, so a 'put' that\n // changes kind/tags/links cannot strand a stale reference.\n this._remove(key);\n if (op === 'put') {\n this._add(entry);\n }\n return succeed(entry);\n }\n\n /** {@inheritDoc IMemoryIndex.entries} */\n public entries(): ReadonlyArray<IIndexedMemoryRecord> {\n return Array.from(this._byKey.values());\n }\n\n /** {@inheritDoc IMemoryIndex.byKind} */\n public byKind(kind: Kind): ReadonlyArray<IMemoryRecord<unknown>> {\n return this._recencyOrdered(this._byKind.get(kind) ?? EMPTY_KEY_SET);\n }\n\n /** {@inheritDoc IMemoryIndex.byTag} */\n public byTag(tag: Tag): ReadonlyArray<IMemoryRecord<unknown>> {\n return this._recencyOrdered(this._byTag.get(tag) ?? EMPTY_KEY_SET);\n }\n\n /** {@inheritDoc IMemoryIndex.byRecency} */\n public byRecency(): ReadonlyArray<IMemoryRecord<unknown>> {\n return this._recencyOrdered(this._byKey.keys());\n }\n\n /** {@inheritDoc IMemoryIndex.byRank} */\n public byRank(): ReadonlyArray<IMemoryRecord<unknown>> {\n return this._rankOrdered(this._byKey.keys());\n }\n\n /** {@inheritDoc IMemoryIndex.backlinks} */\n public backlinks(target: IEdgeTarget): ReadonlyArray<IEdgeTarget> {\n const sources: Map<string, IEdgeTarget> | undefined = this._backlinks.get(edgeTargetKey(target));\n return sources === undefined ? [] : Array.from(sources.values());\n }\n\n /**\n * Resolve a set of composite keys to their records, ordered\n * most-recently-updated first (with a `seq` tiebreak so equal-`updated`\n * records sort deterministically).\n */\n private _recencyOrdered(keys: Iterable<string>): ReadonlyArray<IMemoryRecord<unknown>> {\n const records: IMemoryRecord<unknown>[] = [];\n for (const key of keys) {\n const entry: IIndexedMemoryRecord | undefined = this._byKey.get(key);\n if (entry !== undefined) {\n records.push(entry.record);\n }\n }\n return records.sort((a, b) => {\n const byUpdated: number = b.envelope.updated - a.envelope.updated;\n return byUpdated !== 0 ? byUpdated : b.envelope.seq - a.envelope.seq;\n });\n }\n\n /**\n * Resolve a set of composite keys to their records, ordered by\n * {@link IMemoryEnvelope.rank} descending with recency (`updated`, then `seq`)\n * as the tiebreak. Records with an absent `rank` sort LAST, then by recency\n * among themselves. Computed on call (mirrors {@link MemoryIndex._recencyOrdered}) —\n * no incremental rank-ordered view is maintained, matching the recency view's\n * approach; the sort is over the in-memory index, never a filesystem walk.\n */\n private _rankOrdered(keys: Iterable<string>): ReadonlyArray<IMemoryRecord<unknown>> {\n const records: IMemoryRecord<unknown>[] = [];\n for (const key of keys) {\n const entry: IIndexedMemoryRecord | undefined = this._byKey.get(key);\n if (entry !== undefined) {\n records.push(entry.record);\n }\n }\n return records.sort(MemoryIndex._compareByRank);\n }\n\n /**\n * Rank-descending comparator with an absent-`rank`-last rule and a recency\n * (`updated`, then `seq`) tiebreak. Duplicated from the retrieve packlet's\n * `rankCompare` deliberately: the index must not depend on `retrieve` (that\n * package depends on the index), mirroring how `_recencyOrdered` inlines the\n * recency ordering rather than importing `recencyCompare`.\n */\n private static _compareByRank(a: IMemoryRecord<unknown>, b: IMemoryRecord<unknown>): number {\n const ra: number | undefined = a.envelope.rank;\n const rb: number | undefined = b.envelope.rank;\n // Absent rank sorts last; two absent ranks fall through to the recency tiebreak.\n if (ra === undefined && rb !== undefined) {\n return 1;\n }\n if (rb === undefined && ra !== undefined) {\n return -1;\n }\n if (ra !== undefined && rb !== undefined && ra !== rb) {\n return rb - ra;\n }\n const byUpdated: number = b.envelope.updated - a.envelope.updated;\n return byUpdated !== 0 ? byUpdated : b.envelope.seq - a.envelope.seq;\n }\n\n /** Insert an entry and register all its derived associations. */\n private _add(entry: IIndexedMemoryRecord): void {\n const key: string = MemoryIndex._keyOf(entry.scope, entry.record.envelope.id);\n const envelope: IMemoryRecord<unknown>['envelope'] = entry.record.envelope;\n this._byKey.set(key, entry);\n this._addToSetMap(this._byKind, envelope.kind, key);\n for (const tag of envelope.tags) {\n this._addToSetMap(this._byTag, tag, key);\n }\n for (const edge of envelope.links) {\n this._addBacklink(edge.target, key, { scope: entry.scope, id: envelope.id });\n }\n }\n\n /** Remove the entry at `key` (if present) and all its derived associations. */\n private _remove(key: string): void {\n const entry: IIndexedMemoryRecord | undefined = this._byKey.get(key);\n if (entry === undefined) {\n return;\n }\n const envelope: IMemoryRecord<unknown>['envelope'] = entry.record.envelope;\n this._byKey.delete(key);\n this._removeFromSetMap(this._byKind, envelope.kind, key);\n for (const tag of envelope.tags) {\n this._removeFromSetMap(this._byTag, tag, key);\n }\n for (const edge of envelope.links) {\n this._removeBacklink(edge.target, key);\n }\n }\n\n /** Register `source` (keyed by its composite `sourceKey`) as linking at `target`. */\n private _addBacklink(target: IEdgeTarget, sourceKey: string, source: IEdgeTarget): void {\n const targetKey: string = edgeTargetKey(target);\n const existing: Map<string, IEdgeTarget> | undefined = this._backlinks.get(targetKey);\n if (existing === undefined) {\n this._backlinks.set(targetKey, new Map<string, IEdgeTarget>([[sourceKey, source]]));\n } else {\n existing.set(sourceKey, source);\n }\n }\n\n /** Drop the backlink from `sourceKey` to `target`, removing the target map when empty. */\n private _removeBacklink(target: IEdgeTarget, sourceKey: string): void {\n const targetKey: string = edgeTargetKey(target);\n const existing: Map<string, IEdgeTarget> | undefined = this._backlinks.get(targetKey);\n if (existing === undefined) {\n return;\n }\n existing.delete(sourceKey);\n if (existing.size === 0) {\n this._backlinks.delete(targetKey);\n }\n }\n\n /** Add `member` to the set at `mapKey`, creating the set on first use. */\n private _addToSetMap<K, M>(map: Map<K, Set<M>>, mapKey: K, member: M): void {\n const existing: Set<M> | undefined = map.get(mapKey);\n if (existing === undefined) {\n map.set(mapKey, new Set<M>([member]));\n } else {\n existing.add(member);\n }\n }\n\n /** Remove `member` from the set at `mapKey`, dropping the set when empty. */\n private _removeFromSetMap<K, M>(map: Map<K, Set<M>>, mapKey: K, member: M): void {\n const existing: Set<M> | undefined = map.get(mapKey);\n if (existing === undefined) {\n return;\n }\n existing.delete(member);\n if (existing.size === 0) {\n map.delete(mapKey);\n }\n }\n}\n"]}
1
+ {"version":3,"file":"memoryIndex.js","sourceRoot":"","sources":["../../../src/packlets/index/memoryIndex.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright (c) 2026 Erik Fortune\n * SPDX-License-Identifier: MIT\n */\n\nimport { Result, succeed } from '@fgv/ts-utils';\nimport {\n IEdgeTarget,\n IMemoryEnvelope,\n IMemoryRecord,\n Kind,\n MemoryId,\n MemoryScopeKey,\n Tag,\n edgeTargetKey\n} from '../types';\n\n/**\n * The mutation a {@link IMemoryIndex.patch | patch} applies: a record was\n * written (`'put'`) or removed (`'delete'`).\n * @public\n */\nexport type MemoryIndexPatchOp = 'put' | 'delete';\n\n/** Shared empty key set for `byKind` / `byTag` misses (avoids per-call allocation). */\nconst EMPTY_KEY_SET: ReadonlySet<string> = new Set<string>();\n\n/**\n * A memory record paired with the {@link MemoryScopeKey | scope} it lives\n * under. The scope is not carried on the {@link IMemoryEnvelope} (it is derived\n * from the entity id by the codec), so the store threads it alongside the\n * record when patching or rebuilding the index. The `(scope, id)` pair is the\n * index's primary key, keeping records distinct across scopes that reuse a\n * filename stem (e.g. `turn-0` under different conversations in Phase C).\n * @public\n */\nexport interface IIndexedMemoryRecord {\n /** The scope the record is stored under. */\n readonly scope: MemoryScopeKey;\n /** The memory record itself. */\n readonly record: IMemoryRecord<unknown>;\n}\n\n/**\n * What the index HOLDS and what every index read returns: a record's scope and\n * its {@link IMemoryEnvelope}, and **no body**.\n *\n * @remarks\n * The index is a derived *selection* structure, and selection has never needed a\n * body — every filter the store and the retrievers apply reads envelope fields\n * (`scope` / `kind` / `tags` / `contentHash` / `provenance` / `links` /\n * `temporal` / `updated` / `seq` / `rank`). Returning whole records made every\n * conforming index hold every body by construction, which was the store's\n * resident-memory ceiling; returning envelopes removes it from the contract\n * rather than from one implementation.\n *\n * A caller that needs the body **materializes it explicitly** — through\n * {@link IMemoryStore.getById}, or an `IMemoryRecordResolver` where one is\n * wired. That is deliberately visible: a lazy `body` getter would have kept\n * every call site compiling while turning a memory read into a file read behind\n * an unchanged type, which is a silent performance cliff rather than a migration.\n * @public\n */\nexport interface IIndexedMemoryEntry {\n /** The scope the record is stored under. */\n readonly scope: MemoryScopeKey;\n /** The record's envelope. No body — see the remarks. */\n readonly envelope: IMemoryEnvelope;\n}\n\n/** Project the write-side whole-record form onto the held/read entry form. */\nfunction toEntry(entry: IIndexedMemoryRecord): IIndexedMemoryEntry {\n return { scope: entry.scope, envelope: entry.record.envelope };\n}\n\n/**\n * The derived, in-memory secondary indexes the store maintains over its\n * records. Never the source of truth — the FileTree is. The index is fully\n * rebuildable from a walk of the store ({@link IMemoryIndex.rebuild}) and is\n * patched incrementally on every write ({@link IMemoryIndex.patch}).\n *\n * @remarks\n * Every read returns the projected {@link IIndexedMemoryEntry} — scope and\n * envelope, no body. See that type for why.\n *\n * **The conformance rule: an index is a derived, COMPLETE, FAITHFUL projection\n * of the vault.** An implementation may change *where* entries are stored and\n * *how* they are looked up; it may not change *which* entries exist or *what any\n * envelope says*. Concretely, {@link IMemoryIndex.entries} must return exactly\n * one entry per record the store has written and not deleted, and each entry's\n * envelope must be the one the store patched in.\n *\n * An index that filters, truncates, deduplicates, or synthesizes entries is not\n * a conforming implementation, and the reason is not tidiness: **the store's\n * write path derives from these reads** — content-hash dedup, write-policy\n * admission cohorts, and temporal version histories all read the index. An index\n * that hides an entry does not merely hide it from queries; it changes what the\n * next write does. That is why the previous guidance said only a faithful\n * delegating decorator was safe to inject. This invariant is what that guidance\n * was reaching for, stated so that a genuinely different implementation (a\n * SQLite-backed index, a lazily-paged one) is permitted while the reshaping that\n * was the actual hazard stays out.\n *\n * **Ordering is NOT part of the contract.** {@link IMemoryIndex.entries} may\n * return entries in any order and callers that need one sort explicitly. Note\n * this is a *behavioural* freedom the compiler cannot police: the bundled\n * {@link MemoryIndex} iterates a `Map` and so returns insertion order, which is\n * stable and observable, so code that came to rely on it keeps compiling and\n * changes results. The ordered accessors ({@link IMemoryIndex.byRecency},\n * {@link IMemoryIndex.byRank}, and the recency-ordered `byKind` / `byTag`) are\n * the supported way to ask for an order.\n * @public\n */\nexport interface IMemoryIndex {\n /**\n * Replace the entire index from a full set of entries (a store walk).\n *\n * @remarks\n * Takes the **projected** {@link IIndexedMemoryEntry} form, not whole records,\n * and the distinction is load-bearing rather than cosmetic: **`patch` writes,\n * `rebuild` reads.** A rebuild is a whole-vault read that happens to terminate\n * in the index, so requiring whole records here would force every caller — the\n * store's own open path included — to materialize N bodies purely to feed a\n * structure that projects the envelope back out and discards them. `patch`\n * keeps whole records because it carries exactly one, which its caller already\n * holds.\n *\n * @returns The number of entries indexed.\n */\n rebuild(entries: ReadonlyArray<IIndexedMemoryEntry>): Result<number>;\n\n /**\n * Apply a single incremental change. `'put'` inserts or replaces the entry\n * at its `(scope, id)` key (removing any prior associations first); `'delete'`\n * removes it.\n *\n * @remarks\n * Takes the whole record — see {@link IMemoryIndex.rebuild} for why this one\n * does and that one does not. It costs nothing (the caller is mid-write and\n * holds the record already) and it is the single point at which an index\n * maintaining a body-derived view could observe content without a re-read.\n * What is *held* is still only the projection.\n *\n * @returns The entry that was applied.\n */\n patch(op: MemoryIndexPatchOp, entry: IIndexedMemoryRecord): Result<IIndexedMemoryRecord>;\n\n /** Every indexed entry (scope + envelope). Primary read surface for the store. */\n entries(): ReadonlyArray<IIndexedMemoryEntry>;\n\n /**\n * The entry at a scope-qualified address, or `undefined` if none.\n *\n * @remarks\n * On the contract because its absence made every caller that wanted **one**\n * entry rebuild a map of **all** of them: both `SemanticRetriever` (resolving\n * at most `topK` hits) and `LinkTraversalRetriever` (resolving a BFS frontier)\n * built a full-index `Map` per query for want of this. The index already keys\n * on `(scope, id)` internally, so this exposes a lookup it was doing anyway.\n */\n get(target: IEdgeTarget): IIndexedMemoryEntry | undefined;\n\n /** Entries of the given kind, in recency order (most-recently-updated first). */\n byKind(kind: Kind): ReadonlyArray<IIndexedMemoryEntry>;\n\n /** Entries carrying the given tag, in recency order. */\n byTag(tag: Tag): ReadonlyArray<IIndexedMemoryEntry>;\n\n /** All entries in recency order (most-recently-updated first). */\n byRecency(): ReadonlyArray<IIndexedMemoryEntry>;\n\n /**\n * All entries ordered by store-computed {@link IMemoryEnvelope.rank} descending,\n * with recency (most-recently-updated, then `seq`) as a tiebreak. Entries with\n * an absent `rank` sort LAST (after every ranked entry), then by recency among\n * themselves. Serves a bounded top-M ({@link IMemoryEnvelope.rank}-ordered) page\n * from the in-memory index with no full-vault (filesystem) scan — and since the\n * page is envelope-only, a caller taking the top M materializes M bodies rather\n * than the vault.\n */\n byRank(): ReadonlyArray<IIndexedMemoryEntry>;\n\n /**\n * The scope-qualified sources of records whose `links` point AT `target`\n * (inbound edges), keyed on the target's `(scope, id)` address. The seed map\n * for B2 link-traversal; results are {@link IEdgeTarget}s so a caller can feed\n * them straight back in as further traversal seeds.\n */\n backlinks(target: IEdgeTarget): ReadonlyArray<IEdgeTarget>;\n}\n\n/**\n * Default in-memory {@link IMemoryIndex}. Maintains the derived `byKind` /\n * `byTag` / `byRecency` / `backlinks` views incrementally; a `'put'` for an\n * already-indexed key first removes the prior entry's associations so a changed\n * kind / tag / link set never leaves a stale reference behind.\n * @public\n */\nexport class MemoryIndex implements IMemoryIndex {\n /**\n * Primary store: `(scope, id)` composite key → indexed entry. Holds the\n * PROJECTED form, so the index never retains a body.\n */\n private readonly _byKey: Map<string, IIndexedMemoryEntry>;\n /** kind → set of composite keys. */\n private readonly _byKind: Map<Kind, Set<string>>;\n /** tag → set of composite keys. */\n private readonly _byTag: Map<Tag, Set<string>>;\n /**\n * canonical target key (`edgeTargetKey`) → (source composite key → source\n * {@link IEdgeTarget}). The OUTER map is keyed on the scope-qualified target's\n * canonical `(scope, id)` string — NOT the target's bare id — so an edge to\n * `turn-3` in one conversation is tracked separately from `turn-3` in another.\n * The INNER map is keyed by the source's `(scope, id)` composite so two distinct\n * source records that share an id across scopes are tracked independently and\n * removing one never drops the other's edge.\n */\n private readonly _backlinks: Map<string, Map<string, IEdgeTarget>>;\n\n private constructor() {\n this._byKey = new Map<string, IIndexedMemoryEntry>();\n this._byKind = new Map<Kind, Set<string>>();\n this._byTag = new Map<Tag, Set<string>>();\n this._backlinks = new Map<string, Map<string, IEdgeTarget>>();\n }\n\n /** Family-convention factory. */\n public static create(): Result<MemoryIndex> {\n return succeed(new MemoryIndex());\n }\n\n /**\n * The composite primary key for an entry: scope + id, NUL-separated. NUL\n * is excluded from both components (scope segments pass\n * `assertPortableFilenameStem`; `MemoryId` is portable-filename-safe), so it\n * is a collision-proof separator across every scope/id pair the codecs produce.\n */\n private static _keyOf(scope: MemoryScopeKey, id: MemoryId): string {\n return edgeTargetKey({ scope, id });\n }\n\n /** {@inheritDoc IMemoryIndex.rebuild} */\n public rebuild(entries: ReadonlyArray<IIndexedMemoryEntry>): Result<number> {\n this._byKey.clear();\n this._byKind.clear();\n this._byTag.clear();\n this._backlinks.clear();\n for (const entry of entries) {\n this._add(entry);\n }\n return succeed(this._byKey.size);\n }\n\n /** {@inheritDoc IMemoryIndex.patch} */\n public patch(op: MemoryIndexPatchOp, entry: IIndexedMemoryRecord): Result<IIndexedMemoryRecord> {\n const key: string = MemoryIndex._keyOf(entry.scope, entry.record.envelope.id);\n // Always drop any prior associations for this key first, so a 'put' that\n // changes kind/tags/links cannot strand a stale reference.\n this._remove(key);\n if (op === 'put') {\n // Projected on the way in: the caller's record is used for its envelope and\n // the body is not retained.\n this._add(toEntry(entry));\n }\n return succeed(entry);\n }\n\n /** {@inheritDoc IMemoryIndex.entries} */\n public entries(): ReadonlyArray<IIndexedMemoryEntry> {\n return Array.from(this._byKey.values());\n }\n\n /** {@inheritDoc IMemoryIndex.get} */\n public get(target: IEdgeTarget): IIndexedMemoryEntry | undefined {\n return this._byKey.get(edgeTargetKey(target));\n }\n\n /** {@inheritDoc IMemoryIndex.byKind} */\n public byKind(kind: Kind): ReadonlyArray<IIndexedMemoryEntry> {\n return this._recencyOrdered(this._byKind.get(kind) ?? EMPTY_KEY_SET);\n }\n\n /** {@inheritDoc IMemoryIndex.byTag} */\n public byTag(tag: Tag): ReadonlyArray<IIndexedMemoryEntry> {\n return this._recencyOrdered(this._byTag.get(tag) ?? EMPTY_KEY_SET);\n }\n\n /** {@inheritDoc IMemoryIndex.byRecency} */\n public byRecency(): ReadonlyArray<IIndexedMemoryEntry> {\n return this._recencyOrdered(this._byKey.keys());\n }\n\n /** {@inheritDoc IMemoryIndex.byRank} */\n public byRank(): ReadonlyArray<IIndexedMemoryEntry> {\n return this._rankOrdered(this._byKey.keys());\n }\n\n /** {@inheritDoc IMemoryIndex.backlinks} */\n public backlinks(target: IEdgeTarget): ReadonlyArray<IEdgeTarget> {\n const sources: Map<string, IEdgeTarget> | undefined = this._backlinks.get(edgeTargetKey(target));\n return sources === undefined ? [] : Array.from(sources.values());\n }\n\n /**\n * Resolve a set of composite keys to their records, ordered\n * most-recently-updated first (with a `seq` tiebreak so equal-`updated`\n * records sort deterministically).\n */\n private _recencyOrdered(keys: Iterable<string>): ReadonlyArray<IIndexedMemoryEntry> {\n return MemoryIndex._resolve(this._byKey, keys).sort((a, b) => {\n const byUpdated: number = b.envelope.updated - a.envelope.updated;\n return byUpdated !== 0 ? byUpdated : b.envelope.seq - a.envelope.seq;\n });\n }\n\n /**\n * Resolve a set of composite keys to their records, ordered by\n * {@link IMemoryEnvelope.rank} descending with recency (`updated`, then `seq`)\n * as the tiebreak. Records with an absent `rank` sort LAST, then by recency\n * among themselves. Computed on call (mirrors {@link MemoryIndex._recencyOrdered}) —\n * no incremental rank-ordered view is maintained, matching the recency view's\n * approach; the sort is over the in-memory index, never a filesystem walk.\n */\n private _rankOrdered(keys: Iterable<string>): ReadonlyArray<IIndexedMemoryEntry> {\n return MemoryIndex._resolve(this._byKey, keys).sort(MemoryIndex._compareByRank);\n }\n\n /** Resolve composite keys to their entries, skipping any that are absent. */\n private static _resolve(\n byKey: ReadonlyMap<string, IIndexedMemoryEntry>,\n keys: Iterable<string>\n ): IIndexedMemoryEntry[] {\n const entries: IIndexedMemoryEntry[] = [];\n for (const key of keys) {\n const entry: IIndexedMemoryEntry | undefined = byKey.get(key);\n if (entry !== undefined) {\n entries.push(entry);\n }\n }\n return entries;\n }\n\n /**\n * Rank-descending comparator with an absent-`rank`-last rule and a recency\n * (`updated`, then `seq`) tiebreak. Duplicated from the retrieve packlet's\n * `rankCompare` deliberately: the index must not depend on `retrieve` (that\n * package depends on the index), mirroring how `_recencyOrdered` inlines the\n * recency ordering rather than importing `recencyCompare`.\n */\n private static _compareByRank(a: IIndexedMemoryEntry, b: IIndexedMemoryEntry): number {\n const ra: number | undefined = a.envelope.rank;\n const rb: number | undefined = b.envelope.rank;\n // Absent rank sorts last; two absent ranks fall through to the recency tiebreak.\n if (ra === undefined && rb !== undefined) {\n return 1;\n }\n if (rb === undefined && ra !== undefined) {\n return -1;\n }\n if (ra !== undefined && rb !== undefined && ra !== rb) {\n return rb - ra;\n }\n const byUpdated: number = b.envelope.updated - a.envelope.updated;\n return byUpdated !== 0 ? byUpdated : b.envelope.seq - a.envelope.seq;\n }\n\n /** Insert an entry and register all its derived associations. */\n private _add(entry: IIndexedMemoryEntry): void {\n const key: string = MemoryIndex._keyOf(entry.scope, entry.envelope.id);\n const envelope: IMemoryEnvelope = entry.envelope;\n this._byKey.set(key, entry);\n this._addToSetMap(this._byKind, envelope.kind, key);\n for (const tag of envelope.tags) {\n this._addToSetMap(this._byTag, tag, key);\n }\n for (const edge of envelope.links) {\n this._addBacklink(edge.target, key, { scope: entry.scope, id: envelope.id });\n }\n }\n\n /** Remove the entry at `key` (if present) and all its derived associations. */\n private _remove(key: string): void {\n const entry: IIndexedMemoryEntry | undefined = this._byKey.get(key);\n if (entry === undefined) {\n return;\n }\n const envelope: IMemoryEnvelope = entry.envelope;\n this._byKey.delete(key);\n this._removeFromSetMap(this._byKind, envelope.kind, key);\n for (const tag of envelope.tags) {\n this._removeFromSetMap(this._byTag, tag, key);\n }\n for (const edge of envelope.links) {\n this._removeBacklink(edge.target, key);\n }\n }\n\n /** Register `source` (keyed by its composite `sourceKey`) as linking at `target`. */\n private _addBacklink(target: IEdgeTarget, sourceKey: string, source: IEdgeTarget): void {\n const targetKey: string = edgeTargetKey(target);\n const existing: Map<string, IEdgeTarget> | undefined = this._backlinks.get(targetKey);\n if (existing === undefined) {\n this._backlinks.set(targetKey, new Map<string, IEdgeTarget>([[sourceKey, source]]));\n } else {\n existing.set(sourceKey, source);\n }\n }\n\n /** Drop the backlink from `sourceKey` to `target`, removing the target map when empty. */\n private _removeBacklink(target: IEdgeTarget, sourceKey: string): void {\n const targetKey: string = edgeTargetKey(target);\n const existing: Map<string, IEdgeTarget> | undefined = this._backlinks.get(targetKey);\n if (existing === undefined) {\n return;\n }\n existing.delete(sourceKey);\n if (existing.size === 0) {\n this._backlinks.delete(targetKey);\n }\n }\n\n /** Add `member` to the set at `mapKey`, creating the set on first use. */\n private _addToSetMap<K, M>(map: Map<K, Set<M>>, mapKey: K, member: M): void {\n const existing: Set<M> | undefined = map.get(mapKey);\n if (existing === undefined) {\n map.set(mapKey, new Set<M>([member]));\n } else {\n existing.add(member);\n }\n }\n\n /** Remove `member` from the set at `mapKey`, dropping the set when empty. */\n private _removeFromSetMap<K, M>(map: Map<K, Set<M>>, mapKey: K, member: M): void {\n const existing: Set<M> | undefined = map.get(mapKey);\n if (existing === undefined) {\n return;\n }\n existing.delete(member);\n if (existing.size === 0) {\n map.delete(mapKey);\n }\n }\n}\n"]}
@@ -4,6 +4,7 @@
4
4
  */
5
5
  import { Hash, Logging, fail, mapResults, succeed } from '@fgv/ts-utils';
6
6
  import { Convert, edgeTargetKey, isTemporalRecord, isVersionCurrent } from '../types';
7
+ import { scanEveryRecord } from '../store';
7
8
  import { assertNoCycles } from './cycleGuard';
8
9
  /**
9
10
  * The provenance source stamped on every record the ingest pipeline writes.
@@ -89,7 +90,18 @@ export class MemoryIngestOrchestrator {
89
90
  async _processCandidates(item, candidates) {
90
91
  // Snapshot the store once; stage-4 resolution and the cycle guard reason over
91
92
  // the pre-ingest state.
92
- const snapshotResult = await this._store.list();
93
+ //
94
+ // DECLARED, not optimized. This reads every body in the vault on EVERY
95
+ // ingested item, and `scanEveryRecord()` is how the store now makes a caller
96
+ // say so out loud. Most of what the snapshot is used for downstream — edge
97
+ // target existence, the cycle guard's link walk, scoped-address keying — is
98
+ // envelope-only work that `listEntries()` would serve without reading a
99
+ // single file; only stage-4 layer-1's exact `{ kind, body }` match needs
100
+ // bodies, and it needs them for candidates rather than for the whole vault.
101
+ // Converting this is its own stream (`agent-memory-ingest-snapshot`), because
102
+ // it is on the write path with its own risk surface. Until then the cost is
103
+ // at least visible and greppable rather than implied.
104
+ const snapshotResult = await this._store.list(scanEveryRecord());
93
105
  if (snapshotResult.isFailure()) {
94
106
  return fail(`ingest '${item.id}': failed to snapshot store: ${snapshotResult.message}`);
95
107
  }
@@ -1 +1 @@
1
- {"version":3,"file":"orchestrator.js","sourceRoot":"","sources":["../../../src/packlets/ingest/orchestrator.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright (c) 2026 Erik Fortune\n * SPDX-License-Identifier: MIT\n */\n\nimport { Hash, Logging, Result, fail, mapResults, succeed } from '@fgv/ts-utils';\nimport {\n Convert,\n DedupScope,\n EntityId,\n IEdge,\n IEdgeTarget,\n IIdentityCodec,\n IIdentityCodecResult,\n IMemoryEnvelope,\n IMemoryRecord,\n IProvenance,\n Kind,\n LinkType,\n MemoryId,\n Tag,\n edgeTargetKey,\n isTemporalRecord,\n isVersionCurrent\n} from '../types';\nimport { IBodyConverterRegistry } from '../converters';\nimport { IMemoryStore } from '../store';\nimport { IVectorIndex, MemoryEmbedder } from '../vector';\nimport { ICycleGuardEdge, assertNoCycles } from './cycleGuard';\nimport {\n IEntityResolver,\n IFactExtractor,\n IMemoryClassifier,\n IRelationCandidate,\n IRelationExtractor\n} from './hostStages';\nimport {\n ICandidateEdge,\n ICandidateRecord,\n IEntityResolutionCandidate,\n IIngestItem,\n IIngestItemResult,\n IIngestedRecordResult,\n IMemoryClassification,\n IngestDisposition,\n ResolutionVerdict\n} from './model';\n\n/**\n * The provenance source stamped on every record the ingest pipeline writes.\n * @public\n */\nexport const HOST_INGEST_PROVENANCE_SOURCE: string = 'host-ingest';\n\n/**\n * The link type whose presence on a temporal-kind candidate fires the\n * contradicts→temporal-versioned interlock.\n * @public\n */\nexport const CONTRADICTS_LINK_TYPE: LinkType = 'contradicts' as LinkType;\n\n/**\n * The default stage-4 layer-2 cosine similarity threshold (design note §2). A\n * near-duplicate candidate is surfaced to the {@link IEntityResolver} only when\n * its nearest neighbor scores `>= 0.85`.\n * @public\n */\nexport const DEFAULT_SIMILARITY_THRESHOLD: number = 0.85;\n\n/**\n * The default top-K for stage-4 layer-2 similarity candidate generation.\n * @public\n */\nexport const DEFAULT_SIMILARITY_TOP_K: number = 5;\n\n/**\n * How the write-time cycle guard behaves: `'reject'` (default — a cycle-inducing\n * edge fails the ingest) or `'off'` (no acyclicity constraint, for deployments\n * whose link graphs are legitimately cyclic — e.g. mutual associative links).\n * @public\n */\nexport type CycleGuardMode = 'reject' | 'off';\n\n/**\n * The fgv-owned six-stage ingest orchestrator. Composes the host's staged\n * classify / extract / (optional) resolve / relate machinery around fgv's owned\n * validation boundary, dedup, edge + cycle safety, provenance stamping, and the\n * contradicts→temporal interlock.\n * @public\n */\nexport interface IMemoryIngestOrchestrator {\n /**\n * Ingest a SINGLE item end-to-end (the first-class per-turn streaming path).\n */\n ingestItem(item: IIngestItem): Promise<Result<IIngestItemResult>>;\n\n /**\n * Ingest a batch of items. A convenience loop over {@link\n * IMemoryIngestOrchestrator.ingestItem | ingestItem} — items are processed in\n * order and the first failure aborts the batch.\n */\n ingestBatch(items: ReadonlyArray<IIngestItem>): Promise<Result<ReadonlyArray<IIngestItemResult>>>;\n}\n\n/**\n * Parameters for {@link MemoryIngestOrchestrator.create}.\n * @public\n */\nexport interface IMemoryIngestOrchestratorCreateParams {\n /** The store every write bottoms out in (stage 6). */\n readonly store: IMemoryStore;\n /** Per-kind body converter registry — the stage-3 typed validation boundary. */\n readonly registry: IBodyConverterRegistry;\n /** Per-kind identity codecs (maps a candidate `entityId` to its storage address). */\n readonly codecs?: ReadonlyMap<Kind, IIdentityCodec>;\n /** Default identity codec for kinds without an explicit entry. */\n readonly defaultCodec?: IIdentityCodec;\n /** Stage 2 — the host's classifier. */\n readonly classifier: IMemoryClassifier;\n /** Stage 3 — the host's fact extractor. */\n readonly extractor: IFactExtractor;\n /** Stage 5 — the host's relation extractor. */\n readonly relationExtractor: IRelationExtractor;\n /**\n * Stage 4 (optional, OQ-13) — the host's entity resolver. Absent → stage-4\n * dedup is exact-`{ kind, body }`-hash only (the deterministic-identity path).\n */\n readonly entityResolver?: IEntityResolver;\n /**\n * Optional vector index for stage-4 layer-2 similarity candidate-gen. Wired\n * together with {@link IMemoryIngestOrchestratorCreateParams.embed | embed} AND\n * {@link IMemoryIngestOrchestratorCreateParams.entityResolver | entityResolver};\n * absent (or either co-requisite absent) → layer-2 is skipped and dedup is\n * exact-only.\n */\n readonly vectorIndex?: IVectorIndex;\n /** Optional embedder used to embed a candidate for layer-2 similarity search. */\n readonly embed?: MemoryEmbedder;\n /**\n * Stage-4 layer-2 cosine threshold. Defaults to {@link\n * DEFAULT_SIMILARITY_THRESHOLD} (`0.85`).\n */\n readonly similarityThreshold?: number;\n /**\n * Stage-4 layer-2 top-K. Defaults to {@link DEFAULT_SIMILARITY_TOP_K} (`5`).\n */\n readonly similarityTopK?: number;\n /** Write-time cycle guard mode. Defaults to `'reject'`. */\n readonly cycleGuard?: CycleGuardMode;\n /** Diagnostic logger (defaults to a no-op). */\n readonly logger?: Logging.ILogger;\n}\n\n/** The fully-wired stage-4 layer-2 dependencies (present only when all three are supplied). */\ninterface ISimilarityWiring {\n readonly resolver: IEntityResolver;\n readonly vectorIndex: IVectorIndex;\n readonly embed: MemoryEmbedder;\n}\n\n/** A snapshot record paired with its resolved scope-qualified `(scope, id)` address. */\ninterface IScopedRecord {\n readonly address: IEdgeTarget;\n readonly record: IMemoryRecord<unknown>;\n}\n\n/** Internal per-candidate plan threaded through the pipeline. */\ninterface ICandidatePlan {\n readonly candidate: ICandidateRecord;\n /** The write target's storage address (re-addressed to the target for merge-into). */\n readonly writeAddress: IIdentityCodecResult;\n /** The write target's entity id (re-addressed for merge-into). */\n readonly writeEntityId: EntityId;\n /** The reference id used in stage-5 edges (the write target's `idStem`). */\n readonly refId: MemoryId;\n /**\n * The scope-qualified reference used in stage-5 edges: the write target's\n * `(scope, idStem)`. Stage-5 sources / dedup all key on this scoped address so\n * two candidates that share a stem across scopes never collide.\n */\n readonly refTarget: IEdgeTarget;\n /** The resolution verdict; carries the target id on its target-bearing arms. */\n readonly verdict: ResolutionVerdict;\n /**\n * The pre-ingest target record for a `merge-into` write — its existing\n * `tags` / `links` / `provenance` are UNIONed with the candidate's at stage 6\n * so a wholesale-replace merge never wipes them.\n */\n readonly mergeTarget?: IMemoryRecord<unknown>;\n}\n\n/**\n * Default {@link IMemoryIngestOrchestrator}.\n * @public\n */\nexport class MemoryIngestOrchestrator implements IMemoryIngestOrchestrator {\n private readonly _store: IMemoryStore;\n private readonly _registry: IBodyConverterRegistry;\n private readonly _codecs: ReadonlyMap<Kind, IIdentityCodec>;\n private readonly _defaultCodec: IIdentityCodec | undefined;\n private readonly _classifier: IMemoryClassifier;\n private readonly _extractor: IFactExtractor;\n private readonly _relationExtractor: IRelationExtractor;\n private readonly _similarity: ISimilarityWiring | undefined;\n private readonly _similarityThreshold: number;\n private readonly _similarityTopK: number;\n private readonly _cycleGuard: CycleGuardMode;\n private readonly _logger: Logging.ILogger;\n private readonly _hasher: Hash.Crc32Normalizer;\n\n private constructor(params: IMemoryIngestOrchestratorCreateParams) {\n this._store = params.store;\n this._registry = params.registry;\n this._codecs = params.codecs ?? new Map<Kind, IIdentityCodec>();\n this._defaultCodec = params.defaultCodec;\n this._classifier = params.classifier;\n this._extractor = params.extractor;\n this._relationExtractor = params.relationExtractor;\n this._similarity =\n params.entityResolver !== undefined && params.vectorIndex !== undefined && params.embed !== undefined\n ? { resolver: params.entityResolver, vectorIndex: params.vectorIndex, embed: params.embed }\n : undefined;\n this._similarityThreshold = params.similarityThreshold ?? DEFAULT_SIMILARITY_THRESHOLD;\n this._similarityTopK = params.similarityTopK ?? DEFAULT_SIMILARITY_TOP_K;\n this._cycleGuard = params.cycleGuard ?? 'reject';\n this._logger = params.logger ?? new Logging.NoOpLogger();\n this._hasher = new Hash.Crc32Normalizer();\n }\n\n /** Family-convention factory. */\n public static create(params: IMemoryIngestOrchestratorCreateParams): Result<MemoryIngestOrchestrator> {\n return succeed(new MemoryIngestOrchestrator(params));\n }\n\n /** {@inheritDoc IMemoryIngestOrchestrator.ingestItem} */\n public async ingestItem(item: IIngestItem): Promise<Result<IIngestItemResult>> {\n return (await this._classify(item))\n .thenOnSuccess((classification) => this._extract(item, classification))\n .thenOnSuccess((candidates) => this._processCandidates(item, candidates));\n }\n\n /** {@inheritDoc IMemoryIngestOrchestrator.ingestBatch} */\n public async ingestBatch(\n items: ReadonlyArray<IIngestItem>\n ): Promise<Result<ReadonlyArray<IIngestItemResult>>> {\n const results: IIngestItemResult[] = [];\n for (const item of items) {\n const result: Result<IIngestItemResult> = await this.ingestItem(item);\n if (result.isFailure()) {\n return fail(result.message);\n }\n results.push(result.value);\n }\n return succeed(results);\n }\n\n /** Stage 2 — classify (host), normalizing a rejected promise into a Failure. */\n private async _classify(item: IIngestItem): Promise<Result<IMemoryClassification>> {\n return this._capture(() => this._classifier.classify(item), `ingest '${item.id}': classify`);\n }\n\n /** Stage 3 — extract (host), normalizing a rejected promise into a Failure. */\n private async _extract(\n item: IIngestItem,\n classification: IMemoryClassification\n ): Promise<Result<ReadonlyArray<ICandidateRecord>>> {\n return this._capture(() => this._extractor.extract(item, classification), `ingest '${item.id}': extract`);\n }\n\n /**\n * Stages 3b-6 over the extracted candidates: validate bodies, resolve/dedup\n * (stage 4), relate + cycle guard (stage 5), and load-with-provenance (stage 6).\n */\n private async _processCandidates(\n item: IIngestItem,\n candidates: ReadonlyArray<ICandidateRecord>\n ): Promise<Result<IIngestItemResult>> {\n // Snapshot the store once; stage-4 resolution and the cycle guard reason over\n // the pre-ingest state.\n const snapshotResult: Result<ReadonlyArray<IMemoryRecord<unknown>>> = await this._store.list();\n if (snapshotResult.isFailure()) {\n return fail(`ingest '${item.id}': failed to snapshot store: ${snapshotResult.message}`);\n }\n const snapshot: ReadonlyArray<IMemoryRecord<unknown>> = snapshotResult.value;\n // Resolve every snapshot record's scope-qualified address ONCE, then index it\n // by the canonical scoped key. The single `byKey` view drives BOTH the edge\n // path (validation + cycle guard) AND the verdict/similarity target lookups —\n // so a filename stem reused across scopes never aliases on any path.\n const scopedResult: Result<ReadonlyArray<IScopedRecord>> = this._scopeRecords(snapshot);\n if (scopedResult.isFailure()) {\n return fail(`ingest '${item.id}': ${scopedResult.message}`);\n }\n const scoped: ReadonlyArray<IScopedRecord> = scopedResult.value;\n const byKey: ReadonlyMap<string, IMemoryRecord<unknown>> = new Map<string, IMemoryRecord<unknown>>(\n scoped.map((s) => [edgeTargetKey(s.address), s.record])\n );\n\n // Stage 3b + 4: validate each body and resolve a verdict/plan.\n const plans: ICandidatePlan[] = [];\n for (const candidate of candidates) {\n const planResult: Result<ICandidatePlan> = await this._planCandidate(item, candidate, snapshot, byKey);\n if (planResult.isFailure()) {\n return fail(planResult.message);\n }\n plans.push(planResult.value);\n }\n\n // Stage 5: relate over the writable candidates, validate + cycle-guard.\n const writablePlans: ICandidatePlan[] = plans.filter((plan) => plan.verdict.verdict !== 'duplicate-of');\n const edgesResult: Result<ReadonlyArray<ICandidateEdge>> = await this._relate(\n item,\n writablePlans,\n MemoryIngestOrchestrator._collapseRedirects(plans),\n scoped,\n byKey\n );\n if (edgesResult.isFailure()) {\n return fail(edgesResult.message);\n }\n const edges: ReadonlyArray<ICandidateEdge> = edgesResult.value;\n\n // Stage 6: load-with-provenance, in extraction order.\n const records: IIngestedRecordResult[] = [];\n for (const plan of plans) {\n const outcome: Result<IIngestedRecordResult> = await this._loadCandidate(item, plan, edges);\n if (outcome.isFailure()) {\n return fail(outcome.message);\n }\n records.push(outcome.value);\n }\n return succeed({ item, records });\n }\n\n /** Stage 3b + 4 for one candidate: validate body, resolve address, resolve verdict. */\n private async _planCandidate(\n item: IIngestItem,\n candidate: ICandidateRecord,\n snapshot: ReadonlyArray<IMemoryRecord<unknown>>,\n byKey: ReadonlyMap<string, IMemoryRecord<unknown>>\n ): Promise<Result<ICandidatePlan>> {\n const kind: Kind = candidate.envelope.kind;\n // Stage 3b: the typed validation boundary — no unchecked host body reaches the store.\n const bodyResult: Result<string> = this._registry\n .convert(kind, candidate.body)\n .withErrorFormat((msg) => `ingest '${item.id}': candidate body for kind '${kind}' is invalid: ${msg}`)\n .onSuccess(() => MemoryIngestOrchestrator._asStringBody(item, candidate));\n if (bodyResult.isFailure()) {\n return fail(bodyResult.message);\n }\n const body: string = bodyResult.value;\n\n const addrResult: Result<IIdentityCodecResult> = this._resolveAddress(candidate.envelope.entityId, kind);\n if (addrResult.isFailure()) {\n return fail(`ingest '${item.id}': ${addrResult.message}`);\n }\n const addr: IIdentityCodecResult = addrResult.value;\n\n return (await this._resolveVerdict(candidate, kind, body, addr, snapshot, byKey)).onSuccess((verdict) =>\n this._planFromVerdict(item, candidate, addr, verdict, byKey)\n );\n }\n\n /**\n * Turn a resolved verdict into a candidate plan. A `new` verdict writes under the\n * candidate's own address. Every TARGET-bearing verdict\n * (`duplicate-of` / `supersede` / `merge-into`) is validated uniformly: the\n * target must be a real store record (fgv owns validation — a non-compliant host\n * resolver never smuggles a bogus id through) AND its kind must equal the\n * candidate's kind (a cross-kind target would write to the wrong scope). Only\n * `merge-into` re-addresses the write to the target's entity.\n */\n private _planFromVerdict(\n item: IIngestItem,\n candidate: ICandidateRecord,\n addr: IIdentityCodecResult,\n verdict: ResolutionVerdict,\n byKey: ReadonlyMap<string, IMemoryRecord<unknown>>\n ): Result<ICandidatePlan> {\n if (verdict.verdict === 'new') {\n return Convert.memoryId.convert(addr.idStem).onSuccess((refId) =>\n succeed({\n candidate,\n writeAddress: addr,\n writeEntityId: candidate.envelope.entityId,\n refId,\n refTarget: { scope: addr.scope, id: refId },\n verdict\n })\n );\n }\n // duplicate-of | supersede | merge-into: the target must exist and share the\n // candidate's kind. Lookup is on the canonical scoped key, so a stem reused\n // across scopes binds only the record the verdict actually named.\n const target: IMemoryRecord<unknown> | undefined = byKey.get(edgeTargetKey(verdict.target));\n if (target === undefined) {\n return fail(\n `ingest '${item.id}': ${verdict.verdict} target '${MemoryIngestOrchestrator._formatTarget(\n verdict.target\n )}' does not exist in the store`\n );\n }\n if (target.envelope.kind !== candidate.envelope.kind) {\n return fail(\n `ingest '${item.id}': ${verdict.verdict} target '${MemoryIngestOrchestrator._formatTarget(\n verdict.target\n )}' is kind '${target.envelope.kind}' but the candidate is kind '${candidate.envelope.kind}'`\n );\n }\n if (verdict.verdict === 'merge-into') {\n // Re-address the write to the target's entity, carrying the target record so\n // stage 6 can UNION its existing tags/links (never overwrite them).\n const targetEntityId: EntityId = target.envelope.entityId;\n // The merge target is a snapshot record, and `_scopeRecords` already proved\n // every snapshot record's address resolves before planning runs — so this\n // re-resolve (needed for the full codec result: scope + idStem + isVersioned)\n // cannot fail. No error-context wrap is warranted; a failure here would be a\n // logic contradiction, not a user-facing condition.\n return this._resolveAddress(targetEntityId, target.envelope.kind).onSuccess((targetAddr) =>\n Convert.memoryId.convert(targetAddr.idStem).onSuccess((refId) =>\n succeed({\n candidate,\n writeAddress: targetAddr,\n writeEntityId: targetEntityId,\n refId,\n refTarget: { scope: targetAddr.scope, id: refId },\n verdict,\n mergeTarget: target\n })\n )\n );\n }\n // duplicate-of | supersede: write under the candidate's own address.\n return Convert.memoryId.convert(addr.idStem).onSuccess((refId) =>\n succeed({\n candidate,\n writeAddress: addr,\n writeEntityId: candidate.envelope.entityId,\n refId,\n refTarget: { scope: addr.scope, id: refId },\n verdict\n })\n );\n }\n\n /**\n * Stage 4 — resolve a dedup verdict. Layer 1: an exact `{ kind, body }` match in\n * the candidate's scope is a `duplicate-of`, at the granularity the kind's\n * {@link DedupScope} declares — scope-wide for `'content'`, restricted to the\n * candidate's own entity for `'entity'` (design note §1). Layer 2 (only when\n * a resolver + vector index + embedder are all wired): embed the candidate,\n * surface over-threshold neighbors, and dispatch to the {@link IEntityResolver}.\n * Otherwise the verdict is `new` (the exact-only fall-back path).\n */\n private async _resolveVerdict(\n candidate: ICandidateRecord,\n kind: Kind,\n body: string,\n addr: IIdentityCodecResult,\n snapshot: ReadonlyArray<IMemoryRecord<unknown>>,\n byKey: ReadonlyMap<string, IMemoryRecord<unknown>>\n ): Promise<Result<ResolutionVerdict>> {\n return this._findExactMatch(kind, body, addr, snapshot).thenOnSuccess(async (matchId) => {\n if (matchId !== undefined) {\n // The exact-match cohort is filtered to `addr.scope`, so the match lives\n // under that scope — its scope-qualified target is `(addr.scope, matchId)`.\n return succeed({ verdict: 'duplicate-of', target: { scope: addr.scope, id: matchId } });\n }\n const layer2: ISimilarityWiring | undefined = this._similarity;\n if (layer2 === undefined) {\n // No layer-2: exact-only fall-back (the deterministic-identity host path).\n return succeed({ verdict: 'new' });\n }\n return this._resolveViaSimilarity(candidate, addr, body, layer2, byKey);\n });\n }\n\n /** Layer-2 similarity candidate-gen + resolver dispatch. */\n private async _resolveViaSimilarity(\n candidate: ICandidateRecord,\n addr: IIdentityCodecResult,\n body: string,\n wiring: ISimilarityWiring,\n byKey: ReadonlyMap<string, IMemoryRecord<unknown>>\n ): Promise<Result<ResolutionVerdict>> {\n return MemoryIngestOrchestrator._provisionalRecord(candidate, addr.idStem, body).thenOnSuccess(\n (provisional) => this._resolveViaSimilarityEmbedded(candidate, addr, wiring, byKey, provisional)\n );\n }\n\n /** Layer-2 continuation once the candidate has a provisional record to embed. */\n private async _resolveViaSimilarityEmbedded(\n candidate: ICandidateRecord,\n addr: IIdentityCodecResult,\n wiring: ISimilarityWiring,\n byKey: ReadonlyMap<string, IMemoryRecord<unknown>>,\n provisional: IMemoryRecord<unknown>\n ): Promise<Result<ResolutionVerdict>> {\n const embedded: Result<Float32Array | undefined> = await this._capture(\n () => wiring.embed(provisional),\n `ingest '${candidate.envelope.entityId}': embed candidate`\n );\n if (embedded.isFailure()) {\n return fail(embedded.message);\n }\n // The embedder deliberately declined this candidate, so there is no vector to\n // search with and layer-2 similarity dedup cannot run for it. That is the same\n // position as a search that found nothing similar — `'new'` — and not an\n // error: declining to embed a kind must not make it un-ingestable.\n if (embedded.value === undefined) {\n return succeed({ verdict: 'new' });\n }\n // Hoisted: the `undefined` check above does not narrow across the callback\n // boundary below, and a local keeps the non-null assertion out of the code.\n const vector: Float32Array = embedded.value;\n const queried = await this._capture(\n () => wiring.vectorIndex.query(vector, this._similarityTopK),\n `ingest '${candidate.envelope.entityId}': similarity query`\n );\n if (queried.isFailure()) {\n return fail(queried.message);\n }\n const similar: IEntityResolutionCandidate[] = [];\n for (const hit of queried.value) {\n // Skip below-threshold hits and the candidate's OWN scope-qualified address\n // (a re-ingest of the same entity must not dedup against its prior version).\n // Both the scope AND the id must match to be \"self\" — a same-stem record in\n // another scope is a legitimate distinct neighbor. This field-equality is\n // exactly equivalent to comparing `edgeTargetKey(hit.target)` against the\n // candidate's own key (edgeTargetKey is pure and injective on `(scope, id)`),\n // so this one site reads the components directly rather than re-keying.\n const isSelf: boolean = hit.target.scope === addr.scope && hit.target.id === addr.idStem;\n if (hit.score < this._similarityThreshold || isSelf) {\n continue;\n }\n const record: IMemoryRecord<unknown> | undefined = byKey.get(edgeTargetKey(hit.target));\n if (record !== undefined) {\n similar.push({ target: hit.target, record, score: hit.score });\n }\n }\n if (similar.length === 0) {\n return succeed({ verdict: 'new' });\n }\n return this._capture(\n () => wiring.resolver.resolve(candidate, similar),\n `ingest '${candidate.envelope.entityId}': resolve`\n );\n }\n\n /**\n * Find an existing record whose `{ kind, body }` hash matches the candidate's\n * (layer-1 exact dedup, design note §1). Invalidated temporal versions are\n * excluded — only a live (non-temporal or current) record deduplicates a\n * candidate.\n *\n * @remarks\n * The cohort's granularity is the kind's DECLARED {@link DedupScope}, read\n * through {@link IMemoryStore.dedupScopeFor} so this layer and the store's own\n * write path can never disagree:\n *\n * - `'content'` — every live same-kind record in the candidate's scope,\n * regardless of entity. A cross-id body collision IS a duplicate (the\n * knowledge family; unchanged behavior).\n * - `'entity'` — only records at the candidate's OWN entity address. Two\n * distinct entities with byte-identical bodies never collapse into one\n * another, which is what the declaration has always meant on the direct-put\n * path and now means here too.\n *\n * The same-id collapse itself remains the store's job — this layer only decides\n * whether a cross-id body collision is eligible to be a `duplicate-of` at all.\n */\n private _findExactMatch(\n kind: Kind,\n body: string,\n addr: IIdentityCodecResult,\n snapshot: ReadonlyArray<IMemoryRecord<unknown>>\n ): Result<MemoryId | undefined> {\n const dedupScope: DedupScope = this._store.dedupScopeFor(kind);\n // Same-kind, same-scope, LIVE (non-temporal or current) records are the exact\n // cohort, narrowed to the candidate's own entity address under `'entity'`\n // granularity. `_resolveAddress(...).orDefault()` collapses an unresolved\n // codec to `undefined` — a non-matching address — with no explicit failure\n // branch, exactly as the scope-only filter did before.\n const cohort: ReadonlyArray<IMemoryRecord<unknown>> = snapshot.filter((record) => {\n if (record.envelope.kind !== kind || (isTemporalRecord(record) && !isVersionCurrent(record))) {\n return false;\n }\n const recordAddr: IIdentityCodecResult | undefined = this._resolveAddress(\n record.envelope.entityId,\n record.envelope.kind\n ).orDefault();\n if (recordAddr === undefined || recordAddr.scope !== addr.scope) {\n return false;\n }\n return dedupScope === 'content' || recordAddr.idStem === addr.idStem;\n });\n return this._exactKey(kind, body).onSuccess((key) =>\n mapResults(\n cohort.map((record) =>\n MemoryIngestOrchestrator._recordBodyString(record).onSuccess((recordBody) =>\n this._exactKey(record.envelope.kind, recordBody).onSuccess((recordKey) =>\n succeed({ id: record.envelope.id, key: recordKey })\n )\n )\n )\n ).onSuccess((keyed) => succeed(keyed.find((entry) => entry.key === key)?.id))\n );\n }\n\n /**\n * The body of a persisted record, required to be a string (the store persists\n * only string bodies). Fails loudly rather than blind-casting an `unknown` body\n * into the exact-dedup hash — a non-string existing body is a store-integrity\n * fault, surfaced with context, not a silent miscompute.\n */\n private static _recordBodyString(record: IMemoryRecord<unknown>): Result<string> {\n if (typeof record.body !== 'string') {\n return fail(\n `ingest: stored record '${record.envelope.id}' has a non-string body (got ${typeof record.body})`\n );\n }\n return succeed(record.body);\n }\n\n /** The stage-4 exact-dedup key over `{ kind, body }` (design note §1). */\n private _exactKey(kind: Kind, body: string): Result<string> {\n return this._hasher.computeHash({ kind, body });\n }\n\n /** Stage 5 — relate (host), redirect collapsed targets, validate edges, and run the write-time cycle guard. */\n private async _relate(\n item: IIngestItem,\n writablePlans: ReadonlyArray<ICandidatePlan>,\n redirects: ReadonlyMap<string, IEdgeTarget>,\n scoped: ReadonlyArray<IScopedRecord>,\n byKey: ReadonlyMap<string, IMemoryRecord<unknown>>\n ): Promise<Result<ReadonlyArray<ICandidateEdge>>> {\n const relationCandidates: IRelationCandidate[] = writablePlans.map((plan) => ({\n candidate: plan.candidate,\n id: plan.refTarget\n }));\n const proposed: Result<ReadonlyArray<ICandidateEdge>> = await this._capture(\n () => this._relationExtractor.relate({ item, candidates: relationCandidates }),\n `ingest '${item.id}': relate`\n );\n if (proposed.isFailure()) {\n return proposed;\n }\n // Redirect BEFORE validation and BEFORE the cycle guard: an edge naming a\n // collapsed candidate must be judged — and persisted — against the record it\n // collapsed into, not against the address that no longer gets written.\n const edges: ReadonlyArray<ICandidateEdge> = MemoryIngestOrchestrator._redirectEdges(\n proposed.value,\n redirects\n );\n // refIds and the existing-record view (`byKey`, shared with the verdict path)\n // both key on the canonical scoped address, so a stem reused across scopes\n // never aliases.\n const refIds: ReadonlySet<string> = new Set<string>(\n writablePlans.map((plan) => edgeTargetKey(plan.refTarget))\n );\n const validation: Result<true> = this._validateEdges(item, edges, refIds, byKey);\n if (validation.isFailure()) {\n return fail(validation.message);\n }\n if (this._cycleGuard === 'reject') {\n const guard: Result<true> = assertNoCycles(\n MemoryIngestOrchestrator._existingEdges(scoped),\n edges.map((e) => ({ source: e.source, target: e.edge.target, type: e.edge.type }))\n );\n if (guard.isFailure()) {\n return fail(`ingest '${item.id}': ${guard.message}`);\n }\n }\n return succeed(edges);\n }\n\n /**\n * The stage-5 edge-target redirect map for this pass: every `duplicate-of`\n * candidate's OWN scoped address, mapped to the address of the record its\n * verdict collapsed it into (design note §3).\n *\n * @remarks\n * A `duplicate-of` verdict means \"this candidate IS that record\". The candidate\n * is therefore not written, and its address never becomes a live reference — so\n * a sibling edge built against it in the same pass would resolve to neither a\n * written candidate nor an existing record and would fail the WHOLE ingest item.\n * That is a second-order hazard of collapsing, not a defect in the edge: the\n * host related two candidates it was correctly told about, and one of them\n * turned out to already exist.\n *\n * Redirecting is the honest repair, and it is independent of {@link DedupScope}\n * — it applies equally to a `'content'` kind, where the collapse is exactly\n * right and the ingest still must not fail.\n *\n * The map cannot chain: {@link MemoryIngestOrchestrator._planFromVerdict}\n * requires every target-bearing verdict's target to already exist in the store\n * snapshot, so a redirect destination is always a persisted record and never\n * another collapsed candidate. One pass is sufficient by construction.\n */\n private static _collapseRedirects(plans: ReadonlyArray<ICandidatePlan>): ReadonlyMap<string, IEdgeTarget> {\n const redirects: Map<string, IEdgeTarget> = new Map<string, IEdgeTarget>();\n for (const plan of plans) {\n if (plan.verdict.verdict === 'duplicate-of') {\n redirects.set(edgeTargetKey(plan.refTarget), plan.verdict.target);\n }\n }\n return redirects;\n }\n\n /**\n * Rewrite each edge whose TARGET names a collapsed candidate to point at the\n * record that candidate collapsed into.\n *\n * @remarks\n * Only the target is redirected. An edge whose SOURCE is a collapsed candidate\n * stays untouched and is still rejected by {@link\n * MemoryIngestOrchestrator._validateEdges}: sources are the records an edge is\n * written ONTO, a collapsed candidate is never written, and the relation\n * extractor is only ever offered writable candidates as edge sources in the\n * first place. Silently relocating such an edge onto an existing record would\n * attribute a link the host never asked for — the loud failure is correct there.\n */\n private static _redirectEdges(\n edges: ReadonlyArray<ICandidateEdge>,\n redirects: ReadonlyMap<string, IEdgeTarget>\n ): ReadonlyArray<ICandidateEdge> {\n if (redirects.size === 0) {\n return edges;\n }\n return edges.map((e) => {\n const to: IEdgeTarget | undefined = redirects.get(edgeTargetKey(e.edge.target));\n return to === undefined ? e : { ...e, edge: { ...e.edge, target: to } };\n });\n }\n\n /**\n * Validate stage-5 edges: each `source` must be a candidate being written; each\n * `target` must resolve to a sibling candidate or an existing store record. All\n * matching is on the canonical scope-qualified address.\n */\n private _validateEdges(\n item: IIngestItem,\n edges: ReadonlyArray<ICandidateEdge>,\n refIds: ReadonlySet<string>,\n byKey: ReadonlyMap<string, IMemoryRecord<unknown>>\n ): Result<true> {\n return mapResults(\n edges.map((edge) => {\n if (!refIds.has(edgeTargetKey(edge.source))) {\n return fail(\n `ingest '${item.id}': edge source '${MemoryIngestOrchestrator._formatTarget(\n edge.source\n )}' is not a candidate being written`\n );\n }\n const targetKey: string = edgeTargetKey(edge.edge.target);\n if (!refIds.has(targetKey) && byKey.get(targetKey) === undefined) {\n return fail(\n `ingest '${item.id}': edge target '${MemoryIngestOrchestrator._formatTarget(\n edge.edge.target\n )}' resolves to neither a sibling candidate nor an existing record`\n );\n }\n return succeed(true);\n })\n ).onSuccess(() => succeed(true));\n }\n\n /** Stage 6 — stamp provenance + edges, admit through the store, record the outcome. */\n private async _loadCandidate(\n item: IIngestItem,\n plan: ICandidatePlan,\n allEdges: ReadonlyArray<ICandidateEdge>\n ): Promise<Result<IIngestedRecordResult>> {\n const refKey: string = edgeTargetKey(plan.refTarget);\n const myEdges: ReadonlyArray<ICandidateEdge> = allEdges.filter((e) => edgeTargetKey(e.source) === refKey);\n if (plan.verdict.verdict === 'duplicate-of') {\n // No write: the existing target satisfied the candidate. The target id is a\n // typed field of the narrowed `duplicate-of` verdict — no cast, no guard.\n return succeed({\n candidate: plan.candidate,\n resolution: plan.verdict,\n disposition: 'deduped',\n id: plan.verdict.target.id,\n edges: myEdges\n });\n }\n return this._buildRecord(item, plan, myEdges).thenOnSuccess(async (record) =>\n (await this._store.put(record))\n .withErrorFormat((msg) => `ingest '${item.id}': write failed: ${msg}`)\n .onSuccess((persisted) => {\n const disposition: IngestDisposition = plan.verdict.verdict === 'merge-into' ? 'merged' : 'written';\n const interlock: 'temporal-versioned' | undefined =\n plan.writeAddress.isVersioned && myEdges.some((e) => e.edge.type === CONTRADICTS_LINK_TYPE)\n ? 'temporal-versioned'\n : undefined;\n return succeed({\n candidate: plan.candidate,\n resolution: plan.verdict,\n disposition,\n id: persisted.envelope.id,\n record: persisted,\n edges: myEdges,\n ...(interlock !== undefined ? { interlock } : {})\n });\n })\n );\n }\n\n /** Build the fully-stamped record to persist (provenance + edges + placeholder txn fields). */\n private _buildRecord(\n item: IIngestItem,\n plan: ICandidatePlan,\n myEdges: ReadonlyArray<ICandidateEdge>\n ): Result<IMemoryRecord<unknown>> {\n const base: Omit<IMemoryEnvelope, 'id' | 'seq' | 'contentHash' | 'created' | 'updated'> =\n plan.candidate.envelope;\n // For merge-into the store's `applyUpdate` REPLACES array fields wholesale, so\n // the persisted envelope must already carry the UNION of the target's existing\n // tags/links/provenance and the candidate's — otherwise the merge silently\n // wipes the target's prior links/tags.\n const target: IMemoryRecord<unknown> | undefined = plan.mergeTarget;\n const priorLinks: ReadonlyArray<IEdge> = target !== undefined ? target.envelope.links : [];\n const priorTags: ReadonlyArray<Tag> = target !== undefined ? target.envelope.tags : [];\n const priorProvenance: IProvenance = target !== undefined ? target.envelope.provenance : { source: '' };\n return Convert.memoryId.convert(plan.writeAddress.idStem).onSuccess((id) =>\n // Dedup the combined links by canonical edge key: the target's prior links, the\n // candidate's own links, and the stage-5 edges (two candidates that resolve\n // merge-into the SAME target share a `refId` — hence the same `myEdges` — and a\n // relation extractor may repeat an edge) each appear exactly once.\n this._dedupEdges([...priorLinks, ...base.links, ...myEdges.map((e) => e.edge)]).onSuccess((links) => {\n const provenance: IProvenance = {\n // Preserve the target's provenance extension keys, overlay the candidate's,\n // then stamp the host-ingest source + derivedFrom (target present only for\n // merge-into; otherwise the seed is inert).\n ...(target !== undefined ? priorProvenance : {}),\n ...base.provenance,\n source: HOST_INGEST_PROVENANCE_SOURCE,\n ...(item.sourceId !== undefined ? { derivedFrom: item.sourceId } : {})\n };\n const envelope: IMemoryEnvelope = {\n ...base,\n id,\n entityId: plan.writeEntityId,\n seq: 0,\n contentHash: '',\n created: 0,\n updated: 0,\n provenance,\n tags: MemoryIngestOrchestrator._unionTags(priorTags, base.tags),\n links\n };\n return succeed({ envelope, body: plan.candidate.body });\n })\n );\n }\n\n /** Union two tag lists, de-duplicated, preserving first-occurrence order. */\n private static _unionTags(prior: ReadonlyArray<Tag>, incoming: ReadonlyArray<Tag>): ReadonlyArray<Tag> {\n const seen: Set<string> = new Set<string>();\n const out: Tag[] = [];\n for (const tag of [...prior, ...incoming]) {\n if (!seen.has(tag)) {\n seen.add(tag);\n out.push(tag);\n }\n }\n return out;\n }\n\n /** De-duplicate a link list by canonical edge hash, preserving first-occurrence order. */\n private _dedupEdges(edges: ReadonlyArray<IEdge>): Result<ReadonlyArray<IEdge>> {\n const seen: Set<string> = new Set<string>();\n const deduped: IEdge[] = [];\n return mapResults(\n edges.map((edge) =>\n this._hasher.computeHash(edge).onSuccess((key) => {\n if (!seen.has(key)) {\n seen.add(key);\n deduped.push(edge);\n }\n return succeed(key);\n })\n )\n ).onSuccess(() => succeed(deduped));\n }\n\n /** Resolve a `(kind, entityId)` to its storage address via the registered codec. */\n private _resolveAddress(entityId: EntityId, kind: Kind): Result<IIdentityCodecResult> {\n const codec: IIdentityCodec | undefined = this._codecs.get(kind) ?? this._defaultCodec;\n if (codec === undefined) {\n return fail(`no identity codec registered for kind '${kind}'`);\n }\n return codec.encode(entityId);\n }\n\n /** Run a host hook, normalizing a thrown/rejected hook into a Failure (never throws across the seam). */\n private async _capture<T>(op: () => Promise<Result<T>>, label: string): Promise<Result<T>> {\n try {\n return await op();\n } catch (err) {\n const message: string = `${label} threw: ${String(err)}`;\n this._logger.warn(message);\n return fail(message);\n }\n }\n\n /**\n * Every existing outbound edge in the snapshot, as cycle-guard edges. The\n * source is the record's own scope-qualified address (already resolved by\n * {@link MemoryIngestOrchestrator._scopeRecords}); the target is the edge's\n * own scoped target. Both ends are scoped so the guard never conflates a stem\n * shared across scopes into one graph node.\n */\n private static _existingEdges(scoped: ReadonlyArray<IScopedRecord>): ReadonlyArray<ICycleGuardEdge> {\n const edges: ICycleGuardEdge[] = [];\n for (const s of scoped) {\n for (const edge of s.record.envelope.links) {\n edges.push({ source: s.address, target: edge.target, type: edge.type });\n }\n }\n return edges;\n }\n\n /**\n * Resolve every snapshot record's scope-qualified `(scope, id)` address via its\n * registered codec. Fails loudly if a record's kind has no resolvable codec —\n * the edge path cannot place an un-scopeable record in the graph, and a missing\n * codec for a stored kind is a real misconfiguration, not something to paper over.\n */\n private _scopeRecords(\n records: ReadonlyArray<IMemoryRecord<unknown>>\n ): Result<ReadonlyArray<IScopedRecord>> {\n return mapResults(\n records.map((record) =>\n this._resolveAddress(record.envelope.entityId, record.envelope.kind)\n .withErrorFormat((msg) => `cannot resolve scope for stored record '${record.envelope.id}': ${msg}`)\n .onSuccess((addr) => succeed({ address: { scope: addr.scope, id: record.envelope.id }, record }))\n )\n );\n }\n\n /** Human-readable `scope/id` rendering of a scoped target for edge-validation diagnostics. */\n private static _formatTarget(target: IEdgeTarget): string {\n return `${target.scope}/${target.id}`;\n }\n\n /** A provisional record for embedding a candidate (placeholder txn-time fields). */\n private static _provisionalRecord(\n candidate: ICandidateRecord,\n idStem: string,\n body: string\n ): Result<IMemoryRecord<unknown>> {\n return Convert.memoryId.convert(idStem).onSuccess((id) => {\n const envelope: IMemoryEnvelope = {\n ...candidate.envelope,\n id,\n seq: 0,\n contentHash: '',\n created: 0,\n updated: 0\n };\n return succeed({ envelope, body });\n });\n }\n\n /** Require a candidate body to be a string (the store persists only string bodies). */\n private static _asStringBody(item: IIngestItem, candidate: ICandidateRecord): Result<string> {\n if (typeof candidate.body !== 'string') {\n return fail(\n `ingest '${item.id}': candidate body for kind '${\n candidate.envelope.kind\n }' must be a string (got ${typeof candidate.body})`\n );\n }\n return succeed(candidate.body);\n }\n}\n"]}
1
+ {"version":3,"file":"orchestrator.js","sourceRoot":"","sources":["../../../src/packlets/ingest/orchestrator.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright (c) 2026 Erik Fortune\n * SPDX-License-Identifier: MIT\n */\n\nimport { Hash, Logging, Result, fail, mapResults, succeed } from '@fgv/ts-utils';\nimport {\n Convert,\n DedupScope,\n EntityId,\n IEdge,\n IEdgeTarget,\n IIdentityCodec,\n IIdentityCodecResult,\n IMemoryEnvelope,\n IMemoryRecord,\n IProvenance,\n Kind,\n LinkType,\n MemoryId,\n Tag,\n edgeTargetKey,\n isTemporalRecord,\n isVersionCurrent\n} from '../types';\nimport { IBodyConverterRegistry } from '../converters';\nimport { IMemoryStore, scanEveryRecord } from '../store';\nimport { IVectorIndex, MemoryEmbedder } from '../vector';\nimport { ICycleGuardEdge, assertNoCycles } from './cycleGuard';\nimport {\n IEntityResolver,\n IFactExtractor,\n IMemoryClassifier,\n IRelationCandidate,\n IRelationExtractor\n} from './hostStages';\nimport {\n ICandidateEdge,\n ICandidateRecord,\n IEntityResolutionCandidate,\n IIngestItem,\n IIngestItemResult,\n IIngestedRecordResult,\n IMemoryClassification,\n IngestDisposition,\n ResolutionVerdict\n} from './model';\n\n/**\n * The provenance source stamped on every record the ingest pipeline writes.\n * @public\n */\nexport const HOST_INGEST_PROVENANCE_SOURCE: string = 'host-ingest';\n\n/**\n * The link type whose presence on a temporal-kind candidate fires the\n * contradicts→temporal-versioned interlock.\n * @public\n */\nexport const CONTRADICTS_LINK_TYPE: LinkType = 'contradicts' as LinkType;\n\n/**\n * The default stage-4 layer-2 cosine similarity threshold (design note §2). A\n * near-duplicate candidate is surfaced to the {@link IEntityResolver} only when\n * its nearest neighbor scores `>= 0.85`.\n * @public\n */\nexport const DEFAULT_SIMILARITY_THRESHOLD: number = 0.85;\n\n/**\n * The default top-K for stage-4 layer-2 similarity candidate generation.\n * @public\n */\nexport const DEFAULT_SIMILARITY_TOP_K: number = 5;\n\n/**\n * How the write-time cycle guard behaves: `'reject'` (default — a cycle-inducing\n * edge fails the ingest) or `'off'` (no acyclicity constraint, for deployments\n * whose link graphs are legitimately cyclic — e.g. mutual associative links).\n * @public\n */\nexport type CycleGuardMode = 'reject' | 'off';\n\n/**\n * The fgv-owned six-stage ingest orchestrator. Composes the host's staged\n * classify / extract / (optional) resolve / relate machinery around fgv's owned\n * validation boundary, dedup, edge + cycle safety, provenance stamping, and the\n * contradicts→temporal interlock.\n * @public\n */\nexport interface IMemoryIngestOrchestrator {\n /**\n * Ingest a SINGLE item end-to-end (the first-class per-turn streaming path).\n */\n ingestItem(item: IIngestItem): Promise<Result<IIngestItemResult>>;\n\n /**\n * Ingest a batch of items. A convenience loop over {@link\n * IMemoryIngestOrchestrator.ingestItem | ingestItem} — items are processed in\n * order and the first failure aborts the batch.\n */\n ingestBatch(items: ReadonlyArray<IIngestItem>): Promise<Result<ReadonlyArray<IIngestItemResult>>>;\n}\n\n/**\n * Parameters for {@link MemoryIngestOrchestrator.create}.\n * @public\n */\nexport interface IMemoryIngestOrchestratorCreateParams {\n /** The store every write bottoms out in (stage 6). */\n readonly store: IMemoryStore;\n /** Per-kind body converter registry — the stage-3 typed validation boundary. */\n readonly registry: IBodyConverterRegistry;\n /** Per-kind identity codecs (maps a candidate `entityId` to its storage address). */\n readonly codecs?: ReadonlyMap<Kind, IIdentityCodec>;\n /** Default identity codec for kinds without an explicit entry. */\n readonly defaultCodec?: IIdentityCodec;\n /** Stage 2 — the host's classifier. */\n readonly classifier: IMemoryClassifier;\n /** Stage 3 — the host's fact extractor. */\n readonly extractor: IFactExtractor;\n /** Stage 5 — the host's relation extractor. */\n readonly relationExtractor: IRelationExtractor;\n /**\n * Stage 4 (optional, OQ-13) — the host's entity resolver. Absent → stage-4\n * dedup is exact-`{ kind, body }`-hash only (the deterministic-identity path).\n */\n readonly entityResolver?: IEntityResolver;\n /**\n * Optional vector index for stage-4 layer-2 similarity candidate-gen. Wired\n * together with {@link IMemoryIngestOrchestratorCreateParams.embed | embed} AND\n * {@link IMemoryIngestOrchestratorCreateParams.entityResolver | entityResolver};\n * absent (or either co-requisite absent) → layer-2 is skipped and dedup is\n * exact-only.\n */\n readonly vectorIndex?: IVectorIndex;\n /** Optional embedder used to embed a candidate for layer-2 similarity search. */\n readonly embed?: MemoryEmbedder;\n /**\n * Stage-4 layer-2 cosine threshold. Defaults to {@link\n * DEFAULT_SIMILARITY_THRESHOLD} (`0.85`).\n */\n readonly similarityThreshold?: number;\n /**\n * Stage-4 layer-2 top-K. Defaults to {@link DEFAULT_SIMILARITY_TOP_K} (`5`).\n */\n readonly similarityTopK?: number;\n /** Write-time cycle guard mode. Defaults to `'reject'`. */\n readonly cycleGuard?: CycleGuardMode;\n /** Diagnostic logger (defaults to a no-op). */\n readonly logger?: Logging.ILogger;\n}\n\n/** The fully-wired stage-4 layer-2 dependencies (present only when all three are supplied). */\ninterface ISimilarityWiring {\n readonly resolver: IEntityResolver;\n readonly vectorIndex: IVectorIndex;\n readonly embed: MemoryEmbedder;\n}\n\n/** A snapshot record paired with its resolved scope-qualified `(scope, id)` address. */\ninterface IScopedRecord {\n readonly address: IEdgeTarget;\n readonly record: IMemoryRecord<unknown>;\n}\n\n/** Internal per-candidate plan threaded through the pipeline. */\ninterface ICandidatePlan {\n readonly candidate: ICandidateRecord;\n /** The write target's storage address (re-addressed to the target for merge-into). */\n readonly writeAddress: IIdentityCodecResult;\n /** The write target's entity id (re-addressed for merge-into). */\n readonly writeEntityId: EntityId;\n /** The reference id used in stage-5 edges (the write target's `idStem`). */\n readonly refId: MemoryId;\n /**\n * The scope-qualified reference used in stage-5 edges: the write target's\n * `(scope, idStem)`. Stage-5 sources / dedup all key on this scoped address so\n * two candidates that share a stem across scopes never collide.\n */\n readonly refTarget: IEdgeTarget;\n /** The resolution verdict; carries the target id on its target-bearing arms. */\n readonly verdict: ResolutionVerdict;\n /**\n * The pre-ingest target record for a `merge-into` write — its existing\n * `tags` / `links` / `provenance` are UNIONed with the candidate's at stage 6\n * so a wholesale-replace merge never wipes them.\n */\n readonly mergeTarget?: IMemoryRecord<unknown>;\n}\n\n/**\n * Default {@link IMemoryIngestOrchestrator}.\n * @public\n */\nexport class MemoryIngestOrchestrator implements IMemoryIngestOrchestrator {\n private readonly _store: IMemoryStore;\n private readonly _registry: IBodyConverterRegistry;\n private readonly _codecs: ReadonlyMap<Kind, IIdentityCodec>;\n private readonly _defaultCodec: IIdentityCodec | undefined;\n private readonly _classifier: IMemoryClassifier;\n private readonly _extractor: IFactExtractor;\n private readonly _relationExtractor: IRelationExtractor;\n private readonly _similarity: ISimilarityWiring | undefined;\n private readonly _similarityThreshold: number;\n private readonly _similarityTopK: number;\n private readonly _cycleGuard: CycleGuardMode;\n private readonly _logger: Logging.ILogger;\n private readonly _hasher: Hash.Crc32Normalizer;\n\n private constructor(params: IMemoryIngestOrchestratorCreateParams) {\n this._store = params.store;\n this._registry = params.registry;\n this._codecs = params.codecs ?? new Map<Kind, IIdentityCodec>();\n this._defaultCodec = params.defaultCodec;\n this._classifier = params.classifier;\n this._extractor = params.extractor;\n this._relationExtractor = params.relationExtractor;\n this._similarity =\n params.entityResolver !== undefined && params.vectorIndex !== undefined && params.embed !== undefined\n ? { resolver: params.entityResolver, vectorIndex: params.vectorIndex, embed: params.embed }\n : undefined;\n this._similarityThreshold = params.similarityThreshold ?? DEFAULT_SIMILARITY_THRESHOLD;\n this._similarityTopK = params.similarityTopK ?? DEFAULT_SIMILARITY_TOP_K;\n this._cycleGuard = params.cycleGuard ?? 'reject';\n this._logger = params.logger ?? new Logging.NoOpLogger();\n this._hasher = new Hash.Crc32Normalizer();\n }\n\n /** Family-convention factory. */\n public static create(params: IMemoryIngestOrchestratorCreateParams): Result<MemoryIngestOrchestrator> {\n return succeed(new MemoryIngestOrchestrator(params));\n }\n\n /** {@inheritDoc IMemoryIngestOrchestrator.ingestItem} */\n public async ingestItem(item: IIngestItem): Promise<Result<IIngestItemResult>> {\n return (await this._classify(item))\n .thenOnSuccess((classification) => this._extract(item, classification))\n .thenOnSuccess((candidates) => this._processCandidates(item, candidates));\n }\n\n /** {@inheritDoc IMemoryIngestOrchestrator.ingestBatch} */\n public async ingestBatch(\n items: ReadonlyArray<IIngestItem>\n ): Promise<Result<ReadonlyArray<IIngestItemResult>>> {\n const results: IIngestItemResult[] = [];\n for (const item of items) {\n const result: Result<IIngestItemResult> = await this.ingestItem(item);\n if (result.isFailure()) {\n return fail(result.message);\n }\n results.push(result.value);\n }\n return succeed(results);\n }\n\n /** Stage 2 — classify (host), normalizing a rejected promise into a Failure. */\n private async _classify(item: IIngestItem): Promise<Result<IMemoryClassification>> {\n return this._capture(() => this._classifier.classify(item), `ingest '${item.id}': classify`);\n }\n\n /** Stage 3 — extract (host), normalizing a rejected promise into a Failure. */\n private async _extract(\n item: IIngestItem,\n classification: IMemoryClassification\n ): Promise<Result<ReadonlyArray<ICandidateRecord>>> {\n return this._capture(() => this._extractor.extract(item, classification), `ingest '${item.id}': extract`);\n }\n\n /**\n * Stages 3b-6 over the extracted candidates: validate bodies, resolve/dedup\n * (stage 4), relate + cycle guard (stage 5), and load-with-provenance (stage 6).\n */\n private async _processCandidates(\n item: IIngestItem,\n candidates: ReadonlyArray<ICandidateRecord>\n ): Promise<Result<IIngestItemResult>> {\n // Snapshot the store once; stage-4 resolution and the cycle guard reason over\n // the pre-ingest state.\n //\n // DECLARED, not optimized. This reads every body in the vault on EVERY\n // ingested item, and `scanEveryRecord()` is how the store now makes a caller\n // say so out loud. Most of what the snapshot is used for downstream — edge\n // target existence, the cycle guard's link walk, scoped-address keying — is\n // envelope-only work that `listEntries()` would serve without reading a\n // single file; only stage-4 layer-1's exact `{ kind, body }` match needs\n // bodies, and it needs them for candidates rather than for the whole vault.\n // Converting this is its own stream (`agent-memory-ingest-snapshot`), because\n // it is on the write path with its own risk surface. Until then the cost is\n // at least visible and greppable rather than implied.\n const snapshotResult: Result<ReadonlyArray<IMemoryRecord<unknown>>> = await this._store.list(\n scanEveryRecord()\n );\n if (snapshotResult.isFailure()) {\n return fail(`ingest '${item.id}': failed to snapshot store: ${snapshotResult.message}`);\n }\n const snapshot: ReadonlyArray<IMemoryRecord<unknown>> = snapshotResult.value;\n // Resolve every snapshot record's scope-qualified address ONCE, then index it\n // by the canonical scoped key. The single `byKey` view drives BOTH the edge\n // path (validation + cycle guard) AND the verdict/similarity target lookups —\n // so a filename stem reused across scopes never aliases on any path.\n const scopedResult: Result<ReadonlyArray<IScopedRecord>> = this._scopeRecords(snapshot);\n if (scopedResult.isFailure()) {\n return fail(`ingest '${item.id}': ${scopedResult.message}`);\n }\n const scoped: ReadonlyArray<IScopedRecord> = scopedResult.value;\n const byKey: ReadonlyMap<string, IMemoryRecord<unknown>> = new Map<string, IMemoryRecord<unknown>>(\n scoped.map((s) => [edgeTargetKey(s.address), s.record])\n );\n\n // Stage 3b + 4: validate each body and resolve a verdict/plan.\n const plans: ICandidatePlan[] = [];\n for (const candidate of candidates) {\n const planResult: Result<ICandidatePlan> = await this._planCandidate(item, candidate, snapshot, byKey);\n if (planResult.isFailure()) {\n return fail(planResult.message);\n }\n plans.push(planResult.value);\n }\n\n // Stage 5: relate over the writable candidates, validate + cycle-guard.\n const writablePlans: ICandidatePlan[] = plans.filter((plan) => plan.verdict.verdict !== 'duplicate-of');\n const edgesResult: Result<ReadonlyArray<ICandidateEdge>> = await this._relate(\n item,\n writablePlans,\n MemoryIngestOrchestrator._collapseRedirects(plans),\n scoped,\n byKey\n );\n if (edgesResult.isFailure()) {\n return fail(edgesResult.message);\n }\n const edges: ReadonlyArray<ICandidateEdge> = edgesResult.value;\n\n // Stage 6: load-with-provenance, in extraction order.\n const records: IIngestedRecordResult[] = [];\n for (const plan of plans) {\n const outcome: Result<IIngestedRecordResult> = await this._loadCandidate(item, plan, edges);\n if (outcome.isFailure()) {\n return fail(outcome.message);\n }\n records.push(outcome.value);\n }\n return succeed({ item, records });\n }\n\n /** Stage 3b + 4 for one candidate: validate body, resolve address, resolve verdict. */\n private async _planCandidate(\n item: IIngestItem,\n candidate: ICandidateRecord,\n snapshot: ReadonlyArray<IMemoryRecord<unknown>>,\n byKey: ReadonlyMap<string, IMemoryRecord<unknown>>\n ): Promise<Result<ICandidatePlan>> {\n const kind: Kind = candidate.envelope.kind;\n // Stage 3b: the typed validation boundary — no unchecked host body reaches the store.\n const bodyResult: Result<string> = this._registry\n .convert(kind, candidate.body)\n .withErrorFormat((msg) => `ingest '${item.id}': candidate body for kind '${kind}' is invalid: ${msg}`)\n .onSuccess(() => MemoryIngestOrchestrator._asStringBody(item, candidate));\n if (bodyResult.isFailure()) {\n return fail(bodyResult.message);\n }\n const body: string = bodyResult.value;\n\n const addrResult: Result<IIdentityCodecResult> = this._resolveAddress(candidate.envelope.entityId, kind);\n if (addrResult.isFailure()) {\n return fail(`ingest '${item.id}': ${addrResult.message}`);\n }\n const addr: IIdentityCodecResult = addrResult.value;\n\n return (await this._resolveVerdict(candidate, kind, body, addr, snapshot, byKey)).onSuccess((verdict) =>\n this._planFromVerdict(item, candidate, addr, verdict, byKey)\n );\n }\n\n /**\n * Turn a resolved verdict into a candidate plan. A `new` verdict writes under the\n * candidate's own address. Every TARGET-bearing verdict\n * (`duplicate-of` / `supersede` / `merge-into`) is validated uniformly: the\n * target must be a real store record (fgv owns validation — a non-compliant host\n * resolver never smuggles a bogus id through) AND its kind must equal the\n * candidate's kind (a cross-kind target would write to the wrong scope). Only\n * `merge-into` re-addresses the write to the target's entity.\n */\n private _planFromVerdict(\n item: IIngestItem,\n candidate: ICandidateRecord,\n addr: IIdentityCodecResult,\n verdict: ResolutionVerdict,\n byKey: ReadonlyMap<string, IMemoryRecord<unknown>>\n ): Result<ICandidatePlan> {\n if (verdict.verdict === 'new') {\n return Convert.memoryId.convert(addr.idStem).onSuccess((refId) =>\n succeed({\n candidate,\n writeAddress: addr,\n writeEntityId: candidate.envelope.entityId,\n refId,\n refTarget: { scope: addr.scope, id: refId },\n verdict\n })\n );\n }\n // duplicate-of | supersede | merge-into: the target must exist and share the\n // candidate's kind. Lookup is on the canonical scoped key, so a stem reused\n // across scopes binds only the record the verdict actually named.\n const target: IMemoryRecord<unknown> | undefined = byKey.get(edgeTargetKey(verdict.target));\n if (target === undefined) {\n return fail(\n `ingest '${item.id}': ${verdict.verdict} target '${MemoryIngestOrchestrator._formatTarget(\n verdict.target\n )}' does not exist in the store`\n );\n }\n if (target.envelope.kind !== candidate.envelope.kind) {\n return fail(\n `ingest '${item.id}': ${verdict.verdict} target '${MemoryIngestOrchestrator._formatTarget(\n verdict.target\n )}' is kind '${target.envelope.kind}' but the candidate is kind '${candidate.envelope.kind}'`\n );\n }\n if (verdict.verdict === 'merge-into') {\n // Re-address the write to the target's entity, carrying the target record so\n // stage 6 can UNION its existing tags/links (never overwrite them).\n const targetEntityId: EntityId = target.envelope.entityId;\n // The merge target is a snapshot record, and `_scopeRecords` already proved\n // every snapshot record's address resolves before planning runs — so this\n // re-resolve (needed for the full codec result: scope + idStem + isVersioned)\n // cannot fail. No error-context wrap is warranted; a failure here would be a\n // logic contradiction, not a user-facing condition.\n return this._resolveAddress(targetEntityId, target.envelope.kind).onSuccess((targetAddr) =>\n Convert.memoryId.convert(targetAddr.idStem).onSuccess((refId) =>\n succeed({\n candidate,\n writeAddress: targetAddr,\n writeEntityId: targetEntityId,\n refId,\n refTarget: { scope: targetAddr.scope, id: refId },\n verdict,\n mergeTarget: target\n })\n )\n );\n }\n // duplicate-of | supersede: write under the candidate's own address.\n return Convert.memoryId.convert(addr.idStem).onSuccess((refId) =>\n succeed({\n candidate,\n writeAddress: addr,\n writeEntityId: candidate.envelope.entityId,\n refId,\n refTarget: { scope: addr.scope, id: refId },\n verdict\n })\n );\n }\n\n /**\n * Stage 4 — resolve a dedup verdict. Layer 1: an exact `{ kind, body }` match in\n * the candidate's scope is a `duplicate-of`, at the granularity the kind's\n * {@link DedupScope} declares — scope-wide for `'content'`, restricted to the\n * candidate's own entity for `'entity'` (design note §1). Layer 2 (only when\n * a resolver + vector index + embedder are all wired): embed the candidate,\n * surface over-threshold neighbors, and dispatch to the {@link IEntityResolver}.\n * Otherwise the verdict is `new` (the exact-only fall-back path).\n */\n private async _resolveVerdict(\n candidate: ICandidateRecord,\n kind: Kind,\n body: string,\n addr: IIdentityCodecResult,\n snapshot: ReadonlyArray<IMemoryRecord<unknown>>,\n byKey: ReadonlyMap<string, IMemoryRecord<unknown>>\n ): Promise<Result<ResolutionVerdict>> {\n return this._findExactMatch(kind, body, addr, snapshot).thenOnSuccess(async (matchId) => {\n if (matchId !== undefined) {\n // The exact-match cohort is filtered to `addr.scope`, so the match lives\n // under that scope — its scope-qualified target is `(addr.scope, matchId)`.\n return succeed({ verdict: 'duplicate-of', target: { scope: addr.scope, id: matchId } });\n }\n const layer2: ISimilarityWiring | undefined = this._similarity;\n if (layer2 === undefined) {\n // No layer-2: exact-only fall-back (the deterministic-identity host path).\n return succeed({ verdict: 'new' });\n }\n return this._resolveViaSimilarity(candidate, addr, body, layer2, byKey);\n });\n }\n\n /** Layer-2 similarity candidate-gen + resolver dispatch. */\n private async _resolveViaSimilarity(\n candidate: ICandidateRecord,\n addr: IIdentityCodecResult,\n body: string,\n wiring: ISimilarityWiring,\n byKey: ReadonlyMap<string, IMemoryRecord<unknown>>\n ): Promise<Result<ResolutionVerdict>> {\n return MemoryIngestOrchestrator._provisionalRecord(candidate, addr.idStem, body).thenOnSuccess(\n (provisional) => this._resolveViaSimilarityEmbedded(candidate, addr, wiring, byKey, provisional)\n );\n }\n\n /** Layer-2 continuation once the candidate has a provisional record to embed. */\n private async _resolveViaSimilarityEmbedded(\n candidate: ICandidateRecord,\n addr: IIdentityCodecResult,\n wiring: ISimilarityWiring,\n byKey: ReadonlyMap<string, IMemoryRecord<unknown>>,\n provisional: IMemoryRecord<unknown>\n ): Promise<Result<ResolutionVerdict>> {\n const embedded: Result<Float32Array | undefined> = await this._capture(\n () => wiring.embed(provisional),\n `ingest '${candidate.envelope.entityId}': embed candidate`\n );\n if (embedded.isFailure()) {\n return fail(embedded.message);\n }\n // The embedder deliberately declined this candidate, so there is no vector to\n // search with and layer-2 similarity dedup cannot run for it. That is the same\n // position as a search that found nothing similar — `'new'` — and not an\n // error: declining to embed a kind must not make it un-ingestable.\n if (embedded.value === undefined) {\n return succeed({ verdict: 'new' });\n }\n // Hoisted: the `undefined` check above does not narrow across the callback\n // boundary below, and a local keeps the non-null assertion out of the code.\n const vector: Float32Array = embedded.value;\n const queried = await this._capture(\n () => wiring.vectorIndex.query(vector, this._similarityTopK),\n `ingest '${candidate.envelope.entityId}': similarity query`\n );\n if (queried.isFailure()) {\n return fail(queried.message);\n }\n const similar: IEntityResolutionCandidate[] = [];\n for (const hit of queried.value) {\n // Skip below-threshold hits and the candidate's OWN scope-qualified address\n // (a re-ingest of the same entity must not dedup against its prior version).\n // Both the scope AND the id must match to be \"self\" — a same-stem record in\n // another scope is a legitimate distinct neighbor. This field-equality is\n // exactly equivalent to comparing `edgeTargetKey(hit.target)` against the\n // candidate's own key (edgeTargetKey is pure and injective on `(scope, id)`),\n // so this one site reads the components directly rather than re-keying.\n const isSelf: boolean = hit.target.scope === addr.scope && hit.target.id === addr.idStem;\n if (hit.score < this._similarityThreshold || isSelf) {\n continue;\n }\n const record: IMemoryRecord<unknown> | undefined = byKey.get(edgeTargetKey(hit.target));\n if (record !== undefined) {\n similar.push({ target: hit.target, record, score: hit.score });\n }\n }\n if (similar.length === 0) {\n return succeed({ verdict: 'new' });\n }\n return this._capture(\n () => wiring.resolver.resolve(candidate, similar),\n `ingest '${candidate.envelope.entityId}': resolve`\n );\n }\n\n /**\n * Find an existing record whose `{ kind, body }` hash matches the candidate's\n * (layer-1 exact dedup, design note §1). Invalidated temporal versions are\n * excluded — only a live (non-temporal or current) record deduplicates a\n * candidate.\n *\n * @remarks\n * The cohort's granularity is the kind's DECLARED {@link DedupScope}, read\n * through {@link IMemoryStore.dedupScopeFor} so this layer and the store's own\n * write path can never disagree:\n *\n * - `'content'` — every live same-kind record in the candidate's scope,\n * regardless of entity. A cross-id body collision IS a duplicate (the\n * knowledge family; unchanged behavior).\n * - `'entity'` — only records at the candidate's OWN entity address. Two\n * distinct entities with byte-identical bodies never collapse into one\n * another, which is what the declaration has always meant on the direct-put\n * path and now means here too.\n *\n * The same-id collapse itself remains the store's job — this layer only decides\n * whether a cross-id body collision is eligible to be a `duplicate-of` at all.\n */\n private _findExactMatch(\n kind: Kind,\n body: string,\n addr: IIdentityCodecResult,\n snapshot: ReadonlyArray<IMemoryRecord<unknown>>\n ): Result<MemoryId | undefined> {\n const dedupScope: DedupScope = this._store.dedupScopeFor(kind);\n // Same-kind, same-scope, LIVE (non-temporal or current) records are the exact\n // cohort, narrowed to the candidate's own entity address under `'entity'`\n // granularity. `_resolveAddress(...).orDefault()` collapses an unresolved\n // codec to `undefined` — a non-matching address — with no explicit failure\n // branch, exactly as the scope-only filter did before.\n const cohort: ReadonlyArray<IMemoryRecord<unknown>> = snapshot.filter((record) => {\n if (record.envelope.kind !== kind || (isTemporalRecord(record) && !isVersionCurrent(record))) {\n return false;\n }\n const recordAddr: IIdentityCodecResult | undefined = this._resolveAddress(\n record.envelope.entityId,\n record.envelope.kind\n ).orDefault();\n if (recordAddr === undefined || recordAddr.scope !== addr.scope) {\n return false;\n }\n return dedupScope === 'content' || recordAddr.idStem === addr.idStem;\n });\n return this._exactKey(kind, body).onSuccess((key) =>\n mapResults(\n cohort.map((record) =>\n MemoryIngestOrchestrator._recordBodyString(record).onSuccess((recordBody) =>\n this._exactKey(record.envelope.kind, recordBody).onSuccess((recordKey) =>\n succeed({ id: record.envelope.id, key: recordKey })\n )\n )\n )\n ).onSuccess((keyed) => succeed(keyed.find((entry) => entry.key === key)?.id))\n );\n }\n\n /**\n * The body of a persisted record, required to be a string (the store persists\n * only string bodies). Fails loudly rather than blind-casting an `unknown` body\n * into the exact-dedup hash — a non-string existing body is a store-integrity\n * fault, surfaced with context, not a silent miscompute.\n */\n private static _recordBodyString(record: IMemoryRecord<unknown>): Result<string> {\n if (typeof record.body !== 'string') {\n return fail(\n `ingest: stored record '${record.envelope.id}' has a non-string body (got ${typeof record.body})`\n );\n }\n return succeed(record.body);\n }\n\n /** The stage-4 exact-dedup key over `{ kind, body }` (design note §1). */\n private _exactKey(kind: Kind, body: string): Result<string> {\n return this._hasher.computeHash({ kind, body });\n }\n\n /** Stage 5 — relate (host), redirect collapsed targets, validate edges, and run the write-time cycle guard. */\n private async _relate(\n item: IIngestItem,\n writablePlans: ReadonlyArray<ICandidatePlan>,\n redirects: ReadonlyMap<string, IEdgeTarget>,\n scoped: ReadonlyArray<IScopedRecord>,\n byKey: ReadonlyMap<string, IMemoryRecord<unknown>>\n ): Promise<Result<ReadonlyArray<ICandidateEdge>>> {\n const relationCandidates: IRelationCandidate[] = writablePlans.map((plan) => ({\n candidate: plan.candidate,\n id: plan.refTarget\n }));\n const proposed: Result<ReadonlyArray<ICandidateEdge>> = await this._capture(\n () => this._relationExtractor.relate({ item, candidates: relationCandidates }),\n `ingest '${item.id}': relate`\n );\n if (proposed.isFailure()) {\n return proposed;\n }\n // Redirect BEFORE validation and BEFORE the cycle guard: an edge naming a\n // collapsed candidate must be judged — and persisted — against the record it\n // collapsed into, not against the address that no longer gets written.\n const edges: ReadonlyArray<ICandidateEdge> = MemoryIngestOrchestrator._redirectEdges(\n proposed.value,\n redirects\n );\n // refIds and the existing-record view (`byKey`, shared with the verdict path)\n // both key on the canonical scoped address, so a stem reused across scopes\n // never aliases.\n const refIds: ReadonlySet<string> = new Set<string>(\n writablePlans.map((plan) => edgeTargetKey(plan.refTarget))\n );\n const validation: Result<true> = this._validateEdges(item, edges, refIds, byKey);\n if (validation.isFailure()) {\n return fail(validation.message);\n }\n if (this._cycleGuard === 'reject') {\n const guard: Result<true> = assertNoCycles(\n MemoryIngestOrchestrator._existingEdges(scoped),\n edges.map((e) => ({ source: e.source, target: e.edge.target, type: e.edge.type }))\n );\n if (guard.isFailure()) {\n return fail(`ingest '${item.id}': ${guard.message}`);\n }\n }\n return succeed(edges);\n }\n\n /**\n * The stage-5 edge-target redirect map for this pass: every `duplicate-of`\n * candidate's OWN scoped address, mapped to the address of the record its\n * verdict collapsed it into (design note §3).\n *\n * @remarks\n * A `duplicate-of` verdict means \"this candidate IS that record\". The candidate\n * is therefore not written, and its address never becomes a live reference — so\n * a sibling edge built against it in the same pass would resolve to neither a\n * written candidate nor an existing record and would fail the WHOLE ingest item.\n * That is a second-order hazard of collapsing, not a defect in the edge: the\n * host related two candidates it was correctly told about, and one of them\n * turned out to already exist.\n *\n * Redirecting is the honest repair, and it is independent of {@link DedupScope}\n * — it applies equally to a `'content'` kind, where the collapse is exactly\n * right and the ingest still must not fail.\n *\n * The map cannot chain: {@link MemoryIngestOrchestrator._planFromVerdict}\n * requires every target-bearing verdict's target to already exist in the store\n * snapshot, so a redirect destination is always a persisted record and never\n * another collapsed candidate. One pass is sufficient by construction.\n */\n private static _collapseRedirects(plans: ReadonlyArray<ICandidatePlan>): ReadonlyMap<string, IEdgeTarget> {\n const redirects: Map<string, IEdgeTarget> = new Map<string, IEdgeTarget>();\n for (const plan of plans) {\n if (plan.verdict.verdict === 'duplicate-of') {\n redirects.set(edgeTargetKey(plan.refTarget), plan.verdict.target);\n }\n }\n return redirects;\n }\n\n /**\n * Rewrite each edge whose TARGET names a collapsed candidate to point at the\n * record that candidate collapsed into.\n *\n * @remarks\n * Only the target is redirected. An edge whose SOURCE is a collapsed candidate\n * stays untouched and is still rejected by {@link\n * MemoryIngestOrchestrator._validateEdges}: sources are the records an edge is\n * written ONTO, a collapsed candidate is never written, and the relation\n * extractor is only ever offered writable candidates as edge sources in the\n * first place. Silently relocating such an edge onto an existing record would\n * attribute a link the host never asked for — the loud failure is correct there.\n */\n private static _redirectEdges(\n edges: ReadonlyArray<ICandidateEdge>,\n redirects: ReadonlyMap<string, IEdgeTarget>\n ): ReadonlyArray<ICandidateEdge> {\n if (redirects.size === 0) {\n return edges;\n }\n return edges.map((e) => {\n const to: IEdgeTarget | undefined = redirects.get(edgeTargetKey(e.edge.target));\n return to === undefined ? e : { ...e, edge: { ...e.edge, target: to } };\n });\n }\n\n /**\n * Validate stage-5 edges: each `source` must be a candidate being written; each\n * `target` must resolve to a sibling candidate or an existing store record. All\n * matching is on the canonical scope-qualified address.\n */\n private _validateEdges(\n item: IIngestItem,\n edges: ReadonlyArray<ICandidateEdge>,\n refIds: ReadonlySet<string>,\n byKey: ReadonlyMap<string, IMemoryRecord<unknown>>\n ): Result<true> {\n return mapResults(\n edges.map((edge) => {\n if (!refIds.has(edgeTargetKey(edge.source))) {\n return fail(\n `ingest '${item.id}': edge source '${MemoryIngestOrchestrator._formatTarget(\n edge.source\n )}' is not a candidate being written`\n );\n }\n const targetKey: string = edgeTargetKey(edge.edge.target);\n if (!refIds.has(targetKey) && byKey.get(targetKey) === undefined) {\n return fail(\n `ingest '${item.id}': edge target '${MemoryIngestOrchestrator._formatTarget(\n edge.edge.target\n )}' resolves to neither a sibling candidate nor an existing record`\n );\n }\n return succeed(true);\n })\n ).onSuccess(() => succeed(true));\n }\n\n /** Stage 6 — stamp provenance + edges, admit through the store, record the outcome. */\n private async _loadCandidate(\n item: IIngestItem,\n plan: ICandidatePlan,\n allEdges: ReadonlyArray<ICandidateEdge>\n ): Promise<Result<IIngestedRecordResult>> {\n const refKey: string = edgeTargetKey(plan.refTarget);\n const myEdges: ReadonlyArray<ICandidateEdge> = allEdges.filter((e) => edgeTargetKey(e.source) === refKey);\n if (plan.verdict.verdict === 'duplicate-of') {\n // No write: the existing target satisfied the candidate. The target id is a\n // typed field of the narrowed `duplicate-of` verdict — no cast, no guard.\n return succeed({\n candidate: plan.candidate,\n resolution: plan.verdict,\n disposition: 'deduped',\n id: plan.verdict.target.id,\n edges: myEdges\n });\n }\n return this._buildRecord(item, plan, myEdges).thenOnSuccess(async (record) =>\n (await this._store.put(record))\n .withErrorFormat((msg) => `ingest '${item.id}': write failed: ${msg}`)\n .onSuccess((persisted) => {\n const disposition: IngestDisposition = plan.verdict.verdict === 'merge-into' ? 'merged' : 'written';\n const interlock: 'temporal-versioned' | undefined =\n plan.writeAddress.isVersioned && myEdges.some((e) => e.edge.type === CONTRADICTS_LINK_TYPE)\n ? 'temporal-versioned'\n : undefined;\n return succeed({\n candidate: plan.candidate,\n resolution: plan.verdict,\n disposition,\n id: persisted.envelope.id,\n record: persisted,\n edges: myEdges,\n ...(interlock !== undefined ? { interlock } : {})\n });\n })\n );\n }\n\n /** Build the fully-stamped record to persist (provenance + edges + placeholder txn fields). */\n private _buildRecord(\n item: IIngestItem,\n plan: ICandidatePlan,\n myEdges: ReadonlyArray<ICandidateEdge>\n ): Result<IMemoryRecord<unknown>> {\n const base: Omit<IMemoryEnvelope, 'id' | 'seq' | 'contentHash' | 'created' | 'updated'> =\n plan.candidate.envelope;\n // For merge-into the store's `applyUpdate` REPLACES array fields wholesale, so\n // the persisted envelope must already carry the UNION of the target's existing\n // tags/links/provenance and the candidate's — otherwise the merge silently\n // wipes the target's prior links/tags.\n const target: IMemoryRecord<unknown> | undefined = plan.mergeTarget;\n const priorLinks: ReadonlyArray<IEdge> = target !== undefined ? target.envelope.links : [];\n const priorTags: ReadonlyArray<Tag> = target !== undefined ? target.envelope.tags : [];\n const priorProvenance: IProvenance = target !== undefined ? target.envelope.provenance : { source: '' };\n return Convert.memoryId.convert(plan.writeAddress.idStem).onSuccess((id) =>\n // Dedup the combined links by canonical edge key: the target's prior links, the\n // candidate's own links, and the stage-5 edges (two candidates that resolve\n // merge-into the SAME target share a `refId` — hence the same `myEdges` — and a\n // relation extractor may repeat an edge) each appear exactly once.\n this._dedupEdges([...priorLinks, ...base.links, ...myEdges.map((e) => e.edge)]).onSuccess((links) => {\n const provenance: IProvenance = {\n // Preserve the target's provenance extension keys, overlay the candidate's,\n // then stamp the host-ingest source + derivedFrom (target present only for\n // merge-into; otherwise the seed is inert).\n ...(target !== undefined ? priorProvenance : {}),\n ...base.provenance,\n source: HOST_INGEST_PROVENANCE_SOURCE,\n ...(item.sourceId !== undefined ? { derivedFrom: item.sourceId } : {})\n };\n const envelope: IMemoryEnvelope = {\n ...base,\n id,\n entityId: plan.writeEntityId,\n seq: 0,\n contentHash: '',\n created: 0,\n updated: 0,\n provenance,\n tags: MemoryIngestOrchestrator._unionTags(priorTags, base.tags),\n links\n };\n return succeed({ envelope, body: plan.candidate.body });\n })\n );\n }\n\n /** Union two tag lists, de-duplicated, preserving first-occurrence order. */\n private static _unionTags(prior: ReadonlyArray<Tag>, incoming: ReadonlyArray<Tag>): ReadonlyArray<Tag> {\n const seen: Set<string> = new Set<string>();\n const out: Tag[] = [];\n for (const tag of [...prior, ...incoming]) {\n if (!seen.has(tag)) {\n seen.add(tag);\n out.push(tag);\n }\n }\n return out;\n }\n\n /** De-duplicate a link list by canonical edge hash, preserving first-occurrence order. */\n private _dedupEdges(edges: ReadonlyArray<IEdge>): Result<ReadonlyArray<IEdge>> {\n const seen: Set<string> = new Set<string>();\n const deduped: IEdge[] = [];\n return mapResults(\n edges.map((edge) =>\n this._hasher.computeHash(edge).onSuccess((key) => {\n if (!seen.has(key)) {\n seen.add(key);\n deduped.push(edge);\n }\n return succeed(key);\n })\n )\n ).onSuccess(() => succeed(deduped));\n }\n\n /** Resolve a `(kind, entityId)` to its storage address via the registered codec. */\n private _resolveAddress(entityId: EntityId, kind: Kind): Result<IIdentityCodecResult> {\n const codec: IIdentityCodec | undefined = this._codecs.get(kind) ?? this._defaultCodec;\n if (codec === undefined) {\n return fail(`no identity codec registered for kind '${kind}'`);\n }\n return codec.encode(entityId);\n }\n\n /** Run a host hook, normalizing a thrown/rejected hook into a Failure (never throws across the seam). */\n private async _capture<T>(op: () => Promise<Result<T>>, label: string): Promise<Result<T>> {\n try {\n return await op();\n } catch (err) {\n const message: string = `${label} threw: ${String(err)}`;\n this._logger.warn(message);\n return fail(message);\n }\n }\n\n /**\n * Every existing outbound edge in the snapshot, as cycle-guard edges. The\n * source is the record's own scope-qualified address (already resolved by\n * {@link MemoryIngestOrchestrator._scopeRecords}); the target is the edge's\n * own scoped target. Both ends are scoped so the guard never conflates a stem\n * shared across scopes into one graph node.\n */\n private static _existingEdges(scoped: ReadonlyArray<IScopedRecord>): ReadonlyArray<ICycleGuardEdge> {\n const edges: ICycleGuardEdge[] = [];\n for (const s of scoped) {\n for (const edge of s.record.envelope.links) {\n edges.push({ source: s.address, target: edge.target, type: edge.type });\n }\n }\n return edges;\n }\n\n /**\n * Resolve every snapshot record's scope-qualified `(scope, id)` address via its\n * registered codec. Fails loudly if a record's kind has no resolvable codec —\n * the edge path cannot place an un-scopeable record in the graph, and a missing\n * codec for a stored kind is a real misconfiguration, not something to paper over.\n */\n private _scopeRecords(\n records: ReadonlyArray<IMemoryRecord<unknown>>\n ): Result<ReadonlyArray<IScopedRecord>> {\n return mapResults(\n records.map((record) =>\n this._resolveAddress(record.envelope.entityId, record.envelope.kind)\n .withErrorFormat((msg) => `cannot resolve scope for stored record '${record.envelope.id}': ${msg}`)\n .onSuccess((addr) => succeed({ address: { scope: addr.scope, id: record.envelope.id }, record }))\n )\n );\n }\n\n /** Human-readable `scope/id` rendering of a scoped target for edge-validation diagnostics. */\n private static _formatTarget(target: IEdgeTarget): string {\n return `${target.scope}/${target.id}`;\n }\n\n /** A provisional record for embedding a candidate (placeholder txn-time fields). */\n private static _provisionalRecord(\n candidate: ICandidateRecord,\n idStem: string,\n body: string\n ): Result<IMemoryRecord<unknown>> {\n return Convert.memoryId.convert(idStem).onSuccess((id) => {\n const envelope: IMemoryEnvelope = {\n ...candidate.envelope,\n id,\n seq: 0,\n contentHash: '',\n created: 0,\n updated: 0\n };\n return succeed({ envelope, body });\n });\n }\n\n /** Require a candidate body to be a string (the store persists only string bodies). */\n private static _asStringBody(item: IIngestItem, candidate: ICandidateRecord): Result<string> {\n if (typeof candidate.body !== 'string') {\n return fail(\n `ingest '${item.id}': candidate body for kind '${\n candidate.envelope.kind\n }' must be a string (got ${typeof candidate.body})`\n );\n }\n return succeed(candidate.body);\n }\n}\n"]}