@fgv/ts-agent-memory 5.1.0-39 → 5.1.0-41

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 (210) hide show
  1. package/.rush/temp/{b82cf6bdece20481260e6bab946179eeec9d7b46.tar.log → cbbdbe09515171b4eba8f2592be2dace1e4e8142.tar.log} +14 -2
  2. package/.rush/temp/chunked-rush-logs/ts-agent-memory.build.chunks.jsonl +2 -2
  3. package/.rush/temp/operation/build/all.log +2 -2
  4. package/.rush/temp/operation/build/log-chunks.jsonl +2 -2
  5. package/.rush/temp/operation/build/state.json +1 -1
  6. package/dist/packlets/converters/envelopeConverter.js +17 -3
  7. package/dist/packlets/converters/envelopeConverter.js.map +1 -1
  8. package/dist/packlets/index/memoryIndex.js +58 -10
  9. package/dist/packlets/index/memoryIndex.js.map +1 -1
  10. package/dist/packlets/ingest/cycleGuard.js +13 -6
  11. package/dist/packlets/ingest/cycleGuard.js.map +1 -1
  12. package/dist/packlets/ingest/hostStages.js.map +1 -1
  13. package/dist/packlets/ingest/model.js.map +1 -1
  14. package/dist/packlets/ingest/orchestrator.js +94 -49
  15. package/dist/packlets/ingest/orchestrator.js.map +1 -1
  16. package/dist/packlets/retrieve/hybridRetriever.js +13 -2
  17. package/dist/packlets/retrieve/hybridRetriever.js.map +1 -1
  18. package/dist/packlets/retrieve/linkTraversalRetriever.js +46 -57
  19. package/dist/packlets/retrieve/linkTraversalRetriever.js.map +1 -1
  20. package/dist/packlets/retrieve/recencyRetriever.js +3 -3
  21. package/dist/packlets/retrieve/recencyRetriever.js.map +1 -1
  22. package/dist/packlets/retrieve/retriever.js +50 -7
  23. package/dist/packlets/retrieve/retriever.js.map +1 -1
  24. package/dist/packlets/retrieve/semanticRetriever.js +9 -3
  25. package/dist/packlets/retrieve/semanticRetriever.js.map +1 -1
  26. package/dist/packlets/retrieve/structuredFilterRetriever.js +3 -3
  27. package/dist/packlets/retrieve/structuredFilterRetriever.js.map +1 -1
  28. package/dist/packlets/retrieve/tagRetriever.js +3 -3
  29. package/dist/packlets/retrieve/tagRetriever.js.map +1 -1
  30. package/dist/packlets/retrieve/temporalRetrievers.js +3 -3
  31. package/dist/packlets/retrieve/temporalRetrievers.js.map +1 -1
  32. package/dist/packlets/store/fileTreeMemoryStore.js +72 -16
  33. package/dist/packlets/store/fileTreeMemoryStore.js.map +1 -1
  34. package/dist/packlets/tools/memoryTools.js +104 -21
  35. package/dist/packlets/tools/memoryTools.js.map +1 -1
  36. package/dist/packlets/types/envelope.js +13 -1
  37. package/dist/packlets/types/envelope.js.map +1 -1
  38. package/dist/packlets/vector/inMemoryCosineIndex.js +22 -17
  39. package/dist/packlets/vector/inMemoryCosineIndex.js.map +1 -1
  40. package/dist/packlets/vector/vectorIndex.js.map +1 -1
  41. package/dist/test/unit/converters/antagonistRoundTrip.test.js +3 -3
  42. package/dist/test/unit/converters/antagonistRoundTrip.test.js.map +1 -1
  43. package/dist/test/unit/converters/envelopeConverter.test.js +125 -8
  44. package/dist/test/unit/converters/envelopeConverter.test.js.map +1 -1
  45. package/dist/test/unit/index/memoryIndex.test.js +87 -25
  46. package/dist/test/unit/index/memoryIndex.test.js.map +1 -1
  47. package/dist/test/unit/ingest/antagonistCycleAndParity.test.js +20 -16
  48. package/dist/test/unit/ingest/antagonistCycleAndParity.test.js.map +1 -1
  49. package/dist/test/unit/ingest/cycleGuard.test.js +28 -1
  50. package/dist/test/unit/ingest/cycleGuard.test.js.map +1 -1
  51. package/dist/test/unit/ingest/orchestrator.test.js +187 -45
  52. package/dist/test/unit/ingest/orchestrator.test.js.map +1 -1
  53. package/dist/test/unit/retrieve/linkTraversalRetriever.test.js +106 -31
  54. package/dist/test/unit/retrieve/linkTraversalRetriever.test.js.map +1 -1
  55. package/dist/test/unit/retrieve/retrievers.test.js +299 -37
  56. package/dist/test/unit/retrieve/retrievers.test.js.map +1 -1
  57. package/dist/test/unit/store/embedOnWrite.test.js +69 -12
  58. package/dist/test/unit/store/embedOnWrite.test.js.map +1 -1
  59. package/dist/test/unit/store/listScoped.test.js +109 -0
  60. package/dist/test/unit/store/listScoped.test.js.map +1 -0
  61. package/dist/test/unit/store/rankAxis.test.js +254 -0
  62. package/dist/test/unit/store/rankAxis.test.js.map +1 -0
  63. package/dist/test/unit/tools/memoryTools.test.js +280 -11
  64. package/dist/test/unit/tools/memoryTools.test.js.map +1 -1
  65. package/dist/test/unit/types/writePolicy.test.js +9 -2
  66. package/dist/test/unit/types/writePolicy.test.js.map +1 -1
  67. package/dist/test/unit/vector/inMemoryCosineIndex.test.js +95 -35
  68. package/dist/test/unit/vector/inMemoryCosineIndex.test.js.map +1 -1
  69. package/dist/test/unit/vector/vectorIndex.test.js +24 -15
  70. package/dist/test/unit/vector/vectorIndex.test.js.map +1 -1
  71. package/dist/ts-agent-memory.d.ts +384 -97
  72. package/etc/ts-agent-memory.api.md +64 -22
  73. package/lib/packlets/converters/envelopeConverter.d.ts +8 -1
  74. package/lib/packlets/converters/envelopeConverter.d.ts.map +1 -1
  75. package/lib/packlets/converters/envelopeConverter.js +18 -4
  76. package/lib/packlets/converters/envelopeConverter.js.map +1 -1
  77. package/lib/packlets/index/memoryIndex.d.ts +42 -10
  78. package/lib/packlets/index/memoryIndex.d.ts.map +1 -1
  79. package/lib/packlets/index/memoryIndex.js +58 -10
  80. package/lib/packlets/index/memoryIndex.js.map +1 -1
  81. package/lib/packlets/ingest/cycleGuard.d.ts +5 -5
  82. package/lib/packlets/ingest/cycleGuard.d.ts.map +1 -1
  83. package/lib/packlets/ingest/cycleGuard.js +13 -6
  84. package/lib/packlets/ingest/cycleGuard.js.map +1 -1
  85. package/lib/packlets/ingest/hostStages.d.ts +3 -3
  86. package/lib/packlets/ingest/hostStages.d.ts.map +1 -1
  87. package/lib/packlets/ingest/hostStages.js.map +1 -1
  88. package/lib/packlets/ingest/model.d.ts +25 -14
  89. package/lib/packlets/ingest/model.d.ts.map +1 -1
  90. package/lib/packlets/ingest/model.js.map +1 -1
  91. package/lib/packlets/ingest/orchestrator.d.ts +18 -4
  92. package/lib/packlets/ingest/orchestrator.d.ts.map +1 -1
  93. package/lib/packlets/ingest/orchestrator.js +93 -48
  94. package/lib/packlets/ingest/orchestrator.js.map +1 -1
  95. package/lib/packlets/retrieve/hybridRetriever.d.ts.map +1 -1
  96. package/lib/packlets/retrieve/hybridRetriever.js +12 -1
  97. package/lib/packlets/retrieve/hybridRetriever.js.map +1 -1
  98. package/lib/packlets/retrieve/linkTraversalRetriever.d.ts +18 -23
  99. package/lib/packlets/retrieve/linkTraversalRetriever.d.ts.map +1 -1
  100. package/lib/packlets/retrieve/linkTraversalRetriever.js +45 -56
  101. package/lib/packlets/retrieve/linkTraversalRetriever.js.map +1 -1
  102. package/lib/packlets/retrieve/recencyRetriever.js +2 -2
  103. package/lib/packlets/retrieve/recencyRetriever.js.map +1 -1
  104. package/lib/packlets/retrieve/retriever.d.ts +70 -11
  105. package/lib/packlets/retrieve/retriever.d.ts.map +1 -1
  106. package/lib/packlets/retrieve/retriever.js +52 -7
  107. package/lib/packlets/retrieve/retriever.js.map +1 -1
  108. package/lib/packlets/retrieve/semanticRetriever.d.ts.map +1 -1
  109. package/lib/packlets/retrieve/semanticRetriever.js +9 -3
  110. package/lib/packlets/retrieve/semanticRetriever.js.map +1 -1
  111. package/lib/packlets/retrieve/structuredFilterRetriever.js +2 -2
  112. package/lib/packlets/retrieve/structuredFilterRetriever.js.map +1 -1
  113. package/lib/packlets/retrieve/tagRetriever.js +2 -2
  114. package/lib/packlets/retrieve/tagRetriever.js.map +1 -1
  115. package/lib/packlets/retrieve/temporalRetrievers.js +3 -3
  116. package/lib/packlets/retrieve/temporalRetrievers.js.map +1 -1
  117. package/lib/packlets/store/fileTreeMemoryStore.d.ts +57 -3
  118. package/lib/packlets/store/fileTreeMemoryStore.d.ts.map +1 -1
  119. package/lib/packlets/store/fileTreeMemoryStore.js +72 -16
  120. package/lib/packlets/store/fileTreeMemoryStore.js.map +1 -1
  121. package/lib/packlets/tools/memoryTools.d.ts +24 -0
  122. package/lib/packlets/tools/memoryTools.d.ts.map +1 -1
  123. package/lib/packlets/tools/memoryTools.js +104 -21
  124. package/lib/packlets/tools/memoryTools.js.map +1 -1
  125. package/lib/packlets/types/envelope.d.ts +61 -8
  126. package/lib/packlets/types/envelope.d.ts.map +1 -1
  127. package/lib/packlets/types/envelope.js +14 -0
  128. package/lib/packlets/types/envelope.js.map +1 -1
  129. package/lib/packlets/vector/inMemoryCosineIndex.d.ts +9 -5
  130. package/lib/packlets/vector/inMemoryCosineIndex.d.ts.map +1 -1
  131. package/lib/packlets/vector/inMemoryCosineIndex.js +22 -17
  132. package/lib/packlets/vector/inMemoryCosineIndex.js.map +1 -1
  133. package/lib/packlets/vector/vectorIndex.d.ts +46 -20
  134. package/lib/packlets/vector/vectorIndex.d.ts.map +1 -1
  135. package/lib/packlets/vector/vectorIndex.js.map +1 -1
  136. package/lib/test/unit/converters/antagonistRoundTrip.test.js +3 -3
  137. package/lib/test/unit/converters/antagonistRoundTrip.test.js.map +1 -1
  138. package/lib/test/unit/converters/envelopeConverter.test.js +124 -7
  139. package/lib/test/unit/converters/envelopeConverter.test.js.map +1 -1
  140. package/lib/test/unit/index/memoryIndex.test.js +86 -24
  141. package/lib/test/unit/index/memoryIndex.test.js.map +1 -1
  142. package/lib/test/unit/ingest/antagonistCycleAndParity.test.js +20 -16
  143. package/lib/test/unit/ingest/antagonistCycleAndParity.test.js.map +1 -1
  144. package/lib/test/unit/ingest/cycleGuard.test.js +28 -1
  145. package/lib/test/unit/ingest/cycleGuard.test.js.map +1 -1
  146. package/lib/test/unit/ingest/orchestrator.test.js +186 -44
  147. package/lib/test/unit/ingest/orchestrator.test.js.map +1 -1
  148. package/lib/test/unit/retrieve/linkTraversalRetriever.test.js +106 -31
  149. package/lib/test/unit/retrieve/linkTraversalRetriever.test.js.map +1 -1
  150. package/lib/test/unit/retrieve/retrievers.test.js +298 -36
  151. package/lib/test/unit/retrieve/retrievers.test.js.map +1 -1
  152. package/lib/test/unit/store/embedOnWrite.test.js +68 -11
  153. package/lib/test/unit/store/embedOnWrite.test.js.map +1 -1
  154. package/lib/test/unit/store/listScoped.test.d.ts +2 -0
  155. package/lib/test/unit/store/listScoped.test.d.ts.map +1 -0
  156. package/lib/test/unit/store/listScoped.test.js +111 -0
  157. package/lib/test/unit/store/listScoped.test.js.map +1 -0
  158. package/lib/test/unit/store/rankAxis.test.d.ts +2 -0
  159. package/lib/test/unit/store/rankAxis.test.d.ts.map +1 -0
  160. package/lib/test/unit/store/rankAxis.test.js +256 -0
  161. package/lib/test/unit/store/rankAxis.test.js.map +1 -0
  162. package/lib/test/unit/tools/memoryTools.test.js +280 -11
  163. package/lib/test/unit/tools/memoryTools.test.js.map +1 -1
  164. package/lib/test/unit/types/writePolicy.test.js +9 -2
  165. package/lib/test/unit/types/writePolicy.test.js.map +1 -1
  166. package/lib/test/unit/vector/inMemoryCosineIndex.test.js +95 -35
  167. package/lib/test/unit/vector/inMemoryCosineIndex.test.js.map +1 -1
  168. package/lib/test/unit/vector/vectorIndex.test.js +24 -15
  169. package/lib/test/unit/vector/vectorIndex.test.js.map +1 -1
  170. package/package.json +7 -7
  171. package/rush-logs/ts-agent-memory.build.cache.log +1 -1
  172. package/rush-logs/ts-agent-memory.build.log +2 -2
  173. package/src/packlets/converters/envelopeConverter.ts +27 -4
  174. package/src/packlets/index/memoryIndex.ts +86 -22
  175. package/src/packlets/ingest/cycleGuard.ts +22 -11
  176. package/src/packlets/ingest/hostStages.ts +3 -3
  177. package/src/packlets/ingest/model.ts +25 -14
  178. package/src/packlets/ingest/orchestrator.ts +143 -67
  179. package/src/packlets/retrieve/hybridRetriever.ts +14 -1
  180. package/src/packlets/retrieve/linkTraversalRetriever.ts +51 -62
  181. package/src/packlets/retrieve/recencyRetriever.ts +3 -3
  182. package/src/packlets/retrieve/retriever.ts +97 -13
  183. package/src/packlets/retrieve/semanticRetriever.ts +10 -5
  184. package/src/packlets/retrieve/structuredFilterRetriever.ts +3 -3
  185. package/src/packlets/retrieve/tagRetriever.ts +3 -3
  186. package/src/packlets/retrieve/temporalRetrievers.ts +3 -3
  187. package/src/packlets/store/fileTreeMemoryStore.ts +117 -12
  188. package/src/packlets/tools/memoryTools.ts +152 -25
  189. package/src/packlets/types/envelope.ts +66 -8
  190. package/src/packlets/vector/inMemoryCosineIndex.ts +45 -22
  191. package/src/packlets/vector/vectorIndex.ts +47 -20
  192. package/src/test/unit/converters/antagonistRoundTrip.test.ts +3 -3
  193. package/src/test/unit/converters/envelopeConverter.test.ts +168 -11
  194. package/src/test/unit/index/memoryIndex.test.ts +99 -14
  195. package/src/test/unit/ingest/antagonistCycleAndParity.test.ts +23 -18
  196. package/src/test/unit/ingest/cycleGuard.test.ts +44 -2
  197. package/src/test/unit/ingest/orchestrator.test.ts +234 -41
  198. package/src/test/unit/retrieve/linkTraversalRetriever.test.ts +134 -35
  199. package/src/test/unit/retrieve/retrievers.test.ts +381 -25
  200. package/src/test/unit/store/embedOnWrite.test.ts +83 -11
  201. package/src/test/unit/store/listScoped.test.ts +138 -0
  202. package/src/test/unit/store/rankAxis.test.ts +349 -0
  203. package/src/test/unit/tools/memoryTools.test.ts +362 -13
  204. package/src/test/unit/types/writePolicy.test.ts +11 -2
  205. package/src/test/unit/vector/inMemoryCosineIndex.test.ts +115 -39
  206. package/src/test/unit/vector/vectorIndex.test.ts +33 -17
  207. package/temp/build/lint/_eslint-5eVG3S6w.json +41 -33
  208. package/temp/build/typescript/ts_8nwakTlr.json +1 -1
  209. package/temp/ts-agent-memory.api.json +1080 -136
  210. package/temp/ts-agent-memory.api.md +64 -22
