agenr 2.0.0 → 2.1.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.
@@ -1,109 +1,5 @@
1
- import { E as Expiry, a as EntryType, R as RecallInput, b as RecallPorts, c as RecallOutput } from '../../ports-D2NOK2gR.js';
2
- export { d as EntryFilters, F as FtsCandidate, H as HistoricalPredecessorLookupParams, e as RecallCandidateEntry, f as RecallRankingProfile, V as VectorCandidate } from '../../ports-D2NOK2gR.js';
3
-
4
- /**
5
- * Backend-agnostic lexical search tier used to plan adapter queries.
6
- */
7
- type LexicalSearchTier = {
8
- tier: "exact";
9
- text: string;
10
- } | {
11
- tier: "all_tokens" | "any_tokens";
12
- tokens: string[];
13
- };
14
- /**
15
- * Tokenize free-form text into normalized lexical terms.
16
- *
17
- * @param text - Source text to tokenize.
18
- * @returns Lowercased non-stopword tokens.
19
- */
20
- declare function tokenize(text: string): string[];
21
- /**
22
- * Build a lexical search plan from raw user text.
23
- *
24
- * @param text - Raw query text.
25
- * @returns Exact, all-token, and any-token tiers in cascade order.
26
- */
27
- declare function buildLexicalPlan(text: string): LexicalSearchTier[];
28
- /**
29
- * Compute the lexical overlap score between a query and an entry.
30
- *
31
- * @param query - Raw recall query text.
32
- * @param subject - Entry subject text.
33
- * @param content - Entry content text.
34
- * @returns Normalized lexical overlap signal in the 0-1 range.
35
- */
36
- declare function computeLexicalScore(query: string, subject: string, content: string): number;
37
-
38
- /**
39
- * Score breakdown returned for a ranked recall candidate.
40
- */
41
- interface CandidateScore {
42
- score: number;
43
- scores: {
44
- relevance: number;
45
- vector: number;
46
- lexical: number;
47
- recency: number;
48
- importance: number;
49
- };
50
- }
51
- /**
52
- * Compute the tier-aware half-life recency score for an entry.
53
- *
54
- * @param createdAt - Entry creation timestamp.
55
- * @param expiry - Entry durability tier.
56
- * @param now - Reference time for the age calculation.
57
- * @returns Normalized recency score in the 0-1 range.
58
- */
59
- declare function recencyScore(createdAt: Date | string, expiry: Expiry, now?: Date): number;
60
- /**
61
- * Compute a gaussian recency score centered on an around-date query anchor.
62
- *
63
- * @param createdAt - Entry creation timestamp.
64
- * @param aroundDate - Temporal anchor inferred or provided by the user.
65
- * @param radiusDays - Standard deviation-like radius in days.
66
- * @returns Normalized temporal proximity score in the 0-1 range.
67
- */
68
- declare function gaussianRecency(createdAt: Date | string, aroundDate: Date, radiusDays: number): number;
69
- /**
70
- * Normalize an importance value from the 1-10 domain into the 0.4-1.0 score range.
71
- *
72
- * @param importance - Raw entry importance.
73
- * @returns Normalized importance score in the 0-1 range.
74
- */
75
- declare function importanceScore(importance: number): number;
76
- /**
77
- * Combine vector and lexical relevance signals into a single query-relevance score.
78
- *
79
- * @param vectorSim - Vector similarity score.
80
- * @param lexical - Lexical overlap score.
81
- * @returns Combined relevance score in the 0-1 range.
82
- */
83
- declare function combinedRelevance(vectorSim: number, lexical: number): number;
84
- /**
85
- * Compute the final recall score and its component breakdown for a candidate.
86
- *
87
- * @param params - Candidate signal inputs.
88
- * @returns Final score plus signal breakdown.
89
- */
90
- declare function scoreCandidate(params: {
91
- vectorSim: number;
92
- lexical: number;
93
- recency: number;
94
- importance: number;
95
- }): CandidateScore;
96
- /**
97
- * Compute cosine similarity between two numeric vectors.
98
- *
99
- * Non-finite coordinates are treated as zero and negative similarities are clamped
100
- * to zero because recall scoring only consumes a 0-1 signal.
101
- *
102
- * @param left - Left-hand vector.
103
- * @param right - Right-hand vector.
104
- * @returns Cosine similarity in the 0-1 range.
105
- */
106
- declare function cosineSimilarity(left: number[], right: number[]): number;
1
+ import { E as EntryType, C as CrossEncoderPort, a as Expiry, R as RecallInput, b as RecallPorts, c as RecallOutput } from '../../ports-Nj5nd2Ri.js';
2
+ export { D as DEFAULT_NEIGHBORHOOD_BUDGET, d as DEFAULT_SEEDED_RERANK_WEIGHT, e as DEFAULT_STRONG_SEED_SCORE_GAP, f as DEFAULT_STRONG_SEED_TOP_N, g as EntityAttributeKind, h as EntityAttributeQueryShape, i as EntryFilters, j as EntryNeighborhoodRequest, F as FtsCandidate, N as NeighborhoodFamily, k as RecallCandidateEntry, l as RecallRankingProfile, S as SeededRerankCandidate, m as SeededRerankOptions, V as VectorCandidate, s as seededRerank, n as selectStrongSeeds, o as sharesEntryLineage, p as sharesEpisodeLineage, q as sharesProcedureLineage } from '../../ports-Nj5nd2Ri.js';
107
3
 
