@graphorin/memory 0.6.1 → 0.7.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 (227) hide show
  1. package/CHANGELOG.md +71 -0
  2. package/README.md +1 -1
  3. package/dist/conflict/index.js +0 -1
  4. package/dist/conflict/pipeline.js +0 -1
  5. package/dist/conflict/pipeline.js.map +1 -1
  6. package/dist/conflict/stages/stage3-heuristic-regex.js +0 -1
  7. package/dist/conflict/stages/stage3-heuristic-regex.js.map +1 -1
  8. package/dist/conflict/types.d.ts +1 -1
  9. package/dist/conflict/types.js.map +1 -1
  10. package/dist/consolidator/index.js +2 -0
  11. package/dist/consolidator/index.js.map +1 -1
  12. package/dist/consolidator/lock.js +7 -1
  13. package/dist/consolidator/lock.js.map +1 -1
  14. package/dist/consolidator/phases/deep.js +19 -12
  15. package/dist/consolidator/phases/deep.js.map +1 -1
  16. package/dist/consolidator/phases/induce.d.ts.map +1 -1
  17. package/dist/consolidator/phases/induce.js +2 -3
  18. package/dist/consolidator/phases/induce.js.map +1 -1
  19. package/dist/consolidator/phases/reflect.js +35 -8
  20. package/dist/consolidator/phases/reflect.js.map +1 -1
  21. package/dist/consolidator/phases/standard.js +199 -28
  22. package/dist/consolidator/phases/standard.js.map +1 -1
  23. package/dist/consolidator/reconcile.js +16 -15
  24. package/dist/consolidator/reconcile.js.map +1 -1
  25. package/dist/consolidator/runtime.d.ts.map +1 -1
  26. package/dist/consolidator/runtime.js +129 -47
  27. package/dist/consolidator/runtime.js.map +1 -1
  28. package/dist/consolidator/scheduler.d.ts +17 -3
  29. package/dist/consolidator/scheduler.d.ts.map +1 -1
  30. package/dist/consolidator/scheduler.js.map +1 -1
  31. package/dist/consolidator/types.d.ts +30 -1
  32. package/dist/consolidator/types.d.ts.map +1 -1
  33. package/dist/consolidator/types.js +10 -0
  34. package/dist/consolidator/types.js.map +1 -1
  35. package/dist/context-engine/compaction/clear-tool-results.d.ts +1 -1
  36. package/dist/context-engine/compaction/clear-tool-results.js +1 -1
  37. package/dist/context-engine/compaction/clear-tool-results.js.map +1 -1
  38. package/dist/context-engine/compaction/compactor.d.ts +1 -0
  39. package/dist/context-engine/compaction/compactor.d.ts.map +1 -1
  40. package/dist/context-engine/compaction/compactor.js +32 -8
  41. package/dist/context-engine/compaction/compactor.js.map +1 -1
  42. package/dist/context-engine/compaction/hooks/types.d.ts +1 -1
  43. package/dist/context-engine/compaction/index.d.ts +1 -0
  44. package/dist/context-engine/compaction/templates/marker.d.ts +33 -0
  45. package/dist/context-engine/compaction/templates/marker.d.ts.map +1 -0
  46. package/dist/context-engine/compaction/templates/marker.js +34 -0
  47. package/dist/context-engine/compaction/templates/marker.js.map +1 -0
  48. package/dist/context-engine/compaction/templates/summary-9-section.d.ts +1 -1
  49. package/dist/context-engine/compaction/templates/summary-9-section.d.ts.map +1 -1
  50. package/dist/context-engine/compaction/templates/summary-9-section.js +4 -3
  51. package/dist/context-engine/compaction/templates/summary-9-section.js.map +1 -1
  52. package/dist/context-engine/engine.d.ts +7 -176
  53. package/dist/context-engine/engine.d.ts.map +1 -1
  54. package/dist/context-engine/engine.js +0 -2
  55. package/dist/context-engine/engine.js.map +1 -1
  56. package/dist/context-engine/index.d.ts +8 -49
  57. package/dist/context-engine/io-types.d.ts +144 -0
  58. package/dist/context-engine/io-types.d.ts.map +1 -0
  59. package/dist/context-engine/locale-packs/types.d.ts +1 -1
  60. package/dist/context-engine/locale-packs/types.js.map +1 -1
  61. package/dist/context-engine/privacy-filter.d.ts +1 -2
  62. package/dist/context-engine/privacy-filter.d.ts.map +1 -1
  63. package/dist/context-engine/privacy-filter.js +1 -2
  64. package/dist/context-engine/privacy-filter.js.map +1 -1
  65. package/dist/errors/index.d.ts +2 -2
  66. package/dist/errors/index.js +2 -2
  67. package/dist/errors/index.js.map +1 -1
  68. package/dist/facade.d.ts +43 -52
  69. package/dist/facade.d.ts.map +1 -1
  70. package/dist/facade.js +5 -3
  71. package/dist/facade.js.map +1 -1
  72. package/dist/graph/entity-resolver.js +1 -1
  73. package/dist/graph/entity-resolver.js.map +1 -1
  74. package/dist/index.d.ts +16 -13
  75. package/dist/index.d.ts.map +1 -1
  76. package/dist/index.js +2 -5
  77. package/dist/index.js.map +1 -1
  78. package/dist/internal/envelope.js +57 -0
  79. package/dist/internal/envelope.js.map +1 -0
  80. package/dist/internal/injection-heuristics.js +0 -0
  81. package/dist/internal/injection-heuristics.js.map +1 -1
  82. package/dist/internal/llm-json.js +40 -0
  83. package/dist/internal/llm-json.js.map +1 -0
  84. package/dist/internal/storage-adapter.d.ts +23 -10
  85. package/dist/internal/storage-adapter.d.ts.map +1 -1
  86. package/dist/memory-interface.d.ts +131 -0
  87. package/dist/memory-interface.d.ts.map +1 -0
  88. package/dist/package.js +1 -1
  89. package/dist/package.js.map +1 -1
  90. package/dist/search/iterative.d.ts.map +1 -1
  91. package/dist/search/iterative.js +2 -3
  92. package/dist/search/iterative.js.map +1 -1
  93. package/dist/search/query-transform.d.ts.map +1 -1
  94. package/dist/search/query-transform.js +2 -3
  95. package/dist/search/query-transform.js.map +1 -1
  96. package/dist/tiers/episodic-memory.js +3 -3
  97. package/dist/tiers/episodic-memory.js.map +1 -1
  98. package/dist/tiers/index.d.ts +2 -2
  99. package/dist/tiers/insight-memory.js +1 -1
  100. package/dist/tiers/insight-memory.js.map +1 -1
  101. package/dist/tiers/procedural-memory.d.ts +1 -1
  102. package/dist/tiers/procedural-memory.js +2 -2
  103. package/dist/tiers/procedural-memory.js.map +1 -1
  104. package/dist/tiers/semantic-memory.d.ts +58 -4
  105. package/dist/tiers/semantic-memory.d.ts.map +1 -1
  106. package/dist/tiers/semantic-memory.js +103 -20
  107. package/dist/tiers/semantic-memory.js.map +1 -1
  108. package/dist/tools/block-tools.d.ts +19 -57
  109. package/dist/tools/block-tools.d.ts.map +1 -1
  110. package/dist/tools/block-tools.js.map +1 -1
  111. package/dist/tools/fact-tools.d.ts +53 -214
  112. package/dist/tools/fact-tools.d.ts.map +1 -1
  113. package/dist/tools/fact-tools.js +3 -2
  114. package/dist/tools/fact-tools.js.map +1 -1
  115. package/dist/tools/recall-tools.d.ts +34 -175
  116. package/dist/tools/recall-tools.d.ts.map +1 -1
  117. package/dist/tools/recall-tools.js +5 -4
  118. package/dist/tools/recall-tools.js.map +1 -1
  119. package/dist/tools/runbook-tools.d.ts +15 -58
  120. package/dist/tools/runbook-tools.d.ts.map +1 -1
  121. package/dist/tools/runbook-tools.js.map +1 -1
  122. package/dist/tools/types.d.ts +1 -1
  123. package/package.json +18 -17
  124. package/src/conflict/index.ts +29 -0
  125. package/src/conflict/locale-packs/en.ts +142 -0
  126. package/src/conflict/locale-packs/index.ts +17 -0
  127. package/src/conflict/locale-packs/types.ts +138 -0
  128. package/src/conflict/pipeline.ts +371 -0
  129. package/src/conflict/stages/helpers.ts +65 -0
  130. package/src/conflict/stages/stage1-exact-dedup.ts +40 -0
  131. package/src/conflict/stages/stage2-embedding-three-zone.ts +54 -0
  132. package/src/conflict/stages/stage3-heuristic-regex.ts +51 -0
  133. package/src/conflict/stages/stage4-subject-predicate.ts +46 -0
  134. package/src/conflict/stages/stage5-defer-to-deep.ts +34 -0
  135. package/src/conflict/types.ts +263 -0
  136. package/src/consolidator/budget.ts +312 -0
  137. package/src/consolidator/decay.ts +246 -0
  138. package/src/consolidator/dlq.ts +61 -0
  139. package/src/consolidator/errors.ts +87 -0
  140. package/src/consolidator/idempotency.ts +27 -0
  141. package/src/consolidator/index.ts +269 -0
  142. package/src/consolidator/lock.ts +141 -0
  143. package/src/consolidator/noise-filter.ts +235 -0
  144. package/src/consolidator/phases/deep.ts +308 -0
  145. package/src/consolidator/phases/induce.ts +423 -0
  146. package/src/consolidator/phases/learned-context.ts +233 -0
  147. package/src/consolidator/phases/light.ts +166 -0
  148. package/src/consolidator/phases/reflect.ts +504 -0
  149. package/src/consolidator/phases/standard.ts +1002 -0
  150. package/src/consolidator/reconcile.ts +273 -0
  151. package/src/consolidator/runtime.ts +1049 -0
  152. package/src/consolidator/scheduler.ts +182 -0
  153. package/src/consolidator/triggers.ts +134 -0
  154. package/src/consolidator/types.ts +622 -0
  155. package/src/context-engine/annotations.ts +161 -0
  156. package/src/context-engine/auto-recall.ts +92 -0
  157. package/src/context-engine/compaction/clear-tool-results.ts +232 -0
  158. package/src/context-engine/compaction/compactor.ts +403 -0
  159. package/src/context-engine/compaction/hooks/reanchor-persona-block.ts +55 -0
  160. package/src/context-engine/compaction/hooks/reanchor-pinned-facts.ts +66 -0
  161. package/src/context-engine/compaction/hooks/reanchor-project-rules.ts +57 -0
  162. package/src/context-engine/compaction/hooks/reanchor-recent-results.ts +116 -0
  163. package/src/context-engine/compaction/hooks/types.ts +68 -0
  164. package/src/context-engine/compaction/index.ts +60 -0
  165. package/src/context-engine/compaction/templates/marker.ts +33 -0
  166. package/src/context-engine/compaction/templates/summary-9-section.ts +223 -0
  167. package/src/context-engine/compaction/thresholds.ts +107 -0
  168. package/src/context-engine/compaction/types.ts +255 -0
  169. package/src/context-engine/engine.ts +970 -0
  170. package/src/context-engine/index.ts +118 -0
  171. package/src/context-engine/io-types.ts +162 -0
  172. package/src/context-engine/locale-packs/en.ts +107 -0
  173. package/src/context-engine/locale-packs/index.ts +19 -0
  174. package/src/context-engine/locale-packs/resolver.ts +197 -0
  175. package/src/context-engine/locale-packs/types.ts +177 -0
  176. package/src/context-engine/metadata.ts +114 -0
  177. package/src/context-engine/preambles/inbound-en.ts +19 -0
  178. package/src/context-engine/privacy-filter.ts +190 -0
  179. package/src/context-engine/templates/base-en.ts +19 -0
  180. package/src/context-engine/templates/composer.ts +91 -0
  181. package/src/context-engine/token-budget.ts +319 -0
  182. package/src/context-engine/token-counter.ts +157 -0
  183. package/src/errors/index.ts +273 -0
  184. package/src/facade.ts +750 -0
  185. package/src/graph/entity-resolver.ts +364 -0
  186. package/src/graph/index.ts +24 -0
  187. package/src/index.ts +259 -0
  188. package/src/internal/contextualize.ts +173 -0
  189. package/src/internal/embedder-binding.ts +53 -0
  190. package/src/internal/envelope.ts +70 -0
  191. package/src/internal/id.ts +23 -0
  192. package/src/internal/injection-heuristics.ts +0 -0
  193. package/src/internal/llm-json.ts +38 -0
  194. package/src/internal/spans.ts +35 -0
  195. package/src/internal/storage-adapter.ts +963 -0
  196. package/src/memory-interface.ts +158 -0
  197. package/src/migration/embedder-migration.ts +237 -0
  198. package/src/migration/index.ts +13 -0
  199. package/src/search/explain.ts +123 -0
  200. package/src/search/fit-weights.ts +109 -0
  201. package/src/search/graph-ppr.ts +38 -0
  202. package/src/search/index.ts +55 -0
  203. package/src/search/iterative.ts +658 -0
  204. package/src/search/query-transform.ts +285 -0
  205. package/src/search/rrf.ts +225 -0
  206. package/src/search/trust.ts +55 -0
  207. package/src/search/types.ts +50 -0
  208. package/src/tiers/episodic-memory.ts +463 -0
  209. package/src/tiers/index.ts +50 -0
  210. package/src/tiers/insight-memory.ts +208 -0
  211. package/src/tiers/procedural-memory.ts +482 -0
  212. package/src/tiers/semantic-memory.ts +1729 -0
  213. package/src/tiers/session-memory.ts +271 -0
  214. package/src/tiers/shared-memory.ts +62 -0
  215. package/src/tiers/working-memory.ts +368 -0
  216. package/src/tools/block-tools.ts +172 -0
  217. package/src/tools/fact-tools.ts +473 -0
  218. package/src/tools/index.ts +109 -0
  219. package/src/tools/recall-tools.ts +303 -0
  220. package/src/tools/runbook-tools.ts +107 -0
  221. package/src/tools/taint.ts +39 -0
  222. package/src/tools/types.ts +37 -0
  223. package/dist/conflict/locale-packs/index.js +0 -4
  224. package/dist/context-engine/compaction/index.js +0 -10
  225. package/dist/context-engine/index.d.ts.map +0 -1
  226. package/dist/context-engine/index.js +0 -25
  227. package/dist/context-engine/locale-packs/index.js +0 -4
