@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
@@ -1,4 +1,4 @@
1
- import { EntityRole, Episode as Episode$1, EpisodicMemoryStore, Fact as Fact$1, GraphEntity, Insight as Insight$1, MemoryHit, MemoryStatus, MemoryStore, Message as Message$1, ProceduralMemoryStore, SemanticMemoryStore, SessionMemoryStore, SessionScope as SessionScope$1 } from "@graphorin/core";
1
+ import { EntityRole, Episode as Episode$1, EpisodicMemoryStore, Fact as Fact$1, GraphEntity, Insight as Insight$1, MemoryHit, MemoryOwner, MemoryStatus, MemoryStore, Message as Message$1, ProceduralMemoryStore, Rule, SemanticMemoryStore, SessionMemoryStore, SessionScope as SessionScope$1 } from "@graphorin/core";
2
2
 
3
3
  //#region src/internal/storage-adapter.d.ts
4
4
 
@@ -37,11 +37,11 @@ interface EpisodicMemoryStoreExt extends EpisodicMemoryStore {
37
37
  searchVector?(scope: SessionScope$1, embedding: Float32Array, embedderId: string, topK: number, /** Point-in-time filter (`started_at <= asOf`, ISO-8601). P0-2. */
38
38
  asOf?: string, /** Include quarantined episodes (validation/inspector path). P1-4. */
39
39
  includeQuarantined?: boolean): Promise<ReadonlyArray<MemoryHit<Episode$1>>>;
40
- /** Mark an episode archived. Soft-archive the row stays for replay. */
40
+ /** Mark an episode archived. Soft-archive - the row stays for replay. */
41
41
  archive?(id: string, reason?: string): Promise<void>;
42
42
  /**
43
43
  * Most-recent episodes by end time (newest first), with no FTS / vector
44
- * query recency, not relevance (MCON-1). Powers `EpisodicMemory.recent()`
44
+ * query - recency, not relevance (MCON-1). Powers `EpisodicMemory.recent()`
45
45
  * and the deep-phase reflection gate. The default `@graphorin/store-sqlite`
46
46
  * adapter implements it.
47
47
  */
@@ -49,13 +49,13 @@ interface EpisodicMemoryStoreExt extends EpisodicMemoryStore {
49
49
  includeQuarantined?: boolean;
50
50
  }): Promise<ReadonlyArray<Episode$1>>;
51
51
  /**
52
- * Set an episode's retrieval-trust `status` (MCON-2) promote a quarantined
52
+ * Set an episode's retrieval-trust `status` (MCON-2) - promote a quarantined
53
53
  * (auto-formed) episode into default recall or re-quarantine an active one,
54
54
  * with a `memory_history` audit row. Powers {@link EpisodicMemory.validate}.
55
55
  */
56
56
  setStatus?(id: string, status: MemoryStatus, reason?: string): Promise<void>;
57
57
  /**
58
- * Count the recall-eligible episodes for the scope (CE-5) a `COUNT(*)`,
58
+ * Count the recall-eligible episodes for the scope (CE-5) - a `COUNT(*)`,
59
59
  * never materialising rows. Powers honest `metadata()` counts.
60
60
  */
61
61
  count?(scope: SessionScope$1): Promise<number>;
@@ -79,20 +79,30 @@ interface SemanticMemoryStoreExt extends SemanticMemoryStore {
79
79
  * Include quarantined facts in the KNN result (validation /
80
80
  * inspector path). Default reads exclude them. P1-4.
81
81
  */
82
- includeQuarantined?: boolean): Promise<ReadonlyArray<MemoryHit<Fact$1>>>;
82
+ includeQuarantined?: boolean,
83
+ /**
84
+ * Include superseded / validity-expired facts. Default reads
85
+ * evaluate validity at NOW (memory-retrieval-01).
86
+ */
87
+ includeSuperseded?: boolean,
88
+ /**
89
+ * Retrieval-time principal filter (D3). Rows with no stored owner
90
+ * are treated as `'user'`. Absent ⇒ no owner filter.
91
+ */
92
+ owner?: MemoryOwner | ReadonlyArray<MemoryOwner>): Promise<ReadonlyArray<MemoryHit<Fact$1>>>;
83
93
  /** Lookup a single fact by id (returns `null` when absent or soft-deleted). */
84
94
  get?(id: string): Promise<Fact$1 | null>;
85
95
  /**
86
96
  * Set a fact's retrieval-trust `status` and write a `memory_history`
87
97
  * audit row (P1-4). Promotes a quarantined fact to `active` (the
88
98
  * validation path) or re-quarantines an active one. Never touches
89
- * content / embedding / tombstone quarantine is a retrieval gate.
99
+ * content / embedding / tombstone - quarantine is a retrieval gate.
90
100
  * Powers {@link SemanticMemory.validate}; the default
91
101
  * `@graphorin/store-sqlite` adapter implements it.
92
102
  */
93
103
  setStatus?(factId: string, status: MemoryStatus, reason?: string): Promise<void>;
94
104
  /**
95
- * Count the recall-eligible facts for the scope (CE-5) a `COUNT(*)` with
105
+ * Count the recall-eligible facts for the scope (CE-5) - a `COUNT(*)` with
96
106
  * the default recall filters (live, non-archived, non-quarantined), never
97
107
  * materialising rows. Powers honest `metadata()` counts.
98
108
  */
@@ -152,7 +162,7 @@ interface SessionMemoryStoreExt extends SessionMemoryStore {
152
162
  */
153
163
  listMessagesSince?(scope: SessionScope$1, lastMessageId: string | null, limit: number): Promise<ReadonlyArray<SessionMessageRecord>>;
154
164
  /**
155
- * Count the live messages in the scoped session (CE-5) a `COUNT(*)`, never
165
+ * Count the live messages in the scoped session (CE-5) - a `COUNT(*)`, never
156
166
  * materialising rows; `0` for a user-only scope. Powers honest `metadata()`
157
167
  * counts instead of `list(...)`-materialising up to 1000 rows.
158
168
  */
@@ -226,7 +236,7 @@ interface ConflictAuditInputLike {
226
236
  readonly detectedBy?: string;
227
237
  }
228
238
  /**
229
- * Pending-queue payload Stage 5 (defer-to-deep) hands the row over
239
+ * Pending-queue payload - Stage 5 (defer-to-deep) hands the row over
230
240
  * to the deep-phase LLM judge (Phase 10c).
231
241
  *
232
242
  * @stable
@@ -281,7 +291,7 @@ interface ConflictMemoryStoreExt {
281
291
  * Stamp `attemptedAt` on a pending row whose judge call failed
282
292
  * (MCON-9). The deep phase closes the row as `'judge-unparseable'`
283
293
  * on the NEXT failure, so a poisoned row is billed at most twice.
284
- * Optional without it the deep phase falls back to skip-and-retry.
294
+ * Optional - without it the deep phase falls back to skip-and-retry.
285
295
  */
286
296
  markAttempted?(id: number, attemptedAt?: number): Promise<void>;
287
297
  }