@@ -1 +1 @@
1
- {"version":3,"file":"cycleGuard.test.js","sourceRoot":"","sources":["../../../../src/test/unit/ingest/cycleGuard.test.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright (c) 2026 Erik Fortune\n * SPDX-License-Identifier: MIT\n */\n\nimport '@fgv/ts-utils-jest';\nimport { ICycleGuardEdge, LinkType, MemoryId, assertNoCycles, buildCycleKey } from '../../../index';\n\nconst rel: LinkType = 'rel' as LinkType;\n\nfunction edge(source: string, target: string, type: LinkType = rel): ICycleGuardEdge {\n return { source: source as MemoryId, target: target as MemoryId, type };\n}\n\ndescribe('buildCycleKey', () => {\n test('is deterministic for the same edge', () => {\n expect(buildCycleKey(edge('a', 'b'))).toSucceedAndSatisfy((key: string) => {\n expect(buildCycleKey(edge('a', 'b'))).toSucceedWith(key);\n });\n });\n\n test('differs by direction and type', () => {\n const forward = buildCycleKey(edge('a', 'b')).orThrow();\n const reverse = buildCycleKey(edge('b', 'a')).orThrow();\n const typed = buildCycleKey(edge('a', 'b', 'other' as LinkType)).orThrow();\n expect(forward).not.toBe(reverse);\n expect(forward).not.toBe(typed);\n });\n});\n\ndescribe('assertNoCycles', () => {\n test('accepts an acyclic graph', () => {\n expect(assertNoCycles([edge('a', 'b')], [edge('b', 'c'), edge('c', 'd')])).toSucceed();\n });\n\n test('rejects a proposed edge that closes a cycle against existing edges', () => {\n expect(assertNoCycles([edge('a', 'b'), edge('b', 'c')], [edge('c', 'a')])).toFailWith(\n /would create a cycle/\n );\n });\n\n test('rejects a cycle formed entirely within the proposed batch', () => {\n expect(assertNoCycles([], [edge('a', 'b'), edge('b', 'a')])).toFailWith(/would create a cycle/);\n });\n\n test('rejects a self-loop', () => {\n expect(assertNoCycles([], [edge('a', 'a')])).toFailWith(/self-loop/);\n });\n\n test('tolerates a duplicate proposed edge (deduped by canonical key)', () => {\n expect(assertNoCycles([], [edge('a', 'b'), edge('a', 'b')])).toSucceed();\n });\n\n test('tolerates a duplicate existing edge', () => {\n expect(assertNoCycles([edge('a', 'b'), edge('a', 'b')], [edge('b', 'c')])).toSucceed();\n });\n\n test('accepts a diamond (shared descendant is not a cycle)', () => {\n expect(assertNoCycles([], [edge('a', 'b'), edge('a', 'c'), edge('b', 'd'), edge('c', 'd')])).toSucceed();\n });\n\n test('reachability tolerates a frontier node reached by two paths (revisit guard)', () => {\n // Existing graph: a->c, a->b, b->c. A proposed edge z->a runs reachability\n // from `a`, whose DFS pushes `c` twice before it is visited — exercising the\n // visited-node skip in the traversal.\n expect(assertNoCycles([edge('a', 'c'), edge('a', 'b'), edge('b', 'c')], [edge('z', 'a')])).toSucceed();\n });\n});\n"]}
1
+ {"version":3,"file":"cycleGuard.test.js","sourceRoot":"","sources":["../../../../src/test/unit/ingest/cycleGuard.test.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright (c) 2026 Erik Fortune\n * SPDX-License-Identifier: MIT\n */\n\nimport '@fgv/ts-utils-jest';\nimport {\n ICycleGuardEdge,\n IEdgeTarget,\n LinkType,\n MemoryId,\n MemoryScopeKey,\n assertNoCycles,\n buildCycleKey\n} from '../../../index';\n\nconst rel: LinkType = 'rel' as LinkType;\nconst DEFAULT_SCOPE: string = 'exp';\n\n/** A scope-qualified node, `scope#id` or a bare id defaulting to {@link DEFAULT_SCOPE}. */\nfunction node(spec: string): IEdgeTarget {\n const hash: number = spec.indexOf('#');\n const scope: string = hash >= 0 ? spec.slice(0, hash) : DEFAULT_SCOPE;\n const id: string = hash >= 0 ? spec.slice(hash + 1) : spec;\n return { scope: scope as MemoryScopeKey, id: id as MemoryId };\n}\n\nfunction edge(source: string, target: string, type: LinkType = rel): ICycleGuardEdge {\n return { source: node(source), target: node(target), type };\n}\n\ndescribe('buildCycleKey', () => {\n test('is deterministic for the same edge', () => {\n expect(buildCycleKey(edge('a', 'b'))).toSucceedAndSatisfy((key: string) => {\n expect(buildCycleKey(edge('a', 'b'))).toSucceedWith(key);\n });\n });\n\n test('differs by direction and type', () => {\n const forward = buildCycleKey(edge('a', 'b')).orThrow();\n const reverse = buildCycleKey(edge('b', 'a')).orThrow();\n const typed = buildCycleKey(edge('a', 'b', 'other' as LinkType)).orThrow();\n expect(forward).not.toBe(reverse);\n expect(forward).not.toBe(typed);\n });\n});\n\ndescribe('assertNoCycles', () => {\n test('accepts an acyclic graph', () => {\n expect(assertNoCycles([edge('a', 'b')], [edge('b', 'c'), edge('c', 'd')])).toSucceed();\n });\n\n test('rejects a proposed edge that closes a cycle against existing edges', () => {\n expect(assertNoCycles([edge('a', 'b'), edge('b', 'c')], [edge('c', 'a')])).toFailWith(\n /would create a cycle/\n );\n });\n\n test('rejects a cycle formed entirely within the proposed batch', () => {\n expect(assertNoCycles([], [edge('a', 'b'), edge('b', 'a')])).toFailWith(/would create a cycle/);\n });\n\n test('rejects a self-loop', () => {\n expect(assertNoCycles([], [edge('a', 'a')])).toFailWith(/self-loop/);\n });\n\n test('tolerates a duplicate proposed edge (deduped by canonical key)', () => {\n expect(assertNoCycles([], [edge('a', 'b'), edge('a', 'b')])).toSucceed();\n });\n\n test('tolerates a duplicate existing edge', () => {\n expect(assertNoCycles([edge('a', 'b'), edge('a', 'b')], [edge('b', 'c')])).toSucceed();\n });\n\n test('accepts a diamond (shared descendant is not a cycle)', () => {\n expect(assertNoCycles([], [edge('a', 'b'), edge('a', 'c'), edge('b', 'd'), edge('c', 'd')])).toSucceed();\n });\n\n test('reachability tolerates a frontier node reached by two paths (revisit guard)', () => {\n // Existing graph: a->c, a->b, b->c. A proposed edge z->a runs reachability\n // from `a`, whose DFS pushes `c` twice before it is visited — exercising the\n // visited-node skip in the traversal.\n expect(assertNoCycles([edge('a', 'c'), edge('a', 'b'), edge('b', 'c')], [edge('z', 'a')])).toSucceed();\n });\n\n describe('scope-qualified nodes', () => {\n test('does NOT conflate a stem shared across scopes into one node', () => {\n // ca#x -> cb#y and cb#x -> ca#z reuse the stem `x` across scopes ca/cb.\n // If the guard keyed on bare id, the two `x` nodes would merge and a false\n // cycle could appear; with scoped keys they are distinct and the graph is acyclic.\n expect(assertNoCycles([], [edge('ca#x', 'cb#y'), edge('cb#x', 'ca#z')])).toSucceed();\n });\n\n test('still rejects a genuine within-scope cycle across the same stems', () => {\n expect(assertNoCycles([], [edge('ca#x', 'ca#y'), edge('ca#y', 'ca#x')])).toFailWith(\n /would create a cycle/\n );\n });\n\n test('a self-loop is detected on the full scoped identity', () => {\n expect(assertNoCycles([], [edge('ca#x', 'ca#x')])).toFailWith(/self-loop/);\n // Same stem, different scope is NOT a self-loop.\n expect(assertNoCycles([], [edge('ca#x', 'cb#x')])).toSucceed();\n });\n\n test('names the scope in the diagnostic', () => {\n expect(assertNoCycles([], [edge('ca#x', 'ca#x')])).toFailWith(/ca\\/x/);\n });\n });\n});\n"]}
@@ -5,7 +5,7 @@
5
5
  import '@fgv/ts-utils-jest';
