/** * The Context Curator. Turns a flat pile of candidate grounding sources * into the smallest set of high-signal tokens that fit the target model's * remaining window. * * Principle (from Anthropic's context-engineering framing): every token * in the window is a design decision. The curator makes those decisions * *explicit* — budget, utility, selected, rejected — so users can inspect * why any given optimization looked the way it did. */ import type { ContextBundle, Intent } from '../context/types.js'; import type { MemoryMatch } from '../memory/types.js'; import type { UserProvidedSource } from './types.js'; /** Approximate token count. 4 chars/token is the industry default for English text. */ export declare function approxTokens(s: string): number; /** * Token-budget envelope. Derived from the target model's context window and * the configured output budget (from PromptShape). Does NOT include the * system prompt itself — that's accounted for separately. */ export interface TokenBudget { /** Total available for the user-prompt payload (grounding + original prompt + meta). */ total: number; /** Reserved slack for original prompt + boilerplate; curator can't touch this. */ reservedForPrompt: number; /** What the curator has to work with after reserving the prompt. */ availableForGrounding: number; } export declare function computeBudget(args: { contextWindow: number; systemPromptTokens: number; outputTokens: number; originalPromptTokens: number; slackTokens?: number; }): TokenBudget; /** Candidate grounding section — the curator's input. */ export interface Candidate { source: string; label: string; body: string; tokens: number; baseUtility: number; pinned?: boolean; freshnessMs?: number; intentMatch?: number; authority?: number; } export interface CuratedSection { source: string; label: string; body: string; tokens: number; utility: number; pinned: boolean; } export interface RejectionReason { source: string; tokens: number; utility: number; reason: 'budget-exhausted' | 'zero-utility' | 'duplicate' | 'stale'; } export interface CurationResult { /** Ready-to-inject Grounding Context block. Empty string if nothing selected. */ block: string; /** What the curator kept, in order. */ selected: CuratedSection[]; /** What the curator cut, with reasons. */ rejected: RejectionReason[]; /** Budget snapshot. */ budget: TokenBudget; /** Total tokens used by selected sections. */ used: number; /** Ordered list of source-id strings for quick trace consumption. */ sourceIds: string[]; } /** * Score a candidate. All factors are normalized 0..1; final utility is a * weighted combination. This is the *only* ranking function — everything * else (retrieval, composition) defers to it. */ export declare function scoreCandidate(c: Candidate, nowMs?: number): number; /** * The core curation function. Hard-pin constraints first, then greedy * knapsack by utility-per-token. Duplicate sources are suppressed. */ export declare function curate(candidates: Candidate[], budget: TokenBudget): CurationResult; /** * Build curator candidates from a ContextBundle + web-search + platform * signals + memory matches. Keeps the candidate-construction logic in one * place so the engine wiring stays thin. */ export declare function buildCandidates(args: { bundle?: ContextBundle; webSearchContext?: string; webSearchSources?: string[]; platformInstructions?: string; platformHints?: string[]; acceptedExamples?: Array<{ originalPrompt: string; optimizedPrompt: string; ts: number; }>; memoryMatches?: MemoryMatch[]; intent?: Intent; userProvidedSources?: UserProvidedSource[]; }): Candidate[];