@melandlabs/memory-store 1.3.6 → 1.3.8

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.
@@ -1,988 +0,0 @@
1
- import * as _melandlabs_memory_consolidation from '@melandlabs/memory-consolidation';
2
- import { MemoryApplicabilityContext } from '@melandlabs/memory-consolidation';
3
- import * as _melandlabs_contracts_peer from '@melandlabs/contracts/peer';
4
- import { Peer } from '@melandlabs/contracts/peer';
5
- import { FactType } from '@melandlabs/contracts';
6
- import { DerivedFact } from '@melandlabs/contracts/derived-fact';
7
- import { EntityEdge } from '@melandlabs/contracts/entity-edge';
8
- import { RawMessage, RawMessageSearchChunk } from '@melandlabs/indexeddb';
9
-
10
- /**
11
- * Reranker adapter contract.
12
- *
13
- * A `Reranker` is an optional plug-in applied to the unified-search output
14
- * after per-source merge but before the final `slice(0, limit)`. It lets a
15
- * host swap in a cross-encoder, a learned ranker, or any other deterministic
16
- * ordering without changing the retrieval pipeline.
17
- *
18
- * Implementations MUST be deterministic for the same `(query, candidates)`
19
- * pair — non-deterministic orderings would defeat the existing
20
- * `rrfScore`/`similarity` tie-break tests.
21
- */
22
- interface RerankerCandidate {
23
- id: string;
24
- content: string;
25
- metadata?: Record<string, unknown>;
26
- }
27
- interface RerankerInput {
28
- query: string;
29
- candidates: RerankerCandidate[];
30
- topK?: number;
31
- }
32
- interface RerankerScore {
33
- id: string;
34
- score: number;
35
- }
36
- interface Reranker {
37
- rerank(input: RerankerInput): Promise<RerankerScore[]>;
38
- }
39
-
40
- /**
41
- * Pure helpers used by the unified memory search pipeline.
42
- *
43
- * Kept side-effect free so they can be reused by tests, MCP tools,
44
- * and the HTTP daemon without pulling in any of the heavier
45
- * storage/search implementations.
46
- */
47
-
48
- type UnifiedMemorySearchSource = "memory" | "insights" | "knowledge";
49
- /**
50
- * A retrieval channel that contributed to a hit's final score.
51
- *
52
- * - `semantic` — dense embedding lookup (`searchRawMessagesAnn` or
53
- * the SQLite manager's semantic-search fallback).
54
- * - `lexical` — BM25 / FTS5 keyword match
55
- * (`searchRawMessagesLexical` or the SQLite manager's lexical
56
- * fallback).
57
- * - `entity` — entity-link match supplied by the host's
58
- * `entitySearch` dep.
59
- */
60
- type HitChannel = "semantic" | "lexical" | "hybrid" | "entity";
61
- /**
62
- * Per-hit score breakdown. Always emitted by `search()` (default merge
63
- * strategy) so callers can threshold / re-rank without losing the
64
- * individual channel contributions. The RRF fusion additionally
65
- * populates `signals.rrf` and continues to mirror the fused score
66
- * onto `metadata.rrfScore` (legacy field).
67
- *
68
- * Each numeric field is the raw per-channel score for the *first*
69
- * (highest-ranked) appearance of the same `(type, id)` key in the
70
- * channel's list. Channels that did not contribute are `undefined`.
71
- */
72
- interface HitSignals {
73
- /** Channels the hit actually appeared in. */
74
- channels: HitChannel[];
75
- /** Semantic sub-query similarity in [0..1] (undefined when absent). */
76
- semantic?: number;
77
- /** Lexical sub-query BM25 score (undefined when absent). */
78
- lexical?: number;
79
- /** Native backend dense+BM25 fused score (undefined when absent). */
80
- hybrid?: number;
81
- /** Entity sub-query match score (undefined when absent). */
82
- entity?: number;
83
- /** RRF-fused score (only when mergeStrategy === "rrf"). */
84
- rrf?: number;
85
- }
86
- /**
87
- * Derive simple lexical keywords from a query string. Splits on any
88
- * non-letter/non-digit Unicode boundary, lowercases, drops tokens
89
- * shorter than 2 chars, and caps the list at 16. Used by the unified
90
- * search lexical sub-query and by the `derive` primitive's candidate
91
- * fetch fallback.
92
- */
93
- declare function deriveLexicalKeywords(query: string): string[];
94
- type UnifiedMemoryReasoningStrategy = "none" | "rewrite" | "iterative" | "union";
95
- interface UnifiedMemorySearchWarning {
96
- source: UnifiedMemorySearchSource;
97
- code: string;
98
- message: string;
99
- }
100
- interface UnifiedMemoryReasoningInfo {
101
- strategy: UnifiedMemoryReasoningStrategy;
102
- /**
103
- * True when the caller asked for a non-`"none"` strategy but the
104
- * corresponding reasoning provider was not configured (or its LLM
105
- * call failed), so the unified search silently fell back to the default
106
- * one-shot semantic + lexical path. Callers can use this to surface a
107
- * degraded-mode warning to their users.
108
- */
109
- degraded?: boolean;
110
- /** Query variants produced by the rewriter (original + rewritten). */
111
- rewrittenQueries?: string[];
112
- /** Number of planner iterations executed (iterative mode only). */
113
- iterations?: number;
114
- /** Number of evidence items collected by the planner (iterative mode only). */
115
- evidenceCount?: number;
116
- /** Date range that was applied to the memory source, if any. */
117
- dateRange?: {
118
- from?: string;
119
- to?: string;
120
- };
121
- }
122
- interface UnifiedMemorySearchInput {
123
- userId: string;
124
- query: string;
125
- sources?: UnifiedMemorySearchSource[];
126
- limit?: number;
127
- threshold?: number;
128
- authToken?: string;
129
- includeArchivedInsights?: boolean;
130
- botIds?: string[];
131
- documentIds?: string[];
132
- /**
133
- * Optional ISO-8601 timestamp. When set, graph-aware retrieval and other
134
- * snapshot consumers drop nodes / edges whose `applicability.validFrom` /
135
- * `validUntil` window is closed at that instant. Backward-compatible:
136
- * omitted `asOf` keeps the legacy behaviour exactly.
137
- *
138
- * This is a single-point snapshot, not a date range. For interval filtering
139
- * over memory timestamps use `dateFrom` / `dateTo`.
140
- */
141
- asOf?: string;
142
- /**
143
- * Optional inclusive start date (ISO-8601). When set, the memory source
144
- * filters out candidates whose timestamp is before this boundary. The
145
- * iterative planner also receives this bound and may emit narrower ranges.
146
- *
147
- * This only affects the `memory` source; `insights` and `knowledge` are
148
- * not filtered by this range. Candidates without a recognised timestamp are
149
- * retained.
150
- */
151
- dateFrom?: string;
152
- /**
153
- * Optional inclusive end date (ISO-8601). When set, the memory source
154
- * filters out candidates whose timestamp is after this boundary. Date-only
155
- * strings are treated as the end of that day.
156
- *
157
- * This only affects the `memory` source; `insights` and `knowledge` are
158
- * not filtered by this range. Candidates without a recognised timestamp are
159
- * retained.
160
- */
161
- dateTo?: string;
162
- /**
163
- * How to merge per-source result lists. Defaults to `"rrf"` reciprocal-rank
164
- * fusion. `"similarity"` preserves the legacy global similarity sort.
165
- */
166
- mergeStrategy?: UnifiedMemoryMergeStrategy;
167
- /**
168
- * Optional additive scope-narrowing filter expressed as structured
169
- * peers. Coexists with `userId`/`botIds`. When supplied, the host's
170
- * `UnifiedSearchDeps.peerScopeCheck` is consulted; peers that fall
171
- * outside `userId` are dropped with a `peer_filter_outside_user_scope`
172
- * warning rather than broadening the scope.
173
- */
174
- peerFilter?: ReadonlyArray<Peer>;
175
- /**
176
- * Optional reasoning strategy. `"rewrite"` rewrites the query before
177
- * embedding; `"iterative"` runs an LLM planner that searches, notes
178
- * evidence, and searches again. Defaults to `"none"`.
179
- */
180
- reasoningStrategy?: UnifiedMemoryReasoningStrategy;
181
- /**
182
- * Optional `FactType` read-side filter. When supplied, the memory
183
- * source narrows candidates to rows whose `factType` is in this set.
184
- * Only affects the `memory` source; `insights` and `knowledge` are
185
- * not filtered by this. Empty array is treated as "no filter" so
186
- * callers can pass `searchInput.factTypes ?? []` without surprises.
187
- */
188
- factTypes?: FactType[];
189
- /** Include pre-fusion channel candidates in the response for diagnostics. */
190
- includeRetrievalDiagnostics?: boolean;
191
- /**
192
- * Include messages that have been soft-deprecated
193
- * (`raw_messages.deprecated_at IS NOT NULL`). Default `false` keeps
194
- * `current-truth` retrievals clean: a row that was marked superseded
195
- * via `deprecateMessages` (or the `opencontext deprecate` CLI) is
196
- * excluded from results. Set to `true` for audits and historical
197
- * exploration.
198
- */
199
- includeDeprecated?: boolean;
200
- }
201
- type UnifiedMemoryMergeStrategy = "similarity" | "rrf";
202
- /**
203
- * A single ranked list from one retrieval channel. Used by the RRF merge to
204
- * combine multiple signals (semantic + lexical + insights + knowledge) without
205
- * flattening them into a single similarity score first.
206
- */
207
- interface UnifiedMemoryRankedList {
208
- /** Label for the source list (e.g. a tier or channel). Free-form; the
209
- * RRF fusion only reads `hits`, so `name` is purely diagnostic. */
210
- name: string;
211
- hits: UnifiedMemorySearchResult[];
212
- }
213
- interface UnifiedMemorySearchResult {
214
- type: "memory" | "insight" | "knowledge";
215
- id: string;
216
- content: string;
217
- similarity: number;
218
- metadata: Record<string, unknown>;
219
- /**
220
- * Optional per-channel score breakdown. Always populated by the
221
- * pipeline when at least one channel contributed a non-empty list;
222
- * callers should treat absence as "no channel info available".
223
- */
224
- signals?: HitSignals;
225
- }
226
- interface UnifiedMemorySearchOutput {
227
- query: string;
228
- sources: UnifiedMemorySearchSource[];
229
- results: UnifiedMemorySearchResult[];
230
- count: number;
231
- warnings: UnifiedMemorySearchWarning[];
232
- retrievalDiagnostics?: UnifiedMemoryRetrievalDiagnostics;
233
- /**
234
- * Diagnostic information about reasoning and date filtering. Present
235
- * when (a) a non-`"none"` reasoning strategy was requested, or (b) a
236
- * `dateFrom` / `dateTo` filter was applied to the memory source.
237
- */
238
- reasoning?: UnifiedMemoryReasoningInfo;
239
- }
240
- interface UnifiedMemoryRetrievalDiagnostics {
241
- mergeStrategy: UnifiedMemoryMergeStrategy;
242
- candidateLimit: number;
243
- backend?: string;
244
- semanticDegradedReason?: string;
245
- candidateCounts?: {
246
- semantic: number;
247
- lexical: number;
248
- hybrid: number;
249
- entity: number;
250
- fused: number;
251
- final: number;
252
- };
253
- channels: {
254
- semantic: UnifiedMemorySearchResult[];
255
- lexical: UnifiedMemorySearchResult[];
256
- hybrid?: UnifiedMemorySearchResult[];
257
- entity?: UnifiedMemorySearchResult[];
258
- };
259
- /** Results after channel/source fusion and before an optional host reranker. */
260
- fusedBeforeRerank: UnifiedMemorySearchResult[];
261
- reranker?: {
262
- enabled: boolean;
263
- provider?: string;
264
- model?: string;
265
- inputCount: number;
266
- outputCount: number;
267
- latencyMs: number;
268
- orderChanged: boolean;
269
- };
270
- /** Final results after optional reranking and Top-K truncation. */
271
- final: UnifiedMemorySearchResult[];
272
- }
273
- /**
274
- * Cross-source retrieval surface for the unified search. Mirrors
275
- * `UnifiedMemorySearchSource` but is exposed under a more discoverable
276
- * name.
277
- */
278
- type SearchSource = UnifiedMemorySearchSource;
279
- /**
280
- * Per-tier evidence bucket consulted by `reflect`-style synthesis.
281
- * Includes the `summary` tier that the read-only sources do not surface.
282
- */
283
- type SearchTier = "summary" | "raw" | "insight" | "knowledge";
284
- interface SearchInput {
285
- userId: string;
286
- query: string;
287
- /** Cross-source retrieval surface (defaults to all three sources). */
288
- sources?: ReadonlyArray<SearchSource>;
289
- /**
290
- * Per-tier subset — when omitted, defaults to all four tiers.
291
- * Forwarded to the gather step so the read pipeline can scope the
292
- * evidence. Most callers pass nothing; `synthesize` callers can
293
- * narrow to `["raw"]` or `["summary", "raw"]` for cheaper synthesis.
294
- */
295
- tiers?: ReadonlyArray<SearchTier>;
296
- /**
297
- * Opt-in LLM synthesis. When `true`, runs `reasoning.complete` after
298
- * gathering evidence and returns `answer` in the output. When
299
- * omitted / `false`, no LLM call is made (matches today's
300
- * `searchUnifiedMemory`). The `responseSchema` form lets the LLM
301
- * return JSON; the SDK extracts `{ answer: string }` from the
302
- * payload and surfaces the rest via warnings.
303
- */
304
- synthesize?: boolean | {
305
- responseSchema?: Record<string, unknown>;
306
- };
307
- limit?: number;
308
- threshold?: number;
309
- botIds?: string[];
310
- documentIds?: string[];
311
- dateFrom?: string;
312
- dateTo?: string;
313
- asOf?: string;
314
- peerFilter?: ReadonlyArray<Peer>;
315
- authToken?: string;
316
- factTypes?: FactType[];
317
- mergeStrategy?: UnifiedMemoryMergeStrategy;
318
- reasoningStrategy?: UnifiedMemoryReasoningStrategy;
319
- /**
320
- * Backward-compat pass-through for callers that previously passed
321
- * `responseSchema` at the top level. When set, it overrides
322
- * `synthesize.responseSchema`.
323
- */
324
- responseSchema?: Record<string, unknown>;
325
- /**
326
- * Backward-compat pass-through for callers that previously passed
327
- * `includeArchivedInsights` to the read-only search.
328
- */
329
- includeArchivedInsights?: boolean;
330
- /** Include pre-fusion retrieval candidates; intended for evaluation/debugging. */
331
- includeRetrievalDiagnostics?: boolean;
332
- /**
333
- * Include messages that have been soft-deprecated
334
- * (`raw_messages.deprecated_at IS NOT NULL`). Default `false` keeps
335
- * `current-truth` retrievals clean: a row that was marked superseded
336
- * via `deprecateMessages` (or the `opencontext deprecate` CLI) is
337
- * excluded from results. Set to `true` for audits and historical
338
- * exploration.
339
- */
340
- includeDeprecated?: boolean;
341
- }
342
- interface SearchEvidence {
343
- id: string;
344
- source: SearchSource | SearchTier;
345
- snippet: string;
346
- score: number;
347
- timestamp?: number;
348
- /**
349
- * Forwarded from the underlying hit's `metadata`. Surfaces per-fact
350
- * provenance (`metadata.source`, `metadata.factType`, etc.) to the
351
- * synthesis prompt and the CLI's `--context-only` / `--json` output.
352
- */
353
- metadata?: Record<string, unknown>;
354
- }
355
- interface SearchOutput {
356
- query: string;
357
- sources: ReadonlyArray<SearchSource>;
358
- results: UnifiedMemorySearchResult[];
359
- /** Same hits as `results`, with the source labelled for synthesis callers. */
360
- evidence: SearchEvidence[];
361
- count: number;
362
- warnings: UnifiedMemorySearchWarning[];
363
- retrievalDiagnostics?: UnifiedMemoryRetrievalDiagnostics;
364
- reasoning?: UnifiedMemoryReasoningInfo;
365
- /** Only present when `synthesize` was truthy. */
366
- answer?: string;
367
- }
368
- declare function normalizeUnifiedMemorySearchSources(sources: unknown): UnifiedMemorySearchSource[];
369
- declare function clampUnifiedMemorySearchLimit(limit: unknown): number;
370
- declare function clampUnifiedMemorySearchThreshold(threshold: unknown): number;
371
- declare function mergeUnifiedMemorySearchResults(results: UnifiedMemorySearchResult[], limit: number, options?: {
372
- strategy?: UnifiedMemoryMergeStrategy;
373
- rankedLists?: UnifiedMemoryRankedList[];
374
- }): UnifiedMemorySearchResult[];
375
- declare function normalizeUnifiedMemoryMergeStrategy(value: unknown): UnifiedMemoryMergeStrategy;
376
- declare function normalizeUnifiedMemoryReasoningStrategy(value: unknown, defaultStrategy?: UnifiedMemoryReasoningStrategy): UnifiedMemoryReasoningStrategy;
377
- /**
378
- * Reciprocal Rank Fusion across multiple ranked lists. Each list contributes
379
- * `1 / (k + rank + 1)` to a hit's accumulated score. Hits are de-duplicated by
380
- * `(type, id)` so the same message surfaced by both lexical and semantic
381
- * channels naturally outranks single-channel matches. Tie-break is
382
- * deterministic: descending RRF score, then `(type, id)` lexical.
383
- */
384
- declare function mergeUnifiedMemorySearchResultsRrf(lists: UnifiedMemoryRankedList[], limit: number, k?: number): UnifiedMemorySearchResult[];
385
- declare function toKnowledgeResult(result: {
386
- chunkId: string;
387
- documentId: string;
388
- documentName: string;
389
- content: string;
390
- similarity: number;
391
- chunkIndex: number;
392
- }): UnifiedMemorySearchResult;
393
- declare function toMemoryResult(result: {
394
- id: string;
395
- content: string;
396
- similarity: number;
397
- metadata: Record<string, unknown>;
398
- }): UnifiedMemorySearchResult;
399
- /**
400
- * Build the `signals` breakdown for a single hit from the per-channel
401
- * ranked lists it appeared in. Only the highest-ranked (i.e. first)
402
- * appearance of `(type, id)` per channel contributes — duplicate hits
403
- * later in the same list are ignored so the breakdown stays
404
- * deterministic.
405
- *
406
- * Used by `mergeAcrossSources` to attach `signals` to every emitted
407
- * `UnifiedMemorySearchResult`. Pure; no I/O.
408
- */
409
- declare function materializeSignals(lists: ReadonlyArray<{
410
- name: string;
411
- hits: UnifiedMemorySearchResult[];
412
- }>, hit: UnifiedMemorySearchResult): HitSignals | undefined;
413
- /**
414
- * Map a `UnifiedMemoryRankedList.name` to a `HitChannel`. Unknown names
415
- * (e.g. "insights", "knowledge", "summary") are not channels — they
416
- * surface their similarity through the merge's `similarity` field and
417
- * are simply skipped here so callers can still see which channel set
418
- * the hit came from.
419
- */
420
- declare function listNameToChannel(name: string): HitChannel | undefined;
421
- declare function isRawMemorySemanticResult(result: unknown): result is {
422
- id: string;
423
- content: string;
424
- similarity: number;
425
- metadata: Record<string, unknown>;
426
- };
427
-
428
- /**
429
- * Iterative, LLM-driven memory retrieval.
430
- *
431
- * Instead of issuing one fixed query, a small planner model examines the
432
- * original question and the evidence collected so far, then decides:
433
- * - search again with different keywords / time bounds
434
- * - save some of the last results as evidence
435
- * - finish gathering
436
- *
437
- * This trades latency and token cost for better evidence location on
438
- * multi-hop, temporal, or underspecified questions.
439
- *
440
- * The module is provider-agnostic: callers inject a `complete` callback.
441
- */
442
- interface IterativeRecallCandidate {
443
- id: string;
444
- content: string;
445
- similarity: number;
446
- metadata: Record<string, unknown>;
447
- }
448
- interface IterativeRecallSearchRequest {
449
- keywords: string[];
450
- dateFrom?: string;
451
- dateTo?: string;
452
- }
453
- interface IterativeRecallSearchResult {
454
- candidates: IterativeRecallCandidate[];
455
- }
456
- interface IterativeRecallExecutor {
457
- search(request: IterativeRecallSearchRequest): Promise<IterativeRecallSearchResult>;
458
- }
459
- interface IterativeRecallStats {
460
- iterations: number;
461
- searches: number;
462
- notes: number;
463
- }
464
- interface IterativeRecallResult {
465
- /** Evidence selected by the planner, in the order it was collected. */
466
- evidence: IterativeRecallCandidate[];
467
- /** Diagnostic statistics. */
468
- stats: IterativeRecallStats;
469
- }
470
- interface IterativeRecallPlanner {
471
- plan(input: {
472
- query: string;
473
- executor: IterativeRecallExecutor;
474
- options?: IterativeRecallPlannerOptions;
475
- /**
476
- * Optional absolute date range for the search. The planner passes these
477
- * bounds to the executor and may also emit narrower ranges in later actions.
478
- */
479
- dateFrom?: string;
480
- dateTo?: string;
481
- }): Promise<IterativeRecallResult>;
482
- /**
483
- * Optional. When implemented, returns true iff the most recent `plan()`
484
- * could not run the planner as designed and fell back to a degraded
485
- * result — for example the LLM call threw, every iteration produced an
486
- * unparseable action, or the result relied on the fallback path
487
- * (recent-hits or baseline search) because the planner never committed
488
- * any evidence via a `note` action.
489
- */
490
- lastDegraded?: () => boolean;
491
- }
492
- interface IterativeRecallPlannerOptions {
493
- /** Maximum planner actions. @default 4 */
494
- maxIterations?: number;
495
- /** Results exposed to the planner per internal search. @default 5 */
496
- searchTopK?: number;
497
- /** When true, skip planning and return no evidence. @default false */
498
- disabled?: boolean;
499
- /**
500
- * When true (default), if the planner finishes without noting any
501
- * evidence the planner falls back to either the most recent hits or a
502
- * baseline keyword search against the original query, so the call is
503
- * never a total loss. Set to false to surface empty results honestly
504
- * when the planner never committed anything.
505
- * @default true
506
- */
507
- fallbackToBaseline?: boolean;
508
- }
509
- interface IterativeRecallPlannerDeps {
510
- /** LLM completion callback. */
511
- complete: (prompt: string) => Promise<string>;
512
- /** Default options. */
513
- options?: IterativeRecallPlannerOptions;
514
- }
515
- declare function createIterativeRecallPlanner(deps: IterativeRecallPlannerDeps): IterativeRecallPlanner;
516
- /**
517
- * A no-op planner that never runs iterative retrieval.
518
- */
519
- declare function createIdentityIterativePlanner(): IterativeRecallPlanner;
520
-
521
- /**
522
- * Query rewriting for memory retrieval.
523
- *
524
- * The memory corpus is usually a first-person chat log ("I told you ...",
525
- * "I prefer ..."). A query asked in the assistant's register ("What does the
526
- * user prefer?") often misses the best evidence. A rewriter rephrases the
527
- * question into the register the memory was written in, improving dense
528
- * retrieval without changing the underlying index.
529
- *
530
- * The module is provider-agnostic: callers inject a `complete` callback that
531
- * talks to whatever LLM they already use.
532
- */
533
- interface QueryRewriterInput {
534
- query: string;
535
- userId: string;
536
- authToken?: string;
537
- }
538
- interface QueryRewriter {
539
- /**
540
- * Return one or more query variants. The first element is always the
541
- * original query; subsequent elements are rewritten variants.
542
- */
543
- rewrite(input: QueryRewriterInput): Promise<string[]>;
544
- /**
545
- * Optional hook that reports whether the most recent `rewrite` call
546
- * silently degraded to the original query (for example because the LLM
547
- * call failed or produced no usable variants).
548
- */
549
- lastDegraded?(): boolean;
550
- }
551
- interface QueryRewriterOptions {
552
- /**
553
- * LLM completion callback. Receives the full prompt and returns the raw
554
- * model output. The rewriter parses the output and falls back to the
555
- * original query on any failure.
556
- */
557
- complete: (prompt: string) => Promise<string>;
558
- /**
559
- * Maximum number of rewritten variants to return in addition to the
560
- * original query. Use `0` to return only the original. Higher values ask
561
- * the LLM for more alternatives and multiply embedding/search cost
562
- * proportionally, so callers should bound it (typical: 1–3).
563
- * @default 1
564
- */
565
- maxVariants?: number;
566
- /**
567
- * When true, the rewriter returns only the original query and skips the
568
- * LLM call. Useful as a kill-switch.
569
- * @default false
570
- */
571
- disabled?: boolean;
572
- }
573
- declare function createUserVoiceRewriter(options: QueryRewriterOptions): QueryRewriter;
574
- /**
575
- * A no-op rewriter that always returns the original query.
576
- */
577
- declare function createIdentityRewriter(): QueryRewriter;
578
-
579
- interface MemoryStoreDb {
580
- /** Resolve the active Drizzle DB handle. Must be server-side. */
581
- getDb(): unknown;
582
- /** Whether the DB has finished initializing (optional, for startup gating). */
583
- isDbInitialized?(): boolean;
584
- /** Initialize the DB (optional, called by `createMemoryStore` if present). */
585
- initDb?(): unknown;
586
- /**
587
- * Drizzle table references for the postgres backend. Required when
588
- * running outside Tauri mode without a registered factory. The host
589
- * application owns the actual schema.
590
- */
591
- tables?: {
592
- rawMessages?: unknown;
593
- memorySummaries?: unknown;
594
- };
595
- }
596
- /**
597
- * Placeholder for the historical host-injected environment. The fields
598
- * it used to expose (`isTauriMode`, `getTauriDbPath`, `getTauriDataDir`)
599
- * were Tauri-specific and have been removed. Backend selection is now
600
- * driven entirely by the `OPENCONTEXT_MEMORY_STORE_BACKEND` env var.
601
- *
602
- * The interface is kept (empty) so existing `env?: MemoryStoreEnv`
603
- * parameters on the public surface still type-check, but new code
604
- * should not need to construct a value of this type.
605
- */
606
- interface MemoryStoreEnv {
607
- }
608
- type VectorBackend = "sqlite-vec" | "chroma" | "lancedb" | "milvus";
609
- interface MemoryStoreVectorConfig {
610
- /** Active backend selector. Defaults to env-based detection. */
611
- backend?: VectorBackend;
612
- /** SQLite-vec options (used when backend === "sqlite-vec"). */
613
- sqliteVec?: {
614
- dbPath?: string;
615
- insightsCollection?: string;
616
- };
617
- /** Chroma options (used when backend === "chroma"). */
618
- chroma?: {
619
- url?: string;
620
- rawMessagesCollection?: string;
621
- insightsCollection?: string;
622
- };
623
- /** LanceDB options (used when backend === "lancedb"). */
624
- lancedb?: {
625
- uri: string;
626
- table?: string;
627
- };
628
- /** Milvus options (used when backend === "milvus"). */
629
- milvus?: {
630
- address: string;
631
- token?: string;
632
- database?: string;
633
- collection?: string;
634
- dimension?: number;
635
- };
636
- }
637
- type EmbedQueryFn = (input: {
638
- userId: string;
639
- query: string;
640
- authToken?: string;
641
- }) => Promise<number[]>;
642
- type EmbedDocumentsFn = (input: {
643
- userId: string;
644
- texts: string[];
645
- authToken?: string;
646
- }) => Promise<number[][]>;
647
- interface UnifiedSearchKnowledgeResult {
648
- chunkId: string;
649
- documentId: string;
650
- documentName: string;
651
- content: string;
652
- similarity: number;
653
- chunkIndex: number;
654
- }
655
- interface UnifiedSearchInsightsResult {
656
- id: string;
657
- content: string;
658
- similarity: number;
659
- metadata: Record<string, unknown>;
660
- }
661
- interface UnifiedSearchReasoningDeps {
662
- /**
663
- * LLM single-turn synthesis callback. Wired into
664
- * `search({ synthesize: true })` so a host that wants synthesis
665
- * responses only has to drop in one callable. When omitted, the
666
- * synthesis path returns the gathered evidence with a
667
- * `synthesize_llm_not_configured` warning instead of throwing.
668
- */
669
- complete?: (prompt: string) => Promise<string>;
670
- /** Default merge strategy used by `searchUnifiedMemory` when the caller does not specify one. @default "rrf" */
671
- defaultMergeStrategy?: UnifiedMemoryMergeStrategy;
672
- /** Optional query rewriter. When present, "rewrite" strategy is available. */
673
- queryRewriter?: QueryRewriter;
674
- /** Optional iterative recall planner. When present, "iterative" strategy is available. */
675
- iterativePlanner?: IterativeRecallPlanner;
676
- /** Default reasoning strategy when callers do not specify one. @default "none" */
677
- defaultStrategy?: UnifiedMemoryReasoningStrategy;
678
- }
679
- interface MemorySummaryHit {
680
- summaryId: string;
681
- summaryText: string;
682
- summaryTier?: "L1" | "L2" | "L3";
683
- keywords?: string[];
684
- startTimestamp?: number;
685
- endTimestamp?: number;
686
- }
687
- /**
688
- * Trusted applicability fields forwarded together to retrieval providers.
689
- * They are absent for legacy unscoped searches and always present together
690
- * for scoped searches. Providers must enforce the canonical applicability
691
- * contract at `applicabilityAt`; these values must not be derived from public
692
- * request payloads.
693
- */
694
- interface SearchProviderApplicabilityInput {
695
- applicabilityContexts?: readonly MemoryApplicabilityContext[];
696
- /** Epoch milliseconds resolved once at the `search()` boundary. */
697
- applicabilityAt?: number;
698
- }
699
- interface UnifiedSearchDeps {
700
- /** Embed a query string using the active user's provider. */
701
- embedQuery?: EmbedQueryFn;
702
- /** Batch document embedding used by child indexing when the provider supports it. */
703
- embedDocuments?: EmbedDocumentsFn;
704
- /** Safe, non-secret identity used by health checks and child-index metadata. */
705
- embeddingInfo?: {
706
- provider?: string;
707
- model?: string;
708
- dimensions?: number;
709
- maxTokens?: number;
710
- };
711
- /** Runtime-safe retrieval readiness used by health and diagnostics. */
712
- getRawMessageRetrievalStatus?: () => Promise<{
713
- backend: string;
714
- embeddingProvider?: string;
715
- embeddingModel?: string;
716
- embeddingDimensions?: number;
717
- childCount: number;
718
- embeddedChildCount: number;
719
- indexedDimensions: number[];
720
- semanticReady: boolean;
721
- lexicalReady: boolean;
722
- semanticDegradedReason?: string;
723
- rerankerProvider?: string;
724
- rerankerModel?: string;
725
- rerankerReady?: boolean;
726
- }>;
727
- /** Optional external child-vector index used by the shared write path. */
728
- rawMessageChildIndex?: {
729
- replaceMessages(messages: RawMessage[], chunks: RawMessageSearchChunk[]): Promise<void>;
730
- };
731
- /**
732
- * Dense raw-message search supplied by the selected child-vector backend.
733
- * External Postgres hosts may also provide their legacy parent-level
734
- * implementation here; Postgres schema changes are outside this package.
735
- */
736
- searchRawMessagesAnn?: (input: SearchProviderApplicabilityInput & {
737
- userId: string;
738
- queryEmbedding: number[];
739
- limit: number;
740
- threshold: number;
741
- botId?: string;
742
- /**
743
- * Forward `includeDeprecated` from `UnifiedMemorySearchInput` so
744
- * supersession-aware callers can opt into deprecated rows for
745
- * audits. Default `false` preserves the current-truth behaviour.
746
- */
747
- includeDeprecated?: boolean;
748
- /**
749
- * Forward `asOf` from `UnifiedMemorySearchInput` so time-travel
750
- * callers can ask "what was true at this instant?". Implementations
751
- * that don't yet honour the field should ignore it — the rest of
752
- * the unified pipeline stays correct.
753
- */
754
- asOf?: string;
755
- /** Optional peer scope resolved from `UnifiedMemorySearchInput.peerFilter`. */
756
- peers?: ReadonlyArray<Peer>;
757
- /** Optional `FactType` filter resolved from `UnifiedMemorySearchInput.factTypes`. */
758
- factTypes?: FactType[];
759
- }) => Promise<Array<{
760
- id: string;
761
- content: string;
762
- similarity: number;
763
- metadata: Record<string, unknown>;
764
- }>>;
765
- /** Native dense+BM25 path for backends such as LanceDB and Milvus. */
766
- searchRawMessagesHybrid?: (input: SearchProviderApplicabilityInput & {
767
- userId: string;
768
- query: string;
769
- queryEmbedding: number[];
770
- limit: number;
771
- botId?: string;
772
- }) => Promise<Array<{
773
- id: string;
774
- content: string;
775
- similarity: number;
776
- metadata: Record<string, unknown>;
777
- }>>;
778
- /** RAG over uploaded knowledge documents. Returns a list of chunk hits. */
779
- searchKnowledge?: (input: SearchProviderApplicabilityInput & {
780
- userId: string;
781
- query: string;
782
- options: {
783
- limit: number;
784
- threshold: number;
785
- documentIds?: string[];
786
- };
787
- authToken?: string;
788
- /** Optional peer scope resolved from `UnifiedMemorySearchInput.peerFilter`. */
789
- peers?: ReadonlyArray<Peer>;
790
- }) => Promise<UnifiedSearchKnowledgeResult[]>;
791
- /** Semantic search over extracted insights. */
792
- searchInsights?: (input: SearchProviderApplicabilityInput & {
793
- userId: string;
794
- query: string;
795
- limit: number;
796
- threshold: number;
797
- botIds?: string[];
798
- includeArchived?: boolean;
799
- authToken?: string;
800
- /** Optional peer scope resolved from `UnifiedMemorySearchInput.peerFilter`. */
801
- peers?: ReadonlyArray<Peer>;
802
- }) => Promise<UnifiedSearchInsightsResult[]>;
803
- /**
804
- * Optional BM25 (lexical) sub-query for the memory source. Mirrors the
805
- * `searchRawMessagesAnn` shape but operates on FTS5 keywords instead of
806
- * dense embeddings. When absent, the unified pipeline skips the lexical
807
- * path and emits a `memory_lexical_search_not_configured` warning.
808
- */
809
- searchRawMessagesLexical?: (input: SearchProviderApplicabilityInput & {
810
- userId: string;
811
- keywords: string[];
812
- limit: number;
813
- botId?: string;
814
- /**
815
- * Forward `includeDeprecated` from `UnifiedMemorySearchInput` so
816
- * supersession-aware callers can opt into deprecated rows for
817
- * audits. Default `false` preserves the current-truth behaviour.
818
- */
819
- includeDeprecated?: boolean;
820
- /**
821
- * Forward `asOf` from `UnifiedMemorySearchInput` so time-travel
822
- * callers can ask "what was true at this instant?". Implementations
823
- * that don't yet honour the field should ignore it — the rest of
824
- * the unified pipeline stays correct.
825
- */
826
- asOf?: string;
827
- /** Optional peer scope resolved from `UnifiedMemorySearchInput.peerFilter`. */
828
- peers?: ReadonlyArray<Peer>;
829
- /** Optional `FactType` filter resolved from `UnifiedMemorySearchInput.factTypes`. */
830
- factTypes?: FactType[];
831
- }) => Promise<Array<{
832
- id: string;
833
- content: string;
834
- similarity: number;
835
- metadata: Record<string, unknown>;
836
- }>>;
837
- /**
838
- * Optional summary-tier search. When wired in, `reflect()` consults the
839
- * summary tier alongside raw messages and emits a
840
- * `reflect_summaries_unavailable` warning if the call fails.
841
- */
842
- searchSummaries?: (input: SearchProviderApplicabilityInput & {
843
- userId: string;
844
- query: string;
845
- keywords: string[];
846
- limit: number;
847
- threshold: number;
848
- dateFrom?: string;
849
- dateTo?: string;
850
- authToken?: string;
851
- /** Optional peer scope resolved from `UnifiedMemorySearchInput.peerFilter`. */
852
- peers?: ReadonlyArray<Peer>;
853
- }) => Promise<MemorySummaryHit[]>;
854
- /**
855
- * Optional entity sub-query provider. Hosts wire this in to opt
856
- * into the `entity` channel of unified search: matches are
857
- * projected back as `UnifiedMemorySearchResult`s and fused with
858
- * semantic + lexical via RRF. Without it, the entity channel is
859
- * skipped silently (with a `memory_entity_search_not_configured`
860
- * warning when RRF was requested).
861
- *
862
- * The provider is intentionally narrow: it returns `(messageId,
863
- * label, score)` tuples. The host is responsible for keeping its
864
- * own entity store (graph nodes, separate SQLite table, etc.) —
865
- * the SDK never persists entity edges itself.
866
- */
867
- entitySearch?: (input: SearchProviderApplicabilityInput & {
868
- userId: string;
869
- keywords: string[];
870
- limit: number;
871
- botId?: string;
872
- /** Optional peer scope resolved from `UnifiedMemorySearchInput.peerFilter`. */
873
- peers?: ReadonlyArray<Peer>;
874
- }) => Promise<Array<{
875
- messageId: string;
876
- label: string;
877
- score: number;
878
- }>>;
879
- /**
880
- * Optional entity extractor wired into the `distill` primitive. The
881
- * extractor receives the raw message text and returns a batch of
882
- * `EntityEdge`s the host may persist via the `persist` callback
883
- * supplied on the `distill` input.
884
- *
885
- * When omitted, `distill` short-circuits with a
886
- * `distill_extractor_not_configured` warning — matching the
887
- * `memory_iterative_planner_not_configured` precedent.
888
- */
889
- entityExtractor?: (input: {
890
- userId: string;
891
- messageId: string;
892
- content: string;
893
- }) => Promise<EntityEdge[]>;
894
- /**
895
- * Optional deriver wired into the `derive` primitive. The deriver
896
- * receives a window of candidate fact texts and returns a batch of
897
- * `DerivedFact`s the host may persist via the `persist` callback
898
- * supplied on the `derive` input.
899
- *
900
- * When omitted, `derive` short-circuits with a
901
- * `derive_deriver_not_configured` warning — same precedent.
902
- */
903
- deriver?: (input: {
904
- userId: string;
905
- userScope: {
906
- userId: string;
907
- botIds?: string[];
908
- dateFrom?: string;
909
- dateTo?: string;
910
- };
911
- recentFactTexts: string[];
912
- window?: {
913
- from: number;
914
- to: number;
915
- };
916
- }) => Promise<DerivedFact[]>;
917
- /**
918
- * Optional host-side check that validates whether a `peerFilter` peer is
919
- * actually reachable from this `userId`. Used by
920
- * `resolveScopePeer` to drop out-of-scope peers with a warning
921
- * rather than broadening the search. May be omitted for hosts that
922
- * have no peer-permission model.
923
- */
924
- peerScopeCheck?: (input: {
925
- userId: string;
926
- peers: ReadonlyArray<_melandlabs_contracts_peer.Peer>;
927
- }) => Promise<boolean> | boolean;
928
- /** Optional reasoning providers. */
929
- reasoning?: UnifiedSearchReasoningDeps;
930
- /**
931
- * Optional reranker applied after per-source merge and before the
932
- * final limit. Defaults to an identity pass-through so the merge order
933
- * is preserved.
934
- */
935
- reranker?: Reranker;
936
- /** Safe, non-secret reranker identity surfaced by health and diagnostics. */
937
- rerankerInfo?: {
938
- provider: string;
939
- model?: string;
940
- maxTokens?: number;
941
- ready: boolean;
942
- };
943
- /**
944
- * Optional logger. When set (e.g. via `MemoryStoreConfig.logger`),
945
- * `reflect` / `consolidate` log through it instead of `console`.
946
- * Falls back to `console` when omitted.
947
- */
948
- logger?: Pick<Console, "log" | "warn" | "error">;
949
- }
950
- interface MemoryStoreConfig {
951
- /**
952
- * Database handle factory. Required for the Postgres backend
953
- * (`OPENCONTEXT_MEMORY_STORE_BACKEND=postgres`).
954
- */
955
- db?: MemoryStoreDb;
956
- /**
957
- * Custom SQLite database file path for the default SQLite backend.
958
- *
959
- * If provided, this takes precedence over the `MEMORY_STORE_DB_PATH`
960
- * environment variable and the default `~/.opencontext/memory/store.db`.
961
- *
962
- * For backward compatibility with early tutorials, `db.path` is also
963
- * honoured when `db` is supplied as `{ type, path }`.
964
- */
965
- dbPath?: string;
966
- /** Environment helpers. Defaults to a process.env-only stub if omitted. */
967
- env?: MemoryStoreEnv;
968
- /** Vector backend config. Defaults to env-based detection. */
969
- vector?: MemoryStoreVectorConfig;
970
- /** Optional cross-source search providers. */
971
- unified?: UnifiedSearchDeps;
972
- /**
973
- * Optional write-back graph store. When set, `createMemoryStore` wires
974
- * it into `store.graphStore` and forwards it to `consolidate`. Hosts
975
- * that prefer post-construction wiring can use `attachMemoryGraphStore`.
976
- */
977
- graphStore?: _melandlabs_memory_consolidation.MemoryGraphStoreWithOperationHistory;
978
- /**
979
- * Optional storage adapter carrying `deprecateRecords`. Forwarded into
980
- * `consolidate` for callers that want deprecation writes without
981
- * attaching a full graph store.
982
- */
983
- storage?: _melandlabs_memory_consolidation.MemoryStorageAdapterLike;
984
- /** Optional logger. Defaults to console. */
985
- logger?: Pick<Console, "log" | "warn" | "error">;
986
- }
987
-
988
- export { createUserVoiceRewriter as $, toMemoryResult as A, type IterativeRecallExecutor as B, type IterativeRecallPlanner as C, type IterativeRecallPlannerOptions as D, type EmbedQueryFn as E, type IterativeRecallResult as F, type IterativeRecallSearchRequest as G, type HitChannel as H, type IterativeRecallCandidate as I, type IterativeRecallSearchResult as J, type IterativeRecallStats as K, type MemoryStoreDb as L, type MemoryStoreConfig as M, type QueryRewriterInput as N, type QueryRewriterOptions as O, type SearchProviderApplicabilityInput as P, type QueryRewriter as Q, type UnifiedMemoryReasoningInfo as R, type SearchTier as S, type UnifiedSearchInsightsResult as T, type UnifiedSearchDeps as U, type UnifiedSearchKnowledgeResult as V, type UnifiedSearchReasoningDeps as W, type VectorBackend as X, createIdentityIterativePlanner as Y, createIdentityRewriter as Z, createIterativeRecallPlanner as _, type MemoryStoreEnv as a, type UnifiedMemorySearchWarning as b, type SearchEvidence as c, type SearchInput as d, type SearchOutput as e, type UnifiedMemorySearchInput as f, type UnifiedMemorySearchOutput as g, type UnifiedMemorySearchResult as h, type HitSignals as i, type SearchSource as j, type UnifiedMemoryMergeStrategy as k, type UnifiedMemoryRankedList as l, type UnifiedMemoryReasoningStrategy as m, type UnifiedMemorySearchSource as n, clampUnifiedMemorySearchLimit as o, clampUnifiedMemorySearchThreshold as p, deriveLexicalKeywords as q, isRawMemorySemanticResult as r, listNameToChannel as s, materializeSignals as t, mergeUnifiedMemorySearchResults as u, mergeUnifiedMemorySearchResultsRrf as v, normalizeUnifiedMemoryMergeStrategy as w, normalizeUnifiedMemoryReasoningStrategy as x, normalizeUnifiedMemorySearchSources as y, toKnowledgeResult as z };