/** * Diagnostic sidecar writer for compaction planner output. * * When the one-pass range planner returns malformed output or no usable ranges, * this module writes a private diagnostic file next to the persisted session * file. The sidecar is never written for in-memory sessions. * * Additionally, successful partial recovery from length-truncated responses * writes a private recovery diagnostic sidecar for operational observability. */ import type { Api, AssistantMessage, Model, Usage } from "@bastani/pi-ai/compat"; import type { MorphRangeDiagnostics } from "./morph-compaction.js"; /** Failure categories emitted in diagnostic sidecars. */ export type DiagnosticFailureCategory = "malformed_output" | "no_usable_ranges" | "provider_error" | "policy_refusal" | "stream_error" /** Length stop, no usable record, and billed reasoning tokens. */ | "starved" /** Transient throttling, whether or not a retry was actually scheduled. */ | "rate_limited" /** Quota, billing, or usage-limit exhaustion; never spends backoff. */ | "quota" /** The planner request itself exceeded the model's context window. */ | "context_overflow"; /** Recovery categories emitted in recovery diagnostic sidecars. */ export type DiagnosticRecoveryCategory = "partial_length_recovery"; /** Safe model metadata subset (no credentials, no headers). */ export interface DiagnosticModelMeta { provider: string; id: string; api: string; contextWindow: number; maxTokens: number; } /** Full diagnostic payload written as the sidecar JSON file. */ export interface CompactionDiagnostic { version: 1; timestamp: string; failureCategory: DiagnosticFailureCategory; failureMessage: string; rawResponse: string; stopReason: string | undefined; providerError: string | undefined; usage: Usage | undefined; /** Undefined since the planner sends no output cap. */ requestMaxTokens: number | undefined; /** * Whether a retry was actually scheduled before the final failure. It records * retry activity, not the failure identity: a `rate_limited` record with retry * disabled is `false`, and `quota` is always `false`. */ rateLimitExhausted?: boolean; model: DiagnosticModelMeta; } /** Recovery diagnostic payload written on successful partial recovery. */ export interface RecoveryDiagnostic { version: 1; timestamp: string; recoveryCategory: DiagnosticRecoveryCategory; rawResponse: string; stopReason: string | undefined; usage: Usage | undefined; requestMaxTokens: number | undefined; model: DiagnosticModelMeta; recoveredRangeCount: number; } export interface DiagnosticContext { /** Absolute path to the persisted session file, or undefined for in-memory sessions. */ sessionFilePath: string | undefined; /** The model used for the planner request. */ model: Model; /** The maxTokens value sent in the planner request; undefined when no cap is sent. */ requestMaxTokens: number | undefined; /** The full assistant response message, if one was received. */ response: AssistantMessage | undefined; /** The full raw text extracted from the response. */ rawResponseText: string; /** The failure category. */ failureCategory: DiagnosticFailureCategory; /** The user-facing failure message. */ failureMessage: string; /** Retry activity for a limit failure; see CompactionDiagnostic. */ rateLimitExhausted?: boolean; } declare function buildDiagnosticPayload(ctx: DiagnosticContext): CompactionDiagnostic; /** * Derive the sidecar file path from a session file path and current timestamp. * The sidecar sits alongside the session file with a `-compaction-diagnostic-.json` suffix. */ export declare function diagnosticSidecarPath(sessionFilePath: string): string; /** * Attempt to write a diagnostic sidecar file. Returns the written path on * success, or undefined if the session is in-memory or writing fails. * * The file is written with mode 0o600 (owner-only read/write) on platforms * that support POSIX permissions. */ export declare function writeDiagnosticSidecar(ctx: DiagnosticContext): string | undefined; /** * Build the diagnostic payload without writing. Exported for testing. */ export { buildDiagnosticPayload }; /** Context for writing a recovery diagnostic sidecar. */ export interface RecoveryDiagnosticContext { sessionFilePath: string | undefined; model: Model; requestMaxTokens: number | undefined; response: AssistantMessage; rawResponseText: string; recoveryCategory: DiagnosticRecoveryCategory; recoveredRangeCount: number; } declare function buildRecoveryPayload(ctx: RecoveryDiagnosticContext): RecoveryDiagnostic; /** * Write a recovery diagnostic sidecar on successful partial recovery. * Returns the written path on success, or undefined if write fails or session is in-memory. * Write failures are silently swallowed — recovery success is never affected. */ export declare function writeRecoveryDiagnosticSidecar(ctx: RecoveryDiagnosticContext): string | undefined; export { buildRecoveryPayload }; /** Success categories emitted in success diagnostic sidecars. */ export type DiagnosticSuccessCategory = "borrowed_planner"; /** * Success diagnostic payload written when a *borrowed* fallback model ranked * the lines. The RFC requires the borrowed identity to appear in diagnostics but * defines no payload; this is the chosen shape, and it carries only safe data — * no key, headers, auth, prompt, or transcript. */ export interface SuccessDiagnostic { version: 1; timestamp: string; successCategory: DiagnosticSuccessCategory; /** Effective reasoning level of the successful planner request, when it had one. */ thinkingLevel?: string; /** Undefined since the planner sends no output cap. */ requestMaxTokens: number | undefined; model: DiagnosticModelMeta; } export interface SuccessDiagnosticContext { sessionFilePath: string | undefined; /** The model that actually produced the accepted ranking. */ model: Model; successCategory: DiagnosticSuccessCategory; thinkingLevel?: string; } declare function buildSuccessPayload(ctx: SuccessDiagnosticContext): SuccessDiagnostic; /** * Write a success sidecar naming the borrowed model that ranked the lines. * * Best-effort: an in-memory session or a write failure never changes compaction * success. The filename uses a distinct `-compaction-success-.json` suffix so * it cannot collide with the primary model's failure sidecar. */ export declare function writeSuccessDiagnosticSidecar(ctx: SuccessDiagnosticContext): string | undefined; export { buildSuccessPayload }; export declare function writeMorphDiagnosticSidecar(sessionFilePath: string | undefined, diagnostics: MorphRangeDiagnostics): string | undefined; //# sourceMappingURL=range-planner-diagnostics.d.ts.map