6
6
  import { Converters, Logging, fail, succeed } from '@fgv/ts-utils';
7
7
  import { FileTree } from '@fgv/ts-json-base';
8
- import { BodyConverterRegistry, CONTRADICTS_LINK_TYPE, DEFAULT_SIMILARITY_THRESHOLD, DEFAULT_SIMILARITY_TOP_K, FileTreeMemoryStore, HOST_INGEST_PROVENANCE_SOURCE, InMemoryCosineIndex, KnowledgeIdentityCodec, LtmIdentityCodec, MemoryIngestOrchestrator, TemporalIdentityCodec, TemporalVersionedPolicy, serializeMemoryFile } from '../../../index';
8
+ import { BodyConverterRegistry, CONTRADICTS_LINK_TYPE, DEFAULT_SIMILARITY_THRESHOLD, DEFAULT_SIMILARITY_TOP_K, FileTreeMemoryStore, HOST_INGEST_PROVENANCE_SOURCE, InMemoryCosineIndex, KnowledgeIdentityCodec, LtmIdentityCodec, MtmIdentityCodec, MemoryIngestOrchestrator, TemporalIdentityCodec, TemporalVersionedPolicy, serializeMemoryFile } from '../../../index';
9
9
  // --- kinds --------------------------------------------------------------------
