@whyparabola/cabar 0.0.0-stage → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,255 @@
1
+ import { T as TokenCounter, S as Similarity, C as CabarSelectResult, a as CabarUnit, b as CabarSelectOptions } from './cabar-B-R-rHby.cjs';
2
+ export { A as ADAPTIVE_DEFAULTS, c as CHAR4_COUNTER, d as CabarDecision, e as CabarSelector, f as CabarStats, D as DEFAULTS, g as DEFAULT_RECENCY_DECAY_DAYS, h as cosineSimilarity, i as embeddingSimilarity, j as estimateTokens, r as recencyDecay } from './cabar-B-R-rHby.cjs';
3
+
4
+ /**
5
+ * PrismSelector: CABAR-2 multi-fidelity budgeted selection.
6
+ *
7
+ * Where CabarSelector answers "which units do we spend verbatim budget
8
+ * on, and summarize the rest", Prism answers the finer question "at WHICH
9
+ * FIDELITY do we render each unit". It solves a Multiple-Choice Knapsack
10
+ * Problem (MCKP): each unit offers a small menu of renderings — verbatim /
11
+ * extract / claims / mention — with distinct (cost, utility) points, and the
12
+ * selector greedily packs marginal-utility-per-token, allowed to UPGRADE an
13
+ * already-selected unit to a costlier fidelity when that upgrade is the densest
14
+ * feasible move. All-or-nothing (CABAR-1) is the special case of a two-point
15
+ * menu {verbatim, absent}; Prism dominates it under tight budgets by trading a
16
+ * verbatim unit for several cheaper-fidelity units that together carry more.
17
+ *
18
+ * Design invariants (same purity contract as CabarSelector):
19
+ * - PURE + deterministic: no LLM, no I/O, no Date.now — the caller supplies
20
+ * `now`; `claims` are pre-fetched onto the units by the caller.
21
+ * - Provider-agnostic token cost: the injectable `TokenCounter` defaults to
22
+ * the shared char/4 estimator (prod), and benchmarks inject a real BPE
23
+ * counter. Selection logic never assumes a specific vocabulary.
24
+ * - Cache-stable OUTPUT ORDERING: selected units are emitted by
25
+ * (timestamp asc, id asc), NOT by score, so the same units land in the same
26
+ * prompt slots turn-over-turn (KV-cache friendly).
27
+ * - β-hysteresis: an optional per-unit bonus for re-selecting a unit at
28
+ * ≥ its previous fidelity (keeps prompts stable turn-over-turn). Defaults
29
+ * to 0, a no-op.
30
+ * - Answer-lossless FLOOR: every unit at or above `tauFloor` that misses the
31
+ * budget still gets a mention line in the tail, so nothing recallable is
32
+ * silently dropped.
33
+ */
34
+
35
+ /** The four rendering fidelities, most→least faithful. */
36
+ type Fidelity = 'verbatim' | 'extract' | 'claims' | 'mention';
37
+ /** A retrieved unit plus its caller-pre-fetched claims (the CabarUnit + claims). */
38
+ interface PrismUnit {
39
+ id: string;
40
+ content: string;
41
+ /** Relevance from your retriever, normalized to [0,1] (top item ideally 1.0). */
42
+ score: number;
43
+ timestamp?: Date;
44
+ /** Pre-resolved claim strings; enables the CLAIMS fidelity. Selector never fetches these. */
45
+ claims?: string[];
46
+ /** Optional vector for `similarity: embeddingSimilarity`. */
47
+ embedding?: ArrayLike<number>;
48
+ }
49
+ /** Per-fidelity multipliers φ(f) — how much utility a fidelity retains vs verbatim. */
50
+ type FidelityFactors = Record<Fidelity, number>;
51
+ interface PrismSelectOptions {
52
+ /** Token budget for the guaranteed block + selected renderings. The tail is capped separately by tailBudgetTokens. */
53
+ budgetTokens?: number;
54
+ /** Hard cap on DISTINCT units selected (upgrades do not count against it). */
55
+ maxUnits?: number;
56
+ /** Hard token cap for the mention-line tail (separate from budgetTokens). */
57
+ tailBudgetTokens?: number;
58
+ /** Minimum score for a unit to earn a tail mention line (below this: dropped). */
59
+ tauFloor?: number;
60
+ /** ρ = recencyLambda·decay + (1−recencyLambda). 0 ⇒ ρ≡1 (multi-session safe). */
61
+ recencyLambda?: number;
62
+ /** Recency scale in days for the exponential decay e^(-age/days). Default 365. */
63
+ recencyDecayDays?: number;
64
+ /** Blend on the novelty term: effectiveNovelty = 1 − noveltyWeight·(1 − novelty). */
65
+ noveltyWeight?: number;
66
+ /** Tokens pre-charged against budgetTokens before selection (e.g. resolved-claims block). */
67
+ guaranteedTokens?: number;
68
+ /** Deterministic "current time" for recency. Defaults to `new Date()`. */
69
+ now?: Date;
70
+ /** Injectable token cost model. Defaults to the shared char/4 estimator. */
71
+ tokenCounter?: TokenCounter;
72
+ /**
73
+ * Unit-level similarity used for novelty, in [0, 1]. Defaults to word-set
74
+ * Jaccard between the renderings. Pass `embeddingSimilarity` to catch paraphrases.
75
+ */
76
+ similarity?: Similarity<PrismUnit>;
77
+ /** Per-fidelity utility multipliers φ. Merged over defaults. */
78
+ fidelityFactors?: Partial<FidelityFactors>;
79
+ /** β — multiplicative bonus ×(1+β) for re-selecting a unit at ≥ prev fidelity. */
80
+ hysteresisBonus?: number;
81
+ /** Prior turn's selection (unit → fidelity) for the β-hysteresis predicate. */
82
+ prevSelected?: Array<{
83
+ unitId: string;
84
+ fidelity: Fidelity;
85
+ }>;
86
+ /**
87
+ * Adaptive stopping mode. When true, ADD-move admission
88
+ * (a unit newly entering the selection) is gated: past `minKeep` distinct
89
+ * units, a unit may only be ADD-admitted if its relevance fraction (score /
90
+ * top-candidate score) is >= `relFloor` AND the candidate rendering's
91
+ * novelty vs the already-chosen renderings is >= `novFloor`. UPGRADE moves
92
+ * and the mention-tail are unaffected. `maxCeilingTokens` replaces
93
+ * `budgetTokens` as the only hard token bound (`maxUnits` is ignored).
94
+ * Default false: behavior is byte-identical to the fixed-budget path.
95
+ */
96
+ adaptive?: boolean;
97
+ /** Adaptive mode: distinct units ADD-admitted before the relFloor/novFloor gate applies. Default 3. */
98
+ minKeep?: number;
99
+ /** Adaptive mode: below this fraction of the top candidate's score, ADD admission stops. Default 0.4. */
100
+ relFloor?: number;
101
+ /** Adaptive mode: below this novelty vs chosen renderings, ADD admission stops. Default 0.2. */
102
+ novFloor?: number;
103
+ /** Adaptive mode: hard token ceiling — replaces budgetTokens as the bound. Default 4000. */
104
+ maxCeilingTokens?: number;
105
+ }
106
+ interface PrismSelectedUnit {
107
+ unit: PrismUnit;
108
+ fidelity: Fidelity;
109
+ rendered: string;
110
+ tokens: number;
111
+ }
112
+ interface PrismStats {
113
+ /** Sum of every candidate unit's VERBATIM cost (the pre-selection ceiling). */
114
+ candidateTokens: number;
115
+ selectedTokens: number;
116
+ tailTokens: number;
117
+ budgetTokens: number;
118
+ /** guaranteedTokens + selectedTokens + tailTokens — actual spend vs budgetTokens. */
119
+ utilizedTokens: number;
120
+ fidelityHistogram: Record<Fidelity, number>;
121
+ /** Number of greedy moves applied (adds + upgrades). */
122
+ moves: number;
123
+ }
124
+ interface PrismSelectResult {
125
+ selected: PrismSelectedUnit[];
126
+ /** Unselected units at or above tauFloor — floored into the mention tail. */
127
+ tail: PrismUnit[];
128
+ /** The rendered tail block (mention lines, or verbatim on the tiny-pool guard). */
129
+ tailBlock: string;
130
+ stats: PrismStats;
131
+ }
132
+ declare class PrismSelector {
133
+ /**
134
+ * MCKP-greedy multi-fidelity selection: build each unit's rendering menu,
135
+ * greedily apply the densest feasible ADD/UPGRADE move, run the KMN
136
+ * best-single swap, then floor the remainder into a mention tail.
137
+ */
138
+ static select(units: PrismUnit[], query: string, opts?: PrismSelectOptions): PrismSelectResult;
139
+ }
140
+
141
+ /**
142
+ * Prism renderings: deterministic multi-fidelity renderings of a retrieved
143
+ * unit (the CABAR-2 "Prism" extension of CABAR's all-or-nothing selection).
144
+ *
145
+ * CABAR-1 spent budget on a unit at a single fidelity (verbatim, or nothing —
146
+ * with an extractive tail for the rest). Prism turns that binary into a ladder:
147
+ * a unit can be rendered at VERBATIM (full content), EXTRACT (query-aware
148
+ * sentence extract, ≤~40% of verbatim tokens), CLAIMS (its pre-resolved claim
149
+ * bullets), or MENTION (a one-line dereferenceable pointer). The MCKP selector
150
+ * (PrismSelector) then picks per-unit fidelities to maximize utility under the
151
+ * token budget instead of dropping units wholesale.
152
+ *
153
+ * Every rendering here is PURE and deterministic: no LLM, no I/O, no clock.
154
+ * Costs are measured through an injected `TokenCounter` (char/4 by default,
155
+ * real BPE when you inject one) so the same code costs the same
156
+ * text the caller will bill for. Claims are pre-fetched by the caller and live
157
+ * on the unit — this module never fetches them.
158
+ */
159
+
160
+ /** Lowercased word (3+ chars) token set — same family as CabarSelector.tokenize. */
161
+ declare function tokenize(text: string): Set<string>;
162
+ declare function jaccard(a: Set<string>, b: Set<string>): number;
163
+ /** Sentence split — same regex family as CabarSelector.buildTailSummary. */
164
+ declare function splitSentences(text: string): string[];
165
+ /**
166
+ * Word-boundary truncation to a token budget, appending an ellipsis only when
167
+ * the text is actually cut. Uses the injected counter so the cut respects the
168
+ * caller's tokenization (char/4 or BPE). Always keeps at least the first word.
169
+ */
170
+ declare function truncateToTokens(text: string, maxTokens: number, counter: TokenCounter): string;
171
+ /** VERBATIM — the unit's full content, unchanged. */
172
+ declare function verbatim(u: PrismUnit): string;
173
+ /**
174
+ * EXTRACT — query-aware sentence extraction targeting ≤~40% of verbatim tokens.
175
+ *
176
+ * Sentences are scored by query-term overlap plus a first-sentence positional
177
+ * prior, deduped on normalized text, then greedily packed (highest score first,
178
+ * stable on original order) up to the ~40% token target. Selected sentences are
179
+ * emitted in ORIGINAL narrative order (unlike the score-ordered tail) so the
180
+ * extract reads coherently. Never returns empty for non-empty content: if the
181
+ * target is too small for any sentence, the single top-scored sentence is kept
182
+ * (a single-sentence unit thus extracts to itself, and is dropped later by the
183
+ * selector's cost-dominance guard rather than duplicating verbatim).
184
+ */
185
+ declare function extract(u: PrismUnit, query: string, counter: TokenCounter): string;
186
+ /**
187
+ * CLAIMS — the unit's pre-resolved claims as '- <claim>' bullet lines.
188
+ * UNAVAILABLE (null) when the caller pre-fetched no claims for this unit.
189
+ */
190
+ declare function claimsRendering(u: PrismUnit): string | null;
191
+ /**
192
+ * MENTION — a one-line dereferenceable pointer:
193
+ * [<id>@<yyyy-mm-dd if timestamp>] <first sentence, ≤25 tokens>
194
+ * This is the answer-lossless floor: even a unit that loses the budget contest
195
+ * still leaves a citable trace the caller can dereference back to the full unit.
196
+ */
197
+ declare function mention(u: PrismUnit, counter: TokenCounter): string;
198
+
199
+ /**
200
+ * Score normalization. The selectors expect relevance in [0, 1] with the top
201
+ * candidate at (or near) 1.0: `tauFloor` and the adaptive relevance cliff are
202
+ * fractions of that scale. Raw retriever scores rarely are: cosine
203
+ * similarities bunch up in 0.7–0.85, BM25 and reranker logits are unbounded,
204
+ * and vector-store distances run the wrong way.
205
+ */
206
+ interface NormalizeOptions {
207
+ /**
208
+ * - `minmax` (default): best → 1, worst → 0. Spreads bunched-up scores
209
+ * (cosine similarities) across the full range.
210
+ * - `max`: divide by the best score. Keeps ratios between scores; only for
211
+ * non-negative scores.
212
+ */
213
+ method?: 'minmax' | 'max';
214
+ /** Set to false for distances (lower = more relevant). Default true. */
215
+ higherIsBetter?: boolean;
216
+ }
217
+ /** Returns copies of the units with `score` mapped to [0, 1]. Input order is kept. */
218
+ declare function normalizeScores<T extends {
219
+ score: number;
220
+ }>(units: T[], opts?: NormalizeOptions): T[];
221
+
222
+ /**
223
+ * Helpers around a selection result: turn it into prompt text, and compare it
224
+ * with what you send today (shadow mode).
225
+ */
226
+
227
+ interface ToContextOptions {
228
+ /** Separator between selected units. Default: a blank line. */
229
+ separator?: string;
230
+ /** Heading put above the tail. Default: "Other possibly relevant notes:". Pass '' for none. */
231
+ tailHeading?: string;
232
+ /** Wraps each selected unit, e.g. to add ids or XML tags. Default: the text as is. */
233
+ format?: (text: string, id: string) => string;
234
+ }
235
+ /** Joins a CABAR or Prism result into one string to put in the prompt. */
236
+ declare function toContext(result: CabarSelectResult | PrismSelectResult, opts?: ToContextOptions): string;
237
+ interface ShadowReport {
238
+ /** Tokens of every unit you passed in (what "send everything" costs). */
239
+ sendAllTokens: number;
240
+ /** Tokens CABAR would send: selected + tail. */
241
+ cabarTokens: number;
242
+ /** 1 − cabarTokens / sendAllTokens. */
243
+ savedFraction: number;
244
+ selectedIds: string[];
245
+ tailIds: string[];
246
+ droppedIds: string[];
247
+ result: CabarSelectResult;
248
+ }
249
+ /**
250
+ * Shadow mode: run CABAR next to your current pipeline without changing what
251
+ * you send. Log the report, compare answers, then switch when you trust it.
252
+ */
253
+ declare function shadowReport(units: CabarUnit[], query: string, opts?: CabarSelectOptions): ShadowReport;
254
+
255
+ export { CabarSelectOptions, CabarSelectResult, CabarUnit, type Fidelity, type FidelityFactors, type NormalizeOptions, type PrismSelectOptions, type PrismSelectResult, type PrismSelectedUnit, PrismSelector, type PrismStats, type PrismUnit, type ShadowReport, Similarity, type ToContextOptions, TokenCounter, claimsRendering, extract, jaccard, mention, normalizeScores, shadowReport, splitSentences, toContext, tokenize, truncateToTokens, verbatim };
@@ -0,0 +1,255 @@
1
+ import { T as TokenCounter, S as Similarity, C as CabarSelectResult, a as CabarUnit, b as CabarSelectOptions } from './cabar-B-R-rHby.js';
2
+ export { A as ADAPTIVE_DEFAULTS, c as CHAR4_COUNTER, d as CabarDecision, e as CabarSelector, f as CabarStats, D as DEFAULTS, g as DEFAULT_RECENCY_DECAY_DAYS, h as cosineSimilarity, i as embeddingSimilarity, j as estimateTokens, r as recencyDecay } from './cabar-B-R-rHby.js';
3
+
4
+ /**
5
+ * PrismSelector: CABAR-2 multi-fidelity budgeted selection.
6
+ *
7
+ * Where CabarSelector answers "which units do we spend verbatim budget
8
+ * on, and summarize the rest", Prism answers the finer question "at WHICH
9
+ * FIDELITY do we render each unit". It solves a Multiple-Choice Knapsack
10
+ * Problem (MCKP): each unit offers a small menu of renderings — verbatim /
11
+ * extract / claims / mention — with distinct (cost, utility) points, and the
12
+ * selector greedily packs marginal-utility-per-token, allowed to UPGRADE an
13
+ * already-selected unit to a costlier fidelity when that upgrade is the densest
14
+ * feasible move. All-or-nothing (CABAR-1) is the special case of a two-point
15
+ * menu {verbatim, absent}; Prism dominates it under tight budgets by trading a
16
+ * verbatim unit for several cheaper-fidelity units that together carry more.
17
+ *
18
+ * Design invariants (same purity contract as CabarSelector):
19
+ * - PURE + deterministic: no LLM, no I/O, no Date.now — the caller supplies
20
+ * `now`; `claims` are pre-fetched onto the units by the caller.
21
+ * - Provider-agnostic token cost: the injectable `TokenCounter` defaults to
22
+ * the shared char/4 estimator (prod), and benchmarks inject a real BPE
23
+ * counter. Selection logic never assumes a specific vocabulary.
24
+ * - Cache-stable OUTPUT ORDERING: selected units are emitted by
25
+ * (timestamp asc, id asc), NOT by score, so the same units land in the same
26
+ * prompt slots turn-over-turn (KV-cache friendly).
27
+ * - β-hysteresis: an optional per-unit bonus for re-selecting a unit at
28
+ * ≥ its previous fidelity (keeps prompts stable turn-over-turn). Defaults
29
+ * to 0, a no-op.
30
+ * - Answer-lossless FLOOR: every unit at or above `tauFloor` that misses the
31
+ * budget still gets a mention line in the tail, so nothing recallable is
32
+ * silently dropped.
33
+ */
34
+
35
+ /** The four rendering fidelities, most→least faithful. */
36
+ type Fidelity = 'verbatim' | 'extract' | 'claims' | 'mention';
37
+ /** A retrieved unit plus its caller-pre-fetched claims (the CabarUnit + claims). */
38
+ interface PrismUnit {
39
+ id: string;
40
+ content: string;
41
+ /** Relevance from your retriever, normalized to [0,1] (top item ideally 1.0). */
42
+ score: number;
43
+ timestamp?: Date;
44
+ /** Pre-resolved claim strings; enables the CLAIMS fidelity. Selector never fetches these. */
45
+ claims?: string[];
46
+ /** Optional vector for `similarity: embeddingSimilarity`. */
47
+ embedding?: ArrayLike<number>;
48
+ }
49
+ /** Per-fidelity multipliers φ(f) — how much utility a fidelity retains vs verbatim. */
50
+ type FidelityFactors = Record<Fidelity, number>;
51
+ interface PrismSelectOptions {
52
+ /** Token budget for the guaranteed block + selected renderings. The tail is capped separately by tailBudgetTokens. */
53
+ budgetTokens?: number;
54
+ /** Hard cap on DISTINCT units selected (upgrades do not count against it). */
55
+ maxUnits?: number;
56
+ /** Hard token cap for the mention-line tail (separate from budgetTokens). */
57
+ tailBudgetTokens?: number;
58
+ /** Minimum score for a unit to earn a tail mention line (below this: dropped). */
59
+ tauFloor?: number;
60
+ /** ρ = recencyLambda·decay + (1−recencyLambda). 0 ⇒ ρ≡1 (multi-session safe). */
61
+ recencyLambda?: number;
62
+ /** Recency scale in days for the exponential decay e^(-age/days). Default 365. */
63
+ recencyDecayDays?: number;
64
+ /** Blend on the novelty term: effectiveNovelty = 1 − noveltyWeight·(1 − novelty). */
65
+ noveltyWeight?: number;
66
+ /** Tokens pre-charged against budgetTokens before selection (e.g. resolved-claims block). */
67
+ guaranteedTokens?: number;
68
+ /** Deterministic "current time" for recency. Defaults to `new Date()`. */
69
+ now?: Date;
70
+ /** Injectable token cost model. Defaults to the shared char/4 estimator. */
71
+ tokenCounter?: TokenCounter;
72
+ /**
73
+ * Unit-level similarity used for novelty, in [0, 1]. Defaults to word-set
74
+ * Jaccard between the renderings. Pass `embeddingSimilarity` to catch paraphrases.
75
+ */
76
+ similarity?: Similarity<PrismUnit>;
77
+ /** Per-fidelity utility multipliers φ. Merged over defaults. */
78
+ fidelityFactors?: Partial<FidelityFactors>;
79
+ /** β — multiplicative bonus ×(1+β) for re-selecting a unit at ≥ prev fidelity. */
80
+ hysteresisBonus?: number;
81
+ /** Prior turn's selection (unit → fidelity) for the β-hysteresis predicate. */
82
+ prevSelected?: Array<{
83
+ unitId: string;
84
+ fidelity: Fidelity;
85
+ }>;
86
+ /**
87
+ * Adaptive stopping mode. When true, ADD-move admission
88
+ * (a unit newly entering the selection) is gated: past `minKeep` distinct
89
+ * units, a unit may only be ADD-admitted if its relevance fraction (score /
90
+ * top-candidate score) is >= `relFloor` AND the candidate rendering's
91
+ * novelty vs the already-chosen renderings is >= `novFloor`. UPGRADE moves
92
+ * and the mention-tail are unaffected. `maxCeilingTokens` replaces
93
+ * `budgetTokens` as the only hard token bound (`maxUnits` is ignored).
94
+ * Default false: behavior is byte-identical to the fixed-budget path.
95
+ */
96
+ adaptive?: boolean;
97
+ /** Adaptive mode: distinct units ADD-admitted before the relFloor/novFloor gate applies. Default 3. */
98
+ minKeep?: number;
99
+ /** Adaptive mode: below this fraction of the top candidate's score, ADD admission stops. Default 0.4. */
100
+ relFloor?: number;
101
+ /** Adaptive mode: below this novelty vs chosen renderings, ADD admission stops. Default 0.2. */
102
+ novFloor?: number;
103
+ /** Adaptive mode: hard token ceiling — replaces budgetTokens as the bound. Default 4000. */
104
+ maxCeilingTokens?: number;
105
+ }
106
+ interface PrismSelectedUnit {
107
+ unit: PrismUnit;
108
+ fidelity: Fidelity;
109
+ rendered: string;
110
+ tokens: number;
111
+ }
112
+ interface PrismStats {
113
+ /** Sum of every candidate unit's VERBATIM cost (the pre-selection ceiling). */
114
+ candidateTokens: number;
115
+ selectedTokens: number;
116
+ tailTokens: number;
117
+ budgetTokens: number;
118
+ /** guaranteedTokens + selectedTokens + tailTokens — actual spend vs budgetTokens. */
119
+ utilizedTokens: number;
120
+ fidelityHistogram: Record<Fidelity, number>;
121
+ /** Number of greedy moves applied (adds + upgrades). */
122
+ moves: number;
123
+ }
124
+ interface PrismSelectResult {
125
+ selected: PrismSelectedUnit[];
126
+ /** Unselected units at or above tauFloor — floored into the mention tail. */
127
+ tail: PrismUnit[];
128
+ /** The rendered tail block (mention lines, or verbatim on the tiny-pool guard). */
129
+ tailBlock: string;
130
+ stats: PrismStats;
131
+ }
132
+ declare class PrismSelector {
133
+ /**
134
+ * MCKP-greedy multi-fidelity selection: build each unit's rendering menu,
135
+ * greedily apply the densest feasible ADD/UPGRADE move, run the KMN
136
+ * best-single swap, then floor the remainder into a mention tail.
137
+ */
138
+ static select(units: PrismUnit[], query: string, opts?: PrismSelectOptions): PrismSelectResult;
139
+ }
140
+
141
+ /**
142
+ * Prism renderings: deterministic multi-fidelity renderings of a retrieved
143
+ * unit (the CABAR-2 "Prism" extension of CABAR's all-or-nothing selection).
144
+ *
145
+ * CABAR-1 spent budget on a unit at a single fidelity (verbatim, or nothing —
146
+ * with an extractive tail for the rest). Prism turns that binary into a ladder:
147
+ * a unit can be rendered at VERBATIM (full content), EXTRACT (query-aware
148
+ * sentence extract, ≤~40% of verbatim tokens), CLAIMS (its pre-resolved claim
149
+ * bullets), or MENTION (a one-line dereferenceable pointer). The MCKP selector
150
+ * (PrismSelector) then picks per-unit fidelities to maximize utility under the
151
+ * token budget instead of dropping units wholesale.
152
+ *
153
+ * Every rendering here is PURE and deterministic: no LLM, no I/O, no clock.
154
+ * Costs are measured through an injected `TokenCounter` (char/4 by default,
155
+ * real BPE when you inject one) so the same code costs the same
156
+ * text the caller will bill for. Claims are pre-fetched by the caller and live
157
+ * on the unit — this module never fetches them.
158
+ */
159
+
160
+ /** Lowercased word (3+ chars) token set — same family as CabarSelector.tokenize. */
161
+ declare function tokenize(text: string): Set<string>;
162
+ declare function jaccard(a: Set<string>, b: Set<string>): number;
163
+ /** Sentence split — same regex family as CabarSelector.buildTailSummary. */
164
+ declare function splitSentences(text: string): string[];
165
+ /**
166
+ * Word-boundary truncation to a token budget, appending an ellipsis only when
167
+ * the text is actually cut. Uses the injected counter so the cut respects the
168
+ * caller's tokenization (char/4 or BPE). Always keeps at least the first word.
169
+ */
170
+ declare function truncateToTokens(text: string, maxTokens: number, counter: TokenCounter): string;
171
+ /** VERBATIM — the unit's full content, unchanged. */
172
+ declare function verbatim(u: PrismUnit): string;
173
+ /**
174
+ * EXTRACT — query-aware sentence extraction targeting ≤~40% of verbatim tokens.
175
+ *
176
+ * Sentences are scored by query-term overlap plus a first-sentence positional
177
+ * prior, deduped on normalized text, then greedily packed (highest score first,
178
+ * stable on original order) up to the ~40% token target. Selected sentences are
179
+ * emitted in ORIGINAL narrative order (unlike the score-ordered tail) so the
180
+ * extract reads coherently. Never returns empty for non-empty content: if the
181
+ * target is too small for any sentence, the single top-scored sentence is kept
182
+ * (a single-sentence unit thus extracts to itself, and is dropped later by the
183
+ * selector's cost-dominance guard rather than duplicating verbatim).
184
+ */
185
+ declare function extract(u: PrismUnit, query: string, counter: TokenCounter): string;
186
+ /**
187
+ * CLAIMS — the unit's pre-resolved claims as '- <claim>' bullet lines.
188
+ * UNAVAILABLE (null) when the caller pre-fetched no claims for this unit.
189
+ */
190
+ declare function claimsRendering(u: PrismUnit): string | null;
191
+ /**
192
+ * MENTION — a one-line dereferenceable pointer:
193
+ * [<id>@<yyyy-mm-dd if timestamp>] <first sentence, ≤25 tokens>
194
+ * This is the answer-lossless floor: even a unit that loses the budget contest
195
+ * still leaves a citable trace the caller can dereference back to the full unit.
196
+ */
197
+ declare function mention(u: PrismUnit, counter: TokenCounter): string;
198
+
199
+ /**
200
+ * Score normalization. The selectors expect relevance in [0, 1] with the top
201
+ * candidate at (or near) 1.0: `tauFloor` and the adaptive relevance cliff are
202
+ * fractions of that scale. Raw retriever scores rarely are: cosine
203
+ * similarities bunch up in 0.7–0.85, BM25 and reranker logits are unbounded,
204
+ * and vector-store distances run the wrong way.
205
+ */
206
+ interface NormalizeOptions {
207
+ /**
208
+ * - `minmax` (default): best → 1, worst → 0. Spreads bunched-up scores
209
+ * (cosine similarities) across the full range.
210
+ * - `max`: divide by the best score. Keeps ratios between scores; only for
211
+ * non-negative scores.
212
+ */
213
+ method?: 'minmax' | 'max';
214
+ /** Set to false for distances (lower = more relevant). Default true. */
215
+ higherIsBetter?: boolean;
216
+ }
217
+ /** Returns copies of the units with `score` mapped to [0, 1]. Input order is kept. */
218
+ declare function normalizeScores<T extends {
219
+ score: number;
220
+ }>(units: T[], opts?: NormalizeOptions): T[];
221
+
222
+ /**
223
+ * Helpers around a selection result: turn it into prompt text, and compare it
224
+ * with what you send today (shadow mode).
225
+ */
226
+
227
+ interface ToContextOptions {
228
+ /** Separator between selected units. Default: a blank line. */
229
+ separator?: string;
230
+ /** Heading put above the tail. Default: "Other possibly relevant notes:". Pass '' for none. */
231
+ tailHeading?: string;
232
+ /** Wraps each selected unit, e.g. to add ids or XML tags. Default: the text as is. */
233
+ format?: (text: string, id: string) => string;
234
+ }
235
+ /** Joins a CABAR or Prism result into one string to put in the prompt. */
236
+ declare function toContext(result: CabarSelectResult | PrismSelectResult, opts?: ToContextOptions): string;
237
+ interface ShadowReport {
238
+ /** Tokens of every unit you passed in (what "send everything" costs). */
239
+ sendAllTokens: number;
240
+ /** Tokens CABAR would send: selected + tail. */
241
+ cabarTokens: number;
242
+ /** 1 − cabarTokens / sendAllTokens. */
243
+ savedFraction: number;
244
+ selectedIds: string[];
245
+ tailIds: string[];
246
+ droppedIds: string[];
247
+ result: CabarSelectResult;
248
+ }
249
+ /**
250
+ * Shadow mode: run CABAR next to your current pipeline without changing what
251
+ * you send. Log the report, compare answers, then switch when you trust it.
252
+ */
253
+ declare function shadowReport(units: CabarUnit[], query: string, opts?: CabarSelectOptions): ShadowReport;
254
+
255
+ export { CabarSelectOptions, CabarSelectResult, CabarUnit, type Fidelity, type FidelityFactors, type NormalizeOptions, type PrismSelectOptions, type PrismSelectResult, type PrismSelectedUnit, PrismSelector, type PrismStats, type PrismUnit, type ShadowReport, Similarity, type ToContextOptions, TokenCounter, claimsRendering, extract, jaccard, mention, normalizeScores, shadowReport, splitSentences, toContext, tokenize, truncateToTokens, verbatim };