@graphorin/memory 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (287) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/LICENSE +21 -0
  3. package/README.md +401 -0
  4. package/dist/conflict/index.d.ts +3 -0
  5. package/dist/conflict/index.js +7 -0
  6. package/dist/conflict/locale-packs/en.js +262 -0
  7. package/dist/conflict/locale-packs/en.js.map +1 -0
  8. package/dist/conflict/locale-packs/index.js +4 -0
  9. package/dist/conflict/locale-packs/types.d.ts +103 -0
  10. package/dist/conflict/locale-packs/types.d.ts.map +1 -0
  11. package/dist/conflict/locale-packs/types.js +55 -0
  12. package/dist/conflict/locale-packs/types.js.map +1 -0
  13. package/dist/conflict/pipeline.d.ts +35 -0
  14. package/dist/conflict/pipeline.d.ts.map +1 -0
  15. package/dist/conflict/pipeline.js +257 -0
  16. package/dist/conflict/pipeline.js.map +1 -0
  17. package/dist/conflict/stages/helpers.js +66 -0
  18. package/dist/conflict/stages/helpers.js.map +1 -0
  19. package/dist/conflict/stages/stage1-exact-dedup.js +21 -0
  20. package/dist/conflict/stages/stage1-exact-dedup.js.map +1 -0
  21. package/dist/conflict/stages/stage2-embedding-three-zone.js +37 -0
  22. package/dist/conflict/stages/stage2-embedding-three-zone.js.map +1 -0
  23. package/dist/conflict/stages/stage3-heuristic-regex.js +45 -0
  24. package/dist/conflict/stages/stage3-heuristic-regex.js.map +1 -0
  25. package/dist/conflict/stages/stage4-subject-predicate.js +31 -0
  26. package/dist/conflict/stages/stage4-subject-predicate.js.map +1 -0
  27. package/dist/conflict/stages/stage5-defer-to-deep.js +22 -0
  28. package/dist/conflict/stages/stage5-defer-to-deep.js.map +1 -0
  29. package/dist/conflict/types.d.ts +222 -0
  30. package/dist/conflict/types.d.ts.map +1 -0
  31. package/dist/conflict/types.js +50 -0
  32. package/dist/conflict/types.js.map +1 -0
  33. package/dist/consolidator/budget.d.ts +1 -0
  34. package/dist/consolidator/budget.js +214 -0
  35. package/dist/consolidator/budget.js.map +1 -0
  36. package/dist/consolidator/decay.d.ts +35 -0
  37. package/dist/consolidator/decay.d.ts.map +1 -0
  38. package/dist/consolidator/decay.js +91 -0
  39. package/dist/consolidator/decay.js.map +1 -0
  40. package/dist/consolidator/dlq.js +58 -0
  41. package/dist/consolidator/dlq.js.map +1 -0
  42. package/dist/consolidator/errors.d.ts +54 -0
  43. package/dist/consolidator/errors.d.ts.map +1 -0
  44. package/dist/consolidator/errors.js +67 -0
  45. package/dist/consolidator/errors.js.map +1 -0
  46. package/dist/consolidator/idempotency.d.ts +1 -0
  47. package/dist/consolidator/idempotency.js +22 -0
  48. package/dist/consolidator/idempotency.js.map +1 -0
  49. package/dist/consolidator/index.d.ts +34 -0
  50. package/dist/consolidator/index.d.ts.map +1 -0
  51. package/dist/consolidator/index.js +163 -0
  52. package/dist/consolidator/index.js.map +1 -0
  53. package/dist/consolidator/lock.d.ts +2 -0
  54. package/dist/consolidator/lock.js +89 -0
  55. package/dist/consolidator/lock.js.map +1 -0
  56. package/dist/consolidator/noise-filter.d.ts +2 -0
  57. package/dist/consolidator/noise-filter.js +173 -0
  58. package/dist/consolidator/noise-filter.js.map +1 -0
  59. package/dist/consolidator/phases/deep.d.ts +4 -0
  60. package/dist/consolidator/phases/deep.js +198 -0
  61. package/dist/consolidator/phases/deep.js.map +1 -0
  62. package/dist/consolidator/phases/induce.d.ts +154 -0
  63. package/dist/consolidator/phases/induce.d.ts.map +1 -0
  64. package/dist/consolidator/phases/induce.js +253 -0
  65. package/dist/consolidator/phases/induce.js.map +1 -0
  66. package/dist/consolidator/phases/light.d.ts +4 -0
  67. package/dist/consolidator/phases/light.js +99 -0
  68. package/dist/consolidator/phases/light.js.map +1 -0
  69. package/dist/consolidator/phases/reflect.js +287 -0
  70. package/dist/consolidator/phases/reflect.js.map +1 -0
  71. package/dist/consolidator/phases/standard.d.ts +8 -0
  72. package/dist/consolidator/phases/standard.js +499 -0
  73. package/dist/consolidator/phases/standard.js.map +1 -0
  74. package/dist/consolidator/reconcile.js +192 -0
  75. package/dist/consolidator/reconcile.js.map +1 -0
  76. package/dist/consolidator/runtime.d.ts +62 -0
  77. package/dist/consolidator/runtime.d.ts.map +1 -0
  78. package/dist/consolidator/runtime.js +580 -0
  79. package/dist/consolidator/runtime.js.map +1 -0
  80. package/dist/consolidator/scheduler.d.ts +111 -0
  81. package/dist/consolidator/scheduler.d.ts.map +1 -0
  82. package/dist/consolidator/scheduler.js +71 -0
  83. package/dist/consolidator/scheduler.js.map +1 -0
  84. package/dist/consolidator/triggers.d.ts +40 -0
  85. package/dist/consolidator/triggers.d.ts.map +1 -0
  86. package/dist/consolidator/triggers.js +94 -0
  87. package/dist/consolidator/triggers.js.map +1 -0
  88. package/dist/consolidator/types.d.ts +397 -0
  89. package/dist/consolidator/types.d.ts.map +1 -0
  90. package/dist/consolidator/types.js +117 -0
  91. package/dist/consolidator/types.js.map +1 -0
  92. package/dist/context-engine/annotations.d.ts +118 -0
  93. package/dist/context-engine/annotations.d.ts.map +1 -0
  94. package/dist/context-engine/annotations.js +102 -0
  95. package/dist/context-engine/annotations.js.map +1 -0
  96. package/dist/context-engine/auto-recall.d.ts +52 -0
  97. package/dist/context-engine/auto-recall.d.ts.map +1 -0
  98. package/dist/context-engine/auto-recall.js +41 -0
  99. package/dist/context-engine/auto-recall.js.map +1 -0
  100. package/dist/context-engine/compaction/clear-tool-results.d.ts +55 -0
  101. package/dist/context-engine/compaction/clear-tool-results.d.ts.map +1 -0
  102. package/dist/context-engine/compaction/clear-tool-results.js +97 -0
  103. package/dist/context-engine/compaction/clear-tool-results.js.map +1 -0
  104. package/dist/context-engine/compaction/compactor.d.ts +52 -0
  105. package/dist/context-engine/compaction/compactor.d.ts.map +1 -0
  106. package/dist/context-engine/compaction/compactor.js +196 -0
  107. package/dist/context-engine/compaction/compactor.js.map +1 -0
  108. package/dist/context-engine/compaction/hooks/reanchor-persona-block.d.ts +15 -0
  109. package/dist/context-engine/compaction/hooks/reanchor-persona-block.d.ts.map +1 -0
  110. package/dist/context-engine/compaction/hooks/reanchor-persona-block.js +27 -0
  111. package/dist/context-engine/compaction/hooks/reanchor-persona-block.js.map +1 -0
  112. package/dist/context-engine/compaction/hooks/reanchor-pinned-facts.d.ts +18 -0
  113. package/dist/context-engine/compaction/hooks/reanchor-pinned-facts.d.ts.map +1 -0
  114. package/dist/context-engine/compaction/hooks/reanchor-pinned-facts.js +47 -0
  115. package/dist/context-engine/compaction/hooks/reanchor-pinned-facts.js.map +1 -0
  116. package/dist/context-engine/compaction/hooks/reanchor-project-rules.d.ts +15 -0
  117. package/dist/context-engine/compaction/hooks/reanchor-project-rules.d.ts.map +1 -0
  118. package/dist/context-engine/compaction/hooks/reanchor-project-rules.js +32 -0
  119. package/dist/context-engine/compaction/hooks/reanchor-project-rules.js.map +1 -0
  120. package/dist/context-engine/compaction/hooks/types.d.ts +42 -0
  121. package/dist/context-engine/compaction/hooks/types.d.ts.map +1 -0
  122. package/dist/context-engine/compaction/index.d.ts +9 -0
  123. package/dist/context-engine/compaction/index.js +9 -0
  124. package/dist/context-engine/compaction/templates/summary-9-section.d.ts +84 -0
  125. package/dist/context-engine/compaction/templates/summary-9-section.d.ts.map +1 -0
  126. package/dist/context-engine/compaction/templates/summary-9-section.js +84 -0
  127. package/dist/context-engine/compaction/templates/summary-9-section.js.map +1 -0
  128. package/dist/context-engine/compaction/thresholds.d.ts +68 -0
  129. package/dist/context-engine/compaction/thresholds.d.ts.map +1 -0
  130. package/dist/context-engine/compaction/thresholds.js +63 -0
  131. package/dist/context-engine/compaction/thresholds.js.map +1 -0
  132. package/dist/context-engine/compaction/types.d.ts +202 -0
  133. package/dist/context-engine/compaction/types.d.ts.map +1 -0
  134. package/dist/context-engine/engine.d.ts +258 -0
  135. package/dist/context-engine/engine.d.ts.map +1 -0
  136. package/dist/context-engine/engine.js +424 -0
  137. package/dist/context-engine/engine.js.map +1 -0
  138. package/dist/context-engine/index.d.ts +68 -0
  139. package/dist/context-engine/index.d.ts.map +1 -0
  140. package/dist/context-engine/index.js +24 -0
  141. package/dist/context-engine/locale-packs/en.d.ts +14 -0
  142. package/dist/context-engine/locale-packs/en.d.ts.map +1 -0
  143. package/dist/context-engine/locale-packs/en.js +87 -0
  144. package/dist/context-engine/locale-packs/en.js.map +1 -0
  145. package/dist/context-engine/locale-packs/index.js +4 -0
  146. package/dist/context-engine/locale-packs/resolver.d.ts +41 -0
  147. package/dist/context-engine/locale-packs/resolver.d.ts.map +1 -0
  148. package/dist/context-engine/locale-packs/resolver.js +119 -0
  149. package/dist/context-engine/locale-packs/resolver.js.map +1 -0
  150. package/dist/context-engine/locale-packs/types.d.ts +119 -0
  151. package/dist/context-engine/locale-packs/types.d.ts.map +1 -0
  152. package/dist/context-engine/locale-packs/types.js +24 -0
  153. package/dist/context-engine/locale-packs/types.js.map +1 -0
  154. package/dist/context-engine/metadata.d.ts +40 -0
  155. package/dist/context-engine/metadata.d.ts.map +1 -0
  156. package/dist/context-engine/metadata.js +66 -0
  157. package/dist/context-engine/metadata.js.map +1 -0
  158. package/dist/context-engine/preambles/inbound-en.d.ts +20 -0
  159. package/dist/context-engine/preambles/inbound-en.d.ts.map +1 -0
  160. package/dist/context-engine/preambles/inbound-en.js +23 -0
  161. package/dist/context-engine/preambles/inbound-en.js.map +1 -0
  162. package/dist/context-engine/privacy-filter.d.ts +108 -0
  163. package/dist/context-engine/privacy-filter.d.ts.map +1 -0
  164. package/dist/context-engine/privacy-filter.js +113 -0
  165. package/dist/context-engine/privacy-filter.js.map +1 -0
  166. package/dist/context-engine/templates/base-en.d.ts +19 -0
  167. package/dist/context-engine/templates/base-en.d.ts.map +1 -0
  168. package/dist/context-engine/templates/base-en.js +22 -0
  169. package/dist/context-engine/templates/base-en.js.map +1 -0
  170. package/dist/context-engine/templates/composer.d.ts +55 -0
  171. package/dist/context-engine/templates/composer.d.ts.map +1 -0
  172. package/dist/context-engine/templates/composer.js +51 -0
  173. package/dist/context-engine/templates/composer.js.map +1 -0
  174. package/dist/context-engine/token-budget.d.ts +99 -0
  175. package/dist/context-engine/token-budget.d.ts.map +1 -0
  176. package/dist/context-engine/token-budget.js +211 -0
  177. package/dist/context-engine/token-budget.js.map +1 -0
  178. package/dist/context-engine/token-counter.d.ts +54 -0
  179. package/dist/context-engine/token-counter.d.ts.map +1 -0
  180. package/dist/context-engine/token-counter.js +81 -0
  181. package/dist/context-engine/token-counter.js.map +1 -0
  182. package/dist/errors/index.d.ts +171 -0
  183. package/dist/errors/index.d.ts.map +1 -0
  184. package/dist/errors/index.js +206 -0
  185. package/dist/errors/index.js.map +1 -0
  186. package/dist/facade.d.ts +278 -0
  187. package/dist/facade.d.ts.map +1 -0
  188. package/dist/facade.js +265 -0
  189. package/dist/facade.js.map +1 -0
  190. package/dist/graph/entity-resolver.d.ts +135 -0
  191. package/dist/graph/entity-resolver.d.ts.map +1 -0
  192. package/dist/graph/entity-resolver.js +202 -0
  193. package/dist/graph/entity-resolver.js.map +1 -0
  194. package/dist/graph/index.d.ts +1 -0
  195. package/dist/index.d.ts +103 -0
  196. package/dist/index.d.ts.map +1 -0
  197. package/dist/index.js +95 -0
  198. package/dist/index.js.map +1 -0
  199. package/dist/internal/contextualize.d.ts +14 -0
  200. package/dist/internal/contextualize.d.ts.map +1 -0
  201. package/dist/internal/contextualize.js +110 -0
  202. package/dist/internal/contextualize.js.map +1 -0
  203. package/dist/internal/embedder-binding.js +50 -0
  204. package/dist/internal/embedder-binding.js.map +1 -0
  205. package/dist/internal/id.js +24 -0
  206. package/dist/internal/id.js.map +1 -0
  207. package/dist/internal/injection-heuristics.js +64 -0
  208. package/dist/internal/injection-heuristics.js.map +1 -0
  209. package/dist/internal/spans.js +35 -0
  210. package/dist/internal/spans.js.map +1 -0
  211. package/dist/internal/storage-adapter.d.ts +742 -0
  212. package/dist/internal/storage-adapter.d.ts.map +1 -0
  213. package/dist/migration/embedder-migration.d.ts +124 -0
  214. package/dist/migration/embedder-migration.d.ts.map +1 -0
  215. package/dist/migration/embedder-migration.js +109 -0
  216. package/dist/migration/embedder-migration.js.map +1 -0
  217. package/dist/migration/index.d.ts +2 -0
  218. package/dist/migration/index.js +3 -0
  219. package/dist/search/explain.d.ts +74 -0
  220. package/dist/search/explain.d.ts.map +1 -0
  221. package/dist/search/explain.js +50 -0
  222. package/dist/search/explain.js.map +1 -0
  223. package/dist/search/index.d.ts +6 -0
  224. package/dist/search/index.js +6 -0
  225. package/dist/search/iterative.d.ts +223 -0
  226. package/dist/search/iterative.d.ts.map +1 -0
  227. package/dist/search/iterative.js +330 -0
  228. package/dist/search/iterative.js.map +1 -0
  229. package/dist/search/query-transform.d.ts +112 -0
  230. package/dist/search/query-transform.d.ts.map +1 -0
  231. package/dist/search/query-transform.js +184 -0
  232. package/dist/search/query-transform.js.map +1 -0
  233. package/dist/search/rrf.d.ts +95 -0
  234. package/dist/search/rrf.d.ts.map +1 -0
  235. package/dist/search/rrf.js +166 -0
  236. package/dist/search/rrf.js.map +1 -0
  237. package/dist/search/types.d.ts +50 -0
  238. package/dist/search/types.d.ts.map +1 -0
  239. package/dist/tiers/episodic-memory.d.ts +142 -0
  240. package/dist/tiers/episodic-memory.d.ts.map +1 -0
  241. package/dist/tiers/episodic-memory.js +254 -0
  242. package/dist/tiers/episodic-memory.js.map +1 -0
  243. package/dist/tiers/index.d.ts +8 -0
  244. package/dist/tiers/index.js +9 -0
  245. package/dist/tiers/insight-memory.d.ts +71 -0
  246. package/dist/tiers/insight-memory.d.ts.map +1 -0
  247. package/dist/tiers/insight-memory.js +133 -0
  248. package/dist/tiers/insight-memory.js.map +1 -0
  249. package/dist/tiers/procedural-memory.d.ts +147 -0
  250. package/dist/tiers/procedural-memory.d.ts.map +1 -0
  251. package/dist/tiers/procedural-memory.js +267 -0
  252. package/dist/tiers/procedural-memory.js.map +1 -0
  253. package/dist/tiers/semantic-memory.d.ts +461 -0
  254. package/dist/tiers/semantic-memory.d.ts.map +1 -0
  255. package/dist/tiers/semantic-memory.js +631 -0
  256. package/dist/tiers/semantic-memory.js.map +1 -0
  257. package/dist/tiers/session-memory.d.ts +117 -0
  258. package/dist/tiers/session-memory.d.ts.map +1 -0
  259. package/dist/tiers/session-memory.js +163 -0
  260. package/dist/tiers/session-memory.js.map +1 -0
  261. package/dist/tiers/shared-memory.d.ts +29 -0
  262. package/dist/tiers/shared-memory.d.ts.map +1 -0
  263. package/dist/tiers/shared-memory.js +58 -0
  264. package/dist/tiers/shared-memory.js.map +1 -0
  265. package/dist/tiers/working-memory.d.ts +129 -0
  266. package/dist/tiers/working-memory.d.ts.map +1 -0
  267. package/dist/tiers/working-memory.js +229 -0
  268. package/dist/tiers/working-memory.js.map +1 -0
  269. package/dist/tools/block-tools.d.ts +98 -0
  270. package/dist/tools/block-tools.d.ts.map +1 -0
  271. package/dist/tools/block-tools.js +114 -0
  272. package/dist/tools/block-tools.js.map +1 -0
  273. package/dist/tools/fact-tools.d.ts +294 -0
  274. package/dist/tools/fact-tools.d.ts.map +1 -0
  275. package/dist/tools/fact-tools.js +293 -0
  276. package/dist/tools/fact-tools.js.map +1 -0
  277. package/dist/tools/index.d.ts +35 -0
  278. package/dist/tools/index.d.ts.map +1 -0
  279. package/dist/tools/index.js +36 -0
  280. package/dist/tools/index.js.map +1 -0
  281. package/dist/tools/recall-tools.d.ts +209 -0
  282. package/dist/tools/recall-tools.d.ts.map +1 -0
  283. package/dist/tools/recall-tools.js +170 -0
  284. package/dist/tools/recall-tools.js.map +1 -0
  285. package/dist/tools/types.d.ts +40 -0
  286. package/dist/tools/types.d.ts.map +1 -0
  287. package/package.json +119 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,15 @@