108
4
  /**
109
5
  * Runtime slot-policy classes used by claim-centric read surfaces.
@@ -221,6 +117,84 @@ interface RecallClaimKeyTrace {
221
117
  /** Current-state trusted same-slot duplicates down-ranked for diversity. */
222
118
  redundancyPenalized: number;
223
119
  }
120
+ /**
121
+ * Reciprocal rank fusion facts observed during one recall execution.
122
+ */
123
+ interface RecallRrfTrace {
124
+ /** Whether RRF actually fused at least one non-empty channel. */
125
+ applied: boolean;
126
+ /** Number of non-empty channels supplied to the fusion helper. */
127
+ channelCount: number;
128
+ /** Effective rank constant `k` used for the fusion. */
129
+ rankConstant: number;
130
+ /** Number of unique candidates that received a fused RRF score. */
131
+ fusedCandidateCount: number;
132
+ /** Maximum normalized RRF score observed across fused candidates. */
133
+ maxFusedScore: number;
134
+ }
135
+ /**
136
+ * MMR diversification facts observed during one recall execution.
137
+ */
138
+ interface RecallMmrTrace {
139
+ /** Whether MMR actually reordered the shortlist for this execution. */
140
+ applied: boolean;
141
+ /** Effective lambda used when MMR ran, or the configured default when skipped. */
142
+ lambda: number;
143
+ /**
144
+ * Candidates that MMR identified as near duplicates and demoted below
145
+ * their input position. A zero count means no redundancy was observed.
146
+ */
147
+ droppedDuplicateCount: number;
148
+ /** Candidate IDs whose position shifted relative to the input order. */
149
+ reorderedIds: string[];
150
+ }
151
+ /**
152
+ * Cross-encoder rerank facts observed during one recall execution.
153
+ */
154
+ interface RecallCrossEncoderTrace {
155
+ /** Whether the cross-encoder rerank stage actually ran. */
156
+ applied: boolean;
157
+ /** Effective top-K size handed to the cross-encoder. */
158
+ k: number;
159
+ /** Blend weight between `crossEncoderScore` and the prior composite score. */
160
+ alpha: number;
161
+ /** Wall-clock latency of the cross-encoder rerank stage in milliseconds. */
162
+ latencyMs: number;
163
+ /** Stable degraded-mode reason when the rerank was skipped or failed. */
164
+ degradedReason?: RecallCrossEncoderDegradedReason;
165
+ /** Candidate IDs whose composite score was reshaped by the rerank. */
166
+ rescoredIds: string[];
167
+ }
168
+ /**
169
+ * Stable reasons a cross-encoder rerank may skip or degrade.
170
+ *
171
+ * - `not_configured`: no `CrossEncoderPort` was wired on the ports bundle.
172
+ * - `disabled`: the caller's `rankingPolicy.crossEncoder` is `"disabled"`.
173
+ * - `no_candidates`: the input shortlist was empty after earlier stages.
174
+ * - `provider_error`: the port threw or returned malformed data.
175
+ */
176
+ type RecallCrossEncoderDegradedReason = "not_configured" | "disabled" | "no_candidates" | "provider_error";
177
+ /**
178
+ * Neighborhood expansion and seeded rerank facts observed during one recall execution.
179
+ */
180
+ interface RecallNeighborhoodTrace {
181
+ /** Whether the adapter was asked to expand a lineage neighborhood. */
182
+ expansionRequested: boolean;
183
+ /** Whether the adapter exposes the expandNeighborhood port at all. */
184
+ expansionAvailable: boolean;
185
+ /** Family kinds requested from the adapter for this execution. */
186
+ familiesRequested: string[];
187
+ /** Whether retired rows were allowed into the expansion. */
188
+ includeRetired: boolean;
189
+ /** Seed entry IDs chosen before the adapter expansion. */
190
+ seedIds: string[];
191
+ /** Unique candidates returned by the adapter expansion. */
192
+ expansionCandidates: number;
193
+ /** Strong seed IDs chosen for seededRerank. */
194
+ strongSeedIds: string[];
195
+ /** Candidate IDs that received a seededRerank boost. */
196
+ rerankBoostedIds: string[];
197
+ }
224
198
  /**
225
199
  * Small typed execution summary emitted by the recall core.
226
200
  */