10
10
  const noteKind = 'note';
11
11
  const factKind = 'fact';
@@ -89,10 +89,32 @@ function candidate(kind, entityId, body, opts = {}) {
89
89
  body
90
90
  };
91
91
  }
92
+ /**
93
+ * A scope-qualified target, authored as `scope#id` or a bare id defaulting to the
94
+ * knowledge scope (where the note/num fixtures live). `kt` is the assertion-side
95
+ * spelling of the same helper.
96
+ */
97
+ function scopedTarget(spec) {
98
+ const hash = spec.indexOf('#');
99
+ const scope = hash >= 0 ? spec.slice(0, hash) : 'knowledge';
100
+ const id = hash >= 0 ? spec.slice(hash + 1) : spec;
101
+ return { scope: scope, id: id };
102
+ }
103
+ function kt(id, scope = 'knowledge') {
104
+ return { scope: scope, id: id };
105
+ }
106
+ /** The scope-qualified reference of a candidate about to be written, by its entityId. */
107
+ function srcOf(ctx, entityId) {
108
+ const found = ctx.candidates.find((rc) => rc.candidate.envelope.entityId === entityId);
109
+ if (found === undefined) {
110
+ throw new Error(`test relater: no candidate with entityId '${entityId}'`);
111
+ }
112
+ return found.id;
113
+ }
92
114
  function candEdge(source, type, target, confidence) {
93
115
  return {
94
- source: source,
95
- edge: Object.assign({ type: type, target: target }, (confidence !== undefined ? { confidence } : {}))
116
+ source: scopedTarget(source),
117
+ edge: Object.assign({ type: type, target: scopedTarget(target) }, (confidence !== undefined ? { confidence } : {}))
96
118
  };
97
119
  }