1
+ # @graphorin/memory
2
+
3
+ ## 0.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Phase 10a — initial release of `@graphorin/memory`. Ships the
8
+ `createMemory()` facade with six tier sub-modules (working,
9
+ session, episodic, semantic, procedural, shared), nine memory
10
+ tools registered through `@graphorin/tools`, the built-in
11
+ `RRFReranker` with a pluggable `setReranker(...)` hook, the
12
+ embedder migration runner with `lock-on-first` / `multi-active` /
13
+ `auto-migrate` strategies, and the `compile()` / `metadata()` /
14
+ `consolidator` interface stubs picked up by Phases 10b / 10c /
15
+ 10d.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Oleksiy Stepurenko
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,401 @@
1
+ # @graphorin/memory
2
+
3
+ > Six-tier memory system for the Graphorin framework — the heart of any
4
+ > long-living personal AI assistant built on top of Graphorin.
5
+
6
+ `@graphorin/memory` ships the `createMemory()` facade, the six tier
7
+ sub-modules (working, session, episodic, semantic, procedural, shared),
8
+ the eleven memory tools that the agent runtime registers with
9
+ `@graphorin/tools`, the built-in Reciprocal Rank Fusion reranker (k=60),
10
+ the embedder migration runner, and the interface stubs picked up later
11
+ by the conflict-resolution pipeline, the consolidator, and the context
12
+ engine.
13
+
14
+ The package depends on:
15
+
16
+ - `@graphorin/core` — typed contracts (`MemoryStore`, `EmbedderProvider`,
17
+ `Tool`, `Tracer`, `Sensitivity`, …).
18
+ - `@graphorin/observability` — the `Tracer` used to emit one `AISpan`
19
+ per memory operation.
20
+ - `@graphorin/security` — the memory-modification guard (`MemoryGuardTier`)
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.
25
+
26
+ Storage is provided by any `MemoryStore` implementation; the default
27
+ production adapter is `@graphorin/store-sqlite` (built on
28
+ `better-sqlite3@^12.9.0` + `sqlite-vec@~0.1.9`). Embeddings come from
29
+ any `EmbedderProvider`; the default is
30
+ `@graphorin/embedder-transformersjs` (multilingual e5 base, 768-dim).
31
+
32
+ ## Highlights
33
+
34
+ - **`createMemory({ store, embedder, ...})` facade.** Wires the six
35
+ tiers + the tools array + the `compile()` / `metadata()` /
36
+ `consolidator` interface stubs in one call.
37
+ - **Six tier sub-modules.** Every tier exposes a typed CRUD surface and
38
+ emits one `AISpan` per operation. Writes are append-only (soft-delete
39
+ via `deletedAt` / `validTo`); supersede chains are first-class.
40
+ - **Hybrid search.** Semantic memory composes vector + FTS5 results
41
+ through the built-in `RRFReranker` (k=60 default). The
42
+ `setReranker(custom)` hook accepts any `ReRanker` implementation
43
+ (cross-encoder, LLM judge, custom).
44
+ - **Eleven memory tools.** `block_append`, `block_replace`,
45
+ `block_rethink`, `fact_remember`, `fact_search`, `fact_supersede`,
46
+ `fact_forget`, `recall_episodes`, `conversation_search`,
47
+ `fact_history`, `fact_validate` — every tool is a typed `Tool` with
48
+ `inputSchema` + `outputSchema`, the appropriate `memoryGuardTier`, and
49
+ the right `sideEffectClass`. `fact_search` accepts an `asOf` instant
50
+ for point-in-time reads; `fact_history` returns a fact's bi-temporal
51
+ supersede chain; `fact_validate` promotes a quarantined fact to active.
52
+ - **Provenance + quarantine (memory-safety gate).** Every fact carries
53
+ a `provenance` tag (`user` / `tool` / `extraction` / `reflection` /
54
+ `imported`) and a retrieval-trust `status`. *Derived* writes
55
+ (consolidator extraction, future reflection) and candidates that trip
56
+ the offline injection heuristics (`ignore previous instructions`,
57
+ role-markup smuggling, secrecy / exfiltration directives) land
58
+ `status: 'quarantined'` and are **excluded from default recall** until
59
+ a human promotes them via `fact_validate`. Quarantine is a retrieval
60
+ gate, never a delete — quarantined rows stay fully auditable, and the
61
+ `includeQuarantined` search option surfaces them for the validation
62
+ UI. This is the precondition for safely shipping synthesized memory
63
+ (reflection / reconciliation / induction) against memory-poisoning
64
+ (MINJA, MemoryGraft).
65
+ - **Multi-stage conflict resolution pipeline.** Every
66
+ `SemanticMemory.remember(...)` call now flows through a five-stage
67
+ pipeline (exact dedup → embedding three-zone → heuristic regex →
68
+ subject/predicate → defer-to-deep). Decisions are recorded into
69
+ `fact_conflicts`; ambiguous cases land in the `conflict_check_pending`
70
+ queue for the consolidator's deep phase. The English locale pack
71
+ ships by default; additional locales plug in via
72
+ `defineLocalePack({...})`. Operators can disable the pipeline with
73
+ `createMemory({ conflictPipeline: { mode: 'off' } })` (a one-shot
74
+ WARN surfaces so the regression risk is visible).
75
+ - **Neighbour-aware write reconciliation** (consolidator standard phase).
76
+ Extracted facts are reconciled against their nearest neighbours via an
77
+ extract→reconcile loop: a cheap pre-filter (the pipeline's exact-dedup +
78
+ embedding zones over `SemanticMemory.neighbors(...)`) resolves clear
79
+ duplicates / independents with no LLM call, and only the ambiguous mid-zone
80
+ spends one reconcile pass choosing add / update / noop / conflict. Updates
81
+ and conflicts route through the bi-temporal supersede (never a delete);
82
+ every decision is audited in `fact_conflicts` and new facts inherit the
83
+ `extraction` provenance gate.
84
+ - **Auto-importance + episode formation** (consolidator standard phase).
85
+ Each processed slice is summarized into one episode via a single budgeted
86
+ LLM call that also rates its importance `1–10` (normalized to `[0, 1]`),
87
+ so episodic triple-signal retrieval (recency × relevance × importance) finally
88
+ runs on all three signals. Auto-formed episodes carry `provenance: 'extraction'`
89
+ + `status: 'quarantined'` (P1-4) — surfaced for review via
90
+ `episodic.search(..., { includeQuarantined: true })`. Importance is a *soft*
91
+ signal (never a retention gate). Controlled by the per-tier `formEpisodes` /
92
+ `importanceScoring` flags (on at `standard` / `full`); budget-aware
93
+ (an exhausted budget degrades to fact-only).
94
+ - **Reflection + insight synthesis** (consolidator deep phase). When the
95
+ accumulated importance of recent episodes crosses `importanceThreshold`, the
96
+ deep phase asks the model for the few most salient questions, retrieves
97
+ evidence for each, and synthesizes a higher-order **insight** (Generative
98
+ Agents). Insights are a distinct memory type (`memory.insights`:
99
+ `search` / `list`) that land `provenance: 'reflection'` +
100
+ `status: 'quarantined'`, carry **mandatory citations set from the retrieved
101
+ evidence** (never hallucinated), and are **rank-capped below the facts they
102
+ cite** (`capInsightsBelowFacts`). An ExpeL salience counter (start `2`,
103
+ pruned at `0`) manages the set. Off by default except at the `full` tier;
104
+ budget-aware and a no-op without an episodic tier + insight-capable store.
105
+ - **Contextual retrieval** (write path). Before a fact is embedded + FTS-indexed
106
+ the framework prepends a short **situating context** (entities / timeframe /
107
+ topics, Anthropic's Contextual Retrieval) so a terse fact stays findable; the
108
+ canonical `text` is preserved. The offline default `'late-chunk'`
109
+ (`createMemory({ contextualRetrieval })`) derives the context deterministically
110
+ from the fact's own structured signals with **no extra LLM call** — a no-op for
111
+ plain-text writes. An opt-in, **consolidator-only** `'llm'` mode
112
+ (`consolidator: { contextualRetrieval: 'llm' }`) spends one budgeted cheap-model
113
+ call per write to author the prefix, degrading to late-chunk on any failure.
114
+ - **Recall explainability** ("why was this recalled?"). `explainRecall(hits, {
115
+ query, rerankerId })` decomposes a `search(...)` result into the per-memory
116
+ signals that drove its score (`bm25` / `vector` / fused `rrf` / `decay`), in
117
+ final-rank order; `formatRecallExplanation(...)` renders it. `search` also
118
+ records the decay multiplier as a `decay` signal and attaches the breakdown
119
+ (ids + scores + signals, never the query text) to the `memory.search.semantic`
120
+ span. Operators inspect the rest via `graphorin memory inspect <factId>`
121
+ (supersede chain / quarantine / conflicts / citing insights) and
122
+ `graphorin memory activity` (consolidator / reflection activity).
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,
128
+ first-party fact, `salience === retention`. Setting `decayCapacity`
129
+ (`createMemory({ consolidator: { decayCapacity } })`, default unbounded)
130
+ bounds storage: the lowest-salience facts are **soft-archived** (recoverable —
131
+ `archived = 1`, never deleted) until the window fits.
132
+ - **Query transformation** (read path; multi-query / RAG-Fusion + opt-in HyDE).
133
+ `search(scope, query, { multiQuery: N })` fans the query into up to `N - 1`
134
+ 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
+ differs from the question. `{ hyde: true }` additionally embeds a hypothetical
137
+ answer (arXiv:2212.10496) and fuses its neighbours. Both are **opt-in**, wired
138
+ by `createMemory({ queryTransform: { provider } })`; with no transformer (the
139
+ 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
+ - **Weighted fusion + reranker guidance** (search quality, opt-in). RRF is a
142
+ good zero-tuning default, but once you have labels (the `@graphorin/evals`
143
+ harness) a calibrated weight can do better. `search(scope, query, { fusion: {
144
+ strategy: 'weighted', weights: { vector: 3, fts: 1 } } })` fuses through
145
+ `WeightedRRFReranker`, scaling each candidate list's reciprocal-rank
146
+ 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
+ 'rrf' }`) and behaviour is unchanged; equal weights reproduce RRF exactly. The
149
+ pure `fuseWeighted(lists, weights, k)` is exported for offline weight tuning
150
+ against the eval harness. For a further precision lift, install a cross-encoder
151
+ reranker (`@graphorin/reranker-transformersjs` / `-llm`) via
152
+ `createMemory({ reranker })` or `setReranker(...)` and raise `candidateTopK` —
153
+ reranking pays off **only on an already-high-recall candidate set**, so fix
154
+ recall first (contextual retrieval / multi-query) before reaching for it.
155
+ - **Relation graph + one-hop expansion** (new capability, opt-in). Activates the
156
+ dormant `(subject, predicate, object)` substrate: facts now carry s/p/o, and an
157
+ **entity resolver** folds the subject/object strings into canonical entities
158
+ ("Anna", "Anna S.", "my sister" → one entity) via lexical + embedding dedup —
159
+ with **auditable, reversible merges** (an append-only ledger; `merged_into` is
160
+ single-level). At read time `search(scope, query, { expandHops: 1 })` seeds on
161
+ the candidates and fuses in facts sharing an entity through a recursive CTE,
162
+ answering "what did the person I met in Tbilisi recommend?" without leaving
163
+ SQLite. Enable linking with `createMemory({ graph: { entityResolution: true } })`
164
+ (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
+ adjudication (`graph: { llmAdjudication: true, provider }`) to resolve that band.
167
+ Omit it all and the default path is unchanged + fully offline (`expandHops`
168
+ defaults to `0`).
169
+ - **Agentic / iterative retrieval** (search quality, gated + opt-in). A
170
+ CRAG/Self-RAG-style grade-then-reformulate loop for hard multi-hop / temporal
171
+ questions one pass can't answer. A cheap **local difficulty gate**
172
+ (`assessQueryDifficulty`) keeps simple lookups single-shot; only a query judged
173
+ hard *and* with a grader configured (`createMemory({ iterativeRetrieval: {
174
+ provider } })`) is graded for sufficiency and, when weak, reformulated +
175
+ retrieved again — **widening to one-hop graph expansion** on each reformulation —
176
+ up to a hard-capped `maxIterations` (≤ 5), then **abstaining**
177
+ (`result.abstained === true`) rather than confabulating. Exposed as
178
+ `SemanticMemory.searchIterative(...)` and the gated **`deep_recall`** tool (a
179
+ twelfth tool, registered only when `iterativeRetrieval` is configured). Omit it
180
+ and `searchIterative` degrades to one difficulty-gated pass with **no provider
181
+ call**; the tool surface stays at the canonical eleven.
182
+ - **Procedural memory extraction** (new capability, gated + opt-in). AWM-style
183
+ workflow induction: from a **successful** agent trajectory,
184
+ `ProceduralMemory.induce(scope, trajectory)` distils a reusable procedure —
185
+ goal + value-abstracted steps (`"search for {product}"`) + the variable names
186
+ + Voyager-style success criteria for self-verification on reuse
187
+ (`checkSuccessCriteria`). Induction fires on **success only** (a failed run
188
+ never calls the inducer), and the result lands **quarantined** +
189
+ `provenance: 'induction'` (P1-4) — procedures drive *actions*, so this is the
190
+ highest-poisoning-risk write and is excluded from `activate()` until validated.
191
+ `trajectoryFromRunState(runState)` distils the agent's already-emitted run
192
+ state, so capture needs **no agent change**. Wire it with
193
+ `createMemory({ procedureInduction: { provider } })`; omit it and `induce(...)`
194
+ throws `ProcedureInductionNotConfiguredError` — the procedural tier stays pure
195
+ offline CRUD with no provider call.
196
+ - **Per-record `embedder_id` enforced.** Every embedded write registers
197
+ the embedder via the storage layer's `EmbeddingMetaRepository` and
198
+ records the canonical id (`'<provider>:<model>@<dim>'`); attempts to
199
+ silently swap embedders fail-fast under the default `lock-on-first`
200
+ policy.
201
+ - **Embedder migration runner** (`migrateEmbedder`). Three strategies:
202
+ `lock-on-first` (default; refuses silent swap), `multi-active` (read
203
+ union, write to active), `auto-migrate` (re-embeds existing rows in
204
+ resumable batches; checkpointed via the `migration_state` table; can
205
+ be aborted with `AbortSignal`).
206
+ - **Multi-agent attribution.** Every session message carries an
207
+ optional `agentId`; `Session.list({ agentId })` filters per-agent;
208
+ `Session.attributedFor(...)` exposes the registry; episodes can be
209
+ recorded `'merged'` / `'per-agent'` (the rendering mode is consumed
210
+ by the consolidator in Phase 10c).
211
+ - **Default-on bi-temporal storage.** Fact writes set `validFrom = now`
212
+ and leave `validTo = null`; a supersede closes the old fact's `validTo`
213
+ (so `as_of` queries reflect the change) and keeps the chain intact for
214
+ later replay. The advanced `as_of_date` query API is opt-in
215
+ (post-MVP).
216
+ - **`memory.compile(scope)` + `memory.metadata(scope)`.** The
217
+ interface used by the context engine (Phase 10d) to assemble the
218
+ six-layer memory-aware system prompt; `@graphorin/memory@0.5.0`
219
+ ships the deterministic minimum-viable rendering so the agent
220
+ runtime in Phase 12 can already exercise the surface.
221
+ - **Memory-modification guard wiring.** Every tool's
222
+ `memoryGuardTier` is annotated per `DEC-153`; the executor in
223
+ `@graphorin/tools` honours the tier through the `createGuard(...)`
224
+ helper from `@graphorin/security/guard`.
225
+
226
+ ## Stable sub-paths
227
+
228
+ ```ts
229
+ import { createMemory } from '@graphorin/memory';
230
+ import { RRFReranker } from '@graphorin/memory/search';
231
+ import { migrateEmbedder } from '@graphorin/memory/migration';
232
+ import {
233
+ blockAppendTool,
234
+ factRememberTool,
235
+ } from '@graphorin/memory/tools';
236
+ import {
237
+ EmbedderRegistrationError,
238
+ MemoryToolDeniedError,
239
+ } from '@graphorin/memory/errors';
240
+ import {
241
+ createConflictPipeline,
242
+ defineLocalePack,
243
+ enLocalePack,
244
+ DEFAULT_CONFLICT_THRESHOLDS,
245
+ } from '@graphorin/memory/conflict';
246
+ ```
247
+
248
+ ## Quick start
249
+
250
+ ```ts
251
+ import { createSqliteStore } from '@graphorin/store-sqlite';
252
+ import { createTransformersJsEmbedder } from '@graphorin/embedder-transformersjs';
253
+ import { createMemory } from '@graphorin/memory';
254
+
255
+ const sqlite = await createSqliteStore({ path: './assistant.db' });
256
+ await sqlite.init();
257
+
258
+ const memory = createMemory({
259
+ store: sqlite.memory,
260
+ embeddings: sqlite.embeddings,
261
+ embedder: createTransformersJsEmbedder(),
262
+ });
263
+
264
+ await memory.semantic.remember(
265
+ { userId: 'alex' },
266
+ { text: 'Loves mountain hiking and fresh espresso.' },
267
+ );
268
+
269
+ const hits = await memory.semantic.search(
270
+ { userId: 'alex' },
271
+ 'mountain trip ideas',
272
+ );
273
+ console.log(hits[0]?.record.text);
274
+ ```
275
+
276
+ ## Tier overview
277
+
278
+ | Tier | Surface (read) | Surface (write) |
279
+ |-------------|-----------------------------------------------|-------------------------------------------------------------|
280
+ | working | `list`, `read`, `compile` | `define`, `write`, `patch`, `attach`, `detach` |
281
+ | session | `list`, `search`, `attributedFor` | `push`, `flushImportant`, `compact` |
282
+ | episodic | `recent`, `search` | `record` |
283
+ | semantic | `search` | `remember`, `supersede`, `forget` |
284
+ | procedural | `list`, `activate` | `define`, `remove` |
285
+ | shared | `listFor` | `attach`, `detach` |
286
+
287
+ ## Memory tools (registered with `@graphorin/tools`)
288
+
289
+ `memory.tools` is a typed `Tool[]`. Pass it through to
290
+ `createToolRegistry({ ... })` (Phase 12) so the agent loop sees every
291
+ tool exactly once:
292
+
293
+ | Tool name | Tier | Purpose |
294
+ |----------------------|------------|--------------------------------------------------------|
295
+ | `block_append` | working | Append text to a working memory block. |
296
+ | `block_replace` | working | Replace a unique substring inside a block. |
297
+ | `block_rethink` | working | Replace a block's value entirely. |
298
+ | `fact_remember` | semantic | Persist a new fact through the multi-stage conflict pipeline. |
299
+ | `fact_search` | semantic | Hybrid (vector + FTS5 + RRF) search over facts. |
300
+ | `fact_supersede` | semantic | Mark an old fact superseded by a new one. |
301
+ | `fact_forget` | semantic | Soft-delete a fact (kept for replay). |
302
+ | `recall_episodes` | episodic | Triple-signal episode retrieval. |
303
+ | `conversation_search`| session | FTS5 search over the active session messages. |
304
+ | `fact_history` | semantic | Trace a fact's bi-temporal supersede chain. |
305
+ | `fact_validate` | semantic | Promote a quarantined fact to active (audited). |
306
+
307
+ ## Embedder migration
308
+
309
+ ```ts
310
+ import { migrateEmbedder } from '@graphorin/memory/migration';
311
+ import { createTransformersJsEmbedder } from '@graphorin/embedder-transformersjs';
312
+
313
+ const target = createTransformersJsEmbedder({ model: 'Xenova/multilingual-e5-large' });
314
+
315
+ for await (const progress of migrateEmbedder({
316
+ store: sqlite,
317
+ embeddings: sqlite.embeddings,
318
+ source: memory.embedder,
319
+ target,
320
+ strategy: 'auto-migrate',
321
+ })) {
322
+ console.log(`${progress.processed}/${progress.total} (${progress.kind})`);
323
+ }
324
+ ```
325
+
326
+ `lock-on-first` (default) refuses any silent embedder swap with an
327
+ actionable error pointing at the planned migration. `multi-active`
328
+ keeps both vec0 tables alive (reads union, writes to active).
329
+ `auto-migrate` re-embeds rows in resumable batches (checkpointed
330
+ via the storage layer's `migration_state` table; can be aborted
331
+ with `AbortSignal`).
332
+
333
+ ## Conflict pipeline (Phase 10b)
334
+
335
+ ```ts
336
+ import { createMemory, defineLocalePack } from '@graphorin/memory';
337
+
338
+ const memory = createMemory({
339
+ store: sqlite.memory,
340
+ embeddings: sqlite.embeddings,
341
+ embedder: createTransformersJsEmbedder(),
342
+ conflictPipeline: {
343
+ mode: 'on', // default
344
+ thresholds: { hot: 0.95, nearDup: 0.85, cold: 0.4 },
345
+ localePack: defineLocalePack({ id: 'fr', /* ... */ }),
346
+ },
347
+ });
348
+
349
+ const outcome = await memory.semantic.rememberWithDecision(scope, {
350
+ text: 'I just moved to Tbilisi for the new gig.',
351
+ });
352
+ console.log(outcome.decision.kind); // 'supersede' | 'dedup' | 'pending' | 'admit'
353
+ ```
354
+
355
+ The pipeline visits five stages in order:
356
+
357
+ 1. **Exact dedup** — MD5 hash on the canonical (lowercase + collapsed-
358
+ whitespace + trimmed) candidate body short-circuits on a hit.
359
+ 2. **Embedding three-zone** — top-K neighbours from `searchVector`
360
+ classify the candidate into HOT (`>= 0.95`), NEAR-DUP
361
+ (`>= 0.85`), CONFLICT-CHECK (`> 0.4`), or COLD. HOT zone always
362
+ dedups (semantic identity outranks every other signal).
363
+ 3. **Heuristic regex** — the active locale pack's supersede +
364
+ negation markers fire when the candidate has an explicit
365
+ change signal (`moved to`, `no longer`, `got promoted`, …) and
366
+ there is at least one existing candidate.
367
+ 4. **Subject / predicate** — naive `(subject, predicate, object)`
368
+ split using the locale pack's predicate normalisers; matching
369
+ subject + predicate with a different object is a strong supersede
370
+ signal.
371
+ 5. **Defer to deep LLM judge** — Stages 1–4 yielded no decision but
372
+ the candidate sits in CONFLICT-CHECK zone — the row is admitted
373
+ `pending` and queued in `conflict_check_pending` for the
374
+ consolidator's deep phase (Phase 10c).
375
+
376
+ Every decision lands one row in `fact_conflicts` with the producing
377
+ stage, the detection zone, the cosine similarity (where applicable),
378
+ and a reason string for replay/debug. A `memory.conflict` AISpan is
379
+ emitted per call.
380
+
381
+ `createMemory({ conflictPipeline: { mode: 'off' } })` falls back to the
382
+ 10a straight-through write path; a single per-process WARN surfaces so
383
+ operators notice that bi-temporal supersede / dedup will not fire.
384
+
385
+ ## Forward links
386
+
387
+ | Surface | Owned by |
388
+ |--------------------------------------|------------|
389
+ | Background `Consolidator` (triggers + 3 phases + DLQ + cost budget) | Phase 10c |
390
+ | Six-layer memory-aware system prompt + locale-aware base templates | Phase 10d |
391
+ | `@graphorin/sessions` hybrid facade wrapping `memory.session.*` | Phase 11 |
392
+ | `@graphorin/agent` wiring `memory.tools` into the tool registry | Phase 12 |
393
+ | `/v1/memory` REST + `graphorin memory migrate` CLI | Phases 14 / 15 |
394
+
395
+ ## License
396
+
397
+ MIT © 2026 Oleksiy Stepurenko.
398
+
399
+ ---
400
+
401
+ **Project Graphorin** · v0.5.0 · MIT License · © 2026 Oleksiy Stepurenko · <https://github.com/o-stepper/graphorin>
@@ -0,0 +1,3 @@
1
+ import { LocaleMatch, LocalePack, LocalePatternEntry, LocaleSupersedeKind, defineLocalePack, evaluateMarkers } from "./locale-packs/types.js";
2
+ 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";
@@ -0,0 +1,7 @@
1
+ import { defineLocalePack, evaluateMarkers } from "./locale-packs/types.js";
2
+ import { enLocalePack } from "./locale-packs/en.js";
3
+ import "./locale-packs/index.js";
4
+ import { DEFAULT_CONFLICT_THRESHOLDS, reconcileToConflictDecision } from "./types.js";
5
+ import { _resetBypassWarningForTesting, createConflictPipeline, runConflictPipeline } from "./pipeline.js";
6
+
7
+ export { };