/** * Typed outcomes for one compaction planner attempt. * * `planDeletedLineRanges` used to collapse ranked success, silent partial * recovery, reasoning starvation, rate limiting, malformed output, and context * overflow into "ranges or throw". Callers could not tell a throttle from * garbage without string-matching an error message. This module makes each * outcome a distinct value so the rung ladder, the `0600` diagnostic sidecar, * and the UI can separate them. * * Cancellation is deliberately NOT a variant: an abort still throws. */ import type { Api, AssistantMessage } from "@bastani/pi-ai/compat"; import type { RawLineRange } from "./compaction-types.js"; import type { DiagnosticFailureCategory } from "./range-planner-diagnostics.js"; /** Which limit a `rateLimited` outcome represents. */ export type PlannerLimitClass = "quota" | "rate_limited"; /** Every way one planner attempt can end. */ export type PlannerOutcome = { kind: "ranked"; ranges: RawLineRange[]; } | { kind: "recovered"; ranges: RawLineRange[]; recoveredCount: number; } | { kind: "rateLimited"; /** * Which limit this was, independent of retry activity. Quota/billing * exhaustion and transient throttling are different facts and must stay * separable in code and diagnostics; `exhausted` records only whether a * retry was actually scheduled, which cannot tell them apart when retry * is disabled. */ category: PlannerLimitClass; exhausted: boolean; message: string; diagnosticPath?: string; } | { kind: "unusable"; category: DiagnosticFailureCategory; excerpt: string; diagnosticPath?: string; } | { kind: "overflowed"; diagnosticPath?: string; } | { kind: "policyRefusal"; message: string; diagnosticPath?: string; } | { kind: "providerError"; message: string; diagnosticPath?: string; }; /** A planner outcome that cannot produce a boundary and must advance the ladder. */ export type TerminalPlannerOutcome = Extract; /** Whether this outcome produced validated deletion ranges. */ export declare function isPlannerSuccess(outcome: PlannerOutcome): outcome is Extract; /** How a failed planner response is classified before an outcome is built. */ export type PlannerFailureClass = "overflow" | "quota" | "rate_limited" | "policy_refusal" | "provider_error"; export declare function isProviderPolicyRefusal(message: string): boolean; export declare function isProviderPolicyRefusalResponse(response: AssistantMessage): boolean; /** * Classify one failed planner response. * * Policy refusal wins before overflow, quota, and retryable-provider matching. * Context overflow is recoverable by trimming; quota wins over throttling. */ export declare function classifyPlannerFailure(response: AssistantMessage, contextWindow: number): PlannerFailureClass; /** Build a synthetic error response so a thrown transport failure classifies identically. */ export declare function syntheticErrorResponse(model: { api: Api; provider: string; id: string; }, errorMessage: string): AssistantMessage; /** * Reasoning starvation fingerprint: the response stopped at the output limit, * produced no usable deletion record, and billed reasoning tokens. * * This is a diagnostic category only. Codex has no starvation concept and never * retries one; neither does Atomic. A starved outcome advances the ladder * exactly like any other `unusable` output. */ export declare function isReasoningStarved(response: AssistantMessage, hasUsableRanges: boolean): boolean; //# sourceMappingURL=planner-outcome.d.ts.map