import type { ContextUsage } from "@earendil-works/pi-coding-agent"; import type { EpochConfig } from "./constants.ts"; export type PressureBand = "unknown" | "normal" | "soft" | "critical"; export interface PressureEvaluation { band: PressureBand; /** Tokens still available before the model context window is exhausted. */ remaining: number | null; remainingRatio: number | null; tokens: number | null; contextWindow: number | null; } export function evaluatePressure(usage: ContextUsage | undefined, config: EpochConfig): PressureEvaluation { if (!usage || usage.contextWindow <= 0 || usage.tokens === null) { return { band: "unknown", remaining: null, remainingRatio: null, tokens: null, contextWindow: usage?.contextWindow ?? null }; } const remaining = Math.max(0, usage.contextWindow - usage.tokens); const remainingRatio = remaining / usage.contextWindow; let band: PressureBand = "normal"; if (remainingRatio <= config.criticalRemainingRatio) band = "critical"; else if (remainingRatio <= config.softRemainingRatio) band = "soft"; return { band, remaining, remainingRatio, tokens: usage.tokens, contextWindow: usage.contextWindow }; } function percent(ratio: number): string { return `${Math.round(ratio * 100)}%`; } /** One-shot reminder texts. Never include running counters to avoid prompt churn. */ export function reminderText(band: "soft" | "critical", evaluation: PressureEvaluation): string { const remaining = evaluation.remainingRatio !== null ? percent(evaluation.remainingRatio) : "a small fraction"; if (band === "soft") { return ( `Context pressure is high: about ${remaining} of the model context window remains. ` + "If there is non-reconstructable state (explicit user decisions, constraints, blockers, acceptance criteria), preserve it with context_notes. " + "When the current working set is no longer useful, call new_context to start a fresh context epoch in the same session." ); } return ( `Context is near its safety boundary: about ${remaining} of the model context window remains. ` + "Preserve essential non-reconstructable state with context_notes now, then call new_context to continue in a fresh context epoch." ); }