@graphorin/memory 0.5.0 → 0.6.1

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 (236) hide show
  1. package/CHANGELOG.md +102 -1
  2. package/README.md +54 -37
  3. package/dist/conflict/index.d.ts +3 -1
  4. package/dist/conflict/index.js +1 -1
  5. package/dist/conflict/locale-packs/en.d.ts +13 -0
  6. package/dist/conflict/locale-packs/en.d.ts.map +1 -0
  7. package/dist/conflict/locale-packs/en.js +1 -1
  8. package/dist/conflict/locale-packs/en.js.map +1 -1
  9. package/dist/conflict/pipeline.d.ts +2 -2
  10. package/dist/conflict/pipeline.js +3 -3
  11. package/dist/conflict/pipeline.js.map +1 -1
  12. package/dist/conflict/stages/helpers.js +1 -1
  13. package/dist/conflict/stages/helpers.js.map +1 -1
  14. package/dist/conflict/stages/stage1-exact-dedup.js.map +1 -1
  15. package/dist/conflict/stages/stage2-embedding-three-zone.js.map +1 -1
  16. package/dist/conflict/stages/stage3-heuristic-regex.js +1 -1
  17. package/dist/conflict/stages/stage3-heuristic-regex.js.map +1 -1
  18. package/dist/conflict/stages/stage4-subject-predicate.js.map +1 -1
  19. package/dist/conflict/stages/stage5-defer-to-deep.js.map +1 -1
  20. package/dist/conflict/types.d.ts +7 -7
  21. package/dist/conflict/types.js +1 -1
  22. package/dist/conflict/types.js.map +1 -1
  23. package/dist/consolidator/budget.js +16 -3
  24. package/dist/consolidator/budget.js.map +1 -1
  25. package/dist/consolidator/decay.d.ts +13 -2
  26. package/dist/consolidator/decay.js +17 -5
  27. package/dist/consolidator/decay.js.map +1 -1
  28. package/dist/consolidator/dlq.js +1 -1
  29. package/dist/consolidator/dlq.js.map +1 -1
  30. package/dist/consolidator/errors.js +2 -2
  31. package/dist/consolidator/errors.js.map +1 -1
  32. package/dist/consolidator/index.js +8 -6
  33. package/dist/consolidator/index.js.map +1 -1
  34. package/dist/consolidator/lock.js +1 -1
  35. package/dist/consolidator/lock.js.map +1 -1
  36. package/dist/consolidator/noise-filter.js.map +1 -1
  37. package/dist/consolidator/phases/deep.js +6 -2
  38. package/dist/consolidator/phases/deep.js.map +1 -1
  39. package/dist/consolidator/phases/induce.d.ts +10 -10
  40. package/dist/consolidator/phases/induce.js +6 -6
  41. package/dist/consolidator/phases/induce.js.map +1 -1
  42. package/dist/consolidator/phases/learned-context.js +129 -0
  43. package/dist/consolidator/phases/learned-context.js.map +1 -0
  44. package/dist/consolidator/phases/light.js +2 -1
  45. package/dist/consolidator/phases/light.js.map +1 -1
  46. package/dist/consolidator/phases/reflect.js +5 -4
  47. package/dist/consolidator/phases/reflect.js.map +1 -1
  48. package/dist/consolidator/phases/standard.js +37 -8
  49. package/dist/consolidator/phases/standard.js.map +1 -1
  50. package/dist/consolidator/reconcile.js +1 -1
  51. package/dist/consolidator/reconcile.js.map +1 -1
  52. package/dist/consolidator/runtime.d.ts +4 -4
  53. package/dist/consolidator/runtime.d.ts.map +1 -1
  54. package/dist/consolidator/runtime.js +73 -16
  55. package/dist/consolidator/runtime.js.map +1 -1
  56. package/dist/consolidator/scheduler.d.ts +5 -5
  57. package/dist/consolidator/scheduler.js +1 -1
  58. package/dist/consolidator/scheduler.js.map +1 -1
  59. package/dist/consolidator/triggers.js +7 -7
  60. package/dist/consolidator/triggers.js.map +1 -1
  61. package/dist/consolidator/types.d.ts +68 -20
  62. package/dist/consolidator/types.d.ts.map +1 -1
  63. package/dist/consolidator/types.js +16 -6
  64. package/dist/consolidator/types.js.map +1 -1
  65. package/dist/context-engine/annotations.d.ts +9 -9
  66. package/dist/context-engine/annotations.js +3 -3
  67. package/dist/context-engine/annotations.js.map +1 -1
  68. package/dist/context-engine/auto-recall.js.map +1 -1
  69. package/dist/context-engine/compaction/clear-tool-results.d.ts +18 -3
  70. package/dist/context-engine/compaction/clear-tool-results.d.ts.map +1 -1
  71. package/dist/context-engine/compaction/clear-tool-results.js +26 -5
  72. package/dist/context-engine/compaction/clear-tool-results.js.map +1 -1
  73. package/dist/context-engine/compaction/compactor.js +19 -6
  74. package/dist/context-engine/compaction/compactor.js.map +1 -1
  75. package/dist/context-engine/compaction/hooks/reanchor-persona-block.js +4 -0
  76. package/dist/context-engine/compaction/hooks/reanchor-persona-block.js.map +1 -1
  77. package/dist/context-engine/compaction/hooks/reanchor-pinned-facts.js +1 -0
  78. package/dist/context-engine/compaction/hooks/reanchor-pinned-facts.js.map +1 -1
  79. package/dist/context-engine/compaction/hooks/reanchor-project-rules.js +2 -1
  80. package/dist/context-engine/compaction/hooks/reanchor-project-rules.js.map +1 -1
  81. package/dist/context-engine/compaction/hooks/reanchor-recent-results.d.ts +28 -0
  82. package/dist/context-engine/compaction/hooks/reanchor-recent-results.d.ts.map +1 -0
  83. package/dist/context-engine/compaction/hooks/reanchor-recent-results.js +71 -0
  84. package/dist/context-engine/compaction/hooks/reanchor-recent-results.js.map +1 -0
  85. package/dist/context-engine/compaction/hooks/types.d.ts +15 -3
  86. package/dist/context-engine/compaction/hooks/types.d.ts.map +1 -1
  87. package/dist/context-engine/compaction/index.d.ts +1 -0
  88. package/dist/context-engine/compaction/index.js +2 -1
  89. package/dist/context-engine/compaction/templates/summary-9-section.d.ts +10 -3
  90. package/dist/context-engine/compaction/templates/summary-9-section.d.ts.map +1 -1
  91. package/dist/context-engine/compaction/templates/summary-9-section.js +42 -5
  92. package/dist/context-engine/compaction/templates/summary-9-section.js.map +1 -1
  93. package/dist/context-engine/compaction/thresholds.js.map +1 -1
  94. package/dist/context-engine/compaction/types.d.ts +47 -7
  95. package/dist/context-engine/compaction/types.d.ts.map +1 -1
  96. package/dist/context-engine/engine.d.ts +23 -4
  97. package/dist/context-engine/engine.d.ts.map +1 -1
  98. package/dist/context-engine/engine.js +69 -15
  99. package/dist/context-engine/engine.js.map +1 -1
  100. package/dist/context-engine/index.d.ts +1 -0
  101. package/dist/context-engine/index.d.ts.map +1 -1
  102. package/dist/context-engine/index.js +2 -1
  103. package/dist/context-engine/locale-packs/en.js +14 -13
  104. package/dist/context-engine/locale-packs/en.js.map +1 -1
  105. package/dist/context-engine/locale-packs/types.d.ts +10 -10
  106. package/dist/context-engine/locale-packs/types.d.ts.map +1 -1
  107. package/dist/context-engine/locale-packs/types.js.map +1 -1
  108. package/dist/context-engine/metadata.d.ts +1 -1
  109. package/dist/context-engine/metadata.js +2 -2
  110. package/dist/context-engine/metadata.js.map +1 -1
  111. package/dist/context-engine/preambles/inbound-en.d.ts +1 -1
  112. package/dist/context-engine/preambles/inbound-en.js +1 -1
  113. package/dist/context-engine/preambles/inbound-en.js.map +1 -1
  114. package/dist/context-engine/privacy-filter.d.ts +2 -2
  115. package/dist/context-engine/privacy-filter.js +1 -1
  116. package/dist/context-engine/privacy-filter.js.map +1 -1
  117. package/dist/context-engine/templates/base-en.d.ts +1 -1
  118. package/dist/context-engine/templates/base-en.js +1 -1
  119. package/dist/context-engine/templates/base-en.js.map +1 -1
  120. package/dist/context-engine/templates/composer.d.ts +1 -1
  121. package/dist/context-engine/templates/composer.js.map +1 -1
  122. package/dist/context-engine/token-budget.d.ts +1 -1
  123. package/dist/context-engine/token-budget.js +1 -1
  124. package/dist/context-engine/token-budget.js.map +1 -1
  125. package/dist/context-engine/token-counter.d.ts +16 -7
  126. package/dist/context-engine/token-counter.d.ts.map +1 -1
  127. package/dist/context-engine/token-counter.js +31 -9
  128. package/dist/context-engine/token-counter.js.map +1 -1
  129. package/dist/errors/index.d.ts +2 -2
  130. package/dist/errors/index.js +4 -4
  131. package/dist/errors/index.js.map +1 -1
  132. package/dist/facade.d.ts +44 -15
  133. package/dist/facade.d.ts.map +1 -1
  134. package/dist/facade.js +16 -8
  135. package/dist/facade.js.map +1 -1
  136. package/dist/graph/entity-resolver.d.ts +2 -2
  137. package/dist/graph/entity-resolver.js +2 -2
  138. package/dist/graph/entity-resolver.js.map +1 -1
  139. package/dist/index.d.ts +9 -6
  140. package/dist/index.d.ts.map +1 -1
  141. package/dist/index.js +11 -5
  142. package/dist/index.js.map +1 -1
  143. package/dist/internal/contextualize.js +2 -2
  144. package/dist/internal/contextualize.js.map +1 -1
  145. package/dist/internal/embedder-binding.js +1 -1
  146. package/dist/internal/embedder-binding.js.map +1 -1
  147. package/dist/internal/id.js +1 -1
  148. package/dist/internal/id.js.map +1 -1
  149. package/dist/internal/injection-heuristics.js +25 -2
  150. package/dist/internal/injection-heuristics.js.map +1 -1
  151. package/dist/internal/spans.js +1 -1
  152. package/dist/internal/spans.js.map +1 -1
  153. package/dist/internal/storage-adapter.d.ts +71 -22
  154. package/dist/internal/storage-adapter.d.ts.map +1 -1
  155. package/dist/migration/embedder-migration.d.ts +7 -7
  156. package/dist/migration/embedder-migration.js.map +1 -1
  157. package/dist/package.js +6 -0
  158. package/dist/package.js.map +1 -0
  159. package/dist/search/explain.d.ts +7 -4
  160. package/dist/search/explain.d.ts.map +1 -1
  161. package/dist/search/explain.js +1 -1
  162. package/dist/search/explain.js.map +1 -1
  163. package/dist/search/fit-weights.d.ts +48 -0
  164. package/dist/search/fit-weights.d.ts.map +1 -0
  165. package/dist/search/fit-weights.js +70 -0
  166. package/dist/search/fit-weights.js.map +1 -0
  167. package/dist/search/graph-ppr.d.ts +36 -0
  168. package/dist/search/graph-ppr.d.ts.map +1 -0
  169. package/dist/search/graph-ppr.js +38 -0
  170. package/dist/search/graph-ppr.js.map +1 -0
  171. package/dist/search/index.d.ts +4 -1
  172. package/dist/search/index.js +4 -1
  173. package/dist/search/iterative.d.ts +15 -8
  174. package/dist/search/iterative.d.ts.map +1 -1
  175. package/dist/search/iterative.js +10 -9
  176. package/dist/search/iterative.js.map +1 -1
  177. package/dist/search/query-transform.d.ts +6 -6
  178. package/dist/search/query-transform.js +6 -6
  179. package/dist/search/query-transform.js.map +1 -1
  180. package/dist/search/rrf.d.ts +4 -4
  181. package/dist/search/rrf.js +4 -4
  182. package/dist/search/rrf.js.map +1 -1
  183. package/dist/search/trust.d.ts +30 -0
  184. package/dist/search/trust.d.ts.map +1 -0
  185. package/dist/search/trust.js +50 -0
  186. package/dist/search/trust.js.map +1 -0
  187. package/dist/search/types.d.ts +1 -1
  188. package/dist/tiers/episodic-memory.d.ts +6 -4
  189. package/dist/tiers/episodic-memory.d.ts.map +1 -1
  190. package/dist/tiers/episodic-memory.js +3 -2
  191. package/dist/tiers/episodic-memory.js.map +1 -1
  192. package/dist/tiers/index.d.ts +1 -1
  193. package/dist/tiers/index.js +1 -1
  194. package/dist/tiers/insight-memory.d.ts +4 -4
  195. package/dist/tiers/insight-memory.js +3 -3
  196. package/dist/tiers/insight-memory.js.map +1 -1
  197. package/dist/tiers/procedural-memory.d.ts +29 -7
  198. package/dist/tiers/procedural-memory.d.ts.map +1 -1
  199. package/dist/tiers/procedural-memory.js +66 -6
  200. package/dist/tiers/procedural-memory.js.map +1 -1
  201. package/dist/tiers/semantic-memory.d.ts +111 -30
  202. package/dist/tiers/semantic-memory.d.ts.map +1 -1
  203. package/dist/tiers/semantic-memory.js +120 -27
  204. package/dist/tiers/semantic-memory.js.map +1 -1
  205. package/dist/tiers/session-memory.d.ts +7 -7
  206. package/dist/tiers/session-memory.js +7 -7
  207. package/dist/tiers/session-memory.js.map +1 -1
  208. package/dist/tiers/shared-memory.d.ts +1 -1
  209. package/dist/tiers/shared-memory.js +1 -1
  210. package/dist/tiers/shared-memory.js.map +1 -1
  211. package/dist/tiers/working-memory.d.ts +3 -3
  212. package/dist/tiers/working-memory.js +2 -2
  213. package/dist/tiers/working-memory.js.map +1 -1
  214. package/dist/tools/block-tools.d.ts +3 -3
  215. package/dist/tools/block-tools.js +3 -3
  216. package/dist/tools/block-tools.js.map +1 -1
  217. package/dist/tools/fact-tools.d.ts +48 -31
  218. package/dist/tools/fact-tools.d.ts.map +1 -1
  219. package/dist/tools/fact-tools.js +30 -18
  220. package/dist/tools/fact-tools.js.map +1 -1
  221. package/dist/tools/index.d.ts +9 -2
  222. package/dist/tools/index.d.ts.map +1 -1
  223. package/dist/tools/index.js +4 -2
  224. package/dist/tools/index.js.map +1 -1
  225. package/dist/tools/recall-tools.d.ts +40 -20
  226. package/dist/tools/recall-tools.d.ts.map +1 -1
  227. package/dist/tools/recall-tools.js +29 -10
  228. package/dist/tools/recall-tools.js.map +1 -1
  229. package/dist/tools/runbook-tools.d.ts +76 -0
  230. package/dist/tools/runbook-tools.d.ts.map +1 -0
  231. package/dist/tools/runbook-tools.js +53 -0
  232. package/dist/tools/runbook-tools.js.map +1 -0
  233. package/dist/tools/taint.js +33 -0
  234. package/dist/tools/taint.js.map +1 -0
  235. package/dist/tools/types.d.ts +1 -1
  236. package/package.json +8 -8
