/** * Borrow a configured fallback model for one compaction planner request. * * This is deliberately a *weaker* capability than main-chat model fallback. The * borrowing door returns a `BorrowedPlanner` **value**: it holds no session * handle, so it cannot write `agent.state.model`, append a model-change entry, * change the session thinking level, refresh the system prompt, emit * `model_changed`/`model_select`, or call `agent.continue()`. * `_trySwitchToFallbackModel` does all of those and is not reused or modified. * * The attempted-key set belongs to the compaction run and is read-only here, so * borrowing never touches the main chat's `_fallbackAttemptedKeys`. */ import type { Api, Model } from "@bastani/pi-ai/compat"; import type { ThinkingLevel } from "@earendil-works/pi-agent-core"; import { type FallbackModelLookup } from "../fallback-models.ts"; import type { BorrowedPlanner, PlannerAuth } from "./compaction-types.js"; /** * Everything candidate selection needs. * * The RFC's §5.1 signature takes only `attempted` and `resolveAuth`, which * cannot select a candidate on its own: it names no fallback list, no model * registry, no preferred provider, and no inherited reasoning level. Those * inputs travel in this context object rather than through hidden state. */ export interface FallbackPlannerContext { /** The session's effective `fallbackModels`, in configured order. */ readonly fallbackModels: readonly string[]; /** Model registry used to resolve and authenticate candidates. */ readonly registry: FallbackModelLookup; /** Provider preferred when an unqualified entry is ambiguous. */ readonly preferredProvider: string | undefined; /** Session level inherited when a candidate carries no `:level` suffix. */ readonly sessionThinkingLevel: ThinkingLevel | undefined; readonly isFallbackModelAllowed?: (model: Model, effort: string | undefined) => boolean; readonly selectedModelId?: string; } /** * Identity of one planner attempt: `provider/model:`. * * The key must be derived from the *effective* level the request will carry, * not from the raw optional `model:level` suffix. An unsuffixed entry for the * session model inherits the session level, so keying it as `provider/model:` * would let the identical request run a second time. */ export declare function plannerAttemptKey(planner: BorrowedPlanner): string; /** The exact two-argument borrowing door, bound to one run's configuration. */ export type BorrowFallbackPlanner = (attempted: ReadonlySet, resolveAuth: (model: Model) => Promise) => Promise; /** * Build the borrowing door for one compaction run. * * The RFC's top-level two-argument signature names no fallback list, registry, * preferred provider, inherited reasoning level, or traversal cursor, so a * stateless exported function cannot select a candidate without hidden shared * state — unsafe under concurrent compactions. Binding the configuration and the * run-local cursor in a closure keeps the returned door exactly two arguments * while `attempted` stays a `ReadonlySet` the borrower never writes. * * The walk is one monotonic pass. Each list position is inspected at most once, * whether it fails to resolve, has no usable credentials, duplicates an earlier * identity, or yields a planner. Without the cursor, an entry that could not * resolve earlier could be returned after a later candidate once the registry * changed, producing a B→A order the configured list never asked for. */ export declare function createFallbackPlannerBorrower(context: FallbackPlannerContext): BorrowFallbackPlanner; //# sourceMappingURL=fallback-planner.d.ts.map