/** * Pure presentation + gating for task-scoped context injection * (changes: add-task-scoped-context-injection, * fix-inject-relevance-gate-keyword-mode). * * Extracted from `orient-inject.ts` so it can be reused by hosts that must NOT * load the analyzer in-process — notably the Pi extension, which orients through * a warm daemon over RPC (decision abee8e3e). Everything here is pure and * deterministic; its runtime dependencies are dependency-light pure helpers. * Keep it that way — importing the analyzer (`handleOrient`) or config I/O here would drag the * analyzer back into the Pi host process the daemon split exists to keep lean. * * The block is framed as facts-not-coercion (Epistemic Lease, decision 8e95746d) * and capped by a token budget so it can never dominate the context it economizes. */ import { type ServedContentProvenance } from '../../core/services/served-content.js'; import type { ContextInjectionConfig } from '../../types/index.js'; /** Injection settings with every documented default applied. */ export interface ResolvedInjectionConfig { mode: 'off' | 'task-scoped'; tokenBudget: number; relevanceMinMatches: number; relevanceMinFanIn: number; relevanceMinScore: number; /** Withhold the briefing on repository-management turns. Default on. */ intentGate: boolean; } /** Documented defaults — used when `.openlore/config.json` omits `contextInjection`. */ export declare const INJECTION_DEFAULTS: ResolvedInjectionConfig; /** Smallest budget that can carry the framed task and a factual stale-index line. */ export declare const MIN_INJECTION_TOKEN_BUDGET = 68; /** * The generic pointer line: structural context exists, this turn did not get a * block. Informational, not coercive. Retained as the neutral form (and for * callers that do not track a cause); the emitted lines come from * {@link pointerLineFor}, which states WHICH cause withheld the briefing. */ export declare const POINTER_LINE: string; /** * Why a briefing was withheld. Stable and machine-readable: telemetry counts it, * and the agent-visible pointer line states it. */ export type WithholdReason = 'management-intent' | 'weak-relevance' | 'no-graph' | 'empty-prompt' | 'error'; /** * The agent-visible pointer line for each withhold cause. * * Absence must never be ambiguous. An agent that cannot tell "nothing relevant * was found" from "no lookup was performed" learns to read silence as noise, and * the one turn where the briefing mattered is lost with the rest. Each variant * therefore states the cause AND names the manual call, so a misclassified turn * costs the pre-computed briefing — never the knowledge that one was skipped. */ export declare function pointerLineFor(reason: WithholdReason): string; /** What kind of work a turn is doing. */ export type TurnIntent = 'code-work' | 'repository-management'; /** * Deterministic turn-intent classification: pure, local, no LLM, no new score. * * FAILS OPEN by construction — a turn is repository management only when it * matches a management pattern AND no code-work pattern. Everything else, * including everything unrecognized, is treated as code work and keeps today's * behavior. The gate can therefore only withhold on positive evidence, never on * an absence of recognition. */ export declare function classifyTurnIntent(prompt: string): TurnIntent; /** Apply documented defaults over a partial config block. */ export declare function resolveInjectionConfig(ci: ContextInjectionConfig | undefined): ResolvedInjectionConfig; interface OrientFn { name?: string; filePath?: string; score?: number; fanIn?: number; fanOut?: number; isHub?: boolean; provenance?: ServedContentProvenance; } interface CallNeighbour { name?: string; filePath?: string; } interface OrientCallPath { function?: string; callers?: CallNeighbour[]; callees?: CallNeighbour[]; } /** Compact house-style summary for the touched region (change: add-codebase-style-fingerprint). */ interface RegionStyle { scope?: string; language?: string; communityId?: string; dominantIdioms?: string[]; } export interface LeanOrientResult { task?: string; searchMode?: string; error?: string; /** Set by `notReadyResult` when the index is absent/unbuilt (reason `index-absent`). */ notReady?: boolean; /** * Set when the graph index exists but THIS build cannot read it — after a schema bump, until * the next analyze. The briefing then carries no call paths, provenance, decisions or * change-coupling, and without saying so a reader cannot tell "nothing calls this" from * "I could not look" (change: shrink-receiver-resolution-boundary). */ graphIndexNote?: string; reason?: string; relevantFiles?: string[]; relevantFunctions?: OrientFn[]; specDomains?: Array; callPaths?: OrientCallPath[]; suggestedTools?: string[]; regionStyle?: RegionStyle; parseHealth?: string; indexStaleness?: { staleFiles?: string[]; note?: string; repairScheduled?: true; }; servedContentProvenance?: { relevantFiles?: ServedContentProvenance; relevantFunctions?: ServedContentProvenance; specDomains?: ServedContentProvenance; callPaths?: ServedContentProvenance; }; } export interface RelevanceGateEvaluation { passes: boolean; passedCriteria: string[]; failedCriteria: string[]; /** Present when `passes` is false: why the briefing was withheld. */ reason?: WithholdReason; } /** * Deterministic orientation-relevance gate. Returns true when the task has a * substantial, structurally-connected match in the graph — the case where a * full briefing pays for itself. Otherwise the caller emits the pointer line, * keeping injection out of the small/familiar/shallow arena the scorecard shows * OpenLore should not tax. * * Signals are read off the lean orient result itself (no new analysis pass). * An exact identifier mention passes directly. Otherwise the matched-function * count must reach relevanceMinMatches and one of structural centrality, a * bounded hybrid score, or scale-free keyword rank evidence must pass. * * BM25-fallback scores live on an unbounded, corpus-relative scale, so the score * path is disabled there. Exact identifier and rank evidence make keyword mode * decidable without letting an arbitrary BM25 magnitude wave everything through. */ export declare function evaluateRelevanceGate(result: LeanOrientResult, cfg: ResolvedInjectionConfig): RelevanceGateEvaluation; export declare function passesRelevanceGate(result: LeanOrientResult, cfg: ResolvedInjectionConfig): boolean; /** * Render the full injection block from a lean orient result, hard-capped to the * token budget. The header, framing, and task line are mandatory (a small fixed * floor that is always present so an injected block is unambiguously attributed * and ignorable); detail lines are added in priority order (functions → files → * call neighbours → specs → tools) only while they fit, so the data — regardless * of match size — never pushes the block over budget. * * Every interpolated field is defensively filtered: although `handleOrient` * declares its name/file fields as required strings, a partial/forward-incompat * result must never leak a literal `undefined`, `[object Object]`, or a stray * leading comma into the agent's context. */ export declare function renderInjectionBlock(result: LeanOrientResult, cfg: ResolvedInjectionConfig): string; export {}; //# sourceMappingURL=orient-inject-render.d.ts.map