@@ -0,0 +1,364 @@
1
+ /**
2
+ * Entity resolution for the lightweight in-SQLite relation graph (P2-1).
3
+ *
4
+ * Raw `subject` / `object` strings on facts fragment ("Anna", "Anna S.",
5
+ * "my sister" become unrelated rows), which kills multi-hop recall. This
6
+ * module folds them into **canonical entities** so the one-hop CTE can
7
+ * traverse relationships. Resolution is layered, cheapest first:
8
+ *
9
+ * 1. **lexical** - exact match on the folded {@link normalizeEntityName}.
10
+ * 2. **embedding** - cosine over candidate name vectors; `≥ mergeThreshold`
11
+ * reuses the match.
12
+ * 3. **adjudication** - a middle similarity band (`[adjudicate, merge)`)
13
+ * is *ambiguous*; an opt-in LLM call (provider + `llmAdjudication`)
14
+ * decides. **Offline / by default the ambiguous band mints a new
15
+ * entity** - the resolver never auto-merges on weak evidence, because
16
+ * a wrong merge fuses two distinct people (the stated P2-1 risk).
17
+ *
18
+ * The pure policy ({@link resolveEntityDecision}) is provider-agnostic and
19
+ * does no I/O; {@link EntityResolver} wires it to an injected store +
20
+ * embedder + optional provider. With no embedder it degrades to
21
+ * lexical-only - still useful, and still fully offline.
22
+ *
23
+ * @packageDocumentation
24
+ */
25
+
26
+ import type {
27
+ EmbedderProvider,
28
+ Fact,
29
+ Provider,
30
+ ProviderRequest,
31
+ SessionScope,
32
+ } from '@graphorin/core';
33
+ import type { EntityWithEmbedding, GraphMemoryStoreExt } from '../internal/storage-adapter.js';
34
+
35
+ /** Cosine `≥` this auto-reuses an existing entity (embedding match). */
36
+ export const DEFAULT_MERGE_THRESHOLD = 0.92;
37
+ /** Cosine in `[this, merge)` is *ambiguous* - adjudicate or mint new. */
38
+ export const DEFAULT_ADJUDICATE_THRESHOLD = 0.82;
39
+
40
+ /**
41
+ * Outcome of the pure resolution policy. `match` reuses an existing
42
+ * entity; `ambiguous` flags a middle-similarity candidate for the caller
43
+ * to adjudicate (or conservatively reject); `new` mints a fresh entity.
44
+ *
45
+ * @stable
46
+ */
47
+ export type EntityResolveDecision =
48
+ | {
49
+ readonly kind: 'match';
50
+ readonly entityId: string;
51
+ readonly similarity: number;
52
+ readonly via: 'lexical' | 'embedding';
53
+ }
54
+ | { readonly kind: 'ambiguous'; readonly entityId: string; readonly similarity: number }
55
+ | { readonly kind: 'new' };
56
+
57
+ /** Minimal candidate shape the pure policy compares against. */
58
+ export interface ResolutionCandidate {
59
+ readonly id: string;
60
+ readonly normalizedName: string;
61
+ readonly vector: Float32Array | null;
62
+ /**
63
+ * MST-11: the embedder that produced `vector`. When both this and the
64
+ * query's `vectorEmbedderId` are known and differ, the candidate is skipped
65
+ * for embedding comparison - vectors from different models live in different
66
+ * spaces, so their cosine is meaningless. Absent on either side ⇒ compared
67
+ * (byte-identical to the prior behaviour).
68
+ */
69
+ readonly embedderId?: string | null;
70
+ }
71
+
72
+ /** Inputs to {@link resolveEntityDecision} (all provided by the caller). */
73
+ export interface ResolveDecisionInput {
74
+ readonly normalizedName: string;
75
+ readonly vector?: Float32Array | null;
76
+ /** MST-11: the embedder that produced `vector` (gates cross-embedder cosine). */
77
+ readonly vectorEmbedderId?: string | null;
78
+ readonly candidates: ReadonlyArray<ResolutionCandidate>;
79
+ readonly mergeThreshold: number;
80
+ readonly adjudicateThreshold: number;
81
+ }
82
+
83
+ /**
84
+ * Fold an entity surface form into a canonical lexical key: Unicode
85
+ * NFKC, lowercased, internal whitespace collapsed, surrounding
86
+ * punctuation stripped. `" Anna S. "` → `"anna s"`. Returns `''` for a
87
+ * name with no letters/digits (the resolver treats that as "no entity").
88
+ *
89
+ * @stable
90
+ */
91
+ export function normalizeEntityName(name: string): string {
92
+ return name
93
+ .normalize('NFKC')
94
+ .toLowerCase()
95
+ .replace(/\s+/g, ' ')
96
+ .trim()
97
+ .replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu, '');
98
+ }
99
+
100
+ /**
101
+ * Cosine similarity of two embeddings in `[-1, 1]`. Compares over the
102
+ * shorter length and returns `0` when either vector is empty / zero-norm.
103
+ *
104
+ * @stable
105
+ */
106
+ export function cosineSimilarity(a: Float32Array, b: Float32Array): number {
107
+ const len = Math.min(a.length, b.length);
108
+ let dot = 0;
109
+ let na = 0;
110
+ let nb = 0;
111
+ for (let i = 0; i < len; i++) {
112
+ const ai = a[i] ?? 0;
113
+ const bi = b[i] ?? 0;
114
+ dot += ai * bi;
115
+ na += ai * ai;
116
+ nb += bi * bi;
117
+ }
118
+ if (na === 0 || nb === 0) return 0;
119
+ return dot / Math.sqrt(na * nb);
120
+ }
121
+
122
+ /**
123
+ * Pure resolution policy: lexical exact match → embedding cosine →
124
+ * ambiguous band → new. No I/O; deterministic. The caller decides what
125
+ * to do with `ambiguous` (LLM adjudicate, or conservatively mint new).
126
+ *
127
+ * @stable
128
+ */
129
+ export function resolveEntityDecision(input: ResolveDecisionInput): EntityResolveDecision {
130
+ // 1. Exact lexical match is the cheapest + strongest signal.
131
+ for (const c of input.candidates) {
132
+ if (c.normalizedName === input.normalizedName) {
133
+ return { kind: 'match', entityId: c.id, similarity: 1, via: 'lexical' };
134
+ }
135
+ }
136
+ // 2. Embedding similarity (only when both sides have a vector).
137
+ const v = input.vector;
138
+ if (v === undefined || v === null || v.length === 0) return { kind: 'new' };
139
+ let bestId: string | null = null;
140
+ let bestSim = Number.NEGATIVE_INFINITY;
141
+ for (const c of input.candidates) {
142
+ if (c.vector === null || c.vector.length === 0) continue;
143
+ // MST-11: never compare vectors across embedders - different models live
144
+ // in different vector spaces, so their cosine is meaningless. Skip only
145
+ // when both embedder ids are known and differ.
146
+ if (
147
+ input.vectorEmbedderId != null &&
148
+ c.embedderId != null &&
149
+ c.embedderId !== input.vectorEmbedderId
150
+ ) {
151
+ continue;
152
+ }
153
+ const sim = cosineSimilarity(v, c.vector);
154
+ if (sim > bestSim) {
155
+ bestSim = sim;
156
+ bestId = c.id;
157
+ }
158
+ }
159
+ if (bestId === null) return { kind: 'new' };
160
+ if (bestSim >= input.mergeThreshold) {
161
+ return { kind: 'match', entityId: bestId, similarity: bestSim, via: 'embedding' };
162
+ }
163
+ if (bestSim >= input.adjudicateThreshold) {
164
+ return { kind: 'ambiguous', entityId: bestId, similarity: bestSim };
165
+ }
166
+ return { kind: 'new' };
167
+ }
168
+
169
+ /** Tunable thresholds + LLM-adjudication switch for {@link EntityResolver}. */
170
+ export interface EntityResolutionConfig {
171
+ /** Cosine `≥` this auto-reuses a match. Default {@link DEFAULT_MERGE_THRESHOLD}. */
172
+ readonly mergeThreshold?: number;
173
+ /** Cosine in `[this, merge)` is ambiguous. Default {@link DEFAULT_ADJUDICATE_THRESHOLD}. */
174
+ readonly adjudicateThreshold?: number;
175
+ /**
176
+ * Resolve the ambiguous band with one provider call. Requires a
177
+ * `provider`. Default `false` ⇒ ambiguous mints a new entity (no
178
+ * network call; never auto-merges on weak evidence).
179
+ */
180
+ readonly llmAdjudication?: boolean;
181
+ }
182
+
183
+ /** Construction deps for {@link EntityResolver}. */
184
+ export interface EntityResolverDeps {
185
+ readonly store: GraphMemoryStoreExt;
186
+ readonly embedder?: EmbedderProvider | null;
187
+ readonly embedderId?: () => string | null;
188
+ readonly provider?: Provider | null;
189
+ readonly config?: EntityResolutionConfig;
190
+ }
191
+
192
+ const ADJUDICATION_SYSTEM_PROMPT =
193
+ 'You decide whether two short names refer to the SAME real-world entity ' +
194
+ '(person, place, org, thing). Reply with a single word: "yes" or "no". ' +
195
+ 'Be conservative - answer "no" unless they are clearly the same entity.';
196
+
197
+ /** Build the (pure) adjudication request. Exposed for testing. */
198
+ export function buildAdjudicationRequest(
199
+ nameA: string,
200
+ nameB: string,
201
+ options: { readonly signal?: AbortSignal } = {},
202
+ ): ProviderRequest {
203
+ return {
204
+ messages: [{ role: 'user', content: `Name A: ${nameA}\nName B: ${nameB}\nSame entity?` }],
205
+ systemMessage: ADJUDICATION_SYSTEM_PROMPT,
206
+ temperature: 0,
207
+ maxTokens: 4,
208
+ ...(options.signal !== undefined ? { signal: options.signal } : {}),
209
+ };
210
+ }
211
+
212
+ /** Parse a yes/no adjudication reply. Conservative: only a clear yes is `true`. */
213
+ export function parseAdjudication(text: string): boolean {
214
+ return /^\s*(yes|true|same)\b/i.test(text);
215
+ }
216
+
217
+ /**
218
+ * Resolves a fact's subject / object strings to canonical entity ids and
219
+ * links them, applying {@link resolveEntityDecision} backed by an
220
+ * injected store + embedder (+ optional provider for adjudication).
221
+ * Constructed only when entity resolution is opted in
222
+ * (`createMemory({ graph: { entityResolution: true } })`); otherwise the
223
+ * write path skips it and behaviour is unchanged + offline.
224
+ *
225
+ * @stable
226
+ */
227
+ export class EntityResolver {
228
+ readonly #store: GraphMemoryStoreExt;
229
+ readonly #embedder: EmbedderProvider | null;
230
+ readonly #embedderId: () => string | null;
231
+ readonly #provider: Provider | null;
232
+ readonly #mergeThreshold: number;
233
+ readonly #adjudicateThreshold: number;
234
+ readonly #llmAdjudication: boolean;
235
+
236
+ constructor(deps: EntityResolverDeps) {
237
+ this.#store = deps.store;
238
+ this.#embedder = deps.embedder ?? null;
239
+ this.#embedderId = deps.embedderId ?? (() => null);
240
+ this.#provider = deps.provider ?? null;
241
+ this.#mergeThreshold = deps.config?.mergeThreshold ?? DEFAULT_MERGE_THRESHOLD;
242
+ this.#adjudicateThreshold = deps.config?.adjudicateThreshold ?? DEFAULT_ADJUDICATE_THRESHOLD;
243
+ this.#llmAdjudication = deps.config?.llmAdjudication ?? false;
244
+ }
245
+
246
+ /**
247
+ * Resolve a single name to a canonical entity id (find-or-create),
248
+ * deduping via lexical + embedding similarity. Returns `null` for a
249
+ * name that normalizes to empty (no entity).
250
+ */
251
+ async resolve(
252
+ scope: SessionScope,
253
+ rawName: string,
254
+ opts: { readonly signal?: AbortSignal } = {},
255
+ ): Promise<string | null> {
256
+ const normalizedName = normalizeEntityName(rawName);
257
+ if (normalizedName.length === 0) return null;
258
+ // 1. Exact lexical match - the cheapest, strongest signal. Resolve it via
259
+ // an uncapped indexed lookup so an alias of an arbitrarily-old entity
260
+ // dedups without scanning (and deserializing) the bounded candidate
261
+ // window, and short-circuits before any embedding call (CS-11). Stores
262
+ // that don't implement it fall through to the capped lexical scan below.
263
+ const exact = await this.#store.findEntityByNormalizedName?.(scope, normalizedName);
264
+ if (exact != null) return exact.id;
265
+ const vector = await this.#embed(rawName, opts.signal);
266
+ // 2. Without a query vector, embedding dedup is impossible - skip the
267
+ // BLOB-deserializing candidate scan entirely and mint a new entity.
268
+ if (vector === null || vector.length === 0) {
269
+ return this.#create(scope, rawName, normalizedName, vector);
270
+ }
271
+ const candidates = await this.#store.listEntities(scope);
272
+ const decision = resolveEntityDecision({
273
+ normalizedName,
274
+ vector,
275
+ vectorEmbedderId: this.#embedderId(),
276
+ candidates,
277
+ mergeThreshold: this.#mergeThreshold,
278
+ adjudicateThreshold: this.#adjudicateThreshold,
279
+ });
280
+ if (decision.kind === 'match') return decision.entityId;
281
+ if (decision.kind === 'ambiguous') {
282
+ if (this.#llmAdjudication && this.#provider !== null) {
283
+ const same = await this.#adjudicate(rawName, decision.entityId, candidates, opts.signal);
284
+ if (same) return decision.entityId;
285
+ }
286
+ // Conservative default: weak evidence ⇒ a distinct new entity.
287
+ }
288
+ return this.#create(scope, rawName, normalizedName, vector);
289
+ }
290
+
291
+ /**
292
+ * Resolve + link a fact's subject and object (the predicate is a
293
+ * relation label, never an entity). Idempotent on re-link.
294
+ */
295
+ async linkFact(
296
+ scope: SessionScope,
297
+ fact: Fact,
298
+ opts: { readonly signal?: AbortSignal } = {},
299
+ ): Promise<void> {
300
+ if (fact.subject !== undefined) {
301
+ const id = await this.resolve(scope, fact.subject, opts);
302
+ if (id !== null) await this.#store.linkFactEntity(fact.id, id, 'subject');
303
+ }
304
+ if (fact.object !== undefined) {
305
+ const id = await this.resolve(scope, fact.object, opts);
306
+ if (id !== null) await this.#store.linkFactEntity(fact.id, id, 'object');
307
+ }
308
+ }
309
+
310
+ async #embed(name: string, signal?: AbortSignal): Promise<Float32Array | null> {
311
+ const embedder = this.#embedder;
312
+ if (embedder === null || this.#embedderId() === null) return null;
313
+ try {
314
+ // W-089: forward the caller's AbortSignal so a cancelled write
315
+ // really interrupts a remote embedding. An abort lands in the
316
+ // catch: resolve degrades to mint-new, which is the correct
317
+ // cancellation semantics for a best-effort enrichment. No
318
+ // `taskType` on purpose - entity names are compared name-to-name,
319
+ // so both sides must use the same (document) embedding role.
320
+ const [vector] = await embedder.embed([name], {
321
+ ...(signal !== undefined ? { signal } : {}),
322
+ });
323
+ return vector ?? null;
324
+ } catch {
325
+ return null;
326
+ }
327
+ }
328
+
329
+ async #create(
330
+ scope: SessionScope,
331
+ name: string,
332
+ normalizedName: string,
333
+ vector: Float32Array | null,
334
+ ): Promise<string> {
335
+ const embedderId = this.#embedderId();
336
+ return this.#store.upsertEntity(scope, {
337
+ name,
338
+ normalizedName,
339
+ ...(vector !== null ? { vector } : {}),
340
+ ...(vector !== null && embedderId !== null ? { embedderId } : {}),
341
+ });
342
+ }
343
+
344
+ async #adjudicate(
345
+ name: string,
346
+ candidateId: string,
347
+ candidates: ReadonlyArray<EntityWithEmbedding>,
348
+ signal?: AbortSignal,
349
+ ): Promise<boolean> {
350
+ const provider = this.#provider;
351
+ if (provider === null) return false;
352
+ const candidate = candidates.find((c) => c.id === candidateId);
353
+ if (candidate === undefined) return false;
354
+ try {
355
+ const request = buildAdjudicationRequest(name, candidate.name, {
356
+ ...(signal !== undefined ? { signal } : {}),
357
+ });
358
+ const response = await provider.generate(request);
359
+ return parseAdjudication(response.text ?? '');
360
+ } catch {
361
+ return false;
362
+ }
363
+ }
364
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Lightweight in-SQLite relation graph (P2-1): entity resolution +
3
+ * one-hop expansion. The store-side persistence + recursive CTE live in
4
+ * the adapter's `graph` capability (`@graphorin/store-sqlite`); this
5
+ * sub-module is the provider-agnostic resolution policy.
6
+ *
7
+ * @packageDocumentation
8
+ */
9
+
10
+ export {
11
+ buildAdjudicationRequest,
12
+ cosineSimilarity,
13
+ DEFAULT_ADJUDICATE_THRESHOLD,
14
+ DEFAULT_MERGE_THRESHOLD,
15
+ type EntityResolutionConfig,
16
+ type EntityResolveDecision,
17
+ EntityResolver,
18
+ type EntityResolverDeps,
19
+ normalizeEntityName,
20
+ parseAdjudication,
21
+ type ResolutionCandidate,
22
+ type ResolveDecisionInput,
23
+ resolveEntityDecision,
24
+ } from './entity-resolver.js';
package/src/index.ts ADDED
@@ -0,0 +1,259 @@
1
+ /**
2
+ * `@graphorin/memory` - seven-tier memory system for the Graphorin
3
+ * framework (MST-13: working / session / episodic / semantic /
4
+ * procedural / shared + the read-only insight tier from P1-1).
5
+ *
6
+ * Surface overview:
7
+ *
8
+ * - The {@link createMemory} facade that wires every tier sub-module
9
+ * + the memory tools + the search reranker + the context-engine +
10
+ * the consolidator.
11
+ * - Tier sub-modules under `./tiers`: {@link WorkingMemory},
12
+ * {@link SessionMemory}, {@link EpisodicMemory}, {@link SemanticMemory},
13
+ * {@link ProceduralMemory}, {@link SharedMemory}, plus the read-only
14
+ * {@link InsightMemory}.
15
+ * - Eleven memory tools under `./tools` (plus the gated twelfth,
16
+ * `deep_recall`, registered only when iterative retrieval is
17
+ * configured): `block_append`, `block_replace`, `block_rethink`,
18
+ * `fact_remember`, `fact_search`, `fact_supersede`, `fact_forget`,
19
+ * `recall_episodes`, `conversation_search`, `fact_history` (P0-2),
20
+ * `fact_validate` (P1-4).
21
+ * - The hybrid search composition under `./search`, including the
22
+ * built-in {@link RRFReranker} (k=60 default) and the
23
+ * {@link ReRanker} contract.
24
+ * - The embedder migration runner under `./migration`
25
+ * ({@link migrateEmbedder} with three coexistence policies).
26
+ * - Typed errors under `./errors`.
27
+ *
28
+ * Forward-looking surfaces (for ergonomic typing today):
29
+ *
30
+ * - {@link MemoryContextBlocks} + {@link CompileOptions} from `./context-engine`.
31
+ * - {@link Consolidator}, {@link ConsolidatorTriggerSpec},
32
+ * {@link ConsolidatorTier} from `./consolidator`.
33
+ *
34
+ * @packageDocumentation
35
+ */
36
+
37
+ /** Canonical version constant, derived from `package.json` at build time. */
38
+ import pkg from '../package.json' with { type: 'json' };
39
+
40
+ export const VERSION: string = pkg.version;
41
+
42
+ export * from './conflict/index.js';
43
+ export {
44
+ BudgetExceededError,
45
+ buildInductionRequest,
46
+ CONSOLIDATOR_TIER_DEFAULTS,
47
+ type Consolidator,
48
+ type ConsolidatorBudgetSnapshot,
49
+ type ConsolidatorCatchupPolicy,
50
+ type ConsolidatorCeilings,
51
+ type ConsolidatorConfig,
52
+ type ConsolidatorLastRuns,
53
+ type ConsolidatorPhase,
54
+ type ConsolidatorStatus,
55
+ type ConsolidatorTier,
56
+ type ConsolidatorTriggerReason,
57
+ type ConsolidatorTriggerSpec,
58
+ type CreateConsolidatorOptions,
59
+ CustomTierMisconfiguredError,
60
+ checkSuccessCriteria,
61
+ createConsolidator,
62
+ createConsolidatorPlaceholder,
63
+ createProviderWorkflowInducer,
64
+ DEFAULT_INDUCTION_MAX_TOKENS,
65
+ INDUCTION_SYSTEM_PROMPT,
66
+ type InducedProcedure,
67
+ MAX_PROCEDURE_STEPS,
68
+ MAX_TRAJECTORY_STEPS_SHOWN,
69
+ normalizeInducedProcedure,
70
+ type OnBudgetExceed,
71
+ type ParsedTrigger,
72
+ type PhaseListener,
73
+ type PhaseOutcome,
74
+ ProviderNotConfiguredError,
75
+ parseInducedProcedure,
76
+ parseTriggerSpec,
77
+ type RegisterTriggersOptions,
78
+ type RegisterTriggersResult,
79
+ reasonFromTrigger,
80
+ registerConsolidatorTriggers,
81
+ runWorkflowInduction,
82
+ type SchedulerLike,
83
+ type Trajectory,
84
+ type TrajectoryStep,
85
+ type TriggerDeclarationLike,
86
+ trajectoryFromRunState,
87
+ type VerificationResult,
88
+ type WorkflowInducer,
89
+ type WorkflowInductionOptions,
90
+ } from './consolidator/index.js';
91
+ export {
92
+ _getLocaleFallbackWarningsForTesting,
93
+ _resetLocaleFallbackWarningsForTesting,
94
+ type AllocationResult,
95
+ type AnnotatedPart,
96
+ type AssembledPrompt,
97
+ type AssembleInput,
98
+ type AutoCompactionDefault,
99
+ type AutoRecallConfig,
100
+ type AutoRecallStrategy,
101
+ type AutoRecallStrategyContext,
102
+ type AutoRecallTriggerResult,
103
+ type AutoRecallTriggers,
104
+ adaptTokenCounter,
105
+ allocateTokenBudget,
106
+ annotate,
107
+ BASE_TEMPLATE_EN_FULL,
108
+ BASE_TEMPLATE_EN_MINIMAL,
109
+ type BaseTemplateFragments,
110
+ buildSummarizerPrompt,
111
+ CLEARED_TOOL_RESULT_MARKER,
112
+ type ClearToolResultsOptions,
113
+ type ClearToolResultsOutcome,
114
+ COMPACTION_SUMMARY_CLOSE,
115
+ COMPACTION_SUMMARY_MARKER,
116
+ COMPACTION_SUMMARY_OPEN,
117
+ COMPACTION_SUMMARY_TAG,
118
+ CONTENT_ORIGIN_ATTR,
119
+ type CompactionConfig,
120
+ type CompactionContext,
121
+ type CompactionMetadataPayload,
122
+ type CompactionResult,
123
+ type CompactionSource,
124
+ type CompactionStrategy,
125
+ type CompactionSummarizer,
126
+ type CompactionSummaryTemplate,
127
+ type CompactionTriggerConfig,
128
+ type CompileOptions,
129
+ type CompileScope,
130
+ type ContentAnnotation,
131
+ type ContentOrigin,
132
+ type ContextEngine,
133
+ type ContextEngineConfig,
134
+ type ContextLocalePack,
135
+ type ContextTokenCounter,
136
+ clearOldToolResults,
137
+ composeInboundPreamble,
138
+ composeLayer1,
139
+ composeLayer2,
140
+ composeLayer4Skills,
141
+ countMessageTokens,
142
+ createContextEngine,
143
+ DEFAULT_LAYER_PRIORITY,
144
+ DEFAULT_PRESERVE_RECENT_TURNS,
145
+ DEFAULT_RESERVED_FOR_COMPACTION,
146
+ DEFAULT_RESERVED_FOR_RESPONSE,
147
+ DEFAULT_THRESHOLD_RATIO,
148
+ defaultLocaleHeuristicStrategy,
149
+ defineAutoRecallStrategy,
150
+ defineContextLocalePack,
151
+ type ExecuteCompactionInput,
152
+ effectiveAcceptsSensitivity,
153
+ enLocalePack,
154
+ executeCompaction,
155
+ gatherMemoryMetadata,
156
+ HEURISTIC_TOKEN_COUNTER,
157
+ INBOUND_SANITIZATION_PREAMBLE_EN,
158
+ INBOUND_TRUST_ATTR,
159
+ type InboundSanitizationPreamble,
160
+ type InboundTrust,
161
+ type LayerAllocation,
162
+ type LayerCandidate,
163
+ type LayerConfig,
164
+ type LayerId,
165
+ type LocaleResolverLogger,
166
+ type MemoryBaseMode,
167
+ type MemoryContextBlocks,
168
+ type MemoryMetadataDeps,
169
+ type NamedPostCompactionHook,
170
+ NON_INBOUND_ORIGINS,
171
+ type OverflowMode,
172
+ type PartialContextLocalePack,
173
+ type PartitionResult,
174
+ type PostCompactionHook,
175
+ type PostCompactionHookContext,
176
+ type PrivacyConfig,
177
+ type PrivacyDecision,
178
+ type PrivacyDecisionReason,
179
+ type PrivacyFilterContext,
180
+ partitionBySensitivity,
181
+ privacyDecide,
182
+ type ReanchorRecentResultsOptions,
183
+ type RenderedTemplate,
184
+ type ResolvedContextEngineConfig,
185
+ reanchorPersonaBlock,
186
+ reanchorPinnedFacts,
187
+ reanchorProjectRules,
188
+ reanchorRecentResults,
189
+ renderFinalSummary,
190
+ renderMessageText,
191
+ renderMetadataBlock,
192
+ resolveAutoCompactionDefault,
193
+ resolveLocalePack,
194
+ resolveTriggerThreshold,
195
+ type SkillMetadataCard,
196
+ SUMMARY_TEMPLATE_NAME,
197
+ SUMMARY_TEMPLATE_VERSION,
198
+ shouldFireInboundPreamble,
199
+ toSpanAttributes,
200
+ truncateToTokens,
201
+ } from './context-engine/index.js';
202
+ export * from './errors/index.js';
203
+ export {
204
+ _resetConsolidatorConfigWarningForTesting,
205
+ type CreateMemoryOptions,
206
+ createMemory,
207
+ type Memory,
208
+ } from './facade.js';
209
+ export * from './graph/index.js';
210
+ export type { ContextualRetrievalMode } from './internal/contextualize.js';
211
+ export type {
212
+ ConflictAuditDecision,
213
+ ConflictAuditInputLike,
214
+ ConflictAuditStage,
215
+ ConflictMemoryStoreExt,
216
+ ConsolidatorMemoryStoreExt,
217
+ ConsolidatorRunFinish,
218
+ ConsolidatorRunInput,
219
+ ConsolidatorStatePatch,
220
+ ConsolidatorStateRow,
221
+ DecayMemoryStoreExt,
222
+ DlqBatchInput,
223
+ DlqBatchRow,
224
+ EmbeddedWriteOptions,
225
+ EmbeddingMetaRegistryLike,
226
+ EpisodicMemoryStoreExt,
227
+ GraphMemoryStoreExt,
228
+ InsightListOptions as InsightStoreListOptions,
229
+ InsightMemoryStoreExt,
230
+ InsightSearchStoreOptions,
231
+ MemoryStoreAdapter,
232
+ PendingConflictInputLike,
233
+ PendingConflictRowLike,
234
+ ProceduralMemoryStoreExt,
235
+ SemanticMemoryStoreExt,
236
+ SessionMemoryStoreExt,
237
+ SessionMessageRecord,
238
+ } from './internal/storage-adapter.js';
239
+ export * from './migration/index.js';
240
+ export * from './search/index.js';
241
+ export * from './tiers/index.js';
242
+ export {
243
+ type BuildMemoryToolsOptions,
244
+ buildMemoryTools,
245
+ createBlockAppendTool,
246
+ createBlockReplaceTool,
247
+ createBlockRethinkTool,
248
+ createConversationSearchTool,
249
+ createDeepRecallTool,
250
+ createFactForgetTool,
251
+ createFactHistoryTool,
252
+ createFactRememberTool,
253
+ createFactSearchTool,
254
+ createFactSupersedeTool,
255
+ createFactValidateTool,
256
+ createRecallEpisodesTool,
257
+ type MemoryToolDeps,
258
+ type ScopeResolver,
259
+ } from './tools/index.js';