@@ -399,7 +409,7 @@ interface DlqBatchRow {
399
409
  * for Phase 10c. Mirrors the `consolidator_state`,
400
410
  * `consolidator_runs`, and `consolidator_failed_batches` tables
401
411
  * shipped in Phase 05's migration 009. Adapters that do not
402
- * implement the surface degrade gracefully the consolidator runs
412
+ * implement the surface degrade gracefully - the consolidator runs
403
413
  * in stateless mode (no DLQ, no cursor persistence) and emits a
404
414
  * one-shot WARN.
405
415
  *
@@ -437,7 +447,7 @@ interface ConsolidatorMemoryStoreExt {
437
447
  * oldest backlog drains first.
438
448
  */
439
449
  claimReadyBatches(scope: SessionScope$1, now: number, limit?: number): Promise<ReadonlyArray<DlqBatchRow>>;
440
- /** Mark the row succeeded removes it from the DLQ. */
450
+ /** Mark the row succeeded - removes it from the DLQ. */
441
451
  markBatchSucceeded(id: string): Promise<void>;
442
452
  /**
443
453
  * Schedule the next retry attempt. The caller computes
@@ -462,7 +472,7 @@ interface ConsolidatorMemoryStoreExt {
462
472
  * 10c's light phase reads the strength + last-accessed columns and
463
473
  * archives facts whose retention curve falls below the configured
464
474
  * threshold. Adapters that do not maintain decay columns may omit
465
- * the surface entirely the light phase skips the archive step
475
+ * the surface entirely - the light phase skips the archive step
466
476
  * with an INFO log.
467
477
  *
468
478
  * @stable
@@ -473,7 +483,7 @@ interface DecayMemoryStoreExt {
473
483
  * caller can apply Ebbinghaus retention without scanning the
474
484
  * whole table. `limit` defaults to `1000`.
475
485
  *
476
- * Archived rows are EXCLUDED by default (MCON-6) they never receive
486
+ * Archived rows are EXCLUDED by default (MCON-6) - they never receive
477
487
  * access bumps, so they would pin the LRU head and saturate the decay
478
488
  * window, structurally stopping threshold-archiving and capacity
479
489
  * eviction for live facts. Inspection paths pass
@@ -497,6 +507,12 @@ interface DecayMemoryStoreExt {
497
507
  readonly status: string;
498
508
  /** Trust-provenance tag (P1-4); `null` for first-party rows. */
499
509
  readonly provenance: string | null;
510
+ /**
511
+ * Monotonic retrieval-access counter (D3, migration 027). Feeds
512
+ * the opt-in `accessReinforcement` salience weight. Optional -
513
+ * adapters without the column omit it (treated as `0`).
514
+ */
515
+ readonly accessCount?: number;
500
516
  }>>;
501
517
  /**
502
518
  * Soft-archive a fact (sets `archived = 1`). The audit row in
@@ -506,13 +522,13 @@ interface DecayMemoryStoreExt {
506
522
  /**
507
523
  * Record a retrieval access for the given facts (MRET-7): stamp
508
524
  * `lastAccessedAt` and reinforce `strength` (implementation-capped).
509
- * Optional adapters without decay columns may omit it; callers
525
+ * Optional - adapters without decay columns may omit it; callers
510
526
  * MUST treat failures as non-fatal (the read path never breaks on a
511
527
  * bookkeeping write).
512
528
  */
513
529
  markAccessed?(ids: ReadonlyArray<string>, accessedAt?: number): Promise<void>;
514
530
  /**
515
- * Narrow decay-column read for exactly the given fact ids (MRET-8)
531
+ * Narrow decay-column read for exactly the given fact ids (MRET-8) -
516
532
  * powers per-search decay re-ranking without the old O(scope)
517
533
  * 1000-row window read. Optional; absent ⇒ the tier falls back to
518
534
  * `listForDecay`.
@@ -541,7 +557,7 @@ interface InsightSearchStoreOptions {
541
557
  * (P1-1). The consolidator's reflection pass inserts quarantined,
542
558
  * cited insights here; the thin `InsightMemory` read surface lists /
543
559
  * searches them; the ExpeL salience loop bumps + prunes them. Search is
544
- * FTS-only by design insights are a soft, rank-capped inspector
560
+ * FTS-only by design - insights are a soft, rank-capped inspector
545
561
  * surface, not primary recall.
546
562
  *
547
563
  * Adapters that opt out leave the property undefined; reflection then
@@ -561,7 +577,7 @@ interface InsightMemoryStoreExt {
561
577
  /** Lookup a single insight by id (`null` when absent / pruned). */
562
578
  get?(id: string): Promise<Insight$1 | null>;
563
579
  /**
564
- * Set an insight's retrieval-trust `status` (MCON-2) promote a quarantined
580
+ * Set an insight's retrieval-trust `status` (MCON-2) - promote a quarantined
565
581
  * (reflection) insight or re-quarantine an active one, with a
566
582
  * `memory_history` audit row. Powers {@link InsightMemory.validate}.
567
583
  */
@@ -573,7 +589,7 @@ interface InsightMemoryStoreExt {
573
589
  bumpSalience(id: string, delta: number, reason?: string): Promise<void>;
574
590
  /**
575
591
  * Soft-delete every salience-0 insight for the scope (the ExpeL
576
- * forgetting step). Returns the number pruned. Tombstone only
592
+ * forgetting step). Returns the number pruned. Tombstone only -
577
593
  * pruned insights stay auditable.
578
594
  */
579
595
  prune(scope: SessionScope$1): Promise<number>;
@@ -586,16 +602,28 @@ interface InsightMemoryStoreExt {
586
602
  */
587
603
  interface ProceduralMemoryStoreExt extends ProceduralMemoryStore {
588
604
  /**
589
- * Set a rule's retrieval-trust `status` promote a quarantined (induced)
605
+ * Set a rule's retrieval-trust `status` - promote a quarantined (induced)
590
606
  * procedure into `activate()` or re-quarantine an active one, with a
591
607
  * `memory_history` audit row. Powers {@link ProceduralMemory.validate}.
592
608
  */
593
609
  setStatus?(id: string, status: MemoryStatus, reason?: string): Promise<void>;
610
+ /**
611
+ * Lexical runbook search over rule text (D3, migration 028) - content
612
+ * recall for "find the procedure for this task", as opposed to
613
+ * predicate activation. Quarantined (unvalidated induced) procedures
614
+ * are excluded unless the inspector opts in. Optional - adapters
615
+ * without the index omit it and {@link ProceduralMemory.search} falls
616
+ * back to an in-memory lexical scan over `list(...)`.
617
+ */
618
+ search?(scope: SessionScope$1, query: string, opts?: {
619
+ readonly topK?: number;
620
+ readonly includeQuarantined?: boolean;
621
+ }): Promise<ReadonlyArray<MemoryHit<Rule>>>;
594
622
  /**
595
623
  * Record one demonstrated successful reuse of a rule and return the
596
624
  * new counter value (MCON-2 part 4). Powers
597
625
  * promotion-by-demonstrated-success via
598
- * {@link ProceduralMemory.recordOutcome}. Optional adapters without
626
+ * {@link ProceduralMemory.recordOutcome}. Optional - adapters without
599
627
  * the counter simply never auto-promote.
600
628
  */
601
629
  recordSuccess?(id: string): Promise<number>;
@@ -639,6 +667,11 @@ interface ExpandHopsStoreOptions {
639
667
  readonly includeQuarantined?: boolean;
640
668
  /** Point-in-time filter, ISO-8601 (same semantics as fact search). */
641
669
  readonly asOf?: string;
670
+ /**
671
+ * Include superseded / validity-expired neighbours. Default reads
672
+ * evaluate validity at NOW (memory-retrieval-01).
673
+ */
674
+ readonly includeSuperseded?: boolean;
642
675
  }
643
676
  /**
644
677
  * Optional storage extension for the lightweight in-SQLite relation
@@ -686,6 +719,22 @@ interface GraphMemoryStoreExt {
686
719
  }): Promise<ReadonlyArray<EntityMergeRecord>>;
687
720
  /** Expand seed facts to neighbours sharing a canonical entity (one-hop CTE). */
688
721
  expandOneHop(scope: SessionScope$1, seedFactIds: ReadonlyArray<string>, opts?: ExpandHopsStoreOptions): Promise<ReadonlyArray<Fact$1>>;
722
+ /**
723
+ * PPR-lite graded expansion (D5): like {@link expandOneHop} but returns
724
+ * each neighbour with its minimum hop `depth` from the seeds, so the
725
+ * tier can weight it by damped spreading activation. Optional - stores
726
+ * without it fall back to flat one-hop expansion.
727
+ */
728
+ expandActivation?(scope: SessionScope$1, seedFactIds: ReadonlyArray<string>, opts?: ExpandHopsStoreOptions): Promise<ReadonlyArray<{
729
+ readonly fact: Fact$1;
730
+ readonly depth: number;
731
+ }>>;
732
+ /**
733
+ * Exact entity-match retriever (D5): facts linked to the canonical
734
+ * entity for `normalizedName`. Optional. Powers a precise
735
+ * "facts about <entity>" candidate leg.
736
+ */
737
+ factsForEntityName?(scope: SessionScope$1, normalizedName: string, opts?: ExpandHopsStoreOptions): Promise<ReadonlyArray<Fact$1>>;
689
738
  }
690
739
  /**
691
740
  * Composite shape every `@graphorin/memory` consumer must supply at
@@ -1 +1 @@
1
- {"version":3,"file":"storage-adapter.d.ts","names":[],"sources":["../../src/internal/storage-adapter.ts"],"sourcesContent":[],"mappings":"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"}
1
+ {"version":3,"file":"storage-adapter.d.ts","names":[],"sources":["../../src/internal/storage-adapter.ts"],"sourcesContent":[],"mappings":"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"}
@@ -6,12 +6,12 @@ import { EmbedderProvider } from "@graphorin/core";
6
6
  /**
7
7
  * Coexistence policy for embedder swaps.
8
8
  *
9
- * - `'lock-on-first'` (default) refuses to register a second active
9
+ * - `'lock-on-first'` (default) - refuses to register a second active
10
10
  * embedder; surfaces an actionable error pointing at the planned
11
11
  * migration runner.
12
- * - `'multi-active'` keeps both embedders alive (read union, write
12
+ * - `'multi-active'` - keeps both embedders alive (read union, write
13
13
  * to active); used while a long migration is in flight.
14
- * - `'auto-migrate'` re-embeds existing rows in resumable batches
14
+ * - `'auto-migrate'` - re-embeds existing rows in resumable batches
15
15
  * until the source embedder has zero rows, then retires it.
16
16
  *
17
17
  * @stable
@@ -23,7 +23,7 @@ type EmbedderMigrationStrategy = 'lock-on-first' | 'multi-active' | 'auto-migrat
23
23
  * @stable
24
24
  */
25
25
  interface MigrationProgress {
26
- /** `'fact'`, `'episode'`, or `'message'` which entity is being migrated. */
26
+ /** `'fact'`, `'episode'`, or `'message'` - which entity is being migrated. */
27
27
  readonly kind: 'fact' | 'episode' | 'message';
28
28
  /** Number of records processed so far. */
29
29
  readonly processed: number;
@@ -35,7 +35,7 @@ interface MigrationProgress {
35
35
  readonly target: string;
36
36
  /**
37
37
  * Identifier for this migration run. MST-12: this is an in-memory id for
38
- * the current run there is no persisted `migration_state` cursor today, so
38
+ * the current run - there is no persisted `migration_state` cursor today, so
39
39
  * a migration does not resume across processes.
40
40
  */
41
41
  readonly migrationId: string;
@@ -66,12 +66,12 @@ interface MigrateEmbedderOptions {
66
66
  readonly maxRecordsPerKind?: number;
67
67
  /**
68
68
  * Hook that returns the next batch of rows to re-embed for a given kind.
69
- * MST-12: this is **caller-supplied** there is no store-side helper that
69
+ * MST-12: this is **caller-supplied** - there is no store-side helper that
70
70
  * auto-wires it today, and `auto-migrate` throws without it. Pass a paging
71
71
  * function over your source rows to drive the migration.
72
72
  */
73
73
  readonly nextBatch?: NextBatchHook;
74
- /** Optional abort signal aborting yields one final progress event. */
74
+ /** Optional abort signal - aborting yields one final progress event. */
75
75
  readonly signal?: AbortSignal;
76
76
  }
77
77
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"embedder-migration.js","names":["cursor: string | null"],"sources":["../../src/migration/embedder-migration.ts"],"sourcesContent":["import type { EmbedderProvider } from '@graphorin/core';\nimport {\n EmbedderMigrationAbortedError,\n EmbedderMigrationLockedError,\n EmbedderMigrationStateError,\n} from '../errors/index.js';\nimport { bindEmbedder } from '../internal/embedder-binding.js';\nimport type { EmbeddingMetaRegistryLike } from '../internal/storage-adapter.js';\n\n/**\n * Coexistence policy for embedder swaps.\n *\n * - `'lock-on-first'` (default) refuses to register a second active\n * embedder; surfaces an actionable error pointing at the planned\n * migration runner.\n * - `'multi-active'` keeps both embedders alive (read union, write\n * to active); used while a long migration is in flight.\n * - `'auto-migrate'` re-embeds existing rows in resumable batches\n * until the source embedder has zero rows, then retires it.\n *\n * @stable\n */\nexport type EmbedderMigrationStrategy = 'lock-on-first' | 'multi-active' | 'auto-migrate';\n\n/**\n * Per-iteration progress snapshot yielded by {@link migrateEmbedder}.\n *\n * @stable\n */\nexport interface MigrationProgress {\n /** `'fact'`, `'episode'`, or `'message'` which entity is being migrated. */\n readonly kind: 'fact' | 'episode' | 'message';\n /** Number of records processed so far. */\n readonly processed: number;\n /** Total records expected for this entity (when known). */\n readonly total: number;\n /** Identifier of the source embedder (`'<adapter>:<model>@<dim>'`). */\n readonly source: string;\n /** Identifier of the target embedder. */\n readonly target: string;\n /**\n * Identifier for this migration run. MST-12: this is an in-memory id for\n * the current run there is no persisted `migration_state` cursor today, so\n * a migration does not resume across processes.\n */\n readonly migrationId: string;\n /** Phase discriminator. */\n readonly phase: 'planning' | 'running' | 'paused' | 'committed' | 'aborted';\n}\n\n/**\n * Options accepted by {@link migrateEmbedder}.\n *\n * @stable\n */\nexport interface MigrateEmbedderOptions {\n /** Source embedder (currently active). */\n readonly source: EmbedderProvider;\n /** Target embedder (becomes active when migration commits). */\n readonly target: EmbedderProvider;\n /** Storage layer's embedder registry. */\n readonly embeddings: EmbeddingMetaRegistryLike;\n /** Strategy applied per `embedding_meta` row. Default `'lock-on-first'`. */\n readonly strategy?: EmbedderMigrationStrategy;\n /**\n * Threshold for `auto-migrate`. The runner streams source rows in\n * batches of `batchSize` (default `512`) and yields progress after\n * each batch.\n */\n readonly batchSize?: number;\n /** Optional cap on the number of rows to migrate per kind. */\n readonly maxRecordsPerKind?: number;\n /**\n * Hook that returns the next batch of rows to re-embed for a given kind.\n * MST-12: this is **caller-supplied** there is no store-side helper that\n * auto-wires it today, and `auto-migrate` throws without it. Pass a paging\n * function over your source rows to drive the migration.\n */\n readonly nextBatch?: NextBatchHook;\n /** Optional abort signal aborting yields one final progress event. */\n readonly signal?: AbortSignal;\n}\n\n/**\n * Per-batch loader. Returns up to `batchSize` rows for the supplied\n * `kind` whose `embedder_id` is the source embedder. Returning an\n * empty array signals end-of-stream.\n *\n * @stable\n */\nexport type NextBatchHook = (args: {\n readonly kind: 'fact' | 'episode' | 'message';\n readonly source: string;\n readonly target: string;\n readonly batchSize: number;\n readonly cursor: string | null;\n}) => Promise<{ readonly rows: ReadonlyArray<MigrationRow>; readonly nextCursor: string | null }>;\n\n/**\n * Single row exposed to the migration runner. The runner re-embeds\n * `text` with the target embedder; the storage adapter is responsible\n * for committing the new vector + updating `embedder_id`.\n *\n * @stable\n */\nexport interface MigrationRow {\n readonly id: string;\n readonly text: string;\n readonly write: (vector: Float32Array) => Promise<void>;\n}\n\n/**\n * Stream embedder migrations as `AsyncIterable<MigrationProgress>`.\n *\n * The function is the universal entry point for every migration\n * strategy:\n *\n * - `'lock-on-first'`: surfaces the locked error eagerly so the\n * operator can act before any work happens.\n * - `'multi-active'`: registers the target alongside the source and\n * yields a single `'committed'` progress event.\n * - `'auto-migrate'`: streams batches until the source is fully\n * drained, then retires the source row.\n *\n * @stable\n */\nexport async function* migrateEmbedder(\n options: MigrateEmbedderOptions,\n): AsyncGenerator<MigrationProgress, void, unknown> {\n const { source, target, embeddings } = options;\n const strategy = options.strategy ?? 'lock-on-first';\n const sourceId = source.id();\n const targetId = target.id();\n const migrationId = `migration_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 7)}`;\n\n if (sourceId === targetId) {\n throw new EmbedderMigrationStateError(\n `source and target embedders must differ; both resolved to '${sourceId}'.`,\n );\n }\n\n const sourceMeta = embeddings.get(sourceId);\n if (sourceMeta === null) {\n throw new EmbedderMigrationStateError(\n `source embedder '${sourceId}' is not registered. Run createMemory({ embedder: source }) first.`,\n );\n }\n\n if (strategy === 'lock-on-first') {\n throw new EmbedderMigrationLockedError(sourceId, targetId);\n }\n\n bindEmbedder(target, embeddings);\n\n if (strategy === 'multi-active') {\n yield {\n kind: 'fact',\n processed: 0,\n total: 0,\n source: sourceId,\n target: targetId,\n migrationId,\n phase: 'committed',\n };\n return;\n }\n\n // strategy === 'auto-migrate'\n if (options.nextBatch === undefined) {\n throw new EmbedderMigrationStateError(\n \"'auto-migrate' requires a `nextBatch` hook supplied by the storage adapter.\",\n );\n }\n\n const batchSize = options.batchSize ?? 512;\n const cap = options.maxRecordsPerKind ?? Number.POSITIVE_INFINITY;\n\n for (const kind of ['fact', 'episode', 'message'] as const) {\n let cursor: string | null = null;\n let processed = 0;\n while (processed < cap) {\n if (options.signal?.aborted === true) {\n yield {\n kind,\n processed,\n total: processed,\n source: sourceId,\n target: targetId,\n migrationId,\n phase: 'aborted',\n };\n throw new EmbedderMigrationAbortedError(migrationId);\n }\n const batch = await options.nextBatch({\n kind,\n source: sourceId,\n target: targetId,\n batchSize,\n cursor,\n });\n if (batch.rows.length === 0) break;\n const texts = batch.rows.map((r) => r.text);\n const vectors = await target.embed(texts, {\n ...(options.signal !== undefined ? { signal: options.signal } : {}),\n });\n for (let i = 0; i < batch.rows.length; i++) {\n const row = batch.rows[i];\n const vector = vectors[i];\n if (row === undefined || vector === undefined) continue;\n await row.write(vector);\n }\n processed += batch.rows.length;\n cursor = batch.nextCursor;\n yield {\n kind,\n processed,\n total: processed,\n source: sourceId,\n target: targetId,\n migrationId,\n phase: 'running',\n };\n if (cursor === null) break;\n }\n }\n\n embeddings.retire(sourceId);\n yield {\n kind: 'fact',\n processed: 0,\n total: 0,\n source: sourceId,\n target: targetId,\n migrationId,\n phase: 'committed',\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AA8HA,gBAAuB,gBACrB,SACkD;CAClD,MAAM,EAAE,QAAQ,QAAQ,eAAe;CACvC,MAAM,WAAW,QAAQ,YAAY;CACrC,MAAM,WAAW,OAAO,IAAI;CAC5B,MAAM,WAAW,OAAO,IAAI;CAC5B,MAAM,cAAc,aAAa,KAAK,KAAK,CAAC,SAAS,GAAG,CAAC,GAAG,KAAK,QAAQ,CAAC,SAAS,GAAG,CAAC,MAAM,GAAG,EAAE;AAElG,KAAI,aAAa,SACf,OAAM,IAAI,4BACR,8DAA8D,SAAS,IACxE;AAIH,KADmB,WAAW,IAAI,SAAS,KACxB,KACjB,OAAM,IAAI,4BACR,oBAAoB,SAAS,oEAC9B;AAGH,KAAI,aAAa,gBACf,OAAM,IAAI,6BAA6B,UAAU,SAAS;AAG5D,cAAa,QAAQ,WAAW;AAEhC,KAAI,aAAa,gBAAgB;AAC/B,QAAM;GACJ,MAAM;GACN,WAAW;GACX,OAAO;GACP,QAAQ;GACR,QAAQ;GACR;GACA,OAAO;GACR;AACD;;AAIF,KAAI,QAAQ,cAAc,OACxB,OAAM,IAAI,4BACR,8EACD;CAGH,MAAM,YAAY,QAAQ,aAAa;CACvC,MAAM,MAAM,QAAQ,qBAAqB,OAAO;AAEhD,MAAK,MAAM,QAAQ;EAAC;EAAQ;EAAW;EAAU,EAAW;EAC1D,IAAIA,SAAwB;EAC5B,IAAI,YAAY;AAChB,SAAO,YAAY,KAAK;AACtB,OAAI,QAAQ,QAAQ,YAAY,MAAM;AACpC,UAAM;KACJ;KACA;KACA,OAAO;KACP,QAAQ;KACR,QAAQ;KACR;KACA,OAAO;KACR;AACD,UAAM,IAAI,8BAA8B,YAAY;;GAEtD,MAAM,QAAQ,MAAM,QAAQ,UAAU;IACpC;IACA,QAAQ;IACR,QAAQ;IACR;IACA;IACD,CAAC;AACF,OAAI,MAAM,KAAK,WAAW,EAAG;GAC7B,MAAM,QAAQ,MAAM,KAAK,KAAK,MAAM,EAAE,KAAK;GAC3C,MAAM,UAAU,MAAM,OAAO,MAAM,OAAO,EACxC,GAAI,QAAQ,WAAW,SAAY,EAAE,QAAQ,QAAQ,QAAQ,GAAG,EAAE,EACnE,CAAC;AACF,QAAK,IAAI,IAAI,GAAG,IAAI,MAAM,KAAK,QAAQ,KAAK;IAC1C,MAAM,MAAM,MAAM,KAAK;IACvB,MAAM,SAAS,QAAQ;AACvB,QAAI,QAAQ,UAAa,WAAW,OAAW;AAC/C,UAAM,IAAI,MAAM,OAAO;;AAEzB,gBAAa,MAAM,KAAK;AACxB,YAAS,MAAM;AACf,SAAM;IACJ;IACA;IACA,OAAO;IACP,QAAQ;IACR,QAAQ;IACR;IACA,OAAO;IACR;AACD,OAAI,WAAW,KAAM;;;AAIzB,YAAW,OAAO,SAAS;AAC3B,OAAM;EACJ,MAAM;EACN,WAAW;EACX,OAAO;EACP,QAAQ;EACR,QAAQ;EACR;EACA,OAAO;EACR"}
1
+ {"version":3,"file":"embedder-migration.js","names":["cursor: string | null"],"sources":["../../src/migration/embedder-migration.ts"],"sourcesContent":["import type { EmbedderProvider } from '@graphorin/core';\nimport {\n EmbedderMigrationAbortedError,\n EmbedderMigrationLockedError,\n EmbedderMigrationStateError,\n} from '../errors/index.js';\nimport { bindEmbedder } from '../internal/embedder-binding.js';\nimport type { EmbeddingMetaRegistryLike } from '../internal/storage-adapter.js';\n\n/**\n * Coexistence policy for embedder swaps.\n *\n * - `'lock-on-first'` (default) - refuses to register a second active\n * embedder; surfaces an actionable error pointing at the planned\n * migration runner.\n * - `'multi-active'` - keeps both embedders alive (read union, write\n * to active); used while a long migration is in flight.\n * - `'auto-migrate'` - re-embeds existing rows in resumable batches\n * until the source embedder has zero rows, then retires it.\n *\n * @stable\n */\nexport type EmbedderMigrationStrategy = 'lock-on-first' | 'multi-active' | 'auto-migrate';\n\n/**\n * Per-iteration progress snapshot yielded by {@link migrateEmbedder}.\n *\n * @stable\n */\nexport interface MigrationProgress {\n /** `'fact'`, `'episode'`, or `'message'` - which entity is being migrated. */\n readonly kind: 'fact' | 'episode' | 'message';\n /** Number of records processed so far. */\n readonly processed: number;\n /** Total records expected for this entity (when known). */\n readonly total: number;\n /** Identifier of the source embedder (`'<adapter>:<model>@<dim>'`). */\n readonly source: string;\n /** Identifier of the target embedder. */\n readonly target: string;\n /**\n * Identifier for this migration run. MST-12: this is an in-memory id for\n * the current run - there is no persisted `migration_state` cursor today, so\n * a migration does not resume across processes.\n */\n readonly migrationId: string;\n /** Phase discriminator. */\n readonly phase: 'planning' | 'running' | 'paused' | 'committed' | 'aborted';\n}\n\n/**\n * Options accepted by {@link migrateEmbedder}.\n *\n * @stable\n */\nexport interface MigrateEmbedderOptions {\n /** Source embedder (currently active). */\n readonly source: EmbedderProvider;\n /** Target embedder (becomes active when migration commits). */\n readonly target: EmbedderProvider;\n /** Storage layer's embedder registry. */\n readonly embeddings: EmbeddingMetaRegistryLike;\n /** Strategy applied per `embedding_meta` row. Default `'lock-on-first'`. */\n readonly strategy?: EmbedderMigrationStrategy;\n /**\n * Threshold for `auto-migrate`. The runner streams source rows in\n * batches of `batchSize` (default `512`) and yields progress after\n * each batch.\n */\n readonly batchSize?: number;\n /** Optional cap on the number of rows to migrate per kind. */\n readonly maxRecordsPerKind?: number;\n /**\n * Hook that returns the next batch of rows to re-embed for a given kind.\n * MST-12: this is **caller-supplied** - there is no store-side helper that\n * auto-wires it today, and `auto-migrate` throws without it. Pass a paging\n * function over your source rows to drive the migration.\n */\n readonly nextBatch?: NextBatchHook;\n /** Optional abort signal - aborting yields one final progress event. */\n readonly signal?: AbortSignal;\n}\n\n/**\n * Per-batch loader. Returns up to `batchSize` rows for the supplied\n * `kind` whose `embedder_id` is the source embedder. Returning an\n * empty array signals end-of-stream.\n *\n * @stable\n */\nexport type NextBatchHook = (args: {\n readonly kind: 'fact' | 'episode' | 'message';\n readonly source: string;\n readonly target: string;\n readonly batchSize: number;\n readonly cursor: string | null;\n}) => Promise<{ readonly rows: ReadonlyArray<MigrationRow>; readonly nextCursor: string | null }>;\n\n/**\n * Single row exposed to the migration runner. The runner re-embeds\n * `text` with the target embedder; the storage adapter is responsible\n * for committing the new vector + updating `embedder_id`.\n *\n * @stable\n */\nexport interface MigrationRow {\n readonly id: string;\n readonly text: string;\n readonly write: (vector: Float32Array) => Promise<void>;\n}\n\n/**\n * Stream embedder migrations as `AsyncIterable<MigrationProgress>`.\n *\n * The function is the universal entry point for every migration\n * strategy:\n *\n * - `'lock-on-first'`: surfaces the locked error eagerly so the\n * operator can act before any work happens.\n * - `'multi-active'`: registers the target alongside the source and\n * yields a single `'committed'` progress event.\n * - `'auto-migrate'`: streams batches until the source is fully\n * drained, then retires the source row.\n *\n * @stable\n */\nexport async function* migrateEmbedder(\n options: MigrateEmbedderOptions,\n): AsyncGenerator<MigrationProgress, void, unknown> {\n const { source, target, embeddings } = options;\n const strategy = options.strategy ?? 'lock-on-first';\n const sourceId = source.id();\n const targetId = target.id();\n const migrationId = `migration_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 7)}`;\n\n if (sourceId === targetId) {\n throw new EmbedderMigrationStateError(\n `source and target embedders must differ; both resolved to '${sourceId}'.`,\n );\n }\n\n const sourceMeta = embeddings.get(sourceId);\n if (sourceMeta === null) {\n throw new EmbedderMigrationStateError(\n `source embedder '${sourceId}' is not registered. Run createMemory({ embedder: source }) first.`,\n );\n }\n\n if (strategy === 'lock-on-first') {\n throw new EmbedderMigrationLockedError(sourceId, targetId);\n }\n\n bindEmbedder(target, embeddings);\n\n if (strategy === 'multi-active') {\n yield {\n kind: 'fact',\n processed: 0,\n total: 0,\n source: sourceId,\n target: targetId,\n migrationId,\n phase: 'committed',\n };\n return;\n }\n\n // strategy === 'auto-migrate'\n if (options.nextBatch === undefined) {\n throw new EmbedderMigrationStateError(\n \"'auto-migrate' requires a `nextBatch` hook supplied by the storage adapter.\",\n );\n }\n\n const batchSize = options.batchSize ?? 512;\n const cap = options.maxRecordsPerKind ?? Number.POSITIVE_INFINITY;\n\n for (const kind of ['fact', 'episode', 'message'] as const) {\n let cursor: string | null = null;\n let processed = 0;\n while (processed < cap) {\n if (options.signal?.aborted === true) {\n yield {\n kind,\n processed,\n total: processed,\n source: sourceId,\n target: targetId,\n migrationId,\n phase: 'aborted',\n };\n throw new EmbedderMigrationAbortedError(migrationId);\n }\n const batch = await options.nextBatch({\n kind,\n source: sourceId,\n target: targetId,\n batchSize,\n cursor,\n });\n if (batch.rows.length === 0) break;\n const texts = batch.rows.map((r) => r.text);\n const vectors = await target.embed(texts, {\n ...(options.signal !== undefined ? { signal: options.signal } : {}),\n });\n for (let i = 0; i < batch.rows.length; i++) {\n const row = batch.rows[i];\n const vector = vectors[i];\n if (row === undefined || vector === undefined) continue;\n await row.write(vector);\n }\n processed += batch.rows.length;\n cursor = batch.nextCursor;\n yield {\n kind,\n processed,\n total: processed,\n source: sourceId,\n target: targetId,\n migrationId,\n phase: 'running',\n };\n if (cursor === null) break;\n }\n }\n\n embeddings.retire(sourceId);\n yield {\n kind: 'fact',\n processed: 0,\n total: 0,\n source: sourceId,\n target: targetId,\n migrationId,\n phase: 'committed',\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AA8HA,gBAAuB,gBACrB,SACkD;CAClD,MAAM,EAAE,QAAQ,QAAQ,eAAe;CACvC,MAAM,WAAW,QAAQ,YAAY;CACrC,MAAM,WAAW,OAAO,IAAI;CAC5B,MAAM,WAAW,OAAO,IAAI;CAC5B,MAAM,cAAc,aAAa,KAAK,KAAK,CAAC,SAAS,GAAG,CAAC,GAAG,KAAK,QAAQ,CAAC,SAAS,GAAG,CAAC,MAAM,GAAG,EAAE;AAElG,KAAI,aAAa,SACf,OAAM,IAAI,4BACR,8DAA8D,SAAS,IACxE;AAIH,KADmB,WAAW,IAAI,SAAS,KACxB,KACjB,OAAM,IAAI,4BACR,oBAAoB,SAAS,oEAC9B;AAGH,KAAI,aAAa,gBACf,OAAM,IAAI,6BAA6B,UAAU,SAAS;AAG5D,cAAa,QAAQ,WAAW;AAEhC,KAAI,aAAa,gBAAgB;AAC/B,QAAM;GACJ,MAAM;GACN,WAAW;GACX,OAAO;GACP,QAAQ;GACR,QAAQ;GACR;GACA,OAAO;GACR;AACD;;AAIF,KAAI,QAAQ,cAAc,OACxB,OAAM,IAAI,4BACR,8EACD;CAGH,MAAM,YAAY,QAAQ,aAAa;CACvC,MAAM,MAAM,QAAQ,qBAAqB,OAAO;AAEhD,MAAK,MAAM,QAAQ;EAAC;EAAQ;EAAW;EAAU,EAAW;EAC1D,IAAIA,SAAwB;EAC5B,IAAI,YAAY;AAChB,SAAO,YAAY,KAAK;AACtB,OAAI,QAAQ,QAAQ,YAAY,MAAM;AACpC,UAAM;KACJ;KACA;KACA,OAAO;KACP,QAAQ;KACR,QAAQ;KACR;KACA,OAAO;KACR;AACD,UAAM,IAAI,8BAA8B,YAAY;;GAEtD,MAAM,QAAQ,MAAM,QAAQ,UAAU;IACpC;IACA,QAAQ;IACR,QAAQ;IACR;IACA;IACD,CAAC;AACF,OAAI,MAAM,KAAK,WAAW,EAAG;GAC7B,MAAM,QAAQ,MAAM,KAAK,KAAK,MAAM,EAAE,KAAK;GAC3C,MAAM,UAAU,MAAM,OAAO,MAAM,OAAO,EACxC,GAAI,QAAQ,WAAW,SAAY,EAAE,QAAQ,QAAQ,QAAQ,GAAG,EAAE,EACnE,CAAC;AACF,QAAK,IAAI,IAAI,GAAG,IAAI,MAAM,KAAK,QAAQ,KAAK;IAC1C,MAAM,MAAM,MAAM,KAAK;IACvB,MAAM,SAAS,QAAQ;AACvB,QAAI,QAAQ,UAAa,WAAW,OAAW;AAC/C,UAAM,IAAI,MAAM,OAAO;;AAEzB,gBAAa,MAAM,KAAK;AACxB,YAAS,MAAM;AACf,SAAM;IACJ;IACA;IACA,OAAO;IACP,QAAQ;IACR,QAAQ;IACR;IACA,OAAO;IACR;AACD,OAAI,WAAW,KAAM;;;AAIzB,YAAW,OAAO,SAAS;AAC3B,OAAM;EACJ,MAAM;EACN,WAAW;EACX,OAAO;EACP,QAAQ;EACR,QAAQ;EACR;EACA,OAAO;EACR"}
@@ -0,0 +1,6 @@
1
+ //#region package.json
2
+ var version = "0.6.1";
3
+
4
+ //#endregion
5
+ export { version };
6
+ //# sourceMappingURL=package.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"package.js","names":[],"sources":["../package.json"],"sourcesContent":["{\n \"name\": \"@graphorin/memory\",\n \"version\": \"0.6.1\",\n \"description\": \"Six-tier memory system for the Graphorin framework: createMemory() facade with working / session / episodic / semantic / procedural / shared sub-modules, eleven memory tools wired into @graphorin/tools, hybrid vector + FTS5 search composed through Reciprocal Rank Fusion (k=60 default) with a pluggable reranker hook, a multi-stage conflict resolution pipeline (exact dedup → embedding three-zone → heuristic regex → subject/predicate → defer-to-deep) with an English locale pack default + plug-in locales, an embedder migration runner with three coexistence policies (lock-on-first / multi-active / auto-migrate), per-record embedder_id enforcement, multi-agent attribution, append-only writes with soft-delete + supersede semantics, default-on bi-temporal storage, provenance + quarantine safety gate for synthesized memory, AISpan emission for every operation, and tier-aware memory-modification guard wiring.\",\n \"license\": \"MIT\",\n \"author\": \"Oleksiy Stepurenko\",\n \"homepage\": \"https://github.com/o-stepper/graphorin/tree/main/packages/memory\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/o-stepper/graphorin.git\",\n \"directory\": \"packages/memory\"\n },\n \"bugs\": {\n \"url\": \"https://github.com/o-stepper/graphorin/issues\"\n },\n \"keywords\": [\n \"graphorin\",\n \"ai\",\n \"agents\",\n \"framework\",\n \"memory\",\n \"long-term-memory\",\n \"working-memory\",\n \"session-memory\",\n \"episodic-memory\",\n \"semantic-memory\",\n \"procedural-memory\",\n \"shared-memory\",\n \"rrf\",\n \"hybrid-search\",\n \"rerank\",\n \"conflict-resolution\",\n \"embedder-migration\",\n \"bi-temporal\",\n \"multi-agent\"\n ],\n \"type\": \"module\",\n \"engines\": {\n \"node\": \">=22.0.0\"\n },\n \"main\": \"./dist/index.js\",\n \"module\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"import\": \"./dist/index.js\"\n },\n \"./facade\": {\n \"types\": \"./dist/facade.d.ts\",\n \"import\": \"./dist/facade.js\"\n },\n \"./tiers\": {\n \"types\": \"./dist/tiers/index.d.ts\",\n \"import\": \"./dist/tiers/index.js\"\n },\n \"./tools\": {\n \"types\": \"./dist/tools/index.d.ts\",\n \"import\": \"./dist/tools/index.js\"\n },\n \"./search\": {\n \"types\": \"./dist/search/index.d.ts\",\n \"import\": \"./dist/search/index.js\"\n },\n \"./conflict\": {\n \"types\": \"./dist/conflict/index.d.ts\",\n \"import\": \"./dist/conflict/index.js\"\n },\n \"./migration\": {\n \"types\": \"./dist/migration/index.d.ts\",\n \"import\": \"./dist/migration/index.js\"\n },\n \"./errors\": {\n \"types\": \"./dist/errors/index.d.ts\",\n \"import\": \"./dist/errors/index.js\"\n },\n \"./package.json\": \"./package.json\"\n },\n \"files\": [\n \"dist\",\n \"README.md\",\n \"CHANGELOG.md\",\n \"LICENSE\"\n ],\n \"scripts\": {\n \"build\": \"tsdown\",\n \"typecheck\": \"tsc --noEmit && tsc -p tsconfig.tests.json\",\n \"test\": \"vitest run\",\n \"lint\": \"biome check .\",\n \"clean\": \"rimraf dist .turbo *.tsbuildinfo\"\n },\n \"dependencies\": {\n \"@graphorin/core\": \"workspace:*\",\n \"@graphorin/observability\": \"workspace:*\",\n \"@graphorin/security\": \"workspace:*\",\n \"@graphorin/tools\": \"workspace:*\"\n },\n \"peerDependencies\": {\n \"zod\": \"^3.23.0 || ^4.0.0\"\n },\n \"peerDependenciesMeta\": {\n \"zod\": {\n \"optional\": false\n }\n },\n \"publishConfig\": {\n \"access\": \"public\",\n \"provenance\": true\n },\n \"devDependencies\": {\n \"@graphorin/store-sqlite\": \"workspace:*\",\n \"@graphorin/embedder-transformersjs\": \"workspace:*\",\n \"@graphorin/triggers\": \"workspace:*\",\n \"@types/better-sqlite3\": \"^7.6.13\",\n \"better-sqlite3\": \"^12.9.0\",\n \"fast-check\": \"^3.23.0\",\n \"zod\": \"^3.25.0\"\n }\n}\n"],"mappings":";cAEa"}
@@ -22,8 +22,11 @@ interface RecalledMemoryExplanation {
22
22
  /**
23
23
  * Raw per-signal scores contributing to `score`, exactly as the
24
24
  * pipeline recorded them on the hit: FTS (`bm25`), vector similarity
25
- * (`vector`), the fused reciprocal-rank terms (`rrf` + per-list
26
- * `rrf.list_N`), and the decay multiplier (`decay`) when decay ran.
25
+ * (`vector`), the fused reciprocal-rank terms (`rrf` plus one
26
+ * `rrf.<label>` entry per fused list keyed by the retriever kind,
27
+ * e.g. `rrf.fts` / `rrf.vector`, with `rrf.list_N` as the fallback
28
+ * when a list carries no label), and the decay multiplier (`decay`)
29
+ * when decay ran.
27
30
  */
28
31
  readonly signals: Readonly<Record<string, number>>;
29
32
  }
@@ -31,7 +34,7 @@ interface RecalledMemoryExplanation {
31
34
  * Structured "why was this recalled?" explanation for one
32
35
  * {@link SemanticMemory.search} call: the query, the reranker that
33
36
  * fused the candidate lists, and the per-memory signal breakdown in
34
- * final-rank order. Pure data safe to attach to a span, log, or
37
+ * final-rank order. Pure data - safe to attach to a span, log, or
35
38
  * render. X-3.
36
39
  *
37
40
  * @stable
@@ -61,7 +64,7 @@ declare function explainRecall<TRecord extends MemoryRecord>(hits: ReadonlyArray
61
64
  readonly rerankerId: string;
62
65
  }): RecallExplanation;
63
66
  /**
64
- * Render a {@link RecallExplanation} as a compact ASCII block a
67
+ * Render a {@link RecallExplanation} as a compact ASCII block - a
65
68
  * header line plus one line per recalled memory with its signal
66
69
  * breakdown. Pure (returns a string, no I/O); used by operator tooling
67
70
  * and trace inspectors.
@@ -1 +1 @@
1
- {"version":3,"file":"explain.d.ts","names":[],"sources":["../../src/search/explain.ts"],"sourcesContent":[],"mappings":";;;;AAwGA;;;;;;;;UA9EiB,yBAAA;;;;iBAIA;;;;;;;;;;;oBAWG,SAAS;;;;;;;;;;;UAYZ,iBAAA;;;;;;;;oBAQG,cAAc;;;;;;;;;;;;iBAalB,8BAA8B,oBACtC,cAAc,UAAU;;;IAE7B;;;;;;;;;iBA2Ba,uBAAA,cAAqC"}
1
+ {"version":3,"file":"explain.d.ts","names":[],"sources":["../../src/search/explain.ts"],"sourcesContent":[],"mappings":";;;;AA2GA;;;;;;;;UAjFiB,yBAAA;;;;iBAIA;;;;;;;;;;;;;;oBAcG,SAAS;;;;;;;;;;;UAYZ,iBAAA;;;;;;;;oBAQG,cAAc;;;;;;;;;;;;iBAalB,8BAA8B,oBACtC,cAAc,UAAU;;;IAE7B;;;;;;;;;iBA2Ba,uBAAA,cAAqC"}
@@ -25,7 +25,7 @@ function explainRecall(hits, opts) {
25
25
  });
26
26
  }
27
27
  /**
28
- * Render a {@link RecallExplanation} as a compact ASCII block a
28
+ * Render a {@link RecallExplanation} as a compact ASCII block - a
29
29
  * header line plus one line per recalled memory with its signal
30
30
  * breakdown. Pure (returns a string, no I/O); used by operator tooling
31
31
  * and trace inspectors.
@@ -1 +1 @@
1
- {"version":3,"file":"explain.js","names":[],"sources":["../../src/search/explain.ts"],"sourcesContent":["/**\n * Recall explainability for `@graphorin/memory` \"why was this\n * recalled?\" (X-3). Turns the per-signal scores the hybrid pipeline\n * already records on each {@link MemoryHit} (FTS `bm25`, vector\n * similarity, the fused reciprocal-rank term, the decay multiplier,\n * future graph-hop terms) into a structured, renderable explanation.\n *\n * Pure data: the builder re-uses whatever {@link MemoryHit.signals} the\n * pipeline left behind rather than re-deriving them, so the explanation\n * always matches the ranking that actually happened. The renderer\n * returns a string and performs no I/O, so the explanation can be\n * attached to a span, logged, or surfaced by operator tooling.\n *\n * @packageDocumentation\n */\n\nimport type { MemoryHit, MemoryKind, MemoryRecord } from '@graphorin/core';\n\n/**\n * Per-memory contribution breakdown for one recalled record: the\n * decomposed signals the hybrid pipeline summed into the final score,\n * plus the record's final rank. Surfaces *why* a single memory was\n * recalled and how it ranked relative to its siblings.\n *\n * @stable\n */\nexport interface RecalledMemoryExplanation {\n /** Record id (fact / episode / insight / …). */\n readonly id: string;\n /** Memory kind of the recalled record. */\n readonly kind: MemoryKind;\n /** 1-based position in the final fused + decayed ranking. */\n readonly rank: number;\n /** Final score after fusion and (optional) decay. */\n readonly score: number;\n /**\n * Raw per-signal scores contributing to `score`, exactly as the\n * pipeline recorded them on the hit: FTS (`bm25`), vector similarity\n * (`vector`), the fused reciprocal-rank terms (`rrf` + per-list\n * `rrf.list_N`), and the decay multiplier (`decay`) when decay ran.\n */\n readonly signals: Readonly<Record<string, number>>;\n}\n\n/**\n * Structured \"why was this recalled?\" explanation for one\n * {@link SemanticMemory.search} call: the query, the reranker that\n * fused the candidate lists, and the per-memory signal breakdown in\n * final-rank order. Pure data safe to attach to a span, log, or\n * render. X-3.\n *\n * @stable\n */\nexport interface RecallExplanation {\n /** The query the recall ran for. */\n readonly query: string;\n /** Stable id of the reranker that fused the candidate lists. */\n readonly rerankerId: string;\n /** Number of recalled memories explained. */\n readonly count: number;\n /** Per-memory breakdown, in final-rank order (rank 1 first). */\n readonly results: ReadonlyArray<RecalledMemoryExplanation>;\n}\n\n/**\n * Build a {@link RecallExplanation} from a ranked hit list. Pure and\n * deterministic: it surfaces whatever per-signal scores the hybrid\n * pipeline left on each {@link MemoryHit.signals} (FTS, vector, RRF,\n * decay, future graph-hop terms) without re-deriving them, so the\n * explanation always matches what actually drove the ranking. Hits are\n * assumed to be in final-rank order (as returned by `search`).\n *\n * @stable\n */\nexport function explainRecall<TRecord extends MemoryRecord>(\n hits: ReadonlyArray<MemoryHit<TRecord>>,\n opts: { readonly query: string; readonly rerankerId: string },\n): RecallExplanation {\n const results = hits.map(\n (hit, idx): RecalledMemoryExplanation =>\n Object.freeze({\n id: hit.record.id,\n kind: hit.record.kind,\n rank: idx + 1,\n score: hit.score,\n signals: Object.freeze({ ...(hit.signals ?? {}) }),\n }),\n );\n return Object.freeze({\n query: opts.query,\n rerankerId: opts.rerankerId,\n count: results.length,\n results: Object.freeze(results),\n });\n}\n\n/**\n * Render a {@link RecallExplanation} as a compact ASCII block a\n * header line plus one line per recalled memory with its signal\n * breakdown. Pure (returns a string, no I/O); used by operator tooling\n * and trace inspectors.\n *\n * @stable\n */\nexport function formatRecallExplanation(explanation: RecallExplanation): string {\n const header = `recall ${JSON.stringify(explanation.query)} via ${explanation.rerankerId} -> ${explanation.count} result(s)`;\n if (explanation.results.length === 0) return header;\n const lines = explanation.results.map((r) => {\n const signals = Object.entries(r.signals)\n .map(([key, value]) => `${key}=${formatScore(value)}`)\n .join(' ');\n const suffix = signals === '' ? '' : ` (${signals})`;\n return ` #${r.rank} ${r.id} [${r.kind}] score=${formatScore(r.score)}${suffix}`;\n });\n return [header, ...lines].join('\\n');\n}\n\nfunction formatScore(value: number): string {\n return Number.isFinite(value) ? value.toFixed(4) : String(value);\n}\n"],"mappings":";;;;;;;;;;;AA0EA,SAAgB,cACd,MACA,MACmB;CACnB,MAAM,UAAU,KAAK,KAClB,KAAK,QACJ,OAAO,OAAO;EACZ,IAAI,IAAI,OAAO;EACf,MAAM,IAAI,OAAO;EACjB,MAAM,MAAM;EACZ,OAAO,IAAI;EACX,SAAS,OAAO,OAAO,EAAE,GAAI,IAAI,WAAW,EAAE,EAAG,CAAC;EACnD,CAAC,CACL;AACD,QAAO,OAAO,OAAO;EACnB,OAAO,KAAK;EACZ,YAAY,KAAK;EACjB,OAAO,QAAQ;EACf,SAAS,OAAO,OAAO,QAAQ;EAChC,CAAC;;;;;;;;;;AAWJ,SAAgB,wBAAwB,aAAwC;CAC9E,MAAM,SAAS,UAAU,KAAK,UAAU,YAAY,MAAM,CAAC,OAAO,YAAY,WAAW,MAAM,YAAY,MAAM;AACjH,KAAI,YAAY,QAAQ,WAAW,EAAG,QAAO;AAQ7C,QAAO,CAAC,QAAQ,GAPF,YAAY,QAAQ,KAAK,MAAM;EAC3C,MAAM,UAAU,OAAO,QAAQ,EAAE,QAAQ,CACtC,KAAK,CAAC,KAAK,WAAW,GAAG,IAAI,GAAG,YAAY,MAAM,GAAG,CACrD,KAAK,IAAI;EACZ,MAAM,SAAS,YAAY,KAAK,KAAK,KAAK,QAAQ;AAClD,SAAO,MAAM,EAAE,KAAK,GAAG,EAAE,GAAG,IAAI,EAAE,KAAK,UAAU,YAAY,EAAE,MAAM,GAAG;GACxE,CACuB,CAAC,KAAK,KAAK;;AAGtC,SAAS,YAAY,OAAuB;AAC1C,QAAO,OAAO,SAAS,MAAM,GAAG,MAAM,QAAQ,EAAE,GAAG,OAAO,MAAM"}
1
+ {"version":3,"file":"explain.js","names":[],"sources":["../../src/search/explain.ts"],"sourcesContent":["/**\n * Recall explainability for `@graphorin/memory` - \"why was this\n * recalled?\" (X-3). Turns the per-signal scores the hybrid pipeline\n * already records on each {@link MemoryHit} (FTS `bm25`, vector\n * similarity, the fused reciprocal-rank term, the decay multiplier,\n * future graph-hop terms) into a structured, renderable explanation.\n *\n * Pure data: the builder re-uses whatever {@link MemoryHit.signals} the\n * pipeline left behind rather than re-deriving them, so the explanation\n * always matches the ranking that actually happened. The renderer\n * returns a string and performs no I/O, so the explanation can be\n * attached to a span, logged, or surfaced by operator tooling.\n *\n * @packageDocumentation\n */\n\nimport type { MemoryHit, MemoryKind, MemoryRecord } from '@graphorin/core';\n\n/**\n * Per-memory contribution breakdown for one recalled record: the\n * decomposed signals the hybrid pipeline summed into the final score,\n * plus the record's final rank. Surfaces *why* a single memory was\n * recalled and how it ranked relative to its siblings.\n *\n * @stable\n */\nexport interface RecalledMemoryExplanation {\n /** Record id (fact / episode / insight / …). */\n readonly id: string;\n /** Memory kind of the recalled record. */\n readonly kind: MemoryKind;\n /** 1-based position in the final fused + decayed ranking. */\n readonly rank: number;\n /** Final score after fusion and (optional) decay. */\n readonly score: number;\n /**\n * Raw per-signal scores contributing to `score`, exactly as the\n * pipeline recorded them on the hit: FTS (`bm25`), vector similarity\n * (`vector`), the fused reciprocal-rank terms (`rrf` plus one\n * `rrf.<label>` entry per fused list keyed by the retriever kind,\n * e.g. `rrf.fts` / `rrf.vector`, with `rrf.list_N` as the fallback\n * when a list carries no label), and the decay multiplier (`decay`)\n * when decay ran.\n */\n readonly signals: Readonly<Record<string, number>>;\n}\n\n/**\n * Structured \"why was this recalled?\" explanation for one\n * {@link SemanticMemory.search} call: the query, the reranker that\n * fused the candidate lists, and the per-memory signal breakdown in\n * final-rank order. Pure data - safe to attach to a span, log, or\n * render. X-3.\n *\n * @stable\n */\nexport interface RecallExplanation {\n /** The query the recall ran for. */\n readonly query: string;\n /** Stable id of the reranker that fused the candidate lists. */\n readonly rerankerId: string;\n /** Number of recalled memories explained. */\n readonly count: number;\n /** Per-memory breakdown, in final-rank order (rank 1 first). */\n readonly results: ReadonlyArray<RecalledMemoryExplanation>;\n}\n\n/**\n * Build a {@link RecallExplanation} from a ranked hit list. Pure and\n * deterministic: it surfaces whatever per-signal scores the hybrid\n * pipeline left on each {@link MemoryHit.signals} (FTS, vector, RRF,\n * decay, future graph-hop terms) without re-deriving them, so the\n * explanation always matches what actually drove the ranking. Hits are\n * assumed to be in final-rank order (as returned by `search`).\n *\n * @stable\n */\nexport function explainRecall<TRecord extends MemoryRecord>(\n hits: ReadonlyArray<MemoryHit<TRecord>>,\n opts: { readonly query: string; readonly rerankerId: string },\n): RecallExplanation {\n const results = hits.map(\n (hit, idx): RecalledMemoryExplanation =>\n Object.freeze({\n id: hit.record.id,\n kind: hit.record.kind,\n rank: idx + 1,\n score: hit.score,\n signals: Object.freeze({ ...(hit.signals ?? {}) }),\n }),\n );\n return Object.freeze({\n query: opts.query,\n rerankerId: opts.rerankerId,\n count: results.length,\n results: Object.freeze(results),\n });\n}\n\n/**\n * Render a {@link RecallExplanation} as a compact ASCII block - a\n * header line plus one line per recalled memory with its signal\n * breakdown. Pure (returns a string, no I/O); used by operator tooling\n * and trace inspectors.\n *\n * @stable\n */\nexport function formatRecallExplanation(explanation: RecallExplanation): string {\n const header = `recall ${JSON.stringify(explanation.query)} via ${explanation.rerankerId} -> ${explanation.count} result(s)`;\n if (explanation.results.length === 0) return header;\n const lines = explanation.results.map((r) => {\n const signals = Object.entries(r.signals)\n .map(([key, value]) => `${key}=${formatScore(value)}`)\n .join(' ');\n const suffix = signals === '' ? '' : ` (${signals})`;\n return ` #${r.rank} ${r.id} [${r.kind}] score=${formatScore(r.score)}${suffix}`;\n });\n return [header, ...lines].join('\\n');\n}\n\nfunction formatScore(value: number): string {\n return Number.isFinite(value) ? value.toFixed(4) : String(value);\n}\n"],"mappings":";;;;;;;;;;;AA6EA,SAAgB,cACd,MACA,MACmB;CACnB,MAAM,UAAU,KAAK,KAClB,KAAK,QACJ,OAAO,OAAO;EACZ,IAAI,IAAI,OAAO;EACf,MAAM,IAAI,OAAO;EACjB,MAAM,MAAM;EACZ,OAAO,IAAI;EACX,SAAS,OAAO,OAAO,EAAE,GAAI,IAAI,WAAW,EAAE,EAAG,CAAC;EACnD,CAAC,CACL;AACD,QAAO,OAAO,OAAO;EACnB,OAAO,KAAK;EACZ,YAAY,KAAK;EACjB,OAAO,QAAQ;EACf,SAAS,OAAO,OAAO,QAAQ;EAChC,CAAC;;;;;;;;;;AAWJ,SAAgB,wBAAwB,aAAwC;CAC9E,MAAM,SAAS,UAAU,KAAK,UAAU,YAAY,MAAM,CAAC,OAAO,YAAY,WAAW,MAAM,YAAY,MAAM;AACjH,KAAI,YAAY,QAAQ,WAAW,EAAG,QAAO;AAQ7C,QAAO,CAAC,QAAQ,GAPF,YAAY,QAAQ,KAAK,MAAM;EAC3C,MAAM,UAAU,OAAO,QAAQ,EAAE,QAAQ,CACtC,KAAK,CAAC,KAAK,WAAW,GAAG,IAAI,GAAG,YAAY,MAAM,GAAG,CACrD,KAAK,IAAI;EACZ,MAAM,SAAS,YAAY,KAAK,KAAK,KAAK,QAAQ;AAClD,SAAO,MAAM,EAAE,KAAK,GAAG,EAAE,GAAG,IAAI,EAAE,KAAK,UAAU,YAAY,EAAE,MAAM,GAAG;GACxE,CACuB,CAAC,KAAK,KAAK;;AAGtC,SAAS,YAAY,OAAuB;AAC1C,QAAO,OAAO,SAAS,MAAM,GAAG,MAAM,QAAQ,EAAE,GAAG,OAAO,MAAM"}
@@ -0,0 +1,48 @@
1
+ import { MemoryHit, MemoryRecord } from "@graphorin/core";
2
+
3
+ //#region src/search/fit-weights.d.ts
4
+
5
+ /** One labelled query: per-kind candidate lists + the relevant ids. */
6
+ interface FitFusionCase<TRecord extends MemoryRecord = MemoryRecord> {
7
+ /** FTS candidate list, best-first (as the store returns it). */
8
+ readonly fts: ReadonlyArray<MemoryHit<TRecord>>;
9
+ /** Vector candidate list, best-first. */
10
+ readonly vector: ReadonlyArray<MemoryHit<TRecord>>;
11
+ /** Ids judged relevant for this query (binary gain). */
12
+ readonly relevantIds: ReadonlyArray<string>;
13
+ }
14
+ /** Options for {@link fitFusionWeights}. */
15
+ interface FitFusionOptions {
16
+ /** Candidate weight values per axis. Default `[0.25, 0.5, 1, 2, 4]`. */
17
+ readonly grid?: ReadonlyArray<number>;
18
+ /** nDCG cutoff. Default `10`. */
19
+ readonly k?: number;
20
+ /** RRF constant forwarded to the fuse. Default `60`. */
21
+ readonly rrfK?: number;
22
+ }
23
+ /** Result of a fit: the winning weights + its score and the RRF baseline. */
24
+ interface FitFusionResult {
25
+ readonly weights: {
26
+ readonly fts: number;
27
+ readonly vector: number;
28
+ };
29
+ /** Mean nDCG@k of the winning weights across the cases. */
30
+ readonly score: number;
31
+ /** Mean nDCG@k of unit weights (plain RRF) - compare before adopting. */
32
+ readonly baseline: number;
33
+ }
34
+ /**
35
+ * Binary-gain nDCG@k over a ranked id list.
36
+ *
37
+ * @stable
38
+ */
39
+ declare function ndcgAtK(rankedIds: ReadonlyArray<string>, relevant: ReadonlySet<string>, k: number): number;
40
+ /**
41
+ * Grid-search fusion weights against labelled cases (C5).
42
+ *
43
+ * @stable
44
+ */
45
+ declare function fitFusionWeights<TRecord extends MemoryRecord = MemoryRecord>(cases: ReadonlyArray<FitFusionCase<TRecord>>, options?: FitFusionOptions): FitFusionResult;
46
+ //#endregion
47
+ export { FitFusionCase, FitFusionOptions, FitFusionResult, fitFusionWeights, ndcgAtK };
48
+ //# sourceMappingURL=fit-weights.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fit-weights.d.ts","names":[],"sources":["../../src/search/fit-weights.ts"],"sourcesContent":[],"mappings":";;;;AA4EA;AAAiD,UAvDhC,aAuDgC,CAAA,gBAvDF,YAuDE,GAvDa,YAuDb,CAAA,CAAA;EAAe;EAC3B,SAAA,GAAA,EAtDrB,aAsDqB,CAtDP,SAsDO,CAtDG,OAsDH,CAAA,CAAA;EAAd;EAAd,SAAA,MAAA,EApDU,aAoDV,CApDwB,SAoDxB,CApDkC,OAoDlC,CAAA,CAAA;EACE;EACR,SAAA,WAAA,EApDqB,aAoDrB,CAAA,MAAA,CAAA;;;UAhDc,gBAAA;;kBAEC;;;;;;;UAQD,eAAA;;;;;;;;;;;;;;;iBAaD,OAAA,YACH,iCACD;;;;;;iBAoBI,iCAAiC,eAAe,qBACvD,cAAc,cAAc,qBAC1B,mBACR"}
@@ -0,0 +1,70 @@
1
+ import { fuseWeighted } from "./rrf.js";
2
+
3
+ //#region src/search/fit-weights.ts
4
+ /**
5
+ * Binary-gain nDCG@k over a ranked id list.
6
+ *
7
+ * @stable
8
+ */
9
+ function ndcgAtK(rankedIds, relevant, k) {
10
+ const cutoff = Math.min(k, rankedIds.length);
11
+ let dcg = 0;
12
+ for (let i = 0; i < cutoff; i++) {
13
+ const id = rankedIds[i];
14
+ if (id !== void 0 && relevant.has(id)) dcg += 1 / Math.log2(i + 2);
15
+ }
16
+ const idealHits = Math.min(relevant.size, k);
17
+ let idcg = 0;
18
+ for (let i = 0; i < idealHits; i++) idcg += 1 / Math.log2(i + 2);
19
+ return idcg === 0 ? 0 : dcg / idcg;
20
+ }
21
+ /**
22
+ * Grid-search fusion weights against labelled cases (C5).
23
+ *
24
+ * @stable
25
+ */
26
+ function fitFusionWeights(cases, options = {}) {
27
+ if (cases.length === 0) throw new TypeError("[graphorin/memory] fitFusionWeights: at least one labelled case required");
28
+ const grid = options.grid ?? [
29
+ .25,
30
+ .5,
31
+ 1,
32
+ 2,
33
+ 4
34
+ ];
35
+ const k = options.k ?? 10;
36
+ const rrfK = options.rrfK ?? 60;
37
+ const meanNdcg = (wFts, wVector) => {
38
+ let total = 0;
39
+ for (const c of cases) {
40
+ const rankedIds = fuseWeighted([c.fts, c.vector], [wFts, wVector], rrfK).map((h) => h.record.id);
41
+ total += ndcgAtK(rankedIds, new Set(c.relevantIds), k);
42
+ }
43
+ return total / cases.length;
44
+ };
45
+ const baseline = meanNdcg(1, 1);
46
+ let best = {
47
+ weights: {
48
+ fts: 1,
49
+ vector: 1
50
+ },
51
+ score: baseline,
52
+ baseline
53
+ };
54
+ for (const wFts of grid) for (const wVector of grid) {
55
+ const score = meanNdcg(wFts, wVector);
56
+ if (score > best.score + 1e-12) best = {
57
+ weights: {
58
+ fts: wFts,
59
+ vector: wVector
60
+ },
61
+ score,
62
+ baseline
63
+ };
64
+ }
65
+ return best;
66
+ }
67
+
68
+ //#endregion
69
+ export { fitFusionWeights, ndcgAtK };
70
+ //# sourceMappingURL=fit-weights.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fit-weights.js","names":["best: FitFusionResult"],"sources":["../../src/search/fit-weights.ts"],"sourcesContent":["/**\n * C5: offline fusion-weight fitting. Tuned convex/weighted fusion beats\n * RRF with a handful of labelled queries (Bruch et al., TOIS 2023) - this\n * module turns that into a small, pure routine: grid-search\n * {@link FusionWeights} over labelled cases and return the setting that\n * maximizes mean nDCG@k, together with the plain-RRF baseline so the\n * caller can see whether tuning actually paid.\n *\n * Pure and I/O-free: the caller supplies per-kind candidate lists (run\n * your FTS and vector retrievers once per query, offline) plus the ids\n * judged relevant. Feed the winning weights to\n * `search(..., { fusion: { strategy: 'weighted', weights } })` or a\n * process-default `WeightedRRFReranker`.\n *\n * @packageDocumentation\n */\n\nimport type { MemoryHit, MemoryRecord } from '@graphorin/core';\nimport { fuseWeighted } from './rrf.js';\n\n/** One labelled query: per-kind candidate lists + the relevant ids. */\nexport interface FitFusionCase<TRecord extends MemoryRecord = MemoryRecord> {\n /** FTS candidate list, best-first (as the store returns it). */\n readonly fts: ReadonlyArray<MemoryHit<TRecord>>;\n /** Vector candidate list, best-first. */\n readonly vector: ReadonlyArray<MemoryHit<TRecord>>;\n /** Ids judged relevant for this query (binary gain). */\n readonly relevantIds: ReadonlyArray<string>;\n}\n\n/** Options for {@link fitFusionWeights}. */\nexport interface FitFusionOptions {\n /** Candidate weight values per axis. Default `[0.25, 0.5, 1, 2, 4]`. */\n readonly grid?: ReadonlyArray<number>;\n /** nDCG cutoff. Default `10`. */\n readonly k?: number;\n /** RRF constant forwarded to the fuse. Default `60`. */\n readonly rrfK?: number;\n}\n\n/** Result of a fit: the winning weights + its score and the RRF baseline. */\nexport interface FitFusionResult {\n readonly weights: { readonly fts: number; readonly vector: number };\n /** Mean nDCG@k of the winning weights across the cases. */\n readonly score: number;\n /** Mean nDCG@k of unit weights (plain RRF) - compare before adopting. */\n readonly baseline: number;\n}\n\n/**\n * Binary-gain nDCG@k over a ranked id list.\n *\n * @stable\n */\nexport function ndcgAtK(\n rankedIds: ReadonlyArray<string>,\n relevant: ReadonlySet<string>,\n k: number,\n): number {\n const cutoff = Math.min(k, rankedIds.length);\n let dcg = 0;\n for (let i = 0; i < cutoff; i++) {\n const id = rankedIds[i];\n if (id !== undefined && relevant.has(id)) dcg += 1 / Math.log2(i + 2);\n }\n const idealHits = Math.min(relevant.size, k);\n let idcg = 0;\n for (let i = 0; i < idealHits; i++) idcg += 1 / Math.log2(i + 2);\n return idcg === 0 ? 0 : dcg / idcg;\n}\n\n/**\n * Grid-search fusion weights against labelled cases (C5).\n *\n * @stable\n */\nexport function fitFusionWeights<TRecord extends MemoryRecord = MemoryRecord>(\n cases: ReadonlyArray<FitFusionCase<TRecord>>,\n options: FitFusionOptions = {},\n): FitFusionResult {\n if (cases.length === 0) {\n throw new TypeError('[graphorin/memory] fitFusionWeights: at least one labelled case required');\n }\n const grid = options.grid ?? [0.25, 0.5, 1, 2, 4];\n const k = options.k ?? 10;\n const rrfK = options.rrfK ?? 60;\n\n const meanNdcg = (wFts: number, wVector: number): number => {\n let total = 0;\n for (const c of cases) {\n const fused = fuseWeighted([c.fts, c.vector], [wFts, wVector], rrfK);\n const rankedIds = fused.map((h) => h.record.id);\n total += ndcgAtK(rankedIds, new Set(c.relevantIds), k);\n }\n return total / cases.length;\n };\n\n const baseline = meanNdcg(1, 1);\n let best: FitFusionResult = { weights: { fts: 1, vector: 1 }, score: baseline, baseline };\n for (const wFts of grid) {\n for (const wVector of grid) {\n const score = meanNdcg(wFts, wVector);\n if (score > best.score + 1e-12) {\n best = { weights: { fts: wFts, vector: wVector }, score, baseline };\n }\n }\n }\n return best;\n}\n"],"mappings":";;;;;;;;AAsDA,SAAgB,QACd,WACA,UACA,GACQ;CACR,MAAM,SAAS,KAAK,IAAI,GAAG,UAAU,OAAO;CAC5C,IAAI,MAAM;AACV,MAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,KAAK;EAC/B,MAAM,KAAK,UAAU;AACrB,MAAI,OAAO,UAAa,SAAS,IAAI,GAAG,CAAE,QAAO,IAAI,KAAK,KAAK,IAAI,EAAE;;CAEvE,MAAM,YAAY,KAAK,IAAI,SAAS,MAAM,EAAE;CAC5C,IAAI,OAAO;AACX,MAAK,IAAI,IAAI,GAAG,IAAI,WAAW,IAAK,SAAQ,IAAI,KAAK,KAAK,IAAI,EAAE;AAChE,QAAO,SAAS,IAAI,IAAI,MAAM;;;;;;;AAQhC,SAAgB,iBACd,OACA,UAA4B,EAAE,EACb;AACjB,KAAI,MAAM,WAAW,EACnB,OAAM,IAAI,UAAU,2EAA2E;CAEjG,MAAM,OAAO,QAAQ,QAAQ;EAAC;EAAM;EAAK;EAAG;EAAG;EAAE;CACjD,MAAM,IAAI,QAAQ,KAAK;CACvB,MAAM,OAAO,QAAQ,QAAQ;CAE7B,MAAM,YAAY,MAAc,YAA4B;EAC1D,IAAI,QAAQ;AACZ,OAAK,MAAM,KAAK,OAAO;GAErB,MAAM,YADQ,aAAa,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,MAAM,QAAQ,EAAE,KAAK,CAC5C,KAAK,MAAM,EAAE,OAAO,GAAG;AAC/C,YAAS,QAAQ,WAAW,IAAI,IAAI,EAAE,YAAY,EAAE,EAAE;;AAExD,SAAO,QAAQ,MAAM;;CAGvB,MAAM,WAAW,SAAS,GAAG,EAAE;CAC/B,IAAIA,OAAwB;EAAE,SAAS;GAAE,KAAK;GAAG,QAAQ;GAAG;EAAE,OAAO;EAAU;EAAU;AACzF,MAAK,MAAM,QAAQ,KACjB,MAAK,MAAM,WAAW,MAAM;EAC1B,MAAM,QAAQ,SAAS,MAAM,QAAQ;AACrC,MAAI,QAAQ,KAAK,QAAQ,MACvB,QAAO;GAAE,SAAS;IAAE,KAAK;IAAM,QAAQ;IAAS;GAAE;GAAO;GAAU;;AAIzE,QAAO"}