import type { Capability } from "./resolve.ts"; export declare const CONTEXT_MODES: readonly ["none", "files", "pruned", "summary", "fork"]; export type ContextMode = (typeof CONTEXT_MODES)[number]; /** The capability that authorises one handoff mode. */ export declare function contextCapability(mode: ContextMode): Capability; export declare function isContextCapability(id: Capability): boolean; /** * `context:fork` → every weaker mode, and so on down. The closure is written out rather than walked, because * `expandSubsumed` expands one level only and a partial entry here would silently under-grant. */ export declare const CONTEXT_SUBSUMPTION: Readonly>; /** What a parent asks for. Model-supplied, so every field is validated before anything is read or spawned. */ export interface ContextRequest { mode: ContextMode; /** `files` and `pruned`: repository-relative paths the parent names. */ files?: string[]; /** `summary`: the parent's own words. Model-authored, so it crosses the fence as data. */ summary?: string; /** `pruned`: how many recent turns to keep beside the turns that name a file. */ turns?: number; } /** Bounds on a model-supplied request. Generous enough to be useful, small enough to stay reviewable. */ export declare const MAX_CONTEXT_FILES = 16; export declare const MAX_CONTEXT_TURNS = 50; /** * How many recent turns a `pruned` handoff keeps when the caller names no number. * * **Raised from 6 to 20 on 2026-09-22, by measurement rather than taste.** The handoff probe measured, over 67 * real pi sessions, the share of the terms a task uses that survive into what the child actually receives: * 0.532 at 6 turns, 0.671 at 12, 0.737 at 20, 0.747 at the 50 ceiling. The jump from 6 to 20 is the large one * and well outside the corpus's own run-to-run noise of about 0.02; the remaining 0.010 from 20 to 50 is not, * so 20 is the conservative end of a flat region rather than an optimum. * * **The cost, which the first write-up omitted:** this takes the mean payload from 12.6 KiB to 25.8 KiB, so it * roughly doubles what a child is handed, bounded above by `CONTEXT_MAX_BYTES`. Recall rises with this number * by construction, so a recall figure with no price beside it has no stopping point. * * It was only safe to raise AFTER `keepRank` landed. With the old array-order fill, delivered recall peaked * at 20 and then FELL at 50, because the budget was spent on the oldest turns and the cap cut the newest — * so raising this number used to make a child worse off, which is the opposite of what it reads as doing. */ export declare const DEFAULT_CONTEXT_TURNS = 20; /** Total budget for everything that crosses, matching the chain handoff so one cap governs both channels. */ export declare const CONTEXT_MAX_BYTES: number; /** * Validate a model-supplied request, or refuse it. * * Returns the reason on refusal rather than throwing: the caller turns it into a governance refusal with a code, * and a validator that throws its own error type would lose that code on the way out. */ export declare function parseContextRequest(raw: unknown): { request: ContextRequest; } | { refusal: string; }; /** One labelled block inside the fence. */ /** * What outranks what when the byte budget binds, named here rather than left to whoever pushes a section. * * **The order is an argument, and the first version made it by omission.** Turn sections were given a rank and * everything else defaulted to zero, so a `pruned` handoff dropped the files the parent had EXPLICITLY NAMED * before it dropped any turn a rule happened to select — measured by review, with no header left behind to say * a file had been named at all. That inverts this module's own stated ordering, where `files` carries content * the parent names and `pruned` carries turns a rule guessed at. * * So: what the parent chose beats what a rule chose, and within the rule's own output the turns kept for a * REASON beat the turns kept merely for being recent. Positional index is added within each band, so the * newest survives its band. */ export declare const CONTEXT_RANK: Readonly<{ /** The parent's own words about what the child needs. Nothing it wrote should lose to a turn it did not. */ summary: 4000; /** A file the parent named. Explicit beats inferred. */ file: 3000; /** A turn kept because it names one of those files — the rule's non-recency signal. */ fileMatchedTurn: 2000; /** A turn kept for being recent. Last in, and first out when the budget binds. */ recentTurn: 1000; }>; export interface ContextSection { label: string; body: string; /** * Which sections survive when the budget binds. Higher is kept first; equal ranks keep array order. * * **Measured, not assumed (the 2026-09-22 handoff probe).** Sections used to be filled in array order, and * pruned turns are pushed oldest-first, so the turns dropped when the cap bound were the ones NEAREST the * task — the most relevant ones. Across 78 real pi sessions the cap bound in 13% of them at the default * and 60% at 20 turns, and delivered recall PEAKED at 20 turns and then fell: asking for more context made * the child worse off. Filling newest-first makes it monotone. Presentation order is unchanged, because a * child reading its parent's turns out of order is a different defect. */ keepRank?: number; } export interface FencedContext { text: string; nonce: string; /** Bytes dropped by the budget, so the ledger can record that the handoff was not whole. */ truncatedBytes: number; /** * Indices of the sections that actually crossed, for a caller that has to record what it sent. * * `context-staging.ts` promises the ledger "what actually crossed, never what was asked for", and counted * its sections BEFORE this function ran — so a record could say `keptTurns: 21` while nine of them never * left. Tolerable while the cap bound in 13% of handoffs; not tolerable once raising the default turn count * made it the majority case. `truncatedBytes` meant it was never silent, but the count a reviewer reads * was wrong. */ keptIndices: number[]; } /** * Wrap what crosses so it reads as data. * * Distinct from `fenceHandoff`'s delimiter on purpose. A chain step's fence says "this is the previous agent's * output"; this one says "this is context your parent chose to give you". A child that cannot tell them apart * cannot weigh them differently, and they do deserve different weight: one is another agent's answer, the other is * the operator's own session. * * Sections are filled in order until the budget is spent, and what did not fit is said INSIDE the fence for * `fenceHandoff`'s reason — a notice above the fence reads as the orchestrator's instruction. */ export declare function fenceContext(sections: readonly ContextSection[]): FencedContext; /** One turn of the parent's session, reduced to what the rule needs. The kernel never sees pi's own types. */ export interface PrunableTurn { id: string; text: string; } export interface PrunedSelection { kept: PrunableTurn[]; /** * Ids kept because they NAME one of the caller's files, rather than because they are recent. * * Surfaced because the caller has to rank them. Review measured the first version of `keepRank` handing * these the LOWEST rank — they sit at the front of `kept`, being older — so the one non-recency signal in * the rule was the first thing the byte budget evicted, while the ledger went on calling the rule * `recent+files`. What crossed was `recent` only. */ fileMatched: string[]; droppedCount: number; /** Named so the ledger records WHICH rule ran, not merely that pruning happened. */ rule: "recent+files"; } /** * Keep the last `turns` turns, plus any older turn that names one of `files`. * * Deterministic and explainable in one sentence, which is the whole of its claim. It is NOT a claim that these are * the right turns: whether it keeps what a reader would have kept is unmeasured, and stays unmeasured until the * handoff probe. An advisor may replace the selection later without changing anything else here, which is why the * rule is named in the result rather than assumed by the caller. */ export declare function selectPrunedTurns(all: readonly PrunableTurn[], options?: { turns?: number; files?: readonly string[]; }): PrunedSelection; //# sourceMappingURL=context-handoff.d.ts.map