@melandlabs/memory-store 1.3.7 → 1.4.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.
@@ -0,0 +1,626 @@
1
+ import { Citation, FactType } from '@melandlabs/contracts';
2
+ import { Peer } from '@melandlabs/contracts/peer';
3
+ import { MemoryApplicabilityContext } from '@melandlabs/memory-consolidation';
4
+
5
+ /**
6
+ * Iterative, LLM-driven memory retrieval.
7
+ *
8
+ * Instead of issuing one fixed query, a small planner model examines the
9
+ * original question and the evidence collected so far, then decides:
10
+ * - search again with different keywords / time bounds
11
+ * - save some of the last results as evidence
12
+ * - finish gathering
13
+ *
14
+ * This trades latency and token cost for better evidence location on
15
+ * multi-hop, temporal, or underspecified questions.
16
+ *
17
+ * The module is provider-agnostic: callers inject a `complete` callback.
18
+ */
19
+ interface IterativeRecallCandidate {
20
+ id: string;
21
+ content: string;
22
+ similarity: number;
23
+ metadata: Record<string, unknown>;
24
+ }
25
+ interface IterativeRecallSearchRequest {
26
+ keywords: string[];
27
+ dateFrom?: string;
28
+ dateTo?: string;
29
+ }
30
+ interface IterativeRecallSearchResult {
31
+ candidates: IterativeRecallCandidate[];
32
+ }
33
+ interface IterativeRecallExecutor {
34
+ search(request: IterativeRecallSearchRequest): Promise<IterativeRecallSearchResult>;
35
+ }
36
+ interface IterativeRecallStats {
37
+ iterations: number;
38
+ searches: number;
39
+ notes: number;
40
+ }
41
+ interface IterativeRecallResult {
42
+ /** Evidence selected by the planner, in the order it was collected. */
43
+ evidence: IterativeRecallCandidate[];
44
+ /** Diagnostic statistics. */
45
+ stats: IterativeRecallStats;
46
+ /** Opt-in action diagnostics. Never used to select or rank evidence. */
47
+ diagnostics?: IterativeRecallDiagnostics;
48
+ }
49
+ interface IterativeRecallDiagnostics {
50
+ steps: Array<{
51
+ iteration: number;
52
+ action: PlannerAction["type"] | "invalid" | "completion_error";
53
+ responseExcerpt?: string;
54
+ multipleActions?: boolean;
55
+ errorName?: string;
56
+ httpStatus?: number;
57
+ }>;
58
+ fallback?: "recent-hits" | "baseline";
59
+ searches: number;
60
+ notes: number;
61
+ }
62
+ interface IterativeRecallPlanner {
63
+ plan(input: {
64
+ query: string;
65
+ executor: IterativeRecallExecutor;
66
+ options?: IterativeRecallPlannerOptions;
67
+ /**
68
+ * Optional absolute date range for the search. The planner passes these
69
+ * bounds to the executor and may also emit narrower ranges in later actions.
70
+ */
71
+ dateFrom?: string;
72
+ dateTo?: string;
73
+ }): Promise<IterativeRecallResult>;
74
+ /**
75
+ * Optional. When implemented, returns true iff the most recent `plan()`
76
+ * could not run the planner as designed and fell back to a degraded
77
+ * result — for example the LLM call threw, every iteration produced an
78
+ * unparseable action, or the result relied on the fallback path
79
+ * (recent-hits or baseline search) because the planner never committed
80
+ * any evidence via a `note` action.
81
+ */
82
+ lastDegraded?: () => boolean;
83
+ }
84
+ interface IterativeRecallPlannerOptions {
85
+ /** Maximum planner actions. @default 4 */
86
+ maxIterations?: number;
87
+ /** Results exposed to the planner per internal search. @default 5 */
88
+ searchTopK?: number;
89
+ /** When true, skip planning and return no evidence. @default false */
90
+ disabled?: boolean;
91
+ /**
92
+ * When true (default), if the planner finishes without noting any
93
+ * evidence the planner falls back to either the most recent hits or a
94
+ * baseline keyword search against the original query, so the call is
95
+ * never a total loss. Set to false to surface empty results honestly
96
+ * when the planner never committed anything.
97
+ * @default true
98
+ */
99
+ fallbackToBaseline?: boolean;
100
+ /** Include bounded response excerpts and degradation causes. @default false */
101
+ collectDiagnostics?: boolean;
102
+ }
103
+ interface IterativeRecallCompletionOptions {
104
+ /** Preserve real chat roles instead of asking the model to continue a transcript. */
105
+ messages: ReadonlyArray<{
106
+ role: "system" | "user" | "assistant";
107
+ content: string;
108
+ }>;
109
+ }
110
+ interface IterativeRecallPlannerDeps {
111
+ /** LLM completion callback. Chat transports should send options.messages
112
+ * verbatim; the string prompt is also supplied for plain completion hosts. */
113
+ complete: (prompt: string, options?: IterativeRecallCompletionOptions) => Promise<string>;
114
+ /** Default options. */
115
+ options?: IterativeRecallPlannerOptions;
116
+ }
117
+ type PlannerAction = {
118
+ type: "search";
119
+ keywords: string[];
120
+ dateFrom?: string;
121
+ dateTo?: string;
122
+ } | {
123
+ type: "note";
124
+ indices: number[];
125
+ } | {
126
+ type: "finish";
127
+ };
128
+ declare function createIterativeRecallPlanner(deps: IterativeRecallPlannerDeps): IterativeRecallPlanner;
129
+ /**
130
+ * A no-op planner that never runs iterative retrieval.
131
+ */
132
+ declare function createIdentityIterativePlanner(): IterativeRecallPlanner;
133
+
134
+ type UnifiedMemorySearchSource = "memory" | "insights" | "knowledge" | "workspace";
135
+ /** Retrieval availability and candidate count before cross-source ranking and limits. */
136
+ interface SearchSourceState {
137
+ source: UnifiedMemorySearchSource;
138
+ status: "complete" | "partial" | "not_configured" | "not_ready" | "failed";
139
+ count: number;
140
+ message?: string;
141
+ }
142
+ /**
143
+ * A retrieval channel that contributed to a hit's final score.
144
+ *
145
+ * - `semantic` — dense embedding lookup (`searchRawMessagesAnn` or
146
+ * the SQLite manager's semantic-search fallback).
147
+ * - `lexical` — BM25 / FTS5 keyword match
148
+ * (`searchRawMessagesLexical` or the SQLite manager's lexical
149
+ * fallback).
150
+ * - `planner` — evidence selected by iterative recall.
151
+ * - `entity` — entity-link match supplied by the host's
152
+ * `entitySearch` dep.
153
+ */
154
+ type HitChannel = "semantic" | "lexical" | "planner" | "hybrid" | "entity";
155
+ type UnifiedMemoryRrfWeights = Partial<Record<HitChannel, number>>;
156
+ /**
157
+ * Per-hit score breakdown. Always emitted by `search()` (default merge
158
+ * strategy) so callers can threshold / re-rank without losing the
159
+ * individual channel contributions. The RRF fusion additionally
160
+ * populates `signals.rrf` and continues to mirror the fused score
161
+ * onto `metadata.rrfScore` (legacy field).
162
+ *
163
+ * Each numeric field is the raw per-channel score for the *first*
164
+ * (highest-ranked) appearance of the same `(type, id)` key in the
165
+ * channel's list. Channels that did not contribute are `undefined`.
166
+ */
167
+ interface HitSignals {
168
+ /** Channels the hit actually appeared in. */
169
+ channels: HitChannel[];
170
+ /** Semantic sub-query similarity in [0..1] (undefined when absent). */
171
+ semantic?: number;
172
+ /** Lexical sub-query BM25 score (undefined when absent). */
173
+ lexical?: number;
174
+ /** Iterative planner evidence score (undefined when absent). */
175
+ planner?: number;
176
+ /** Native backend dense+BM25 fused score (undefined when absent). */
177
+ hybrid?: number;
178
+ /** Entity sub-query match score (undefined when absent). */
179
+ entity?: number;
180
+ /** RRF-fused score (only when mergeStrategy === "rrf"). */
181
+ rrf?: number;
182
+ }
183
+ /**
184
+ * Derive simple lexical keywords from a query string. Splits on any
185
+ * non-letter/non-digit Unicode boundary, lowercases, drops tokens
186
+ * shorter than 2 chars, deduplicates in first-occurrence order, and caps
187
+ * the unique list at 16. Used by the unified
188
+ * search lexical sub-query and by the `derive` primitive's candidate
189
+ * fetch fallback.
190
+ */
191
+ declare function deriveLexicalKeywords(query: string): string[];
192
+ type UnifiedMemoryReasoningStrategy = "none" | "rewrite" | "iterative" | "union";
193
+ interface UnifiedMemorySearchWarning {
194
+ source: UnifiedMemorySearchSource;
195
+ code: string;
196
+ message: string;
197
+ }
198
+ interface UnifiedMemoryReasoningInfo {
199
+ strategy: UnifiedMemoryReasoningStrategy;
200
+ /**
201
+ * True when the caller asked for a non-`"none"` strategy but the
202
+ * corresponding reasoning provider was not configured (or its LLM
203
+ * call failed), so the unified search silently fell back to the default
204
+ * one-shot semantic + lexical path. Callers can use this to surface a
205
+ * degraded-mode warning to their users.
206
+ */
207
+ degraded?: boolean;
208
+ /** Query variants produced by the rewriter (original + rewritten). */
209
+ rewrittenQueries?: string[];
210
+ /** Bounded query variants actually used by the opt-in lexical rewrite path. */
211
+ lexicalRewrittenQueries?: string[];
212
+ /** Present only when RRF fusion of multiple semantic query variants ran. */
213
+ semanticVariantMerge?: "rrf";
214
+ /** Number of planner iterations executed (iterative mode only). */
215
+ iterations?: number;
216
+ /** Number of evidence items collected by the planner (iterative mode only). */
217
+ evidenceCount?: number;
218
+ /** Opt-in planner action ledger, only when retrieval diagnostics were requested. */
219
+ plannerDiagnostics?: IterativeRecallDiagnostics;
220
+ /** Date range that was applied to the memory source, if any. */
221
+ dateRange?: {
222
+ from?: string;
223
+ to?: string;
224
+ };
225
+ }
226
+ interface UnifiedMemorySearchInput {
227
+ userId: string;
228
+ query: string;
229
+ sources?: UnifiedMemorySearchSource[];
230
+ limit?: number;
231
+ /** Optional diagnostic candidate budget; defaults to max(limit, limit * 4). */
232
+ candidateLimit?: number;
233
+ threshold?: number;
234
+ authToken?: string;
235
+ includeArchivedInsights?: boolean;
236
+ botIds?: string[];
237
+ documentIds?: string[];
238
+ /**
239
+ * Optional ISO-8601 timestamp. When set, graph-aware retrieval and other
240
+ * snapshot consumers drop nodes / edges whose `applicability.validFrom` /
241
+ * `validUntil` window is closed at that instant. Backward-compatible:
242
+ * omitted `asOf` keeps the legacy behaviour exactly.
243
+ *
244
+ * This is a single-point snapshot, not a date range. For interval filtering
245
+ * over memory timestamps use `dateFrom` / `dateTo`.
246
+ */
247
+ asOf?: string;
248
+ /**
249
+ * Optional inclusive start date (ISO-8601). When set, the memory source
250
+ * filters out candidates whose timestamp is before this boundary. The
251
+ * iterative planner also receives this bound and may emit narrower ranges.
252
+ *
253
+ * This only affects the `memory` source; `insights` and `knowledge` are
254
+ * not filtered by this range. Candidates without a recognised timestamp are
255
+ * retained.
256
+ */
257
+ dateFrom?: string;
258
+ /**
259
+ * Optional inclusive end date (ISO-8601). When set, the memory source
260
+ * filters out candidates whose timestamp is after this boundary. Date-only
261
+ * strings are treated as the end of that day.
262
+ *
263
+ * This only affects the `memory` source; `insights` and `knowledge` are
264
+ * not filtered by this range. Candidates without a recognised timestamp are
265
+ * retained.
266
+ */
267
+ dateTo?: string;
268
+ /**
269
+ * How to merge per-source result lists. Defaults to `"rrf"` reciprocal-rank
270
+ * fusion. `"similarity"` preserves the legacy global similarity sort.
271
+ */
272
+ mergeStrategy?: UnifiedMemoryMergeStrategy;
273
+ /** Optional per-channel RRF weights. Omitted channels retain weight 1. */
274
+ rrfWeights?: UnifiedMemoryRrfWeights;
275
+ /** Optional reciprocal-rank damping constant. Defaults to 60. */
276
+ rrfK?: number;
277
+ /** Opt-in post-fusion session-neighbor expansion used by retrieval experiments. */
278
+ sessionNeighborExpansion?: SessionNeighborExpansionOptions;
279
+ /**
280
+ * Optional additive scope-narrowing filter expressed as structured
281
+ * peers. Coexists with `userId`/`botIds`. When supplied, the host's
282
+ * `UnifiedSearchDeps.peerScopeCheck` is consulted; peers that fall
283
+ * outside `userId` are dropped with a `peer_filter_outside_user_scope`
284
+ * warning rather than broadening the scope.
285
+ */
286
+ peerFilter?: ReadonlyArray<Peer>;
287
+ /**
288
+ * Optional reasoning strategy. `"rewrite"` rewrites the query before
289
+ * embedding; `"iterative"` runs an LLM planner that searches, notes
290
+ * evidence, and searches again. Defaults to `"none"`.
291
+ */
292
+ reasoningStrategy?: UnifiedMemoryReasoningStrategy;
293
+ /**
294
+ * Optional `FactType` read-side filter. When supplied, the memory
295
+ * source narrows candidates to rows whose `factType` is in this set.
296
+ * Only affects the `memory` source; `insights` and `knowledge` are
297
+ * not filtered by this. Empty array is treated as "no filter" so
298
+ * callers can pass `searchInput.factTypes ?? []` without surprises.
299
+ */
300
+ factTypes?: FactType[];
301
+ /** Include pre-fusion channel candidates in the response for diagnostics. */
302
+ includeRetrievalDiagnostics?: boolean;
303
+ /**
304
+ * Include messages that have been soft-deprecated
305
+ * (`raw_messages.deprecated_at IS NOT NULL`). Default `false` keeps
306
+ * `current-truth` retrievals clean: a row that was marked superseded
307
+ * via `deprecateMessages` (or the `opencontext deprecate` CLI) is
308
+ * excluded from results. Set to `true` for audits and historical
309
+ * exploration.
310
+ */
311
+ includeDeprecated?: boolean;
312
+ }
313
+ type UnifiedMemoryMergeStrategy = "similarity" | "rrf";
314
+ /**
315
+ * A single ranked list from one retrieval channel. Used by the RRF merge to
316
+ * combine multiple signals (semantic + lexical + insights + knowledge) without
317
+ * flattening them into a single similarity score first.
318
+ */
319
+ interface UnifiedMemoryRankedList {
320
+ /** Label for the source list (e.g. a tier or channel). Free-form; the
321
+ * RRF fusion only reads `hits`, so `name` is purely diagnostic. */
322
+ name: string;
323
+ hits: UnifiedMemorySearchResult[];
324
+ }
325
+ type SessionNeighborExpansionMode = "union" | "protected";
326
+ interface SessionNeighborExpansionOptions {
327
+ /** Number of fused seed messages used to find neighbors. */
328
+ seedLimit?: number;
329
+ /** Number of messageSequence steps to inspect on each side. */
330
+ window?: number;
331
+ /** `union` lets reranking choose all candidates; `protected` reserves slots for direct hits. */
332
+ mode?: SessionNeighborExpansionMode;
333
+ /** Maximum neighbor results in `protected` mode. */
334
+ maxNeighborSlots?: number;
335
+ }
336
+ interface UnifiedMemorySearchResult {
337
+ type: "memory" | "insight" | "knowledge" | "workspace";
338
+ id: string;
339
+ content: string;
340
+ similarity: number;
341
+ metadata: Record<string, unknown>;
342
+ /** Host-supplied evidence identity; absent fields remain explicitly unknown. */
343
+ provenance?: Partial<SearchEvidenceProvenance>;
344
+ /**
345
+ * Optional per-channel score breakdown. Always populated by the
346
+ * pipeline when at least one channel contributed a non-empty list;
347
+ * callers should treat absence as "no channel info available".
348
+ */
349
+ signals?: HitSignals;
350
+ }
351
+ interface UnifiedMemorySearchOutput {
352
+ query: string;
353
+ sources: UnifiedMemorySearchSource[];
354
+ results: UnifiedMemorySearchResult[];
355
+ count: number;
356
+ warnings: UnifiedMemorySearchWarning[];
357
+ sourceStates: SearchSourceState[];
358
+ retrievalDiagnostics?: UnifiedMemoryRetrievalDiagnostics;
359
+ /**
360
+ * Diagnostic information about reasoning and date filtering. Present
361
+ * when (a) a non-`"none"` reasoning strategy was requested, or (b) a
362
+ * `dateFrom` / `dateTo` filter was applied to the memory source.
363
+ */
364
+ reasoning?: UnifiedMemoryReasoningInfo;
365
+ }
366
+ interface UnifiedMemoryRetrievalDiagnostics {
367
+ mergeStrategy: UnifiedMemoryMergeStrategy;
368
+ candidateLimit: number;
369
+ rrf?: {
370
+ k: number;
371
+ weights: UnifiedMemoryRrfWeights;
372
+ };
373
+ backend?: string;
374
+ semanticDegradedReason?: string;
375
+ /** Wall-clock timings for the retrieval stages, in milliseconds. */
376
+ timings?: {
377
+ totalMs: number;
378
+ memorySourceMs: number;
379
+ semanticMs: number;
380
+ lexicalMs: number;
381
+ hybridMs: number;
382
+ plannerMs: number;
383
+ fusionMs: number;
384
+ rerankerMs: number;
385
+ neighborMs?: number;
386
+ };
387
+ candidateCounts?: {
388
+ semantic: number;
389
+ lexical: number;
390
+ planner?: number;
391
+ hybrid: number;
392
+ entity: number;
393
+ fused: number;
394
+ expanded?: number;
395
+ final: number;
396
+ };
397
+ channels: {
398
+ semantic: UnifiedMemorySearchResult[];
399
+ lexical: UnifiedMemorySearchResult[];
400
+ planner?: UnifiedMemorySearchResult[];
401
+ hybrid?: UnifiedMemorySearchResult[];
402
+ entity?: UnifiedMemorySearchResult[];
403
+ };
404
+ /** Results after channel/source fusion and before an optional host reranker. */
405
+ fusedBeforeRerank: UnifiedMemorySearchResult[];
406
+ /** Candidates after optional session-neighbor expansion and before reranking. */
407
+ expandedBeforeRerank?: UnifiedMemorySearchResult[];
408
+ sessionNeighborExpansion?: {
409
+ enabled: boolean;
410
+ mode?: SessionNeighborExpansionMode;
411
+ seedCount: number;
412
+ neighborCount: number;
413
+ sessions: number;
414
+ latencyMs: number;
415
+ };
416
+ reranker?: {
417
+ enabled: boolean;
418
+ provider?: string;
419
+ model?: string;
420
+ inputCount: number;
421
+ outputCount: number;
422
+ latencyMs: number;
423
+ orderChanged: boolean;
424
+ };
425
+ /** Final results after optional reranking and Top-K truncation. */
426
+ final: UnifiedMemorySearchResult[];
427
+ }
428
+ /**
429
+ * Cross-source retrieval surface for the unified search. Mirrors
430
+ * `UnifiedMemorySearchSource` but is exposed under a more discoverable
431
+ * name.
432
+ */
433
+ type SearchSource = UnifiedMemorySearchSource;
434
+ /**
435
+ * Per-tier evidence bucket consulted by `reflect`-style synthesis.
436
+ * Includes the `summary` tier that the read-only sources do not surface.
437
+ */
438
+ type SearchTier = "summary" | "raw" | "insight" | "knowledge" | "workspace";
439
+ interface SearchInput {
440
+ userId: string;
441
+ query: string;
442
+ /** Cross-source retrieval surface (defaults to all three sources). */
443
+ sources?: ReadonlyArray<SearchSource>;
444
+ /**
445
+ * Per-tier subset — when omitted, defaults to all four tiers.
446
+ * Forwarded to the gather step so the read pipeline can scope the
447
+ * evidence. Most callers pass nothing; `synthesize` callers can
448
+ * narrow to `["raw"]` or `["summary", "raw"]` for cheaper synthesis.
449
+ */
450
+ tiers?: ReadonlyArray<SearchTier>;
451
+ /**
452
+ * Opt-in LLM synthesis. When `true`, runs `reasoning.complete` after
453
+ * gathering evidence and returns `answer` in the output. When
454
+ * omitted / `false`, no LLM call is made (matches today's
455
+ * `searchUnifiedMemory`). The `responseSchema` form lets the LLM
456
+ * return JSON; the SDK extracts `{ answer: string }` from the
457
+ * payload and surfaces the rest via warnings.
458
+ */
459
+ synthesize?: boolean | {
460
+ responseSchema?: Record<string, unknown>;
461
+ };
462
+ limit?: number;
463
+ /** Optional diagnostic candidate budget; defaults to max(limit, limit * 4). */
464
+ candidateLimit?: number;
465
+ threshold?: number;
466
+ botIds?: string[];
467
+ documentIds?: string[];
468
+ dateFrom?: string;
469
+ dateTo?: string;
470
+ asOf?: string;
471
+ peerFilter?: ReadonlyArray<Peer>;
472
+ authToken?: string;
473
+ factTypes?: FactType[];
474
+ mergeStrategy?: UnifiedMemoryMergeStrategy;
475
+ rrfWeights?: UnifiedMemoryRrfWeights;
476
+ rrfK?: number;
477
+ sessionNeighborExpansion?: SessionNeighborExpansionOptions;
478
+ reasoningStrategy?: UnifiedMemoryReasoningStrategy;
479
+ /**
480
+ * Backward-compat pass-through for callers that previously passed
481
+ * `responseSchema` at the top level. When set, it overrides
482
+ * `synthesize.responseSchema`.
483
+ */
484
+ responseSchema?: Record<string, unknown>;
485
+ /**
486
+ * Backward-compat pass-through for callers that previously passed
487
+ * `includeArchivedInsights` to the read-only search.
488
+ */
489
+ includeArchivedInsights?: boolean;
490
+ /** Include pre-fusion retrieval candidates; intended for evaluation/debugging. */
491
+ includeRetrievalDiagnostics?: boolean;
492
+ /**
493
+ * Include messages that have been soft-deprecated
494
+ * (`raw_messages.deprecated_at IS NOT NULL`). Default `false` keeps
495
+ * `current-truth` retrievals clean: a row that was marked superseded
496
+ * via `deprecateMessages` (or the `opencontext deprecate` CLI) is
497
+ * excluded from results. Set to `true` for audits and historical
498
+ * exploration.
499
+ */
500
+ includeDeprecated?: boolean;
501
+ }
502
+ /** Host-owned provenance. Null means unknown, never inferred from ranking or index readiness. */
503
+ interface SearchEvidenceProvenance {
504
+ citation: Citation | null;
505
+ origin: string | null;
506
+ businessReference: string | null;
507
+ version: string | number | null;
508
+ contentHash: string | null;
509
+ scope: readonly MemoryApplicabilityContext[] | null;
510
+ /** Empty means known to have no derivation; null means unknown. */
511
+ derivedFrom: readonly string[] | null;
512
+ confirmation: "confirmed" | "unconfirmed" | null;
513
+ freshness: {
514
+ sourceUpdatedAt: string | null;
515
+ indexedAt: string | null;
516
+ } | null;
517
+ }
518
+ interface SearchEvidence {
519
+ id: string;
520
+ source: SearchSource | SearchTier;
521
+ snippet: string;
522
+ score: number;
523
+ provenance: SearchEvidenceProvenance;
524
+ timestamp?: number;
525
+ /**
526
+ * Forwarded from the underlying hit's `metadata`. Surfaces per-fact
527
+ * provenance (`metadata.source`, `metadata.factType`, etc.) to the
528
+ * synthesis prompt and the CLI's `--context-only` / `--json` output.
529
+ */
530
+ metadata?: Record<string, unknown>;
531
+ }
532
+ interface SearchOutput {
533
+ query: string;
534
+ sources: ReadonlyArray<SearchSource>;
535
+ results: UnifiedMemorySearchResult[];
536
+ /** Same hits as `results`, with the source labelled for synthesis callers. */
537
+ evidence: SearchEvidence[];
538
+ count: number;
539
+ warnings: UnifiedMemorySearchWarning[];
540
+ sourceStates: SearchSourceState[];
541
+ retrievalDiagnostics?: UnifiedMemoryRetrievalDiagnostics;
542
+ reasoning?: UnifiedMemoryReasoningInfo;
543
+ /** Only present when `synthesize` was truthy. */
544
+ answer?: string;
545
+ }
546
+ declare function normalizeUnifiedMemorySearchSources(sources: unknown): UnifiedMemorySearchSource[];
547
+ declare function clampUnifiedMemorySearchLimit(limit: unknown): number;
548
+ declare function clampUnifiedMemorySearchThreshold(threshold: unknown): number;
549
+ declare function mergeUnifiedMemorySearchResults(results: UnifiedMemorySearchResult[], limit: number, options?: {
550
+ strategy?: UnifiedMemoryMergeStrategy;
551
+ rankedLists?: UnifiedMemoryRankedList[];
552
+ rrfK?: number;
553
+ rrfWeights?: UnifiedMemoryRrfWeights;
554
+ }): UnifiedMemorySearchResult[];
555
+ declare function normalizeUnifiedMemoryMergeStrategy(value: unknown): UnifiedMemoryMergeStrategy;
556
+ declare function normalizeUnifiedMemoryReasoningStrategy(value: unknown, defaultStrategy?: UnifiedMemoryReasoningStrategy): UnifiedMemoryReasoningStrategy;
557
+ /**
558
+ * Reciprocal Rank Fusion across multiple ranked lists. Each list contributes
559
+ * `1 / (k + rank + 1)` to a hit's accumulated score. Hits are de-duplicated by
560
+ * `(type, id)` so the same message surfaced by both lexical and semantic
561
+ * channels naturally outranks single-channel matches. Tie-break is
562
+ * deterministic: descending RRF score, then `(type, id)` lexical.
563
+ */
564
+ declare function mergeUnifiedMemorySearchResultsRrf(lists: UnifiedMemoryRankedList[], limit: number, k?: number, weights?: UnifiedMemoryRrfWeights): UnifiedMemorySearchResult[];
565
+ declare function toKnowledgeResult(result: {
566
+ chunkId: string;
567
+ documentId: string;
568
+ documentName: string;
569
+ content: string;
570
+ similarity: number;
571
+ chunkIndex: number;
572
+ }): UnifiedMemorySearchResult;
573
+ declare function toMemoryResult(result: {
574
+ id: string;
575
+ content: string;
576
+ similarity: number;
577
+ metadata: Record<string, unknown>;
578
+ }): UnifiedMemorySearchResult;
579
+ /**
580
+ * Build the `signals` breakdown for a single hit from the per-channel
581
+ * ranked lists it appeared in. Only the highest-ranked (i.e. first)
582
+ * appearance of `(type, id)` per channel contributes — duplicate hits
583
+ * later in the same list are ignored so the breakdown stays
584
+ * deterministic.
585
+ *
586
+ * Used by `mergeAcrossSources` to attach `signals` to every emitted
587
+ * `UnifiedMemorySearchResult`. Pure; no I/O.
588
+ */
589
+ declare function materializeSignals(lists: ReadonlyArray<{
590
+ name: string;
591
+ hits: UnifiedMemorySearchResult[];
592
+ }>, hit: UnifiedMemorySearchResult): HitSignals | undefined;
593
+ /**
594
+ * Map a `UnifiedMemoryRankedList.name` to a `HitChannel`. Unknown names
595
+ * (e.g. "insights", "knowledge", "summary") are not channels — they
596
+ * surface their similarity through the merge's `similarity` field and
597
+ * are simply skipped here so callers can still see which channel set
598
+ * the hit came from.
599
+ */
600
+ declare function listNameToChannel(name: string): HitChannel | undefined;
601
+ declare function isRawMemorySemanticResult(result: unknown): result is {
602
+ id: string;
603
+ content: string;
604
+ similarity: number;
605
+ metadata: Record<string, unknown>;
606
+ };
607
+
608
+ interface EvidenceSelector {
609
+ select(input: {
610
+ query: string;
611
+ hits: UnifiedMemorySearchResult[];
612
+ }): Promise<{
613
+ hits: UnifiedMemorySearchResult[];
614
+ warnings: UnifiedMemorySearchWarning[];
615
+ }>;
616
+ }
617
+ interface EvidenceSelectorOptions {
618
+ complete: (prompt: string) => Promise<string>;
619
+ /** Serialized source character budget; oversized hits remain intact. */
620
+ maxSourceCharacters?: number;
621
+ }
622
+ /** Extractive selection AFTER ranking. No writes, invented text, new sources,
623
+ * changed ranking, or reranker-input changes. Disabled unless explicitly wired. */
624
+ declare function createExtractiveEvidenceSelector(options: EvidenceSelectorOptions): EvidenceSelector;
625
+
626
+ export { normalizeUnifiedMemorySearchSources as A, toKnowledgeResult as B, toMemoryResult as C, type EvidenceSelectorOptions as D, type EvidenceSelector as E, type IterativeRecallCompletionOptions as F, type IterativeRecallExecutor as G, type HitChannel as H, type IterativeRecallCandidate as I, type IterativeRecallPlanner as J, type IterativeRecallPlannerOptions as K, type IterativeRecallResult as L, type IterativeRecallSearchRequest as M, type IterativeRecallSearchResult as N, type IterativeRecallStats as O, type UnifiedMemoryReasoningInfo as P, createExtractiveEvidenceSelector as Q, createIdentityIterativePlanner as R, type SearchTier as S, createIterativeRecallPlanner as T, type UnifiedMemorySearchWarning as U, type SearchEvidence as a, type SearchInput as b, type SearchOutput as c, type UnifiedMemorySearchInput as d, type UnifiedMemorySearchOutput as e, type UnifiedMemorySearchResult as f, type HitSignals as g, type SearchEvidenceProvenance as h, type SearchSource as i, type SearchSourceState as j, type SessionNeighborExpansionOptions as k, type UnifiedMemoryMergeStrategy as l, type UnifiedMemoryRankedList as m, type UnifiedMemoryReasoningStrategy as n, type UnifiedMemoryRrfWeights as o, type UnifiedMemorySearchSource as p, clampUnifiedMemorySearchLimit as q, clampUnifiedMemorySearchThreshold as r, deriveLexicalKeywords as s, isRawMemorySemanticResult as t, listNameToChannel as u, materializeSignals as v, mergeUnifiedMemorySearchResults as w, mergeUnifiedMemorySearchResultsRrf as x, normalizeUnifiedMemoryMergeStrategy as y, normalizeUnifiedMemoryReasoningStrategy as z };
package/dist/http.d.ts CHANGED
@@ -1,6 +1,7 @@
1
- import { M as MemoryStoreConfig } from './config-BuWr-nvt.js';
1
+ import { M as MemoryStoreConfig } from './config-Cnk2b7fH.js';
2
2
  import '@melandlabs/memory-consolidation';
3
3
  import '@melandlabs/contracts/peer';
4
+ import './evidence-selector-C_ZUQsio.js';
4
5
  import '@melandlabs/contracts';
5
6
  import '@melandlabs/contracts/derived-fact';
6
7
  import '@melandlabs/contracts/entity-edge';