98
120
  function buildStore(params = {}) {
@@ -163,7 +185,10 @@ describe('MemoryIngestOrchestrator', () => {
163
185
  test('classifies, extracts, and writes one record end-to-end with host-ingest provenance + derivedFrom', async () => {
164
186
  const store = buildStore();
165
187
  const orch = buildOrchestrator({ store });
166
- const sourceId = 'turn-3';
188
+ const sourceId = {
189
+ scope: 'conversations/c1',
190
+ id: 'turn-3'
191
+ };
167
192
  const result = await orch.ingestItem({ id: 'doc-1', content: 'the sky is blue', sourceId });
168
193
  expect(result).toSucceedAndSatisfy((r) => {
169
194
  var _a, _b, _c;
@@ -174,7 +199,7 @@ describe('MemoryIngestOrchestrator', () => {
174
199
  expect(rec.resolution.verdict).toBe('new');
175
200
  expect(rec.id).toBe('doc-1');
176
201
  expect((_a = rec.record) === null || _a === void 0 ? void 0 : _a.envelope.provenance.source).toBe(HOST_INGEST_PROVENANCE_SOURCE);
177
- expect((_b = rec.record) === null || _b === void 0 ? void 0 : _b.envelope.provenance.derivedFrom).toBe('turn-3');
202
+ expect((_b = rec.record) === null || _b === void 0 ? void 0 : _b.envelope.provenance.derivedFrom).toEqual(sourceId);
178
203
  expect((_c = rec.record) === null || _c === void 0 ? void 0 : _c.body).toBe('the sky is blue');
179
204
  expect(rec.interlock).toBeUndefined();
180
205
  });
@@ -249,6 +274,25 @@ describe('MemoryIngestOrchestrator', () => {
249
274
  });
250
275
  expect(await orch.ingestItem({ id: 'o-1', content: 'x' })).toFailWith(/no identity codec/);
251
276
  });
277
+ test('fails loudly when a pre-existing snapshot record cannot be scoped (no codec for its kind)', async () => {
278
+ // The store can persist a note (it has the note codec), but the orchestrator
279
+ // below is wired WITHOUT a note codec — so when it snapshots the store to run
280
+ // the edge path, it cannot resolve the seeded note's scope and fails loudly
281
+ // (never silently drops it from the cycle graph). This is the branch the
282
+ // scope-qualified edge change added.
283
+ const store = buildStore();
284
+ await putNote(store, 'legacy', 'anchor');
285
+ const orch = MemoryIngestOrchestrator.create({
286
+ store,
287
+ registry: registry(),
288
+ // Deliberately omit the note codec (and any default) that the seeded record needs.
289
+ codecs: new Map([[numKind, new KnowledgeIdentityCodec()]]),
290
+ classifier: classifier(() => succeed({ kind: numKind })),
291
+ extractor: extractor(() => succeed([candidate(numKind, 'n-1', 7)])),
292
+ relationExtractor: noEdges
293
+ }).orThrow();
294
+ expect(await orch.ingestItem({ id: 'i', content: 'x' })).toFailWith(/cannot resolve scope for stored record 'legacy'.*no identity codec registered for kind 'note'/i);
295
+ });
252
296
  });
253
297
  describe('stage 4 — exact dedup (layer 1)', () => {
254
298
  test('an exact {kind,body} match in scope is duplicate-of (no resolver)', async () => {
@@ -259,7 +303,7 @@ describe('MemoryIngestOrchestrator', () => {
259
303
  });
260
304
  expect(await orch.ingestItem({ id: 'i', content: 'x' })).toSucceedAndSatisfy((r) => {
261
305
  expect(r.records[0].disposition).toBe('deduped');
262
- expect(r.records[0].resolution).toEqual({ verdict: 'duplicate-of', target: 'doc-a' });
306
+ expect(r.records[0].resolution).toEqual({ verdict: 'duplicate-of', target: kt('doc-a') });
263
307
  expect(r.records[0].id).toBe('doc-a');
264
308
  });
265
309
  // doc-b was never written.
@@ -316,13 +360,17 @@ async function putNote(store, id, body) {
316
360
  (await store.put(rec)).orThrow();
317
361
  }
318
362
  function mockStore(overrides) {
319
- var _a, _b, _c, _d, _e;
363
+ var _a, _b, _c, _d, _e, _f, _g;
320
364
  return {
321
365
  get: (_a = overrides.get) !== null && _a !== void 0 ? _a : (() => Promise.resolve(succeed(undefined))),
322
366
  getById: (_b = overrides.getById) !== null && _b !== void 0 ? _b : (() => Promise.resolve(succeed(undefined))),
323
367
  list: (_c = overrides.list) !== null && _c !== void 0 ? _c : (() => Promise.resolve(succeed([]))),
324
- put: (_d = overrides.put) !== null && _d !== void 0 ? _d : ((r) => Promise.resolve(succeed(r))),
325
- delete: (_e = overrides.delete) !== null && _e !== void 0 ? _e : (() => Promise.resolve(fail('n/a')))
368
+ listScoped: (_d = overrides.listScoped) !== null && _d !== void 0 ? _d : (() => Promise.resolve(succeed([]))),
369
+ asRecordSource: (_e = overrides.asRecordSource) !== null && _e !== void 0 ? _e : (() => ({
370
+ list: () => Promise.resolve(succeed([]))
371
+ })),
372
+ put: (_f = overrides.put) !== null && _f !== void 0 ? _f : ((r) => Promise.resolve(succeed(r))),
373
+ delete: (_g = overrides.delete) !== null && _g !== void 0 ? _g : (() => Promise.resolve(fail('n/a')))
326
374
  };
327
375
  }
328
376
  describe('MemoryIngestOrchestrator — stage 4 similarity (layer 2)', () => {
@@ -350,7 +398,7 @@ describe('MemoryIngestOrchestrator — stage 4 similarity (layer 2)', () => {
350
398
  store,
351
399
  vectorIndex,
352
400
  embed,
353
- entityResolver: resolver((__c, similar) => succeed({ verdict: 'duplicate-of', target: similar[0].id })),
401
+ entityResolver: resolver((__c, similar) => succeed({ verdict: 'duplicate-of', target: similar[0].target })),
354
402
  extractor: extractor(() => succeed([candidate(noteKind, 'doc-b', 'apple tart')]))
355
403
  });
356
404
  expect(await orch.ingestItem({ id: 'i', content: 'x' })).toSucceedAndSatisfy((r) => {
@@ -367,12 +415,12 @@ describe('MemoryIngestOrchestrator — stage 4 similarity (layer 2)', () => {
367
415
  store,
368
416
  vectorIndex,
369
417
  embed,
370
- entityResolver: resolver((__c, similar) => succeed({ verdict: 'supersede', target: similar[0].id })),
418
+ entityResolver: resolver((__c, similar) => succeed({ verdict: 'supersede', target: similar[0].target })),
371
419
  extractor: extractor(() => succeed([candidate(noteKind, 'doc-b', 'apple tart')]))
372
420
  });
373
421
  expect(await orch.ingestItem({ id: 'i', content: 'x' })).toSucceedAndSatisfy((r) => {
374
422
  expect(r.records[0].disposition).toBe('written');
375
- expect(r.records[0].resolution).toEqual({ verdict: 'supersede', target: 'doc-a' });
423
+ expect(r.records[0].resolution).toEqual({ verdict: 'supersede', target: kt('doc-a') });
376
424
  expect(r.records[0].id).toBe('doc-b');
377
425
  });
378
426
  expect(await store.get(noteKind, 'doc-b')).toSucceedAndSatisfy((rec) => expect(rec === null || rec === void 0 ? void 0 : rec.body).toBe('apple tart'));
@@ -385,7 +433,7 @@ describe('MemoryIngestOrchestrator — stage 4 similarity (layer 2)', () => {
385
433
  store,
386
434
  vectorIndex,
387
435
  embed,
388
- entityResolver: resolver((__c, similar) => succeed({ verdict: 'merge-into', target: similar[0].id })),
436
+ entityResolver: resolver((__c, similar) => succeed({ verdict: 'merge-into', target: similar[0].target })),
389
437
  extractor: extractor(() => succeed([candidate(noteKind, 'doc-b', 'apple tart')]))
390
438
  });
391
439
  expect(await orch.ingestItem({ id: 'i', content: 'x' })).toSucceedAndSatisfy((r) => {
@@ -404,10 +452,10 @@ describe('MemoryIngestOrchestrator — stage 4 similarity (layer 2)', () => {
404
452
  store,
405
453
  vectorIndex,
406
454
  embed,
407
- entityResolver: resolver(() => succeed({ verdict: 'merge-into', target: 'ghost' })),
455
+ entityResolver: resolver(() => succeed({ verdict: 'merge-into', target: kt('ghost') })),
408
456
  extractor: extractor(() => succeed([candidate(noteKind, 'doc-b', 'apple tart')]))
409
457
  });
410
- expect(await orch.ingestItem({ id: 'i', content: 'x' })).toFailWith(/merge-into target 'ghost' does not exist/);
458
+ expect(await orch.ingestItem({ id: 'i', content: 'x' })).toFailWith(/merge-into target 'knowledge\/ghost' does not exist/);
411
459
  });
412
460
  test('a below-threshold neighbor is not surfaced (resolver not called)', async () => {
413
461
  const vectorIndex = InMemoryCosineIndex.create().orThrow();
@@ -444,9 +492,9 @@ describe('MemoryIngestOrchestrator — stage 4 similarity (layer 2)', () => {
444
492
  });
445
493
  test('a surfaced hit with no backing record is skipped', async () => {
446
494
  const ghostIndex = {
447
- add: (id) => Promise.resolve(succeed(id)),
448
- remove: (id) => Promise.resolve(succeed(id)),
449
- query: () => Promise.resolve(succeed([{ id: 'ghost', score: 0.99 }]))
495
+ add: (t) => Promise.resolve(succeed(t.id)),
496
+ remove: (t) => Promise.resolve(succeed(t)),
497
+ query: () => Promise.resolve(succeed([{ target: kt('ghost'), score: 0.99 }]))
450
498
  };
451
499
  const orch = buildOrchestrator({
452
500
  store: buildStore(),
@@ -472,8 +520,8 @@ describe('MemoryIngestOrchestrator — stage 4 similarity (layer 2)', () => {
472
520
  });
473
521
  test('a similarity-query failure surfaces loudly', async () => {
474
522
  const failingIndex = {
475
- add: (id) => Promise.resolve(succeed(id)),
476
- remove: (id) => Promise.resolve(succeed(id)),
523
+ add: (t) => Promise.resolve(succeed(t.id)),
524
+ remove: (t) => Promise.resolve(succeed(t)),
477
525
  query: () => Promise.resolve(fail('query kaput'))
478
526
  };
479
527
  const orch = buildOrchestrator({
@@ -545,7 +593,7 @@ describe('MemoryIngestOrchestrator — stage 5 relate + cycle guard', () => {
545
593
  var _a;
546
594
  expect(r.records[0].edges).toHaveLength(1);
547
595
  expect((_a = r.records[0].record) === null || _a === void 0 ? void 0 : _a.envelope.links).toEqual([
548
- { type: 'mentions', target: 'doc-a', confidence: 0.7 }
596
+ { type: 'mentions', target: kt('doc-a'), confidence: 0.7 }
549
597
  ]);
550
598
  });
551
599
  });
@@ -558,7 +606,7 @@ describe('MemoryIngestOrchestrator — stage 5 relate + cycle guard', () => {
558
606
  expect(await orch.ingestItem({ id: 'i', content: 'x' })).toSucceedAndSatisfy((r) => {
559
607
  var _a;
560
608
  expect(r.records).toHaveLength(2);
561
- expect((_a = r.records[0].record) === null || _a === void 0 ? void 0 : _a.envelope.links).toEqual([{ type: 'rel', target: 'doc-c' }]);
609
+ expect((_a = r.records[0].record) === null || _a === void 0 ? void 0 : _a.envelope.links).toEqual([{ type: 'rel', target: kt('doc-c') }]);
562
610
  expect(r.records[1].edges).toEqual([]);
563
611
  });
564
612
  });
@@ -568,7 +616,7 @@ describe('MemoryIngestOrchestrator — stage 5 relate + cycle guard', () => {
568
616
  extractor: extractor(() => succeed([candidate(noteKind, 'doc-b', 'x')])),
569
617
  relationExtractor: relater(() => succeed([candEdge('ghost', 'rel', 'doc-b')]))
570
618
  });
571
- expect(await orch.ingestItem({ id: 'i', content: 'x' })).toFailWith(/edge source 'ghost' is not a candidate being written/);
619
+ expect(await orch.ingestItem({ id: 'i', content: 'x' })).toFailWith(/edge source 'knowledge\/ghost' is not a candidate being written/);
572
620
  });
573
621
  test('rejects an edge whose target resolves to nothing', async () => {
574
622
  const orch = buildOrchestrator({
@@ -576,7 +624,7 @@ describe('MemoryIngestOrchestrator — stage 5 relate + cycle guard', () => {
576
624
  extractor: extractor(() => succeed([candidate(noteKind, 'doc-b', 'x')])),
577
625
  relationExtractor: relater(() => succeed([candEdge('doc-b', 'rel', 'ghost')]))
578
626
  });
579
- expect(await orch.ingestItem({ id: 'i', content: 'x' })).toFailWith(/edge target 'ghost' resolves to neither/);
627
+ expect(await orch.ingestItem({ id: 'i', content: 'x' })).toFailWith(/edge target 'knowledge\/ghost' resolves to neither/);
580
628
  });
581
629
  test('rejects a cycle-inducing edge (default guard)', async () => {
582
630
  const orch = buildOrchestrator({
@@ -595,8 +643,8 @@ describe('MemoryIngestOrchestrator — stage 5 relate + cycle guard', () => {
595
643
  });
596
644
  expect(await orch.ingestItem({ id: 'i', content: 'x' })).toSucceedAndSatisfy((r) => {
597
645
  var _a, _b;
598
- expect((_a = r.records[0].record) === null || _a === void 0 ? void 0 : _a.envelope.links).toEqual([{ type: 'rel', target: 'doc-c' }]);
599
- expect((_b = r.records[1].record) === null || _b === void 0 ? void 0 : _b.envelope.links).toEqual([{ type: 'rel', target: 'doc-b' }]);
646
+ expect((_a = r.records[0].record) === null || _a === void 0 ? void 0 : _a.envelope.links).toEqual([{ type: 'rel', target: kt('doc-c') }]);
647
+ expect((_b = r.records[1].record) === null || _b === void 0 ? void 0 : _b.envelope.links).toEqual([{ type: 'rel', target: kt('doc-b') }]);
600
648
  });
601
649
  });
602
650
  test('a contradicts edge on a NON-temporal kind persists as a plain link (no interlock)', async () => {
@@ -610,7 +658,89 @@ describe('MemoryIngestOrchestrator — stage 5 relate + cycle guard', () => {
610
658
  expect(await orch.ingestItem({ id: 'i', content: 'x' })).toSucceedAndSatisfy((r) => {
611
659
  var _a;
612
660
  expect(r.records[0].interlock).toBeUndefined();
613
- expect((_a = r.records[0].record) === null || _a === void 0 ? void 0 : _a.envelope.links).toEqual([{ type: 'contradicts', target: 'doc-a' }]);
661
+ expect((_a = r.records[0].record) === null || _a === void 0 ? void 0 : _a.envelope.links).toEqual([{ type: 'contradicts', target: kt('doc-a') }]);
662
+ });
663
+ });
664
+ describe('_validateEdges cross-scope target disambiguation', () => {
665
+ // Two EXISTING records share the idStem 'turn-3' across two conversations
666
+ // (the MTM codec mints per-scope stems). A stage-5 edge target must resolve
667
+ // to the CORRECT (scope, id) — with bare-id keying both would alias and a
668
+ // wrong-scope target would silently validate. Guards the ingest-validation seam.
669
+ const mtmKind = 'mtm';
670
+ function mtmRegistry() {
671
+ const reg = registry();
672
+ reg.register(mtmKind, Converters.string);
673
+ return reg;
674
+ }
675
+ const mtmCodecs = new Map([
676
+ [mtmKind, new MtmIdentityCodec()]
677
+ ]);
678
+ async function seedTurn(store, entityId, body) {
679
+ const addr = new MtmIdentityCodec().encode(entityId).orThrow();
680
+ const rec = {
681
+ envelope: {
682
+ id: addr.idStem,
683
+ entityId: entityId,
684
+ kind: mtmKind,
685
+ tags: [],
686
+ links: [],
687
+ created: 0,
688
+ updated: 0,
689
+ seq: 0,
690
+ contentHash: '',
691
+ provenance: { source: 'agent' }
692
+ },
693
+ body
694
+ };
695
+ (await store.put(rec)).orThrow();
696
+ }
697
+ function buildMtmOrchestrator(store, relationExtractor) {
698
+ return MemoryIngestOrchestrator.create({
699
+ store,
700
+ registry: mtmRegistry(),
701
+ codecs: mtmCodecs,
702
+ classifier: classifier(() => succeed({ kind: mtmKind })),
703
+ // The candidate is a fresh turn-5 under conv-a (distinct stem from the seeds).
704
+ extractor: extractor(() => succeed([candidate(mtmKind, 'conv-a:5', 'a new turn')])),
705
+ relationExtractor
706
+ }).orThrow();
707
+ }
708
+ async function seededStore() {
709
+ const store = FileTreeMemoryStore.create({
710
+ root: mutableRoot(),
711
+ registry: mtmRegistry(),
712
+ codecs: mtmCodecs,
713
+ clock
714
+ }).orThrow();
715
+ await seedTurn(store, 'conv-a:3', 'anchor in conversation a');
716
+ await seedTurn(store, 'conv-b:3', 'anchor in conversation b');
717
+ return store;
718
+ }
719
+ test('accepts a same-stem edge target that resolves to the correct scope', async () => {
720
+ const store = await seededStore();
721
+ const orch = buildMtmOrchestrator(store, relater((ctx) => succeed([
722
+ {
723
+ source: srcOf(ctx, 'conv-a:5'),
724
+ edge: { type: 'rel', target: kt('turn-3', 'conversations/conv-a') }
725
+ }
726
+ ])));
727
+ expect(await orch.ingestItem({ id: 'i', content: 'x' })).toSucceedAndSatisfy((r) => {
728
+ var _a;
729
+ expect((_a = r.records[0].record) === null || _a === void 0 ? void 0 : _a.envelope.links).toEqual([
730
+ { type: 'rel', target: kt('turn-3', 'conversations/conv-a') }
731
+ ]);
732
+ });
733
+ });
734
+ test('rejects a same-stem edge target whose scope has no matching record', async () => {
735
+ const store = await seededStore();
736
+ const orch = buildMtmOrchestrator(store, relater((ctx) => succeed([
737
+ {
738
+ source: srcOf(ctx, 'conv-a:5'),
739
+ // turn-3 exists under conv-a and conv-b, but NOT conv-z.
740
+ edge: { type: 'rel', target: kt('turn-3', 'conversations/conv-z') }
741
+ }
742
+ ])));
743
+ expect(await orch.ingestItem({ id: 'i', content: 'x' })).toFailWith(/edge target 'conversations\/conv-z\/turn-3' resolves to neither a sibling candidate nor an existing record/);
614
744
  });
615
745
  });
616
746
  });
@@ -631,13 +761,20 @@ describe('MemoryIngestOrchestrator — contradicts→temporal interlock', () =>
631
761
  store,
632
762
  classifier: classifyFact,
633
763
  extractor: extractor(() => succeed([candidate(factKind, 'fact-1', 'the sky is grey')])),
634
- relationExtractor: relater(() => succeed([candEdge('fact-1', 'contradicts', v1Id)]))
764
+ relationExtractor: relater((ctx) => succeed([
765
+ {
766
+ source: srcOf(ctx, 'fact-1'),
767
+ edge: { type: CONTRADICTS_LINK_TYPE, target: kt(v1Id, 'facts/entities/fact-1') }
768
+ }
769
+ ]))
635
770
  });
636
771
  expect(await secondOrch.ingestItem({ id: 'fact-1', content: 'the sky is grey' })).toSucceedAndSatisfy((r) => {
637
772
  var _a;
638
773
  expect(r.records[0].interlock).toBe('temporal-versioned');
639
774
  expect(r.records[0].disposition).toBe('written');
640
- expect((_a = r.records[0].record) === null || _a === void 0 ? void 0 : _a.envelope.links).toEqual([{ type: CONTRADICTS_LINK_TYPE, target: v1Id }]);
775
+ expect((_a = r.records[0].record) === null || _a === void 0 ? void 0 : _a.envelope.links).toEqual([
776
+ { type: CONTRADICTS_LINK_TYPE, target: kt(v1Id, 'facts/entities/fact-1') }
777
+ ]);
641
778
  });
642
779
  // The current version is the new (contradicting) one.
643
780
  expect(await store.get(factKind, 'fact-1')).toSucceedAndSatisfy((rec) => expect(rec === null || rec === void 0 ? void 0 : rec.body).toBe('the sky is grey'));
@@ -659,7 +796,12 @@ describe('MemoryIngestOrchestrator — contradicts→temporal interlock', () =>
659
796
  store,
660
797
  classifier: classifyFact,
661
798
  extractor: extractor(() => succeed([candidate(factKind, 'fact-1', 'grey')])),
662
- relationExtractor: relater(() => succeed([candEdge('fact-1', 'contradicts', v1Id)]))
799
+ relationExtractor: relater((ctx) => succeed([
800
+ {
801
+ source: srcOf(ctx, 'fact-1'),
802
+ edge: { type: CONTRADICTS_LINK_TYPE, target: kt(v1Id, 'facts/entities/fact-1') }
803
+ }
804
+ ]))
663
805
  });
664
806
  (await o2.ingestItem({ id: 'fact-1', content: 'grey' })).orThrow();
665
807
  // Re-introduce the ORIGINAL body: the invalidated v1 must be skipped by exact
@@ -790,7 +932,7 @@ describe('MemoryIngestOrchestrator — review-punch-list fixes', () => {
790
932
  });
791
933
  expect(await orch.ingestItem({ id: 'i', content: 'x' })).toSucceedAndSatisfy((r) => {
792
934
  var _a;
793
- expect((_a = r.records[0].record) === null || _a === void 0 ? void 0 : _a.envelope.links).toEqual([{ type: 'rel', target: 'doc-a' }]);
935
+ expect((_a = r.records[0].record) === null || _a === void 0 ? void 0 : _a.envelope.links).toEqual([{ type: 'rel', target: kt('doc-a') }]);
794
936
  });
795
937
  });
796
938
  test('two candidates merging into the same target do not duplicate a shared edge', async () => {
@@ -802,13 +944,13 @@ describe('MemoryIngestOrchestrator — review-punch-list fixes', () => {
802
944
  store,
803
945
  vectorIndex,
804
946
  embed,
805
- entityResolver: resolver((__c, similar) => succeed({ verdict: 'merge-into', target: similar[0].id })),
947
+ entityResolver: resolver((__c, similar) => succeed({ verdict: 'merge-into', target: similar[0].target })),
806
948
  extractor: extractor(() => succeed([candidate(noteKind, 'doc-b', 'apple tart'), candidate(noteKind, 'doc-c', 'apple tart')])),
807
949
  relationExtractor: relater(() => succeed([candEdge('doc-a', 'rel', 'doc-x')]))
808
950
  });
809
951
  expect(await orch.ingestItem({ id: 'i', content: 'x' })).toSucceed();
810
952
  expect(await store.get(noteKind, 'doc-a')).toSucceedAndSatisfy((rec) => {
811
- expect(rec === null || rec === void 0 ? void 0 : rec.envelope.links).toEqual([{ type: 'rel', target: 'doc-x' }]);
953
+ expect(rec === null || rec === void 0 ? void 0 : rec.envelope.links).toEqual([{ type: 'rel', target: kt('doc-x') }]);
812
954
  });
813
955
  });
814
956
  });
@@ -838,18 +980,18 @@ describe('MemoryIngestOrchestrator — merge-into safety (second review)', () =>
838
980
  const store = buildStore({ vectorIndex, embed });
839
981
  await putFull(store, noteKind, 'doc-a', 'apple pie', {
840
982
  tags: ['old-tag'],
841
- links: [{ type: 'existing', target: 'doc-z' }]
983
+ links: [{ type: 'existing', target: kt('doc-z') }]
842
984
  });
843
985
  await putFull(store, noteKind, 'doc-x', 'target of stage-5');
844
986
  const orch = buildOrchestrator({
845
987
  store,
846
988
  vectorIndex,
847
989
  embed,
848
- entityResolver: resolver((__c, similar) => succeed({ verdict: 'merge-into', target: similar[0].id })),
990
+ entityResolver: resolver((__c, similar) => succeed({ verdict: 'merge-into', target: similar[0].target })),
849
991
  extractor: extractor(() => succeed([
850
992
  candidate(noteKind, 'doc-b', 'apple tart', {
851
993
  tags: ['new-tag'],
852
- links: [{ type: 'candlink', target: 'doc-z' }]
994
+ links: [{ type: 'candlink', target: kt('doc-z') }]
853
995
  })
854
996
  ])),
855
997
  relationExtractor: relater(() => succeed([candEdge('doc-a', 'stage5', 'doc-x')]))
@@ -863,9 +1005,9 @@ describe('MemoryIngestOrchestrator — merge-into safety (second review)', () =>
863
1005
  expect([...((_a = rec === null || rec === void 0 ? void 0 : rec.envelope.tags) !== null && _a !== void 0 ? _a : [])].sort()).toEqual(['new-tag', 'old-tag']);
864
1006
  // Original link + candidate link + stage-5 edge, each exactly once.
865
1007
  expect(rec === null || rec === void 0 ? void 0 : rec.envelope.links).toEqual([
866
- { type: 'existing', target: 'doc-z' },
867
- { type: 'candlink', target: 'doc-z' },
868
- { type: 'stage5', target: 'doc-x' }
1008
+ { type: 'existing', target: kt('doc-z') },
1009
+ { type: 'candlink', target: kt('doc-z') },
1010
+ { type: 'stage5', target: kt('doc-x') }
869
1011
  ]);
870
1012
  });
871
1013
  });
@@ -878,10 +1020,10 @@ describe('MemoryIngestOrchestrator — merge-into safety (second review)', () =>
878
1020
  store,
879
1021
  vectorIndex,
880
1022
  embed,
881
- entityResolver: resolver((__c, similar) => succeed({ verdict: 'merge-into', target: similar[0].id })),
1023
+ entityResolver: resolver((__c, similar) => succeed({ verdict: 'merge-into', target: similar[0].target })),
882
1024
  extractor: extractor(() => succeed([candidate(noteKind, 'doc-b', 'apple tart')]))
883
1025
  });
884
- expect(await orch.ingestItem({ id: 'i', content: 'x' })).toFailWith(/merge-into target 'conv-1' is kind 'ltm' but the candidate is kind 'note'/);
1026
+ expect(await orch.ingestItem({ id: 'i', content: 'x' })).toFailWith(/merge-into target 'conversations\/conv-1' is kind 'ltm' but the candidate is kind 'note'/);
885
1027
  });
886
1028
  test('supersede with a non-existent target fails loudly', async () => {
887
1029
  const vectorIndex = InMemoryCosineIndex.create().orThrow();
@@ -891,10 +1033,10 @@ describe('MemoryIngestOrchestrator — merge-into safety (second review)', () =>
891
1033
  store,
892
1034
  vectorIndex,
893
1035
  embed,
894
- entityResolver: resolver(() => succeed({ verdict: 'supersede', target: 'ghost' })),
1036
+ entityResolver: resolver(() => succeed({ verdict: 'supersede', target: kt('ghost') })),
895
1037
  extractor: extractor(() => succeed([candidate(noteKind, 'doc-b', 'apple tart')]))
896
1038
  });
897
- expect(await orch.ingestItem({ id: 'i', content: 'x' })).toFailWith(/supersede target 'ghost' does not exist/);
1039
+ expect(await orch.ingestItem({ id: 'i', content: 'x' })).toFailWith(/supersede target 'knowledge\/ghost' does not exist/);
898
1040
  });
899
1041
  test('duplicate-of with a non-existent target fails loudly', async () => {
900
1042
  const vectorIndex = InMemoryCosineIndex.create().orThrow();
@@ -904,10 +1046,10 @@ describe('MemoryIngestOrchestrator — merge-into safety (second review)', () =>
904
1046
  store,
905
1047
  vectorIndex,
906
1048
  embed,
907
- entityResolver: resolver(() => succeed({ verdict: 'duplicate-of', target: 'ghost' })),
1049
+ entityResolver: resolver(() => succeed({ verdict: 'duplicate-of', target: kt('ghost') })),
908
1050
  extractor: extractor(() => succeed([candidate(noteKind, 'doc-b', 'apple tart')]))
909
1051
  });
910
- expect(await orch.ingestItem({ id: 'i', content: 'x' })).toFailWith(/duplicate-of target 'ghost' does not exist/);
1052
+ expect(await orch.ingestItem({ id: 'i', content: 'x' })).toFailWith(/duplicate-of target 'knowledge\/ghost' does not exist/);
911
1053
  });
912
1054
  });
913
1055
  //# sourceMappingURL=orchestrator.test.js.map