package/CHANGELOG.md CHANGED
@@ -1,10 +1,111 @@
1
1
  # @graphorin/memory
2
2
 
3
+ ## 0.6.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [#142](https://github.com/o-stepper/graphorin/pull/142) [`436d6ca`](https://github.com/o-stepper/graphorin/commit/436d6ca5ebbd16df094e915682d3915c279a8430) Thanks [@o-stepper](https://github.com/o-stepper)! - Version constants and version-bearing strings now derive from each package's manifest at build time (`VERSION = pkg.version`; writer ids, client/server info, OTLP framework attributes, build-info metrics interpolate it). No behavioral change at the current version: the rendered strings are byte-identical. A release bump no longer edits source; the new `check-version-consistency` gate fails any reintroduced hardcoded framework version.
8
+
9
+ - Updated dependencies [[`826ee59`](https://github.com/o-stepper/graphorin/commit/826ee5933ad38693b0dd2f20a110abfecba7d23d), [`436d6ca`](https://github.com/o-stepper/graphorin/commit/436d6ca5ebbd16df094e915682d3915c279a8430)]:
10
+ - @graphorin/observability@0.6.1
11
+ - @graphorin/core@0.6.1
12
+ - @graphorin/security@0.6.1
13
+ - @graphorin/tools@0.6.1
14
+
15
+ ## 0.6.0
16
+
17
+ ### Minor Changes
18
+
19
+ - [#136](https://github.com/o-stepper/graphorin/pull/136) [`17a2d30`](https://github.com/o-stepper/graphorin/commit/17a2d30564154ca2ab87473335cdef43a5089c84) Thanks [@o-stepper](https://github.com/o-stepper)! - Context-engineering adoptions (audit 2026-07-04 Wave C, cluster C4).
20
+
21
+ - Compaction failure hardening: one retry with backoff on summarizer failure; an auto-trigger pass that does not shrink the buffer FAILS (compression-loop class); after 3 consecutive failures the auto trigger disables itself until a later successful pass re-arms it.
22
+ - Recent user messages survive compaction verbatim (`preserveUserMessages`, default 2) - only assistant/tool content is summarized away.
23
+ - Summary prompt upgrades: handoff-to-another-LLM framing, quote-identifiers-VERBATIM rule, new "Constraints and non-negotiables" section (12 sections, v1.3); template id renamed to `summary-sections` (context-engine-14).
24
+ - Clearing-tier parity with `clear_tool_uses_20250919`: `clearToolInputs` blanks paired assistant tool-call args; `readResultToolName: null` keeps the externalized-handle placeholder honest when `read_result` is not registered (context-engine-11).
25
+ - New `reanchorRecentResults` post-compaction hook (+ `ctx.droppedMessages` on the hook context): re-injects the result handles a compaction just dropped, with bounded previews via an optional `readPreview` resolver.
26
+
27
+ - [#136](https://github.com/o-stepper/graphorin/pull/136) [`17a2d30`](https://github.com/o-stepper/graphorin/commit/17a2d30564154ca2ab87473335cdef43a5089c84) Thanks [@o-stepper](https://github.com/o-stepper)! - Retrieval trust and quality (audit 2026-07-04 Wave C, cluster C5).
28
+
29
+ - Trust-aware retrieval ranking (MINJA defense): search multiplies each fused score by a rank-time trust factor reusing the eviction path's `SalienceWeights` - quarantined-but-included rows x0.3, foreign provenance (tool/imported/reflection/induction) x0.8, first-party UNTOUCHED. Surfaced as the `trust` signal on hits / `explainRecall`; per-call `trustWeighting: 'off'` escape hatch; weights follow `consolidator.salienceWeights`.
30
+ - Offline fusion-weight fitting: new pure `fitFusionWeights(cases, { grid, k })` + `ndcgAtK` in `@graphorin/memory/search` grid-search `FusionWeights` against labelled queries and report the plain-RRF baseline alongside.
31
+ - Extraction decontextualization (memory-consolidation-08): each extracted fact must now be a self-contained proposition (pronouns/ellipses resolved, entities inlined).
32
+ - rerankers.md rewritten: warns against installing a positional `WeightedRRFReranker` as process default (mis-assigns weights under fan-out - memory-retrieval-04), promotes the local cross-encoder as the first learned reranker to reach for, documents the fitting routine and the trust discount.
33
+
34
+ - [#136](https://github.com/o-stepper/graphorin/pull/136) [`17a2d30`](https://github.com/o-stepper/graphorin/commit/17a2d30564154ca2ab87473335cdef43a5089c84) Thanks [@o-stepper](https://github.com/o-stepper)! - Deterministic security adoptions (audit 2026-07-04 Wave C, cluster C6).
35
+
36
+ - Derived-taint propagation: opt-in `dataFlowPolicy.derivedTaint: 'strict'` fires the paraphrase-robust `derived-untrusted-to-sink` flow for every model-driven sink call once untrusted content entered the run (CaMeL-style control-flow integrity); the agent also records each tainted step's assistant text as `llm-derived` spans so model-echoed phrasing trips the verbatim probe.
37
+ - Taint into memory (cross-session MINJA leg): `ToolReturn` gains a widen-only `taint` override honoured through the executor record path; `fact_search` / `deep_recall` / `recall_episodes` attach it when any returned item is quarantined or foreign-provenance, re-arming the ledger at recall. `RunState.taintSummary` additionally carries one-way FNV-1a span-tile hashes (no plaintext), so a resumed run re-detects pre-suspend verbatim copies.
38
+ - MCP pinning completed: `toTools({ pinStore })` records fingerprints on first use and REJECTS drift by default when a store is present (rug-pull defense; `onPinMismatch: 'warn'` downgrades); tool-description injection hits at registration are counted (`mcp.tool-description.injection-flagged.total`).
39
+ - Signal-only heuristics + Unicode pre-pass: shared `normalizeForMatching` (NFKC + zero-width strip) applied in the guardrails injection catalogue and the memory quarantine heuristics; security.md repositions all pattern catalogues as best-effort signal, never a sole gate. `TaintLabel.sourceKind` widened to `string` for the new descriptive kinds.
40
+
41
+ - [#137](https://github.com/o-stepper/graphorin/pull/137) [`ce06b47`](https://github.com/o-stepper/graphorin/commit/ce06b472af9e30ac5d0792f7a8b6f42170a94627) Thanks [@o-stepper](https://github.com/o-stepper)! - Memory architecture evolution (audit 2026-07-04 Wave D, cluster D3) - opt-in; defaults byte-identical.
42
+
43
+ - Learned-context digest block (Letta sleep-time): a deep-phase pass rewrites the reserved `learned_context` working block from recent evidence via one budgeted LLM call (`consolidator.learnedContext`, off at every tier).
44
+ - Principal/owner dimension (`MemoryOwner` on facts/episodes/rules/insights, migration 026) stamped `'agent'` on synthesized writes, with a retrieval-time `owner` filter (`FactSearchOptions.owner`); default reads apply no filter.
45
+ - Retrieval-frequency reinforcement: `facts.access_count` (migration 027) + opt-in `SalienceWeights.accessReinforcement` (default 0 = inert).
46
+ - Runbook memory: `rules_fts` (migration 028) + `ProceduralMemory.search` returns whole validated procedures, with a gated `runbook_search` tool (`createMemory({ runbookSearch: true })`).
47
+
48
+ - [#137](https://github.com/o-stepper/graphorin/pull/137) [`ce06b47`](https://github.com/o-stepper/graphorin/commit/ce06b472af9e30ac5d0792f7a8b6f42170a94627) Thanks [@o-stepper](https://github.com/o-stepper)! - Retrieval SOTA graph wins (audit 2026-07-04 Wave D, cluster D5) - offline, opt-in; the billed/migration-heavy legs stay eval-gated.
49
+
50
+ - PPR-lite graded expansion (HippoRAG-style damped spreading activation): `SqliteGraphStore.expandActivation` + `FactSearchOptions.expandHops: 0|1|2` + `graphScoring: 'ppr'` score neighbours by `damping^hopDepth` instead of a flat 1.
51
+ - Graph + entity as first-class tunable fusion weights (`FusionWeights.graph` / `.entity`, were hardcoded neutral).
52
+ - Exact entity-match retriever: `SqliteGraphStore.factsForEntityName` + `FactSearchOptions.entityMatch` add a precise "facts about <entity>" candidate leg.
53
+ - `longmemeval` harness gains `--retrieval ppr` / `--retrieval entity`. Bitemporal event time, Matryoshka truncation, and cascade LLM reranking remain eval-gated.
54
+
55
+ ### Patch Changes
56
+
57
+ - [#134](https://github.com/o-stepper/graphorin/pull/134) [`32f20c1`](https://github.com/o-stepper/graphorin/commit/32f20c110f184f8cef7eec85bf39f5f07c886cb6) Thanks [@o-stepper](https://github.com/o-stepper)! - Fix three context-assembly defects (context-engine-02/03/04). The default post-compaction hooks (persona block, project rules, pinned facts) now apply the same D2 privacy decision as `assemble()`, so `sensitivity:'secret'` content the assembly withholds can no longer leak to the provider via the post-compaction splice. `renderMessageText` renders assistant tool-call arguments and `adaptTokenCounter` preserves the native `count(messages)` path, so tool-call args (file writes, code_execute scripts) finally contribute to trigger/before/after token arithmetic. And the trigger, SOTA-4 reclaim floor, and anti-thrash guard now share one full-buffer basis: `shouldCompact` takes `compactableFromIndex` (the pinned prefix is no longer counted as reclaimable) and `compactNow` takes `prefixMessages` so the guard arms against prefix + body + essentials instead of the sliced body - previously any real system prompt defeated the guard and a summarizer LLM call re-fired every step at the context edge.
58
+
59
+ - [#134](https://github.com/o-stepper/graphorin/pull/134) [`32f20c1`](https://github.com/o-stepper/graphorin/commit/32f20c110f184f8cef7eec85bf39f5f07c886cb6) Thanks [@o-stepper](https://github.com/o-stepper)! - Enforce transcript well-formedness so no path emits provider-rejected message sequences (agent-01, tools-07, context-engine-01). The durable-HITL pre-screen now collects EVERY approval-gated call in a step (not just the first) and executes the non-gated remainder before suspending, so the persisted transcript never carries dangling `tool_use` ids; `executeBatch` synthesizes an `execution_failed` outcome instead of silently dropping a slot whose `executeOne` rejected (e.g. a throwing tracer); summarize-compaction snaps its boundary backward so the preserved window never starts with an orphan `tool` message. The agent mock-provider harness now asserts transcript well-formedness on every request by default.
60
+
61
+ - [#135](https://github.com/o-stepper/graphorin/pull/135) [`4f850d9`](https://github.com/o-stepper/graphorin/commit/4f850d9bc0a05d6256c59c5117b010336fcb41d3) Thanks [@o-stepper](https://github.com/o-stepper)! - Retrieval and consolidation now honour the trust contracts they document (audit 2026-07-04 Wave B, cluster B3).
62
+
63
+ - memory-retrieval-01: default fact reads (FTS / vector / graph) behave as `asOf = now`, so superseded and validity-expired facts no longer surface as current - exactly what the `fact_supersede` tool promises. New `includeSuperseded: true` escape hatch (core `MemorySearchOptions`, memory `FactSearchOptions`, graph expand options) restores the full history for inspector / audit paths; `fact_search` and `deep_recall` outputs expose `validTo` / `supersededBy`.
64
+ - memory-retrieval-02: `deep_recall` passes `forceHard: true` (choosing the tool IS the hardness signal; the local heuristic gate rejected the tool's own documented examples and is English-only). Iterative results carry a new `graded` flag so ungraded single-shot passes stop claiming sufficiency as a verdict.
65
+ - memory-retrieval-03: a tagged search widens the fusion pool the same way decay does - the record-level tags filter runs after the topK cut, so tagged searches no longer silently return fewer than topK hits.
66
+ - memory-consolidation-01: the deep-phase dedup verdict now soft-forgets (replayable tombstone) instead of preferring the GDPR hard-delete `purge`, and a vanished conflicting fact skips the judge entirely (admit, no provider call) - a model verdict can no longer hard-delete the only surviving copy.
67
+ - memory-consolidation-07: the standard phase gains an embedder-independent exact-text duplicate guard (FTS + string equality, quarantine-aware), so replaying a partially-committed slice (DLQ / cursor retry) without an embedder no longer duplicates already-committed facts.
68
+
69
+ - [#135](https://github.com/o-stepper/graphorin/pull/135) [`4f850d9`](https://github.com/o-stepper/graphorin/commit/4f850d9bc0a05d6256c59c5117b010336fcb41d3) Thanks [@o-stepper](https://github.com/o-stepper)! - Compaction-seam hardening (audit 2026-07-04 Wave B, cluster B4).
70
+
71
+ - context-engine-05: the agent's pinned-prefix scan stops at the `<graphorin_compaction_summary>` marker, so a compaction summary resumed from a suspended run stays compactable instead of being absorbed into the uncompactable prefix (one extra pinned summary per compact-then-resume cycle).
72
+ - context-engine-06: hard context overflow gets a last-resort tier - on a `context-length` provider error the agent forces ONE aggressive compaction (`preserveRecentTurns: 2`) and retries the same provider before walking the fallback chain or failing. Thrown `ProviderHttpError`s now classify by their canonical `errorKind`, so a thrown 429 / overflow is treated like the structured-event equivalent.
73
+ - context-engine-07: the summarizer prompt's older-messages dump is capped (default 96k chars, `summarizerInputCharBudget` strategy option; oldest lines elided with a marker) so pointing `summarizerModel` at a smaller model no longer overflows its window and silently disables compaction.
74
+ - context-engine-09: `<<<older_messages>>>` / `<<</older_messages>>>` sequences inside message text are neutralized before entering the summarizer prompt's data-only envelope - a tool result carrying the closing marker can no longer inject instructions into the summarizer.
75
+ - context-engine-10: new integration test drives the REAL context engine through the agent loop with tool loops crossing the threshold; the mock provider's transcript invariant validates every post-compaction request.
76
+
77
+ - [#135](https://github.com/o-stepper/graphorin/pull/135) [`4f850d9`](https://github.com/o-stepper/graphorin/commit/4f850d9bc0a05d6256c59c5117b010336fcb41d3) Thanks [@o-stepper](https://github.com/o-stepper)! - Consolidator operational promises now hold (audit 2026-07-04 Wave B, cluster B5).
78
+
79
+ - memory-consolidation-02: `onExceed: 'log'` (the shipped standard/full presets) finally WARNs - once per resource per budget window, via a pluggable `BudgetTrackerOptions.logger`. New `priceUsage` hook on `CreateConsolidatorOptions` / `createMemory({ consolidator })` threads a USD pricer into every phase, so `maxCostPerDay` can actually accumulate spend (previously every call priced at $0 and the USD ceiling was inert at every tier).
80
+ - memory-consolidation-03: `drainDlq` has a production caller - every `trigger(...)` dispatch first replays ready (backoff-gated) dead-letter batches, so failed slices no longer accumulate forever. A separate-process `graphorin consolidator drain` CLI was deliberately NOT added: replays require the provider, which lives in the server process (same IP-4 boundary the CLI already documents for set-tier/stop).
81
+ - memory-consolidation-04: at tiers without a deep phase (free/cheap defaults) pending CONFLICT-CHECK rows older than 7 days are expired as `admit` (the safe direction - the candidate stays live), so the queue cannot grow monotonically.
82
+ - memory-consolidation-05/06: the guide's phase table now describes what the phases actually do (light = zero-LLM forgetting; standard = extraction + reconcile + episodes; deep = conflict judge + reflection; no procedural extraction, no shared-tier promotion) plus a new "Making it run" subsection documenting that the library-mode consolidator is dormant until `start()` + trigger wiring.
83
+ - memory-consolidation-08: extraction is temporally anchored - each transcript line carries its ISO timestamp and the prompt states today's date with an instruction to resolve relative dates into absolute ones.
84
+
85
+ - [#138](https://github.com/o-stepper/graphorin/pull/138) [`6293a25`](https://github.com/o-stepper/graphorin/commit/6293a2531c5c9265294da22fb365a08f2bdf445a) Thanks [@o-stepper](https://github.com/o-stepper)! - Documentation-truth sweep (audit 2026-07-04 Wave E, cluster E10): stale npm package descriptions rewritten (cli "Phase 14a three commands", mcp "upcoming auth CLI", store-sqlite's nonexistent WorkerPool), the store-sqlite WorkerPool TSDoc and the cipher-pragma ordering comment corrected, the executor timeout-precedence JSDoc fixed to the real `inlineToolTimeoutMs > tier timeoutMs > default` order, the `rrf.<label>` explain signals documented, and the skills spec-snapshot wording no longer claims a CI cron refreshes it (the diff is a manual `--upstream` pass; the release gate only parses the bundled snapshot).
86
+
87
+ - [#138](https://github.com/o-stepper/graphorin/pull/138) [`6293a25`](https://github.com/o-stepper/graphorin/commit/6293a2531c5c9265294da22fb365a08f2bdf445a) Thanks [@o-stepper](https://github.com/o-stepper)! - Release-pipeline and tarball-surface fixes (audit 2026-07-04 Wave E, cluster E2). `@graphorin/memory`'s `./conflict` subpath was runtime-EMPTY on npm 0.5.0 (`export { };` - preserveModules emitted the module without an explicit tsdown entry); it now ships all 8 runtime exports. `@graphorin/server`'s internal `workspace:*` peer dependencies are ranged (`workspace:>=0.5.0 <1.0.0`) so changesets stops escalating every sibling bump into a bogus MAJOR for the whole fixed group (the 1.0.0 landmine on the release bot's branch). `@graphorin/eslint-plugin` gains the `./package.json` self-export. All 27 per-package CHANGELOGs gain the 0.5.0 section (they were frozen at 0.1.0 inside every published tarball), and the `mvp-readiness` release gate now rejects a stale-CHANGELOG or unresolvable-exports release.
88
+
89
+ - Updated dependencies [[`32f20c1`](https://github.com/o-stepper/graphorin/commit/32f20c110f184f8cef7eec85bf39f5f07c886cb6), [`32f20c1`](https://github.com/o-stepper/graphorin/commit/32f20c110f184f8cef7eec85bf39f5f07c886cb6), [`32f20c1`](https://github.com/o-stepper/graphorin/commit/32f20c110f184f8cef7eec85bf39f5f07c886cb6), [`32f20c1`](https://github.com/o-stepper/graphorin/commit/32f20c110f184f8cef7eec85bf39f5f07c886cb6), [`4f850d9`](https://github.com/o-stepper/graphorin/commit/4f850d9bc0a05d6256c59c5117b010336fcb41d3), [`4f850d9`](https://github.com/o-stepper/graphorin/commit/4f850d9bc0a05d6256c59c5117b010336fcb41d3), [`4f850d9`](https://github.com/o-stepper/graphorin/commit/4f850d9bc0a05d6256c59c5117b010336fcb41d3), [`4f850d9`](https://github.com/o-stepper/graphorin/commit/4f850d9bc0a05d6256c59c5117b010336fcb41d3), [`17a2d30`](https://github.com/o-stepper/graphorin/commit/17a2d30564154ca2ab87473335cdef43a5089c84), [`17a2d30`](https://github.com/o-stepper/graphorin/commit/17a2d30564154ca2ab87473335cdef43a5089c84), [`17a2d30`](https://github.com/o-stepper/graphorin/commit/17a2d30564154ca2ab87473335cdef43a5089c84), [`17a2d30`](https://github.com/o-stepper/graphorin/commit/17a2d30564154ca2ab87473335cdef43a5089c84), [`17a2d30`](https://github.com/o-stepper/graphorin/commit/17a2d30564154ca2ab87473335cdef43a5089c84), [`ce06b47`](https://github.com/o-stepper/graphorin/commit/ce06b472af9e30ac5d0792f7a8b6f42170a94627), [`ce06b47`](https://github.com/o-stepper/graphorin/commit/ce06b472af9e30ac5d0792f7a8b6f42170a94627), [`ce06b47`](https://github.com/o-stepper/graphorin/commit/ce06b472af9e30ac5d0792f7a8b6f42170a94627), [`ce06b47`](https://github.com/o-stepper/graphorin/commit/ce06b472af9e30ac5d0792f7a8b6f42170a94627), [`ce06b47`](https://github.com/o-stepper/graphorin/commit/ce06b472af9e30ac5d0792f7a8b6f42170a94627), [`ce06b47`](https://github.com/o-stepper/graphorin/commit/ce06b472af9e30ac5d0792f7a8b6f42170a94627), [`6293a25`](https://github.com/o-stepper/graphorin/commit/6293a2531c5c9265294da22fb365a08f2bdf445a), [`6293a25`](https://github.com/o-stepper/graphorin/commit/6293a2531c5c9265294da22fb365a08f2bdf445a)]:
90
+ - @graphorin/tools@0.6.0
91
+ - @graphorin/core@0.6.0
92
+ - @graphorin/security@0.6.0
93
+ - @graphorin/observability@0.6.0
94
+
95
+ ## 0.5.0
96
+
97
+ First version published to the npm registry (with Sigstore build
98
+ provenance). The 0.2.0, 0.3.0, and 0.4.0 versions were internal lockstep
99
+ milestones and were never published. All `@graphorin/*` packages release
100
+ lockstep at the same version; the full release notes for 0.2.0-0.5.0 live
101
+ in the repository-level
102
+ [CHANGELOG](https://github.com/o-stepper/graphorin/blob/main/CHANGELOG.md).
103
+
3
104
  ## 0.1.0
4
105
 
5
106
  ### Minor Changes
6
107
 
7
- - Phase 10a initial release of `@graphorin/memory`. Ships the
108
+ - Phase 10a - initial release of `@graphorin/memory`. Ships the
8
109
  `createMemory()` facade with six tier sub-modules (working,
9
110
  session, episodic, semantic, procedural, shared), nine memory
10
111
  tools registered through `@graphorin/tools`, the built-in
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @graphorin/memory
2
2
 
3
- > Six-tier memory system for the Graphorin framework the heart of any
3
+ > Six-tier memory system for the Graphorin framework - the heart of any
4
4
  > long-living personal AI assistant built on top of Graphorin.
5
5
 
6
6
  `@graphorin/memory` ships the `createMemory()` facade, the six tier
@@ -13,15 +13,15 @@ engine.
13
13
 
14
14
  The package depends on:
15
15
 
16
- - `@graphorin/core` typed contracts (`MemoryStore`, `EmbedderProvider`,
16
+ - `@graphorin/core` - typed contracts (`MemoryStore`, `EmbedderProvider`,
17
17
  `Tool`, `Tracer`, `Sensitivity`, …).
18
- - `@graphorin/observability` the `Tracer` used to emit one `AISpan`
18
+ - `@graphorin/observability` - the `Tracer` used to emit one `AISpan`
19
19
  per memory operation.
20
- - `@graphorin/security` the memory-modification guard (`MemoryGuardTier`)
20
+ - `@graphorin/security` - the memory-modification guard (`MemoryGuardTier`)
21
21
  every memory tool is wired against.
22
- - `@graphorin/tools` the `tool({...})` builder used to declare the
23
- ten memory tools.
24
- - `zod` (peer) schema typing for the memory tools.
22
+ - `@graphorin/tools` - the `tool({...})` builder used to declare the
23
+ eleven memory tools.
24
+ - `zod` (peer) - schema typing for the memory tools.
25
25
 
26
26
  Storage is provided by any `MemoryStore` implementation; the default
27
27
  production adapter is `@graphorin/store-sqlite` (built on
@@ -44,7 +44,7 @@ any `EmbedderProvider`; the default is
44
44
  - **Eleven memory tools.** `block_append`, `block_replace`,
45
45
  `block_rethink`, `fact_remember`, `fact_search`, `fact_supersede`,
46
46
  `fact_forget`, `recall_episodes`, `conversation_search`,
47
- `fact_history`, `fact_validate` every tool is a typed `Tool` with
47
+ `fact_history`, `fact_validate` - every tool is a typed `Tool` with
48
48
  `inputSchema` + `outputSchema`, the appropriate `memoryGuardTier`, and
49
49
  the right `sideEffectClass`. `fact_search` accepts an `asOf` instant
50
50
  for point-in-time reads; `fact_history` returns a fact's bi-temporal
@@ -57,7 +57,7 @@ any `EmbedderProvider`; the default is
57
57
  role-markup smuggling, secrecy / exfiltration directives) land
58
58
  `status: 'quarantined'` and are **excluded from default recall** until
59
59
  a human promotes them via `fact_validate`. Quarantine is a retrieval
60
- gate, never a delete quarantined rows stay fully auditable, and the
60
+ gate, never a delete - quarantined rows stay fully auditable, and the
61
61
  `includeQuarantined` search option surfaces them for the validation
62
62
  UI. This is the precondition for safely shipping synthesized memory
63
63
  (reflection / reconciliation / induction) against memory-poisoning
@@ -83,10 +83,10 @@ any `EmbedderProvider`; the default is
83
83
  `extraction` provenance gate.
84
84
  - **Auto-importance + episode formation** (consolidator standard phase).
85
85
  Each processed slice is summarized into one episode via a single budgeted
86
- LLM call that also rates its importance `110` (normalized to `[0, 1]`),
86
+ LLM call that also rates its importance `1-10` (normalized to `[0, 1]`),
87
87
  so episodic triple-signal retrieval (recency × relevance × importance) finally
88
88
  runs on all three signals. Auto-formed episodes carry `provenance: 'extraction'`
89
- + `status: 'quarantined'` (P1-4) surfaced for review via
89
+ + `status: 'quarantined'` (P1-4) - surfaced for review via
90
90
  `episodic.search(..., { includeQuarantined: true })`. Importance is a *soft*
91
91
  signal (never a retention gate). Controlled by the per-tier `formEpisodes` /
92
92
  `importanceScoring` flags (on at `standard` / `full`); budget-aware
@@ -107,7 +107,7 @@ any `EmbedderProvider`; the default is
107
107
  topics, Anthropic's Contextual Retrieval) so a terse fact stays findable; the
108
108
  canonical `text` is preserved. The offline default `'late-chunk'`
109
109
  (`createMemory({ contextualRetrieval })`) derives the context deterministically
110
- from the fact's own structured signals with **no extra LLM call** a no-op for
110
+ from the fact's own structured signals with **no extra LLM call** - a no-op for
111
111
  plain-text writes. An opt-in, **consolidator-only** `'llm'` mode
112
112
  (`consolidator: { contextualRetrieval: 'llm' }`) spends one budgeted cheap-model
113
113
  call per write to author the prefix, degrading to late-chunk on any failure.
@@ -121,50 +121,55 @@ any `EmbedderProvider`; the default is
121
121
  (supersede chain / quarantine / conflicts / citing insights) and
122
122
  `graphorin memory activity` (consolidator / reflection activity).
123
123
  - **Multi-signal forgetting** (cost / staleness control, *not* an accuracy
124
- lever). The light phase scores each fact with `salience(...)` the Ebbinghaus
125
- `retention` curve (recency + access frequency) combined with the `importance`
126
- hint and a security-risk negative term (a quarantined fact is evicted first, a
127
- foreign-provenance one slightly sooner). With neutral importance + an active,
124
+ lever). The light phase scores each fact with `salience(...)` - the Ebbinghaus
125
+ `retention` curve (recency + access frequency, including an opt-in
126
+ `accessReinforcement` term fed by per-fact recall counters) combined with the
127
+ `importance` hint and a security-risk negative term (a quarantined fact is
128
+ evicted first, a foreign-provenance one slightly sooner). With neutral importance + an active,
128
129
  first-party fact, `salience === retention`. Setting `decayCapacity`
129
130
  (`createMemory({ consolidator: { decayCapacity } })`, default unbounded)
130
- bounds storage: the lowest-salience facts are **soft-archived** (recoverable
131
+ bounds storage: the lowest-salience facts are **soft-archived** (recoverable -
131
132
  `archived = 1`, never deleted) until the window fits.
132
133
  - **Query transformation** (read path; multi-query / RAG-Fusion + opt-in HyDE).
133
134
  `search(scope, query, { multiQuery: N })` fans the query into up to `N - 1`
134
135
  reworded variants via one cheap LLM call, retrieves each, and fuses every list
135
- through the same RRF reranker recovering memories whose stored wording
136
+ through the same RRF reranker - recovering memories whose stored wording
136
137
  differs from the question. `{ hyde: true }` additionally embeds a hypothetical
137
138
  answer (arXiv:2212.10496) and fuses its neighbours. Both are **opt-in**, wired
138
139
  by `createMemory({ queryTransform: { provider } })`; with no transformer (the
139
140
  default) search stays **offline + single-shot** and these options are silent
140
- no-ops. Reserve it for retrieval-heavy recall it adds provider latency.
141
+ no-ops. Reserve it for retrieval-heavy recall - it adds provider latency.
141
142
  - **Weighted fusion + reranker guidance** (search quality, opt-in). RRF is a
142
143
  good zero-tuning default, but once you have labels (the `@graphorin/evals`
143
144
  harness) a calibrated weight can do better. `search(scope, query, { fusion: {
144
145
  strategy: 'weighted', weights: { vector: 3, fts: 1 } } })` fuses through
145
146
  `WeightedRRFReranker`, scaling each candidate list's reciprocal-rank
146
147
  contribution by its retriever *kind* (FTS vs vector; the HyDE list counts as
147
- vector). **RRF stays the default** omit `fusion` (or pass `{ strategy:
148
+ vector). **RRF stays the default** - omit `fusion` (or pass `{ strategy:
148
149
  'rrf' }`) and behaviour is unchanged; equal weights reproduce RRF exactly. The
149
150
  pure `fuseWeighted(lists, weights, k)` is exported for offline weight tuning
150
151
  against the eval harness. For a further precision lift, install a cross-encoder
151
152
  reranker (`@graphorin/reranker-transformersjs` / `-llm`) via
152
- `createMemory({ reranker })` or `setReranker(...)` and raise `candidateTopK`
153
+ `createMemory({ reranker })` or `setReranker(...)` and raise `candidateTopK` -
153
154
  reranking pays off **only on an already-high-recall candidate set**, so fix
154
155
  recall first (contextual retrieval / multi-query) before reaching for it.
155
156
  - **Relation graph + one-hop expansion** (new capability, opt-in). Activates the
156
157
  dormant `(subject, predicate, object)` substrate: facts now carry s/p/o, and an
157
158
  **entity resolver** folds the subject/object strings into canonical entities
158
- ("Anna", "Anna S.", "my sister" → one entity) via lexical + embedding dedup
159
+ ("Anna", "Anna S.", "my sister" → one entity) via lexical + embedding dedup -
159
160
  with **auditable, reversible merges** (an append-only ledger; `merged_into` is
160
161
  single-level). At read time `search(scope, query, { expandHops: 1 })` seeds on
161
162
  the candidates and fuses in facts sharing an entity through a recursive CTE,
162
163
  answering "what did the person I met in Tbilisi recommend?" without leaving
163
164
  SQLite. Enable linking with `createMemory({ graph: { entityResolution: true } })`
164
165
  (offline; lexical + embedding). The ambiguous-similarity band mints a *new*
165
- entity by default it never auto-merges on weak evidence; opt into LLM
166
+ entity by default - it never auto-merges on weak evidence; opt into LLM
166
167
  adjudication (`graph: { llmAdjudication: true, provider }`) to resolve that band.
167
- Omit it all and the default path is unchanged + fully offline (`expandHops`
168
+ On top of hop expansion: `graphScoring: 'ppr'` weights neighbours by a
169
+ PPR-lite random-walk score instead of flat inclusion, `entityMatch: true`
170
+ adds exact entity-name hits as a candidate list, and `expandHops: 2` is
171
+ available for offline / analysis flows (expensive under dense hubs). Omit it
172
+ all and the default path is unchanged + fully offline (`expandHops`
168
173
  defaults to `0`).
169
174
  - **Agentic / iterative retrieval** (search quality, gated + opt-in). A
170
175
  CRAG/Self-RAG-style grade-then-reformulate loop for hard multi-hop / temporal
@@ -172,7 +177,7 @@ any `EmbedderProvider`; the default is
172
177
  (`assessQueryDifficulty`) keeps simple lookups single-shot; only a query judged
173
178
  hard *and* with a grader configured (`createMemory({ iterativeRetrieval: {
174
179
  provider } })`) is graded for sufficiency and, when weak, reformulated +
175
- retrieved again **widening to one-hop graph expansion** on each reformulation
180
+ retrieved again - **widening to one-hop graph expansion** on each reformulation -
176
181
  up to a hard-capped `maxIterations` (≤ 5), then **abstaining**
177
182
  (`result.abstained === true`) rather than confabulating. Exposed as
178
183
  `SemanticMemory.searchIterative(...)` and the gated **`deep_recall`** tool (a
@@ -181,18 +186,25 @@ any `EmbedderProvider`; the default is
181
186
  call**; the tool surface stays at the canonical eleven.
182
187
  - **Procedural memory extraction** (new capability, gated + opt-in). AWM-style
183
188
  workflow induction: from a **successful** agent trajectory,
184
- `ProceduralMemory.induce(scope, trajectory)` distils a reusable procedure
189
+ `ProceduralMemory.induce(scope, trajectory)` distils a reusable procedure -
185
190
  goal + value-abstracted steps (`"search for {product}"`) + the variable names
186
191
  + Voyager-style success criteria for self-verification on reuse
187
192
  (`checkSuccessCriteria`). Induction fires on **success only** (a failed run
188
193
  never calls the inducer), and the result lands **quarantined** +
189
- `provenance: 'induction'` (P1-4) procedures drive *actions*, so this is the
194
+ `provenance: 'induction'` (P1-4) - procedures drive *actions*, so this is the
190
195
  highest-poisoning-risk write and is excluded from `activate()` until validated.
191
196
  `trajectoryFromRunState(runState)` distils the agent's already-emitted run
192
197
  state, so capture needs **no agent change**. Wire it with
193
198
  `createMemory({ procedureInduction: { provider } })`; omit it and `induce(...)`
194
- throws `ProcedureInductionNotConfiguredError` the procedural tier stays pure
199
+ throws `ProcedureInductionNotConfiguredError` - the procedural tier stays pure
195
200
  offline CRUD with no provider call.
201
+ - **Memory evolution knobs** (all opt-in). `runbookSearch: true` registers the
202
+ gated `runbook_search` tool over the procedural tier; `consolidator:
203
+ { learnedContext: true, learnedContextMaxChars }` maintains a compact
204
+ learned-context digest block from consolidation output; facts, episodes, and
205
+ rules carry an optional `owner` principal so multi-principal deployments can
206
+ filter reads per owner; and per-fact access counters feed the salience
207
+ `accessReinforcement` term.
196
208
  - **Per-record `embedder_id` enforced.** Every embedded write registers
197
209
  the embedder via the storage layer's `EmbeddingMetaRepository` and
198
210
  records the canonical id (`'<provider>:<model>@<dim>'`); attempts to
@@ -215,7 +227,7 @@ any `EmbedderProvider`; the default is
215
227
  (post-MVP).
216
228
  - **`memory.compile(scope)` + `memory.metadata(scope)`.** The
217
229
  interface used by the context engine (Phase 10d) to assemble the
218
- six-layer memory-aware system prompt; `@graphorin/memory@0.5.0`
230
+ six-layer memory-aware system prompt; `@graphorin/memory@0.6.0`
219
231
  ships the deterministic minimum-viable rendering so the agent
220
232
  runtime in Phase 12 can already exercise the surface.
221
233
  - **Memory-modification guard wiring.** Every tool's
@@ -277,7 +289,7 @@ console.log(hits[0]?.record.text);
277
289
 
278
290
  | Tier | Surface (read) | Surface (write) |
279
291
  |-------------|-----------------------------------------------|-------------------------------------------------------------|
280
- | working | `list`, `read`, `compile` | `define`, `write`, `patch`, `attach`, `detach` |
292
+ | working | `list`, `read`, `compile` | `define`, `write`, `append`, `replace`, `rethink`, `forget`, `attach`, `detach` |
281
293
  | session | `list`, `search`, `attributedFor` | `push`, `flushImportant`, `compact` |
282
294
  | episodic | `recent`, `search` | `record` |
283
295
  | semantic | `search` | `remember`, `supersede`, `forget` |
@@ -304,6 +316,11 @@ tool exactly once:
304
316
  | `fact_history` | semantic | Trace a fact's bi-temporal supersede chain. |
305
317
  | `fact_validate` | semantic | Promote a quarantined fact to active (audited). |
306
318
 
319
+ Two further tools are **gated** behind their configuration and absent by
320
+ default: `deep_recall` (registered when `iterativeRetrieval` is configured)
321
+ and `runbook_search` (procedural runbook lookup, registered when
322
+ `createMemory({ runbookSearch: true })`).
323
+
307
324
  ## Embedder migration
308
325
 
309
326
  ```ts
@@ -354,22 +371,22 @@ console.log(outcome.decision.kind); // 'supersede' | 'dedup' | 'pending' | 'admi
354
371
 
355
372
  The pipeline visits five stages in order:
356
373
 
357
- 1. **Exact dedup** MD5 hash on the canonical (lowercase + collapsed-
374
+ 1. **Exact dedup** - MD5 hash on the canonical (lowercase + collapsed-
358
375
  whitespace + trimmed) candidate body short-circuits on a hit.
359
- 2. **Embedding three-zone** top-K neighbours from `searchVector`
376
+ 2. **Embedding three-zone** - top-K neighbours from `searchVector`
360
377
  classify the candidate into HOT (`>= 0.95`), NEAR-DUP
361
378
  (`>= 0.85`), CONFLICT-CHECK (`> 0.4`), or COLD. HOT zone always
362
379
  dedups (semantic identity outranks every other signal).
363
- 3. **Heuristic regex** the active locale pack's supersede +
380
+ 3. **Heuristic regex** - the active locale pack's supersede +
364
381
  negation markers fire when the candidate has an explicit
365
382
  change signal (`moved to`, `no longer`, `got promoted`, …) and
366
383
  there is at least one existing candidate.
367
- 4. **Subject / predicate** naive `(subject, predicate, object)`
384
+ 4. **Subject / predicate** - naive `(subject, predicate, object)`
368
385
  split using the locale pack's predicate normalisers; matching
369
386
  subject + predicate with a different object is a strong supersede
370
387
  signal.
371
- 5. **Defer to deep LLM judge** Stages 14 yielded no decision but
372
- the candidate sits in CONFLICT-CHECK zone the row is admitted
388
+ 5. **Defer to deep LLM judge** - Stages 1-4 yielded no decision but
389
+ the candidate sits in CONFLICT-CHECK zone - the row is admitted
373
390
  `pending` and queued in `conflict_check_pending` for the
374
391
  consolidator's deep phase (Phase 10c).
375
392
 
@@ -398,4 +415,4 @@ MIT © 2026 Oleksiy Stepurenko.
398
415
 
399
416
  ---
400
417
 
401
- **Project Graphorin** · v0.5.0 · MIT License · © 2026 Oleksiy Stepurenko · <https://github.com/o-stepper/graphorin>
418
+ **Project Graphorin** · v0.6.1 · MIT License · © 2026 Oleksiy Stepurenko · <https://github.com/o-stepper/graphorin>
@@ -1,3 +1,5 @@
1
1
  import { LocaleMatch, LocalePack, LocalePatternEntry, LocaleSupersedeKind, defineLocalePack, evaluateMarkers } from "./locale-packs/types.js";
2
+ import { enLocalePack } from "./locale-packs/en.js";
2
3
  import { ConflictDecision, ConflictPipeline, ConflictPipelineDeps, ConflictPipelineOptions, ConflictStage, ConflictThresholds, DEFAULT_CONFLICT_THRESHOLDS, PipelineStage, ReconcileAction, ReconcileDecision, StageContext, StageOutcome, reconcileToConflictDecision } from "./types.js";
3
- import { _resetBypassWarningForTesting, createConflictPipeline, runConflictPipeline } from "./pipeline.js";
4
+ import { _resetBypassWarningForTesting, createConflictPipeline, runConflictPipeline } from "./pipeline.js";
5
+ export { type ConflictDecision, type ConflictPipeline, type ConflictPipelineDeps, type ConflictPipelineOptions, type ConflictStage, type ConflictThresholds, DEFAULT_CONFLICT_THRESHOLDS, LocaleMatch, LocalePack, LocalePatternEntry, LocaleSupersedeKind, type PipelineStage, type ReconcileAction, type ReconcileDecision, type StageContext, type StageOutcome, _resetBypassWarningForTesting, createConflictPipeline, defineLocalePack, enLocalePack, evaluateMarkers, reconcileToConflictDecision, runConflictPipeline };
@@ -4,4 +4,4 @@ import "./locale-packs/index.js";
4
4
  import { DEFAULT_CONFLICT_THRESHOLDS, reconcileToConflictDecision } from "./types.js";
5
5
  import { _resetBypassWarningForTesting, createConflictPipeline, runConflictPipeline } from "./pipeline.js";
6
6
 
7
- export { };
7
+ export { DEFAULT_CONFLICT_THRESHOLDS, _resetBypassWarningForTesting, createConflictPipeline, defineLocalePack, enLocalePack, evaluateMarkers, reconcileToConflictDecision, runConflictPipeline };
@@ -0,0 +1,13 @@
1
+ import { LocalePack } from "./types.js";
2
+
3
+ //#region src/conflict/locale-packs/en.d.ts
4
+
5
+ /**
6
+ * The default English `LocalePack`. Frozen - share the singleton.
7
+ *
8
+ * @stable
9
+ */
10
+ declare const enLocalePack: LocalePack;
11
+ //#endregion
12
+ export { enLocalePack };
13
+ //# sourceMappingURL=en.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"en.d.ts","names":[],"sources":["../../../src/conflict/locale-packs/en.ts"],"sourcesContent":[],"mappings":";;;;;;;;;cAqBa,cAAc"}
@@ -15,7 +15,7 @@ import { defineLocalePack } from "./types.js";
15
15
  * @packageDocumentation
16
16
  */
17
17
  /**
18
- * The default English `LocalePack`. Frozen share the singleton.
18
+ * The default English `LocalePack`. Frozen - share the singleton.
19
19
  *
20
20
  * @stable
21
21
  */
@@ -1 +1 @@
1
- {"version":3,"file":"en.js","names":["enLocalePack: LocalePack"],"sources":["../../../src/conflict/locale-packs/en.ts"],"sourcesContent":["/**\n * Bundled English locale pack used by the multi-stage conflict\n * resolution pipeline (DEC-117 / ADR-018 ext / RB-02). The patterns\n * cover the common change signals personal-assistant scenarios surface\n * on a daily basis: relocation, job change, preference flips,\n * relationship transitions, and health updates.\n *\n * Operators supplying a different locale register their own pack via\n * {@link defineLocalePack}. The framework ships English by default so\n * the pipeline always has a working baseline.\n *\n * @packageDocumentation\n */\n\nimport { defineLocalePack, type LocalePack } from './types.js';\n\n/**\n * The default English `LocalePack`. Frozen share the singleton.\n *\n * @stable\n */\nexport const enLocalePack: LocalePack = defineLocalePack({\n id: 'en',\n supersedeMarkers: [\n // Location.\n { regex: /\\bmoved (?:to|from)\\b/i, kind: 'location', confidence: 0.9 },\n {\n regex: /\\bnow (?:live|living|based|residing) (?:in|at)\\b/i,\n kind: 'location',\n confidence: 0.9,\n },\n { regex: /\\brelocated\\b/i, kind: 'location', confidence: 0.85 },\n { regex: /\\bjust moved\\b/i, kind: 'location', confidence: 0.85 },\n { regex: /\\bI live in\\b/i, kind: 'location', confidence: 0.7 },\n\n // Job.\n { regex: /\\b(?:got|started|landed)(?: a)?(?: new)? job\\b/i, kind: 'job', confidence: 0.9 },\n { regex: /\\bgot promoted\\b/i, kind: 'job', confidence: 0.9 },\n { regex: /\\bgot fired\\b/i, kind: 'job', confidence: 0.9 },\n { regex: /\\b(?:I )?quit (?:my )?job\\b/i, kind: 'job', confidence: 0.9 },\n { regex: /\\bnew employer\\b/i, kind: 'job', confidence: 0.85 },\n { regex: /\\bnew job\\b/i, kind: 'job', confidence: 0.8 },\n { regex: /\\bswitched (?:companies|jobs|employers)\\b/i, kind: 'job', confidence: 0.9 },\n { regex: /\\bnow work(?:s|ing)? (?:at|for)\\b/i, kind: 'job', confidence: 0.85 },\n\n // Preference.\n { regex: /\\bswitched (?:from|to)\\b/i, kind: 'preference', confidence: 0.85 },\n { regex: /\\bchanged my (?:mind|preference|opinion)\\b/i, kind: 'preference', confidence: 0.85 },\n {\n regex: /\\bused to (?:like|love|prefer|drink|eat|use)\\b/i,\n kind: 'preference',\n confidence: 0.85,\n },\n { regex: /\\bused to (?:live|work|study|play|travel)\\b/i, kind: 'generic', confidence: 0.85 },\n { regex: /\\bI prefer\\b/i, kind: 'preference', confidence: 0.6 },\n\n // Relationship.\n { regex: /\\bgot (?:married|divorced|engaged)\\b/i, kind: 'relationship', confidence: 0.9 },\n { regex: /\\bbroke up\\b/i, kind: 'relationship', confidence: 0.85 },\n\n // Health.\n { regex: /\\bdiagnosed (?:with|as)\\b/i, kind: 'health', confidence: 0.85 },\n { regex: /\\brecovered from\\b/i, kind: 'health', confidence: 0.85 },\n\n // Generic.\n { regex: /\\bformerly\\b/i, kind: 'generic', confidence: 0.7 },\n { regex: /\\bpreviously\\b/i, kind: 'generic', confidence: 0.7 },\n { regex: /\\bused to be\\b/i, kind: 'generic', confidence: 0.7 },\n { regex: /\\bis now\\b/i, kind: 'generic', confidence: 0.7 },\n ],\n negationMarkers: [\n { regex: /\\bno longer\\b/i, kind: 'preference', confidence: 0.85 },\n { regex: /\\bdoesn(?:'|\\u2019)?t\\b/i, confidence: 0.6 },\n {\n regex:\n /\\b(?:don(?:'|\\u2019)?t|do not) (?:like|prefer|use|drink|eat|live|work|play|go|want)\\b/i,\n kind: 'preference',\n confidence: 0.85,\n },\n { regex: /\\bnever\\b/i, confidence: 0.6 },\n { regex: /\\bnot anymore\\b/i, kind: 'preference', confidence: 0.85 },\n { regex: /\\bisn(?:'|\\u2019)?t\\b/i, confidence: 0.6 },\n {\n regex: /\\bnot (?:a fan|interested|willing|going|planning|in|into|looking) /i,\n kind: 'preference',\n confidence: 0.7,\n },\n { regex: /\\b(?:is|are|am|was|were) not\\b/i, confidence: 0.5 },\n ],\n predicateNormalisers: [\n 'is',\n 'are',\n 'am',\n 'was',\n 'were',\n 'be',\n 'been',\n 'being',\n 'has',\n 'have',\n 'had',\n 'do',\n 'does',\n 'did',\n 'lives',\n 'live',\n 'lived',\n 'living',\n 'works',\n 'work',\n 'worked',\n 'working',\n 'plays',\n 'play',\n 'played',\n 'playing',\n 'likes',\n 'like',\n 'liked',\n 'loves',\n 'love',\n 'loved',\n 'prefers',\n 'prefer',\n 'preferred',\n 'drinks',\n 'drink',\n 'drank',\n 'drunk',\n 'eats',\n 'eat',\n 'ate',\n 'eaten',\n 'in',\n 'at',\n 'on',\n 'with',\n 'for',\n 'to',\n ],\n subjectStopWords: ['a', 'an', 'the', 'my', 'your', 'his', 'her', 'their', 'our', 'this', 'that'],\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAqBA,MAAaA,eAA2B,iBAAiB;CACvD,IAAI;CACJ,kBAAkB;EAEhB;GAAE,OAAO;GAA0B,MAAM;GAAY,YAAY;GAAK;EACtE;GACE,OAAO;GACP,MAAM;GACN,YAAY;GACb;EACD;GAAE,OAAO;GAAkB,MAAM;GAAY,YAAY;GAAM;EAC/D;GAAE,OAAO;GAAmB,MAAM;GAAY,YAAY;GAAM;EAChE;GAAE,OAAO;GAAkB,MAAM;GAAY,YAAY;GAAK;EAG9D;GAAE,OAAO;GAAmD,MAAM;GAAO,YAAY;GAAK;EAC1F;GAAE,OAAO;GAAqB,MAAM;GAAO,YAAY;GAAK;EAC5D;GAAE,OAAO;GAAkB,MAAM;GAAO,YAAY;GAAK;EACzD;GAAE,OAAO;GAAgC,MAAM;GAAO,YAAY;GAAK;EACvE;GAAE,OAAO;GAAqB,MAAM;GAAO,YAAY;GAAM;EAC7D;GAAE,OAAO;GAAgB,MAAM;GAAO,YAAY;GAAK;EACvD;GAAE,OAAO;GAA8C,MAAM;GAAO,YAAY;GAAK;EACrF;GAAE,OAAO;GAAsC,MAAM;GAAO,YAAY;GAAM;EAG9E;GAAE,OAAO;GAA6B,MAAM;GAAc,YAAY;GAAM;EAC5E;GAAE,OAAO;GAA+C,MAAM;GAAc,YAAY;GAAM;EAC9F;GACE,OAAO;GACP,MAAM;GACN,YAAY;GACb;EACD;GAAE,OAAO;GAAgD,MAAM;GAAW,YAAY;GAAM;EAC5F;GAAE,OAAO;GAAiB,MAAM;GAAc,YAAY;GAAK;EAG/D;GAAE,OAAO;GAAyC,MAAM;GAAgB,YAAY;GAAK;EACzF;GAAE,OAAO;GAAiB,MAAM;GAAgB,YAAY;GAAM;EAGlE;GAAE,OAAO;GAA8B,MAAM;GAAU,YAAY;GAAM;EACzE;GAAE,OAAO;GAAuB,MAAM;GAAU,YAAY;GAAM;EAGlE;GAAE,OAAO;GAAiB,MAAM;GAAW,YAAY;GAAK;EAC5D;GAAE,OAAO;GAAmB,MAAM;GAAW,YAAY;GAAK;EAC9D;GAAE,OAAO;GAAmB,MAAM;GAAW,YAAY;GAAK;EAC9D;GAAE,OAAO;GAAe,MAAM;GAAW,YAAY;GAAK;EAC3D;CACD,iBAAiB;EACf;GAAE,OAAO;GAAkB,MAAM;GAAc,YAAY;GAAM;EACjE;GAAE,OAAO;GAA4B,YAAY;GAAK;EACtD;GACE,OACE;GACF,MAAM;GACN,YAAY;GACb;EACD;GAAE,OAAO;GAAc,YAAY;GAAK;EACxC;GAAE,OAAO;GAAoB,MAAM;GAAc,YAAY;GAAM;EACnE;GAAE,OAAO;GAA0B,YAAY;GAAK;EACpD;GACE,OAAO;GACP,MAAM;GACN,YAAY;GACb;EACD;GAAE,OAAO;GAAmC,YAAY;GAAK;EAC9D;CACD,sBAAsB;EACpB;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD;CACD,kBAAkB;EAAC;EAAK;EAAM;EAAO;EAAM;EAAQ;EAAO;EAAO;EAAS;EAAO;EAAQ;EAAO;CACjG,CAAC"}
1
+ {"version":3,"file":"en.js","names":["enLocalePack: LocalePack"],"sources":["../../../src/conflict/locale-packs/en.ts"],"sourcesContent":["/**\n * Bundled English locale pack used by the multi-stage conflict\n * resolution pipeline (DEC-117 / ADR-018 ext / RB-02). The patterns\n * cover the common change signals personal-assistant scenarios surface\n * on a daily basis: relocation, job change, preference flips,\n * relationship transitions, and health updates.\n *\n * Operators supplying a different locale register their own pack via\n * {@link defineLocalePack}. The framework ships English by default so\n * the pipeline always has a working baseline.\n *\n * @packageDocumentation\n */\n\nimport { defineLocalePack, type LocalePack } from './types.js';\n\n/**\n * The default English `LocalePack`. Frozen - share the singleton.\n *\n * @stable\n */\nexport const enLocalePack: LocalePack = defineLocalePack({\n id: 'en',\n supersedeMarkers: [\n // Location.\n { regex: /\\bmoved (?:to|from)\\b/i, kind: 'location', confidence: 0.9 },\n {\n regex: /\\bnow (?:live|living|based|residing) (?:in|at)\\b/i,\n kind: 'location',\n confidence: 0.9,\n },\n { regex: /\\brelocated\\b/i, kind: 'location', confidence: 0.85 },\n { regex: /\\bjust moved\\b/i, kind: 'location', confidence: 0.85 },\n { regex: /\\bI live in\\b/i, kind: 'location', confidence: 0.7 },\n\n // Job.\n { regex: /\\b(?:got|started|landed)(?: a)?(?: new)? job\\b/i, kind: 'job', confidence: 0.9 },\n { regex: /\\bgot promoted\\b/i, kind: 'job', confidence: 0.9 },\n { regex: /\\bgot fired\\b/i, kind: 'job', confidence: 0.9 },\n { regex: /\\b(?:I )?quit (?:my )?job\\b/i, kind: 'job', confidence: 0.9 },\n { regex: /\\bnew employer\\b/i, kind: 'job', confidence: 0.85 },\n { regex: /\\bnew job\\b/i, kind: 'job', confidence: 0.8 },\n { regex: /\\bswitched (?:companies|jobs|employers)\\b/i, kind: 'job', confidence: 0.9 },\n { regex: /\\bnow work(?:s|ing)? (?:at|for)\\b/i, kind: 'job', confidence: 0.85 },\n\n // Preference.\n { regex: /\\bswitched (?:from|to)\\b/i, kind: 'preference', confidence: 0.85 },\n { regex: /\\bchanged my (?:mind|preference|opinion)\\b/i, kind: 'preference', confidence: 0.85 },\n {\n regex: /\\bused to (?:like|love|prefer|drink|eat|use)\\b/i,\n kind: 'preference',\n confidence: 0.85,\n },\n { regex: /\\bused to (?:live|work|study|play|travel)\\b/i, kind: 'generic', confidence: 0.85 },\n { regex: /\\bI prefer\\b/i, kind: 'preference', confidence: 0.6 },\n\n // Relationship.\n { regex: /\\bgot (?:married|divorced|engaged)\\b/i, kind: 'relationship', confidence: 0.9 },\n { regex: /\\bbroke up\\b/i, kind: 'relationship', confidence: 0.85 },\n\n // Health.\n { regex: /\\bdiagnosed (?:with|as)\\b/i, kind: 'health', confidence: 0.85 },\n { regex: /\\brecovered from\\b/i, kind: 'health', confidence: 0.85 },\n\n // Generic.\n { regex: /\\bformerly\\b/i, kind: 'generic', confidence: 0.7 },\n { regex: /\\bpreviously\\b/i, kind: 'generic', confidence: 0.7 },\n { regex: /\\bused to be\\b/i, kind: 'generic', confidence: 0.7 },\n { regex: /\\bis now\\b/i, kind: 'generic', confidence: 0.7 },\n ],\n negationMarkers: [\n { regex: /\\bno longer\\b/i, kind: 'preference', confidence: 0.85 },\n { regex: /\\bdoesn(?:'|\\u2019)?t\\b/i, confidence: 0.6 },\n {\n regex:\n /\\b(?:don(?:'|\\u2019)?t|do not) (?:like|prefer|use|drink|eat|live|work|play|go|want)\\b/i,\n kind: 'preference',\n confidence: 0.85,\n },\n { regex: /\\bnever\\b/i, confidence: 0.6 },\n { regex: /\\bnot anymore\\b/i, kind: 'preference', confidence: 0.85 },\n { regex: /\\bisn(?:'|\\u2019)?t\\b/i, confidence: 0.6 },\n {\n regex: /\\bnot (?:a fan|interested|willing|going|planning|in|into|looking) /i,\n kind: 'preference',\n confidence: 0.7,\n },\n { regex: /\\b(?:is|are|am|was|were) not\\b/i, confidence: 0.5 },\n ],\n predicateNormalisers: [\n 'is',\n 'are',\n 'am',\n 'was',\n 'were',\n 'be',\n 'been',\n 'being',\n 'has',\n 'have',\n 'had',\n 'do',\n 'does',\n 'did',\n 'lives',\n 'live',\n 'lived',\n 'living',\n 'works',\n 'work',\n 'worked',\n 'working',\n 'plays',\n 'play',\n 'played',\n 'playing',\n 'likes',\n 'like',\n 'liked',\n 'loves',\n 'love',\n 'loved',\n 'prefers',\n 'prefer',\n 'preferred',\n 'drinks',\n 'drink',\n 'drank',\n 'drunk',\n 'eats',\n 'eat',\n 'ate',\n 'eaten',\n 'in',\n 'at',\n 'on',\n 'with',\n 'for',\n 'to',\n ],\n subjectStopWords: ['a', 'an', 'the', 'my', 'your', 'his', 'her', 'their', 'our', 'this', 'that'],\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAqBA,MAAaA,eAA2B,iBAAiB;CACvD,IAAI;CACJ,kBAAkB;EAEhB;GAAE,OAAO;GAA0B,MAAM;GAAY,YAAY;GAAK;EACtE;GACE,OAAO;GACP,MAAM;GACN,YAAY;GACb;EACD;GAAE,OAAO;GAAkB,MAAM;GAAY,YAAY;GAAM;EAC/D;GAAE,OAAO;GAAmB,MAAM;GAAY,YAAY;GAAM;EAChE;GAAE,OAAO;GAAkB,MAAM;GAAY,YAAY;GAAK;EAG9D;GAAE,OAAO;GAAmD,MAAM;GAAO,YAAY;GAAK;EAC1F;GAAE,OAAO;GAAqB,MAAM;GAAO,YAAY;GAAK;EAC5D;GAAE,OAAO;GAAkB,MAAM;GAAO,YAAY;GAAK;EACzD;GAAE,OAAO;GAAgC,MAAM;GAAO,YAAY;GAAK;EACvE;GAAE,OAAO;GAAqB,MAAM;GAAO,YAAY;GAAM;EAC7D;GAAE,OAAO;GAAgB,MAAM;GAAO,YAAY;GAAK;EACvD;GAAE,OAAO;GAA8C,MAAM;GAAO,YAAY;GAAK;EACrF;GAAE,OAAO;GAAsC,MAAM;GAAO,YAAY;GAAM;EAG9E;GAAE,OAAO;GAA6B,MAAM;GAAc,YAAY;GAAM;EAC5E;GAAE,OAAO;GAA+C,MAAM;GAAc,YAAY;GAAM;EAC9F;GACE,OAAO;GACP,MAAM;GACN,YAAY;GACb;EACD;GAAE,OAAO;GAAgD,MAAM;GAAW,YAAY;GAAM;EAC5F;GAAE,OAAO;GAAiB,MAAM;GAAc,YAAY;GAAK;EAG/D;GAAE,OAAO;GAAyC,MAAM;GAAgB,YAAY;GAAK;EACzF;GAAE,OAAO;GAAiB,MAAM;GAAgB,YAAY;GAAM;EAGlE;GAAE,OAAO;GAA8B,MAAM;GAAU,YAAY;GAAM;EACzE;GAAE,OAAO;GAAuB,MAAM;GAAU,YAAY;GAAM;EAGlE;GAAE,OAAO;GAAiB,MAAM;GAAW,YAAY;GAAK;EAC5D;GAAE,OAAO;GAAmB,MAAM;GAAW,YAAY;GAAK;EAC9D;GAAE,OAAO;GAAmB,MAAM;GAAW,YAAY;GAAK;EAC9D;GAAE,OAAO;GAAe,MAAM;GAAW,YAAY;GAAK;EAC3D;CACD,iBAAiB;EACf;GAAE,OAAO;GAAkB,MAAM;GAAc,YAAY;GAAM;EACjE;GAAE,OAAO;GAA4B,YAAY;GAAK;EACtD;GACE,OACE;GACF,MAAM;GACN,YAAY;GACb;EACD;GAAE,OAAO;GAAc,YAAY;GAAK;EACxC;GAAE,OAAO;GAAoB,MAAM;GAAc,YAAY;GAAM;EACnE;GAAE,OAAO;GAA0B,YAAY;GAAK;EACpD;GACE,OAAO;GACP,MAAM;GACN,YAAY;GACb;EACD;GAAE,OAAO;GAAmC,YAAY;GAAK;EAC9D;CACD,sBAAsB;EACpB;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD;CACD,kBAAkB;EAAC;EAAK;EAAM;EAAO;EAAM;EAAQ;EAAO;EAAO;EAAS;EAAO;EAAQ;EAAO;CACjG,CAAC"}
@@ -4,7 +4,7 @@ import { Fact } from "@graphorin/core";
4
4
  //#region src/conflict/pipeline.d.ts
5
5
 
6
6
  /**
7
- * Reset the one-shot bypass-warning flag. Test-only helper production
7
+ * Reset the one-shot bypass-warning flag. Test-only helper - production
8
8
  * callers never invoke this.
9
9
  *
10
10
  * @internal
@@ -12,7 +12,7 @@ import { Fact } from "@graphorin/core";
12
12
  declare function _resetBypassWarningForTesting(): void;
13
13
  /**
14
14
  * One-shot helper that mirrors RB-02 §8.1's `runConflictPipeline({...})`
15
- * spec convenient for callers that do not need to pre-build + cache
15
+ * spec - convenient for callers that do not need to pre-build + cache
16
16
  * the pipeline. Production wiring should still go through
17
17
  * {@link createConflictPipeline} (`SemanticMemory` re-uses the cached
18
18
  * instance per `Memory`).
@@ -11,7 +11,7 @@ import { DEFAULT_CONFLICT_THRESHOLDS } from "./types.js";
11
11
  //#region src/conflict/pipeline.ts
12
12
  let bypassWarnedOnce = false;
13
13
  /**
14
- * Reset the one-shot bypass-warning flag. Test-only helper production
14
+ * Reset the one-shot bypass-warning flag. Test-only helper - production
15
15
  * callers never invoke this.
16
16
  *
17
17
  * @internal
@@ -21,7 +21,7 @@ function _resetBypassWarningForTesting() {
21
21
  }
22
22
  /**
23
23
  * One-shot helper that mirrors RB-02 §8.1's `runConflictPipeline({...})`
24
- * spec convenient for callers that do not need to pre-build + cache
24
+ * spec - convenient for callers that do not need to pre-build + cache
25
25
  * the pipeline. Production wiring should still go through
26
26
  * {@link createConflictPipeline} (`SemanticMemory` re-uses the cached
27
27
  * instance per `Memory`).
@@ -249,7 +249,7 @@ function checkAbort(signal) {
249
249
  }
250
250
  }
251
251
  function warnBypass() {
252
- console.warn("[graphorin/memory] conflict pipeline disabled (mode: \"off\"). Semantic writes fall back to a straight-through path bi-temporal supersede / dedup will not fire until you re-enable the pipeline.");
252
+ console.warn("[graphorin/memory] conflict pipeline disabled (mode: \"off\"). Semantic writes fall back to a straight-through path - bi-temporal supersede / dedup will not fire until you re-enable the pipeline.");
253
253
  }
254
254
 
255
255
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"pipeline.js","names":["mode: 'on' | 'off'","localePack: LocalePack","thresholds: ConflictThresholds","auditInput: Parameters<NonNullable<typeof conflictStore>['recordDecision']>[0]","hits: ReadonlyArray<MemoryHit<Fact>>"],"sources":["../../src/conflict/pipeline.ts"],"sourcesContent":["/**\n * `runConflictPipeline` — orchestrator for the multi-stage conflict\n * resolution pipeline shipped in `@graphorin/memory` Phase 10b\n * (DEC-117 / ADR-018 ext / RB-02).\n *\n * @packageDocumentation\n */\n\nimport type { Fact, MemoryHit } from '@graphorin/core';\nimport { withMemorySpan } from '../internal/spans.js';\nimport { enLocalePack, type LocalePack } from './locale-packs/index.js';\nimport { stage1ExactDedup } from './stages/stage1-exact-dedup.js';\nimport { stage2EmbeddingThreeZone } from './stages/stage2-embedding-three-zone.js';\nimport { stage3HeuristicRegex } from './stages/stage3-heuristic-regex.js';\nimport { stage4SubjectPredicate } from './stages/stage4-subject-predicate.js';\nimport { stage5DeferToDeep } from './stages/stage5-defer-to-deep.js';\nimport {\n type ConflictDecision,\n type ConflictPipeline,\n type ConflictPipelineDeps,\n type ConflictPipelineOptions,\n type ConflictStage,\n type ConflictThresholds,\n DEFAULT_CONFLICT_THRESHOLDS,\n type StageContext,\n type StageOutcome,\n} from './types.js';\n\nlet bypassWarnedOnce = false;\n\n/**\n * Reset the one-shot bypass-warning flag. Test-only helper — production\n * callers never invoke this.\n *\n * @internal\n */\nexport function _resetBypassWarningForTesting(): void {\n bypassWarnedOnce = false;\n}\n\n/**\n * One-shot helper that mirrors RB-02 §8.1's `runConflictPipeline({...})`\n * spec — convenient for callers that do not need to pre-build + cache\n * the pipeline. Production wiring should still go through\n * {@link createConflictPipeline} (`SemanticMemory` re-uses the cached\n * instance per `Memory`).\n *\n * @stable\n */\nexport async function runConflictPipeline(args: {\n readonly candidate: Fact;\n readonly deps: ConflictPipelineDeps;\n readonly options?: ConflictPipelineOptions;\n}): Promise<ConflictDecision> {\n const pipeline = createConflictPipeline(args.options ?? {});\n return pipeline.run(args.deps, args.candidate);\n}\n\n/**\n * Returns a frozen pipeline handle wired with the supplied options.\n *\n * @stable\n */\nexport function createConflictPipeline(options: ConflictPipelineOptions = {}): ConflictPipeline {\n const mode: 'on' | 'off' = options.mode ?? 'on';\n const localePack: LocalePack = options.localePack ?? enLocalePack;\n const thresholds: ConflictThresholds = Object.freeze({\n hot: options.thresholds?.hot ?? DEFAULT_CONFLICT_THRESHOLDS.hot,\n nearDup: options.thresholds?.nearDup ?? DEFAULT_CONFLICT_THRESHOLDS.nearDup,\n cold: options.thresholds?.cold ?? DEFAULT_CONFLICT_THRESHOLDS.cold,\n });\n validateThresholds(thresholds);\n const stage2TopK = options.stage2TopK ?? 5;\n const now = options.now ?? ((): string => new Date().toISOString());\n\n async function run(deps: ConflictPipelineDeps, candidate: Fact): Promise<ConflictDecision> {\n if (mode === 'off') {\n if (!bypassWarnedOnce) {\n bypassWarnedOnce = true;\n warnBypass();\n }\n return { kind: 'admit', stage: 'exact-dedup', reason: 'pipeline-mode-off' };\n }\n\n checkAbort(deps.signal);\n const conflictStore = options.conflictStore ?? deps.store.conflicts;\n\n return withMemorySpan(\n deps.tracer,\n 'memory.conflict',\n {\n userId: candidate.userId,\n ...(candidate.sessionId !== undefined ? { sessionId: candidate.sessionId } : {}),\n ...(candidate.agentId !== undefined ? { agentId: candidate.agentId } : {}),\n },\n {\n 'memory.conflict.candidate_id': candidate.id,\n 'memory.conflict.locale_pack': localePack.id,\n },\n async (span) => {\n const existing = await collectVectorCandidates(deps, candidate, stage2TopK);\n const ctx: StageContext = {\n candidate,\n existing,\n localePack,\n thresholds,\n };\n const decision = await orchestrate(ctx);\n await persistDecision(decision, candidate, conflictStore, now);\n span.setAttributes({\n 'memory.conflict.stage': decision.stage,\n 'memory.conflict.decision': decision.kind,\n 'memory.conflict.candidate_count': existing.length,\n ...(decision.kind === 'dedup' && decision.similarity !== undefined\n ? { 'memory.conflict.similarity': decision.similarity }\n : {}),\n ...(decision.kind === 'pending' && decision.similarity !== undefined\n ? { 'memory.conflict.similarity': decision.similarity }\n : {}),\n ...(decision.kind !== 'admit'\n ? { 'memory.conflict.reason': decision.reason ?? 'unspecified' }\n : decision.reason !== undefined\n ? { 'memory.conflict.reason': decision.reason }\n : {}),\n });\n return decision;\n },\n );\n }\n\n /**\n * Visit the five stages in the order documented by RB-02 §8.1 and\n * the conflict-resolution policy (DEC-117). Stage 1 short-circuits\n * exact duplicates. Stage 2 always computes the zone but the\n * orchestrator splits its decision in two halves: the HOT zone is\n * the only zone that overrides Stage 3 / Stage 4 (semantic identity\n * outranks any explicit user marker); every other zone runs Stages\n * 3 and 4 first to give the heuristic markers a chance to supersede\n * before falling back to Stage 2's default decision.\n *\n * Stage 3 is declared \"orthogonal to Stage 2\", which is why the\n * heuristic stages run regardless of Stage 2's COLD / NEAR-DUP /\n * CONFLICT-CHECK assignment as long as Stage 2 surfaced at least\n * one candidate. The HOT-zone short-circuit preserves the dedup\n * invariant for semantic identity.\n */\n async function orchestrate(ctx: StageContext): Promise<ConflictDecision> {\n const stage1 = await stage1ExactDedup.evaluate(ctx);\n if (stage1.kind !== 'continue') {\n return mapOutcomeToDecision('exact-dedup', stage1, ctx.candidate);\n }\n\n const stage2 = await stage2EmbeddingThreeZone.evaluate(ctx);\n if (stage2.kind === 'dedup' && stage2.reason === 'embedding-hot-zone') {\n return mapOutcomeToDecision('embedding-three-zone', stage2, ctx.candidate);\n }\n\n if (ctx.existing.length > 0) {\n const stage3 = await stage3HeuristicRegex.evaluate(ctx);\n if (stage3.kind !== 'continue') {\n return mapOutcomeToDecision('heuristic-regex', stage3, ctx.candidate);\n }\n const stage4 = await stage4SubjectPredicate.evaluate(ctx);\n if (stage4.kind !== 'continue') {\n return mapOutcomeToDecision('subject-predicate', stage4, ctx.candidate);\n }\n }\n\n if (stage2.kind === 'dedup' || stage2.kind === 'admit') {\n return mapOutcomeToDecision('embedding-three-zone', stage2, ctx.candidate);\n }\n\n const stage5 = await stage5DeferToDeep.evaluate(ctx);\n return mapOutcomeToDecision('defer-to-deep', stage5, ctx.candidate);\n }\n\n return Object.freeze({\n mode,\n localePack,\n thresholds,\n run,\n }) satisfies ConflictPipeline;\n}\n\nfunction mapOutcomeToDecision(\n stage: ConflictStage,\n outcome: StageOutcome,\n candidate: Fact,\n): ConflictDecision {\n switch (outcome.kind) {\n case 'continue':\n return { kind: 'admit', stage, reason: 'continue-fallthrough' };\n case 'admit':\n return {\n kind: 'admit',\n stage,\n ...(outcome.reason !== undefined ? { reason: outcome.reason } : {}),\n };\n case 'dedup':\n return {\n kind: 'dedup',\n stage,\n existingId: outcome.existingId,\n ...(outcome.similarity !== undefined ? { similarity: outcome.similarity } : {}),\n ...(outcome.reason !== undefined ? { reason: outcome.reason } : {}),\n };\n case 'supersede':\n return {\n kind: 'supersede',\n stage,\n existingId: outcome.existingId,\n reason: outcome.reason,\n };\n case 'pending':\n return {\n kind: 'pending',\n stage,\n candidateId: candidate.id,\n conflictingIds: outcome.conflictingIds,\n ...(outcome.similarity !== undefined ? { similarity: outcome.similarity } : {}),\n ...(outcome.reason !== undefined ? { reason: outcome.reason } : {}),\n };\n }\n}\n\nasync function persistDecision(\n decision: ConflictDecision,\n candidate: Fact,\n conflictStore: ConflictPipelineDeps['store']['conflicts'],\n now: () => string,\n): Promise<void> {\n if (conflictStore === undefined) return;\n const scope = {\n userId: candidate.userId,\n ...(candidate.sessionId !== undefined ? { sessionId: candidate.sessionId } : {}),\n ...(candidate.agentId !== undefined ? { agentId: candidate.agentId } : {}),\n };\n const detectionZone = decisionToZone(decision);\n const auditInput: Parameters<NonNullable<typeof conflictStore>['recordDecision']>[0] = {\n scope,\n candidateId: candidate.id,\n decision: decision.kind,\n stage: decision.stage,\n ...(detectionZone !== undefined ? { detectionZone } : {}),\n ...(decision.kind === 'dedup' && decision.existingId !== undefined\n ? { existingId: decision.existingId }\n : {}),\n ...(decision.kind === 'supersede' ? { existingId: decision.existingId } : {}),\n ...(decision.kind === 'dedup' && decision.similarity !== undefined\n ? { similarity: decision.similarity }\n : {}),\n ...(decision.kind === 'pending' && decision.similarity !== undefined\n ? { similarity: decision.similarity }\n : {}),\n ...(decision.kind !== 'admit' && decision.reason !== undefined\n ? { reason: decision.reason }\n : {}),\n ...(decision.kind === 'admit' && decision.reason !== undefined\n ? { reason: decision.reason }\n : {}),\n };\n await conflictStore.recordDecision(auditInput);\n if (decision.kind === 'pending') {\n await conflictStore.enqueuePending({\n scope,\n factId: candidate.id,\n candidateText: candidate.text,\n stage: decision.stage,\n conflictingIds: decision.conflictingIds,\n ...(decision.reason !== undefined ? { reason: decision.reason } : {}),\n });\n }\n void now;\n}\n\nfunction decisionToZone(decision: ConflictDecision): string | undefined {\n switch (decision.kind) {\n case 'admit':\n if (decision.reason === 'embedding-cold-zone') return 'cold';\n return undefined;\n case 'dedup':\n if (decision.reason === 'embedding-hot-zone') return 'hot';\n if (decision.reason === 'embedding-near-dup-zone') return 'near-dup';\n if (decision.reason === 'exact-hash-match') return 'exact';\n if (decision.reason === 'subject-predicate-match-same-object') return 'subject-predicate';\n return undefined;\n case 'supersede':\n if (decision.reason.startsWith('regex-supersede-marker')) return 'heuristic';\n if (decision.reason.startsWith('regex-negation')) return 'heuristic';\n if (decision.reason.startsWith('subject-predicate-match')) return 'subject-predicate';\n return undefined;\n case 'pending':\n return 'conflict-check';\n }\n}\n\nasync function collectVectorCandidates(\n deps: ConflictPipelineDeps,\n candidate: Fact,\n topK: number,\n): Promise<ReadonlyArray<MemoryHit<Fact>>> {\n if (deps.embedder === null || deps.embedderId === null) return [];\n const adapter = deps.store.semantic;\n if (typeof adapter.searchVector !== 'function') return [];\n checkAbort(deps.signal);\n // PS-10: the candidate is compared against stored facts, which are embedded\n // as `passage` — keep it in the same space for the asymmetric (E5) embedder.\n const [vector] = await deps.embedder.embed([candidate.text], { taskType: 'passage' });\n if (vector === undefined) return [];\n checkAbort(deps.signal);\n const scope = {\n userId: candidate.userId,\n ...(candidate.sessionId !== undefined ? { sessionId: candidate.sessionId } : {}),\n ...(candidate.agentId !== undefined ? { agentId: candidate.agentId } : {}),\n };\n // Vector search may throw when the storage layer's vector extension\n // is missing or mis-configured (e.g. `sqlite-vec` not loaded). Fall\n // back to \"no candidates\" so the conflict pipeline degrades to the\n // 10a straight-through write rather than blocking the agent loop.\n // AbortError is re-raised so cancellation propagates up.\n let hits: ReadonlyArray<MemoryHit<Fact>>;\n try {\n hits = await adapter.searchVector(scope, vector, deps.embedderId, topK);\n } catch (err) {\n if (err instanceof Error && err.name === 'AbortError') throw err;\n return [];\n }\n return [...hits].filter((hit) => hit.record.id !== candidate.id);\n}\n\nfunction validateThresholds(thresholds: ConflictThresholds): void {\n if (\n !(\n thresholds.hot >= thresholds.nearDup &&\n thresholds.nearDup >= thresholds.cold &&\n thresholds.hot <= 1 &&\n thresholds.cold >= 0\n )\n ) {\n throw new TypeError(\n `[graphorin/memory] invalid conflict thresholds: expected 0 <= cold <= nearDup <= hot <= 1, got ${JSON.stringify(thresholds)}.`,\n );\n }\n}\n\n/**\n * Throw the canonical `AbortError` when the supplied signal is\n * already aborted. Safe to call with `undefined`.\n *\n * @internal\n */\nfunction checkAbort(signal: AbortSignal | undefined): void {\n if (signal === undefined) return;\n if (signal.aborted) {\n if (typeof signal.throwIfAborted === 'function') {\n signal.throwIfAborted();\n }\n throw new DOMException('The operation was aborted.', 'AbortError');\n }\n}\n\nfunction warnBypass(): void {\n // Use the structured logger once it lands (Phase 04 follow-up). For\n // now, surface a one-shot console.warn so operators notice the\n // regression risk per the Phase 10b spec (\"WARN-once per process\").\n console.warn(\n '[graphorin/memory] conflict pipeline disabled (mode: \"off\"). ' +\n 'Semantic writes fall back to a straight-through path — bi-temporal supersede / dedup ' +\n 'will not fire until you re-enable the pipeline.',\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"pipeline.js","names":["mode: 'on' | 'off'","localePack: LocalePack","thresholds: ConflictThresholds","auditInput: Parameters<NonNullable<typeof conflictStore>['recordDecision']>[0]","hits: ReadonlyArray<MemoryHit<Fact>>"],"sources":["../../src/conflict/pipeline.ts"],"sourcesContent":["/**\n * `runConflictPipeline` - orchestrator for the multi-stage conflict\n * resolution pipeline shipped in `@graphorin/memory` Phase 10b\n * (DEC-117 / ADR-018 ext / RB-02).\n *\n * @packageDocumentation\n */\n\nimport type { Fact, MemoryHit } from '@graphorin/core';\nimport { withMemorySpan } from '../internal/spans.js';\nimport { enLocalePack, type LocalePack } from './locale-packs/index.js';\nimport { stage1ExactDedup } from './stages/stage1-exact-dedup.js';\nimport { stage2EmbeddingThreeZone } from './stages/stage2-embedding-three-zone.js';\nimport { stage3HeuristicRegex } from './stages/stage3-heuristic-regex.js';\nimport { stage4SubjectPredicate } from './stages/stage4-subject-predicate.js';\nimport { stage5DeferToDeep } from './stages/stage5-defer-to-deep.js';\nimport {\n type ConflictDecision,\n type ConflictPipeline,\n type ConflictPipelineDeps,\n type ConflictPipelineOptions,\n type ConflictStage,\n type ConflictThresholds,\n DEFAULT_CONFLICT_THRESHOLDS,\n type StageContext,\n type StageOutcome,\n} from './types.js';\n\nlet bypassWarnedOnce = false;\n\n/**\n * Reset the one-shot bypass-warning flag. Test-only helper - production\n * callers never invoke this.\n *\n * @internal\n */\nexport function _resetBypassWarningForTesting(): void {\n bypassWarnedOnce = false;\n}\n\n/**\n * One-shot helper that mirrors RB-02 §8.1's `runConflictPipeline({...})`\n * spec - convenient for callers that do not need to pre-build + cache\n * the pipeline. Production wiring should still go through\n * {@link createConflictPipeline} (`SemanticMemory` re-uses the cached\n * instance per `Memory`).\n *\n * @stable\n */\nexport async function runConflictPipeline(args: {\n readonly candidate: Fact;\n readonly deps: ConflictPipelineDeps;\n readonly options?: ConflictPipelineOptions;\n}): Promise<ConflictDecision> {\n const pipeline = createConflictPipeline(args.options ?? {});\n return pipeline.run(args.deps, args.candidate);\n}\n\n/**\n * Returns a frozen pipeline handle wired with the supplied options.\n *\n * @stable\n */\nexport function createConflictPipeline(options: ConflictPipelineOptions = {}): ConflictPipeline {\n const mode: 'on' | 'off' = options.mode ?? 'on';\n const localePack: LocalePack = options.localePack ?? enLocalePack;\n const thresholds: ConflictThresholds = Object.freeze({\n hot: options.thresholds?.hot ?? DEFAULT_CONFLICT_THRESHOLDS.hot,\n nearDup: options.thresholds?.nearDup ?? DEFAULT_CONFLICT_THRESHOLDS.nearDup,\n cold: options.thresholds?.cold ?? DEFAULT_CONFLICT_THRESHOLDS.cold,\n });\n validateThresholds(thresholds);\n const stage2TopK = options.stage2TopK ?? 5;\n const now = options.now ?? ((): string => new Date().toISOString());\n\n async function run(deps: ConflictPipelineDeps, candidate: Fact): Promise<ConflictDecision> {\n if (mode === 'off') {\n if (!bypassWarnedOnce) {\n bypassWarnedOnce = true;\n warnBypass();\n }\n return { kind: 'admit', stage: 'exact-dedup', reason: 'pipeline-mode-off' };\n }\n\n checkAbort(deps.signal);\n const conflictStore = options.conflictStore ?? deps.store.conflicts;\n\n return withMemorySpan(\n deps.tracer,\n 'memory.conflict',\n {\n userId: candidate.userId,\n ...(candidate.sessionId !== undefined ? { sessionId: candidate.sessionId } : {}),\n ...(candidate.agentId !== undefined ? { agentId: candidate.agentId } : {}),\n },\n {\n 'memory.conflict.candidate_id': candidate.id,\n 'memory.conflict.locale_pack': localePack.id,\n },\n async (span) => {\n const existing = await collectVectorCandidates(deps, candidate, stage2TopK);\n const ctx: StageContext = {\n candidate,\n existing,\n localePack,\n thresholds,\n };\n const decision = await orchestrate(ctx);\n await persistDecision(decision, candidate, conflictStore, now);\n span.setAttributes({\n 'memory.conflict.stage': decision.stage,\n 'memory.conflict.decision': decision.kind,\n 'memory.conflict.candidate_count': existing.length,\n ...(decision.kind === 'dedup' && decision.similarity !== undefined\n ? { 'memory.conflict.similarity': decision.similarity }\n : {}),\n ...(decision.kind === 'pending' && decision.similarity !== undefined\n ? { 'memory.conflict.similarity': decision.similarity }\n : {}),\n ...(decision.kind !== 'admit'\n ? { 'memory.conflict.reason': decision.reason ?? 'unspecified' }\n : decision.reason !== undefined\n ? { 'memory.conflict.reason': decision.reason }\n : {}),\n });\n return decision;\n },\n );\n }\n\n /**\n * Visit the five stages in the order documented by RB-02 §8.1 and\n * the conflict-resolution policy (DEC-117). Stage 1 short-circuits\n * exact duplicates. Stage 2 always computes the zone but the\n * orchestrator splits its decision in two halves: the HOT zone is\n * the only zone that overrides Stage 3 / Stage 4 (semantic identity\n * outranks any explicit user marker); every other zone runs Stages\n * 3 and 4 first to give the heuristic markers a chance to supersede\n * before falling back to Stage 2's default decision.\n *\n * Stage 3 is declared \"orthogonal to Stage 2\", which is why the\n * heuristic stages run regardless of Stage 2's COLD / NEAR-DUP /\n * CONFLICT-CHECK assignment as long as Stage 2 surfaced at least\n * one candidate. The HOT-zone short-circuit preserves the dedup\n * invariant for semantic identity.\n */\n async function orchestrate(ctx: StageContext): Promise<ConflictDecision> {\n const stage1 = await stage1ExactDedup.evaluate(ctx);\n if (stage1.kind !== 'continue') {\n return mapOutcomeToDecision('exact-dedup', stage1, ctx.candidate);\n }\n\n const stage2 = await stage2EmbeddingThreeZone.evaluate(ctx);\n if (stage2.kind === 'dedup' && stage2.reason === 'embedding-hot-zone') {\n return mapOutcomeToDecision('embedding-three-zone', stage2, ctx.candidate);\n }\n\n if (ctx.existing.length > 0) {\n const stage3 = await stage3HeuristicRegex.evaluate(ctx);\n if (stage3.kind !== 'continue') {\n return mapOutcomeToDecision('heuristic-regex', stage3, ctx.candidate);\n }\n const stage4 = await stage4SubjectPredicate.evaluate(ctx);\n if (stage4.kind !== 'continue') {\n return mapOutcomeToDecision('subject-predicate', stage4, ctx.candidate);\n }\n }\n\n if (stage2.kind === 'dedup' || stage2.kind === 'admit') {\n return mapOutcomeToDecision('embedding-three-zone', stage2, ctx.candidate);\n }\n\n const stage5 = await stage5DeferToDeep.evaluate(ctx);\n return mapOutcomeToDecision('defer-to-deep', stage5, ctx.candidate);\n }\n\n return Object.freeze({\n mode,\n localePack,\n thresholds,\n run,\n }) satisfies ConflictPipeline;\n}\n\nfunction mapOutcomeToDecision(\n stage: ConflictStage,\n outcome: StageOutcome,\n candidate: Fact,\n): ConflictDecision {\n switch (outcome.kind) {\n case 'continue':\n return { kind: 'admit', stage, reason: 'continue-fallthrough' };\n case 'admit':\n return {\n kind: 'admit',\n stage,\n ...(outcome.reason !== undefined ? { reason: outcome.reason } : {}),\n };\n case 'dedup':\n return {\n kind: 'dedup',\n stage,\n existingId: outcome.existingId,\n ...(outcome.similarity !== undefined ? { similarity: outcome.similarity } : {}),\n ...(outcome.reason !== undefined ? { reason: outcome.reason } : {}),\n };\n case 'supersede':\n return {\n kind: 'supersede',\n stage,\n existingId: outcome.existingId,\n reason: outcome.reason,\n };\n case 'pending':\n return {\n kind: 'pending',\n stage,\n candidateId: candidate.id,\n conflictingIds: outcome.conflictingIds,\n ...(outcome.similarity !== undefined ? { similarity: outcome.similarity } : {}),\n ...(outcome.reason !== undefined ? { reason: outcome.reason } : {}),\n };\n }\n}\n\nasync function persistDecision(\n decision: ConflictDecision,\n candidate: Fact,\n conflictStore: ConflictPipelineDeps['store']['conflicts'],\n now: () => string,\n): Promise<void> {\n if (conflictStore === undefined) return;\n const scope = {\n userId: candidate.userId,\n ...(candidate.sessionId !== undefined ? { sessionId: candidate.sessionId } : {}),\n ...(candidate.agentId !== undefined ? { agentId: candidate.agentId } : {}),\n };\n const detectionZone = decisionToZone(decision);\n const auditInput: Parameters<NonNullable<typeof conflictStore>['recordDecision']>[0] = {\n scope,\n candidateId: candidate.id,\n decision: decision.kind,\n stage: decision.stage,\n ...(detectionZone !== undefined ? { detectionZone } : {}),\n ...(decision.kind === 'dedup' && decision.existingId !== undefined\n ? { existingId: decision.existingId }\n : {}),\n ...(decision.kind === 'supersede' ? { existingId: decision.existingId } : {}),\n ...(decision.kind === 'dedup' && decision.similarity !== undefined\n ? { similarity: decision.similarity }\n : {}),\n ...(decision.kind === 'pending' && decision.similarity !== undefined\n ? { similarity: decision.similarity }\n : {}),\n ...(decision.kind !== 'admit' && decision.reason !== undefined\n ? { reason: decision.reason }\n : {}),\n ...(decision.kind === 'admit' && decision.reason !== undefined\n ? { reason: decision.reason }\n : {}),\n };\n await conflictStore.recordDecision(auditInput);\n if (decision.kind === 'pending') {\n await conflictStore.enqueuePending({\n scope,\n factId: candidate.id,\n candidateText: candidate.text,\n stage: decision.stage,\n conflictingIds: decision.conflictingIds,\n ...(decision.reason !== undefined ? { reason: decision.reason } : {}),\n });\n }\n void now;\n}\n\nfunction decisionToZone(decision: ConflictDecision): string | undefined {\n switch (decision.kind) {\n case 'admit':\n if (decision.reason === 'embedding-cold-zone') return 'cold';\n return undefined;\n case 'dedup':\n if (decision.reason === 'embedding-hot-zone') return 'hot';\n if (decision.reason === 'embedding-near-dup-zone') return 'near-dup';\n if (decision.reason === 'exact-hash-match') return 'exact';\n if (decision.reason === 'subject-predicate-match-same-object') return 'subject-predicate';\n return undefined;\n case 'supersede':\n if (decision.reason.startsWith('regex-supersede-marker')) return 'heuristic';\n if (decision.reason.startsWith('regex-negation')) return 'heuristic';\n if (decision.reason.startsWith('subject-predicate-match')) return 'subject-predicate';\n return undefined;\n case 'pending':\n return 'conflict-check';\n }\n}\n\nasync function collectVectorCandidates(\n deps: ConflictPipelineDeps,\n candidate: Fact,\n topK: number,\n): Promise<ReadonlyArray<MemoryHit<Fact>>> {\n if (deps.embedder === null || deps.embedderId === null) return [];\n const adapter = deps.store.semantic;\n if (typeof adapter.searchVector !== 'function') return [];\n checkAbort(deps.signal);\n // PS-10: the candidate is compared against stored facts, which are embedded\n // as `passage` - keep it in the same space for the asymmetric (E5) embedder.\n const [vector] = await deps.embedder.embed([candidate.text], { taskType: 'passage' });\n if (vector === undefined) return [];\n checkAbort(deps.signal);\n const scope = {\n userId: candidate.userId,\n ...(candidate.sessionId !== undefined ? { sessionId: candidate.sessionId } : {}),\n ...(candidate.agentId !== undefined ? { agentId: candidate.agentId } : {}),\n };\n // Vector search may throw when the storage layer's vector extension\n // is missing or mis-configured (e.g. `sqlite-vec` not loaded). Fall\n // back to \"no candidates\" so the conflict pipeline degrades to the\n // 10a straight-through write rather than blocking the agent loop.\n // AbortError is re-raised so cancellation propagates up.\n let hits: ReadonlyArray<MemoryHit<Fact>>;\n try {\n hits = await adapter.searchVector(scope, vector, deps.embedderId, topK);\n } catch (err) {\n if (err instanceof Error && err.name === 'AbortError') throw err;\n return [];\n }\n return [...hits].filter((hit) => hit.record.id !== candidate.id);\n}\n\nfunction validateThresholds(thresholds: ConflictThresholds): void {\n if (\n !(\n thresholds.hot >= thresholds.nearDup &&\n thresholds.nearDup >= thresholds.cold &&\n thresholds.hot <= 1 &&\n thresholds.cold >= 0\n )\n ) {\n throw new TypeError(\n `[graphorin/memory] invalid conflict thresholds: expected 0 <= cold <= nearDup <= hot <= 1, got ${JSON.stringify(thresholds)}.`,\n );\n }\n}\n\n/**\n * Throw the canonical `AbortError` when the supplied signal is\n * already aborted. Safe to call with `undefined`.\n *\n * @internal\n */\nfunction checkAbort(signal: AbortSignal | undefined): void {\n if (signal === undefined) return;\n if (signal.aborted) {\n if (typeof signal.throwIfAborted === 'function') {\n signal.throwIfAborted();\n }\n throw new DOMException('The operation was aborted.', 'AbortError');\n }\n}\n\nfunction warnBypass(): void {\n // Use the structured logger once it lands (Phase 04 follow-up). For\n // now, surface a one-shot console.warn so operators notice the\n // regression risk per the Phase 10b spec (\"WARN-once per process\").\n console.warn(\n '[graphorin/memory] conflict pipeline disabled (mode: \"off\"). ' +\n 'Semantic writes fall back to a straight-through path - bi-temporal supersede / dedup ' +\n 'will not fire until you re-enable the pipeline.',\n );\n}\n"],"mappings":"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"}