@@ -233,6 +207,14 @@ interface RecallExecutionTraceSummary {
233
207
  candidateCounts: RecallCoreCandidateCountsTrace;
234
208
  /** Claim-key lineage and diversity shaping facts observed during ranking. */
235
209
  claimKey: RecallClaimKeyTrace;
210
+ /** Reciprocal rank fusion facts observed during ranking. */
211
+ rrf: RecallRrfTrace;
212
+ /** Neighborhood expansion and seeded rerank facts observed during ranking. */
213
+ neighborhood: RecallNeighborhoodTrace;
214
+ /** MMR diversification facts observed during ranking. */
215
+ mmr: RecallMmrTrace;
216
+ /** Cross-encoder rerank facts observed during ranking. */
217
+ crossEncoder: RecallCrossEncoderTrace;
236
218
  /** Whether recall had to degrade into lexical-only ranking. */
237
219
  degraded: RecallDegradedTrace;
238
220
  /** Core-only timings observed inside the ranking flow. */
@@ -249,6 +231,81 @@ interface RecallTraceSink {
249
231
  */
250
232
  reportSummary(summary: RecallExecutionTraceSummary): void;
251
233
  }
234
+ /**
235
+ * Tunable ranking policy applied to one recall execution.
236
+ *
237
+ * Every field is optional so callers can opt into individual stages
238
+ * without having to restate the full policy. Evals that A/B one stage
239
+ * at a time rely on the per-stage kill switches here.
240
+ */
241
+ interface RecallRankingPolicy {
242
+ /**
243
+ * Whether to apply reciprocal rank fusion across retrieval channels.
244
+ * Defaults to `"enabled"`. When set to `"disabled"`, the recall
245
+ * pipeline falls back to single-channel vector ordering (with a
246
+ * lexical fallback when vectors are unavailable) so evals can A/B
247
+ * the fusion stage without stripping channels from the pipeline.
248
+ */
249
+ rrf?: "enabled" | "disabled";
250
+ /**
251
+ * Optional override for the RRF rank constant `k`. Higher values
252
+ * flatten the contribution of the top ranks across channels; lower
253
+ * values sharpen them. Defaults to the canonical Cormack et al.
254
+ * constant `60`.
255
+ */
256
+ rrfRankConstant?: number;
257
+ /**
258
+ * Optional override for the small-pool RRF rank constant `k` that is
259
+ * used when the fused candidate pool is at or below
260
+ * `SMALL_POOL_RRF_POOL_SIZE`. Defaults to
261
+ * `DEFAULT_RRF_SMALL_POOL_RANK_CONSTANT` (`8`), which sharpens the
262
+ * rank-1 versus rank-2 gap enough to keep small-magnitude recency and
263
+ * importance differences from flipping a clear vector leader. Set to
264
+ * the same value as `rrfRankConstant` to disable the sharpening.
265
+ */
266
+ rrfSmallPoolRankConstant?: number;
267
+ /**
268
+ * Whether to apply the neighborhood expansion plus seeded rerank
269
+ * stage. Defaults to `"enabled"`. When set to `"disabled"`, the
270
+ * pipeline skips both the adapter-scoped `expandNeighborhood()` call
271
+ * and the `seededRerank()` pass so evals can isolate fusion from
272
+ * lineage-aware rerank effects.
273
+ */
274
+ neighborhood?: "enabled" | "disabled";
275
+ /**
276
+ * Whether to apply the MMR diversification stage. Defaults to
277
+ * `"enabled"` so the default pipeline gets diversity out of the box.
278
+ */
279
+ mmr?: "enabled" | "disabled";
280
+ /** Effective MMR lambda in the inclusive 0-1 range. */
281
+ mmrLambda?: number;
282
+ /**
283
+ * Minimum candidate-pool size required for MMR to run. Defaults to
284
+ * the core `DEFAULT_MMR_MIN_POOL_SIZE`. Set to `0` to disable the
285
+ * small-pool gate entirely so MMR runs on every non-empty shortlist
286
+ * (the pre-phase-4 behavior).
287
+ */
288
+ mmrMinPoolSize?: number;
289
+ /**
290
+ * Whether to apply the cross-encoder rerank stage when a
291
+ * `CrossEncoderPort` is wired. Defaults to `"enabled"`; evals can pass
292
+ * `"disabled"` to A/B the stage without unwiring the port.
293
+ */
294
+ crossEncoder?: "enabled" | "disabled";
295
+ /**
296
+ * Optional top-K override for the cross-encoder shortlist. The stage
297
+ * reranks only the first K candidates after claim-key shaping and MMR
298
+ * diversification, so smaller values keep provider cost predictable.
299
+ */
300
+ crossEncoderTopK?: number;
301
+ /**
302
+ * Blend weight for the cross-encoder score against the prior composite
303
+ * score. Final relevance becomes
304
+ * `alpha * crossEncoderScore + (1 - alpha) * compositeScore`.
305
+ * Clamped into the inclusive 0-1 range; defaults to `0.6`.
306
+ */
307
+ crossEncoderAlpha?: number;
308
+ }
252
309
  /**
253
310
  * Optional execution controls for one recall call.
254
311
  */
@@ -257,6 +314,8 @@ interface RecallExecutionOptions {
257
314
  trace?: RecallTraceSink;
258
315
  /** Optional runtime slot-policy overrides used during claim-aware ranking. */
259
316
  slotPolicyConfig?: ClaimSlotPolicyConfig;
317
+ /** Optional ranking policy overrides, including MMR toggles. */
318
+ rankingPolicy?: RecallRankingPolicy;
260
319
  }
261
320
  /**
262
321
  * Returns the shared null-object recall trace sink used when no observation is requested.
@@ -265,6 +324,375 @@ interface RecallExecutionOptions {
265
324
  */
266
325
  declare function createNoopRecallTraceSink(): RecallTraceSink;
267
326
 
327
+ /**
328
+ * Cross-encoder rerank orchestration helper.
329
+ *
330
+ * - Keeps core recall free of adapter concerns: the helper only needs a
331
+ * `CrossEncoderPort` contract defined in `src/core/ports.ts`.
332
+ * - Reranks only the top-K shortlist supplied by the caller. Candidates
333
+ * past the shortlist keep their input order and composite score.
334
+ * - Combines the provider score with the prior composite score through a
335
+ * linear blend: `alpha * crossEncoderScore + (1 - alpha) * prior`, so
336
+ * MMR, claim-key shaping, and seeded rerank still participate in the
337
+ * final ordering.
338
+ * - Fails closed: any thrown or malformed adapter response short-circuits
339
+ * into a pass-through result with a trace-visible degraded reason.
340
+ *
341
+ * The helper is pure and deterministic given a deterministic port: it
342
+ * does not mutate callers' inputs, does not read ambient state, and does
343
+ * not log.
344
+ */
345
+ /**
346
+ * Default top-K shortlist size. Bounded small so cross-encoder latency
347
+ * and provider cost stay predictable even at full recall fan-out.
348
+ */
349
+ declare const DEFAULT_CROSS_ENCODER_TOP_K = 10;
350
+ /**
351
+ * Default blend weight between the cross-encoder score and the prior
352
+ * composite score. Chosen to let the cross-encoder dominate while still
353
+ * letting prior shaping stages shape the final order.
354
+ */
355
+ declare const DEFAULT_CROSS_ENCODER_ALPHA = 0.6;
356
+ /**
357
+ * One input candidate for the cross-encoder rerank helper.
358
+ *
359
+ * @template TCandidate - Caller-provided candidate shape passed through untouched.
360
+ */
361
+ interface CrossEncoderRerankCandidate<TCandidate> {
362
+ /** Stable identifier used to correlate provider scores back to the candidate. */
363
+ id: string;
364
+ /** Caller-provided free-form text fed into the cross-encoder. */
365
+ text: string;
366
+ /** Prior composite score retained for the linear blend. */
367
+ score: number;
368
+ /** Original candidate reference returned to the caller. */
369
+ candidate: TCandidate;
370
+ }
371
+ /**
372
+ * Call options for `applyCrossEncoderRerank`.
373
+ */
374
+ interface CrossEncoderRerankOptions<TCandidate> {
375
+ /** Natural-language recall query text. */
376
+ query: string;
377
+ /** Candidates in their caller-preferred order. */
378
+ candidates: readonly CrossEncoderRerankCandidate<TCandidate>[];
379
+ /** Cross-encoder port when available. When undefined, the stage skips. */
380
+ port: CrossEncoderPort | undefined;
381
+ /** Whether the caller explicitly disabled the stage. */
382
+ disabled?: boolean;
383
+ /** Optional top-K override. Clamped into `[1, candidates.length]`. */
384
+ topK?: number;
385
+ /** Optional blend weight override. Clamped into `[0, 1]`. */
386
+ alpha?: number;
387
+ }
388
+ /**
389
+ * Result returned by the cross-encoder rerank helper.
390
+ *
391
+ * @template TCandidate - Caller-provided candidate shape passed through untouched.
392
+ */
393
+ interface CrossEncoderRerankResult<TCandidate> {
394
+ /** Whether the stage actually invoked the cross-encoder port. */
395
+ applied: boolean;
396
+ /** Effective top-K size used by the stage. */
397
+ k: number;
398
+ /** Effective blend weight used by the stage. */
399
+ alpha: number;
400
+ /** Wall-clock latency of the stage in milliseconds. */
401
+ latencyMs: number;
402
+ /** Stable degraded-mode reason when the stage did not rerank. */
403
+ degradedReason?: RecallCrossEncoderDegradedReason;
404
+ /**
405
+ * Candidates in their reranked order. Candidates past the shortlist
406
+ * keep their input order and their prior score.
407
+ */
408
+ candidates: ReadonlyArray<{
409
+ /** Original candidate reference. */
410
+ candidate: TCandidate;
411
+ /** Final composite score after blending. */
412
+ score: number;
413
+ /** Raw cross-encoder score when the candidate was reranked. */
414
+ crossEncoderScore?: number;
415
+ }>;
416
+ /** Candidate IDs whose composite score was actually reshaped. */
417
+ rescoredIds: string[];
418
+ }
419
+ /**
420
+ * Apply a cross-encoder rerank over the top-K shortlist of candidates.
421
+ *
422
+ * The helper is pure: no IO happens outside the provided
423
+ * `CrossEncoderPort`. The caller owns reading the resulting candidate
424
+ * order and merging the returned scores back into its ranking model.
425
+ *
426
+ * @template TCandidate - Candidate payload shape returned by the caller.
427
+ * @param options - Orchestration options including the port and limits.
428
+ * @returns Reranked candidates with trace-visible facts for diagnostics.
429
+ */
430
+ declare function applyCrossEncoderRerank<TCandidate>(options: CrossEncoderRerankOptions<TCandidate>): Promise<CrossEncoderRerankResult<TCandidate>>;
431
+
432
+ /**
433
+ * Reciprocal rank fusion (RRF) helpers used by entry, episode, and procedure
434
+ * recall to combine multiple retrieval channels into a single relevance signal.
435
+ *
436
+ * - A larger default constant (`k = 60`) matching the canonical Cormack et al.
437
+ * RRF paper so early ranks do not dominate the fused score.
438
+ * - A post-pass that normalizes scores into the `0-1` range so downstream
439
+ * composite scoring can mix fused relevance with recency and importance.
440
+ */
441
+ /**
442
+ * Default RRF rank constant. The Cormack et al. RRF paper uses `k = 60` as a
443
+ * conservative anchor that flattens the contribution of the top handful of
444
+ * ranks without letting any single channel dominate.
445
+ */
446
+ declare const DEFAULT_RRF_RANK_CONSTANT = 60;
447
+ /**
448
+ * One input channel of ranked identifiers.
449
+ *
450
+ * Each channel should be an ordered list from most-relevant to least-relevant,
451
+ * as produced by vector search, FTS, temporal match, or any other retrieval
452
+ * path. Channels may overlap or be disjoint and ties inside a channel are
453
+ * resolved by the caller before passing them in.
454
+ */
455
+ type RrfChannel = readonly string[];
456
+ /**
457
+ * Fuse one or more ranked channels into a single normalized score map.
458
+ *
459
+ * An identifier's unnormalized RRF score is the sum across channels of
460
+ * `1 / (rankIndex + k)`, where `rankIndex` is the candidate's zero-based
461
+ * position inside that channel. Candidates missing from a channel contribute
462
+ * nothing from that channel.
463
+ *
464
+ * The result is then normalized by the theoretical maximum for the supplied
465
+ * channel count so that an identifier appearing at the top of every supplied
466
+ * channel maps to `1.0` and a single appearance in one of `N` channels maps
467
+ * to roughly `1/N` at the very top rank. Empty channels are ignored and do
468
+ * not count toward the normalization denominator.
469
+ *
470
+ * The helper is deliberately pure and side-effect free so the composite
471
+ * recall pipeline can invoke it for entries, episodes, and procedures
472
+ * without depending on any adapter.
473
+ *
474
+ * @param channels - Ordered rank lists from each retrieval channel.
475
+ * @param rankConstant - Optional override for the RRF rank constant `k`.
476
+ * @returns Map from identifier to normalized RRF score in the `0-1` range.
477
+ */
478
+ declare function rrfFuse(channels: readonly RrfChannel[], rankConstant?: number): Map<string, number>;
479
+ /**
480
+ * Convenience wrapper for the common two-channel case used by entry and
481
+ * procedure recall.
482
+ *
483
+ * @param vectorRanks - Vector retrieval channel, most-relevant first.
484
+ * @param lexicalRanks - Lexical retrieval channel, most-relevant first.
485
+ * @param rankConstant - Optional override for the RRF rank constant `k`.
486
+ * @returns Map from identifier to normalized RRF score.
487
+ */
488
+ declare function rrfFuseVectorLexical(vectorRanks: readonly string[], lexicalRanks: readonly string[], rankConstant?: number): Map<string, number>;
489
+
490
+ /**
491
+ * Backend-agnostic lexical search tier used to plan adapter queries.
492
+ */
493
+ type LexicalSearchTier = {
494
+ tier: "exact";
495
+ text: string;
496
+ } | {
497
+ tier: "all_tokens" | "any_tokens";
498
+ tokens: string[];
499
+ };
500
+ /**
501
+ * Tokenize free-form text into normalized lexical terms.
502
+ *
503
+ * @param text - Source text to tokenize.
504
+ * @returns Lowercased non-stopword tokens.
505
+ */
506
+ declare function tokenize(text: string): string[];
507
+ /**
508
+ * Build a lexical search plan from raw user text.
509
+ *
510
+ * @param text - Raw query text.
511
+ * @returns Exact, all-token, and any-token tiers in cascade order.
512
+ */
513
+ declare function buildLexicalPlan(text: string): LexicalSearchTier[];
514
+ /**
515
+ * Compute the lexical overlap score between a query and an entry.
516
+ *
517
+ * @param query - Raw recall query text.
518
+ * @param subject - Entry subject text.
519
+ * @param content - Entry content text.
520
+ * @returns Normalized lexical overlap signal in the 0-1 range.
521
+ */
522
+ declare function computeLexicalScore(query: string, subject: string, content: string): number;
523
+
524
+ /**
525
+ * Maximal Marginal Relevance (MMR) diversification helper used by entry,
526
+ * episode, and procedure recall.
527
+ *
528
+ * - Compute pairwise cosine similarity between every candidate embedding.
529
+ * - For each candidate, combine relevance (cosine similarity to the query)
530
+ * with a diversity penalty equal to the maximum pairwise similarity to
531
+ * any other candidate.
532
+ * - Sort candidates by the combined MMR score.
533
+ *
534
+ * Unlike the classical iterative MMR, the one-shot variant does not need
535
+ * the caller to track an already-selected set and produces a stable
536
+ * re-ranking directly from the candidate list. It is a good fit for the
537
+ * small shortlists that arrive after RRF, claim-key shaping, and seeded
538
+ * lineage rerank.
539
+ */
540
+ /**
541
+ * Default MMR lambda chosen to favor relevance while still giving the
542
+ * diversity penalty room to demote near-duplicate candidates. Matches the
543
+ * value called out in the phase 3 plan.
544
+ */
545
+ declare const DEFAULT_MMR_LAMBDA = 0.7;
546
+ /**
547
+ * Threshold above which two candidate embeddings are treated as near
548
+ * duplicates for trace accounting. Only influences the
549
+ * `droppedDuplicateCount` counter; it does not affect the MMR ordering.
550
+ */
551
+ declare const NEAR_DUPLICATE_SIMILARITY = 0.95;
552
+ /**
553
+ * Input candidate consumed by the MMR helper.
554
+ */
555
+ interface MmrCandidate {
556
+ /** Stable identifier used to match candidates back to the caller's list. */
557
+ id: string;
558
+ /** Optional embedding vector. Candidates without embeddings skip MMR. */
559
+ embedding?: number[];
560
+ /**
561
+ * Optional caller-provided relevance signal in the 0-1 range. When
562
+ * supplied, MMR uses this value as the relevance term instead of the
563
+ * default `cos(queryVector, embedding)` fallback.
564
+ *
565
+ * Pipelines that have already performed shaping (historical lineage
566
+ * boosts, claim-key trust and redundancy penalties) should pass their
567
+ * shaped composite score so MMR respects that prior work instead of
568
+ * silently undoing it.
569
+ */
570
+ relevance?: number;
571
+ }
572
+ /**
573
+ * MMR reorder outcome returned to the caller.
574
+ */
575
+ interface MmrReorderResult {
576
+ /** Whether MMR actually ran instead of passing through the input order. */
577
+ applied: boolean;
578
+ /** Effective lambda that was used during ranking. */
579
+ lambda: number;
580
+ /** Reordered candidate IDs. Includes every input candidate exactly once. */
581
+ orderedIds: string[];
582
+ /**
583
+ * Number of candidates that MMR identified as near duplicates (max
584
+ * pairwise similarity with another candidate at or above
585
+ * `NEAR_DUPLICATE_SIMILARITY`) and pushed down in the final order.
586
+ */
587
+ droppedDuplicateCount: number;
588
+ /**
589
+ * IDs whose position changed from the input order as a result of MMR
590
+ * re-ranking. Empty when MMR was skipped.
591
+ */
592
+ reorderedIds: string[];
593
+ }
594
+ /**
595
+ * MMR call signature.
596
+ */
597
+ interface MmrOptions {
598
+ /** Query embedding used as the relevance signal. */
599
+ queryVector: number[];
600
+ /** Candidates in their caller-preferred order. */
601
+ candidates: readonly MmrCandidate[];
602
+ /** Optional lambda override. Clamped into `[0, 1]`. */
603
+ lambda?: number;
604
+ /** Optional final result limit applied after MMR ordering. */
605
+ limit?: number;
606
+ /**
607
+ * Optional minimum pool size required for MMR to run. Defaults to
608
+ * `DEFAULT_MMR_MIN_POOL_SIZE`. When `candidates.length` is at or below
609
+ * this size, MMR returns `applied: false` with the input order
610
+ * preserved. Set to `0` to disable the gate entirely.
611
+ */
612
+ minPoolSize?: number;
613
+ }
614
+ /**
615
+ * Reorder candidates by maximal marginal relevance.
616
+ *
617
+ * The helper is pure and does not mutate any input. Candidates without a
618
+ * usable embedding keep their input order after the embedded candidates,
619
+ * so missing-embedding fallbacks degrade to pass-through rather than
620
+ * crashing. When the query vector is empty or at most one candidate has
621
+ * an embedding, MMR is skipped and the input order is returned as-is.
622
+ *
623
+ * @param options - Query embedding plus candidate list and tuning knobs.
624
+ * @returns Reorder result with ordered IDs, MMR scores, and trace facts.
625
+ */
626
+ declare function maximalMarginalRelevance(options: MmrOptions): MmrReorderResult;
627
+
628
+ /**
629
+ * Score breakdown returned for a ranked recall candidate.
630
+ */
631
+ interface CandidateScore {
632
+ score: number;
633
+ scores: {
634
+ relevance: number;
635
+ vector: number;
636
+ lexical: number;
637
+ recency: number;
638
+ importance: number;
639
+ };
640
+ }
641
+ /**
642
+ * Compute the tier-aware half-life recency score for an entry.
643
+ *
644
+ * @param createdAt - Entry creation timestamp.
645
+ * @param expiry - Entry durability tier.
646
+ * @param now - Reference time for the age calculation.
647
+ * @returns Normalized recency score in the 0-1 range.
648
+ */
649
+ declare function recencyScore(createdAt: Date | string, expiry: Expiry, now?: Date): number;
650
+ /**
651
+ * Compute a gaussian recency score centered on an around-date query anchor.
652
+ *
653
+ * @param createdAt - Entry creation timestamp.
654
+ * @param aroundDate - Temporal anchor inferred or provided by the user.
655
+ * @param radiusDays - Standard deviation-like radius in days.
656
+ * @returns Normalized temporal proximity score in the 0-1 range.
657
+ */
658
+ declare function gaussianRecency(createdAt: Date | string, aroundDate: Date, radiusDays: number): number;
659
+ /**
660
+ * Normalize an importance value from the 1-10 domain into the 0.4-1.0 score range.
661
+ *
662
+ * @param importance - Raw entry importance.
663
+ * @returns Normalized importance score in the 0-1 range.
664
+ */
665
+ declare function importanceScore(importance: number): number;
666
+ /**
667
+ * Compute the final recall score and its component breakdown for a candidate.
668
+ *
669
+ * Callers are expected to supply the fused `relevance` signal through
670
+ * reciprocal rank fusion. `vectorSim` and `lexical` are kept on the score
671
+ * breakdown as evidence-only diagnostics so trace summaries can still
672
+ * display the raw retrieval signals without influencing the composite.
673
+ *
674
+ * @param params - Candidate signal inputs.
675
+ * @returns Final score plus signal breakdown.
676
+ */
677
+ declare function scoreCandidate(params: {
678
+ relevance: number;
679
+ vectorSim: number;
680
+ lexical: number;
681
+ recency: number;
682
+ importance: number;
683
+ }): CandidateScore;
684
+ /**
685
+ * Compute cosine similarity between two numeric vectors.
686
+ *
687
+ * Non-finite coordinates are treated as zero and negative similarities are clamped
688
+ * to zero because recall scoring only consumes a 0-1 signal.
689
+ *
690
+ * @param left - Left-hand vector.
691
+ * @param right - Right-hand vector.
692
+ * @returns Cosine similarity in the 0-1 range.
693
+ */
694
+ declare function cosineSimilarity(left: number[], right: number[]): number;
695
+
268
696
  /**
269
697
  * Execute the v1 recall pipeline against the provided adapter ports.
270
698
  *
@@ -292,4 +720,4 @@ declare function inferAroundDate(text: string, now?: Date): Date | null;
292
720
  */
293
721
  declare function parseRelativeDate(input: string, now?: Date): Date | null;
294
722
 
295
- export { type RecallClaimKeyTrace, type RecallCoreCandidateCountsTrace, type RecallCoreTimingTrace, type RecallDegradedReason, type RecallDegradedTrace, type RecallExecutionOptions, type RecallExecutionTraceSummary, type RecallFilterTrace, RecallInput, type RecallNoResultReason, RecallOutput, type RecallRankingTrace, type RecallTraceSink, buildLexicalPlan, combinedRelevance, computeLexicalScore, cosineSimilarity, createNoopRecallTraceSink, gaussianRecency, importanceScore, inferAroundDate, parseRelativeDate, recall, recencyScore, scoreCandidate, tokenize };
723
+ export { type CrossEncoderRerankCandidate, type CrossEncoderRerankOptions, type CrossEncoderRerankResult, DEFAULT_CROSS_ENCODER_ALPHA, DEFAULT_CROSS_ENCODER_TOP_K, DEFAULT_MMR_LAMBDA, DEFAULT_RRF_RANK_CONSTANT, type MmrCandidate, type MmrOptions, type MmrReorderResult, NEAR_DUPLICATE_SIMILARITY, type RecallClaimKeyTrace, type RecallCoreCandidateCountsTrace, type RecallCoreTimingTrace, type RecallCrossEncoderDegradedReason, type RecallCrossEncoderTrace, type RecallDegradedReason, type RecallDegradedTrace, type RecallExecutionOptions, type RecallExecutionTraceSummary, type RecallFilterTrace, RecallInput, type RecallMmrTrace, type RecallNeighborhoodTrace, type RecallNoResultReason, RecallOutput, type RecallRankingPolicy, type RecallRankingTrace, type RecallRrfTrace, type RecallTraceSink, type RrfChannel, applyCrossEncoderRerank, buildLexicalPlan, computeLexicalScore, cosineSimilarity, createNoopRecallTraceSink, gaussianRecency, importanceScore, inferAroundDate, maximalMarginalRelevance, parseRelativeDate, recall, recencyScore, rrfFuse, rrfFuseVectorLexical, scoreCandidate, tokenize };