/** * Shared registry (client + server) of models and settings of the agent of * cloud code (MIN-46). NO server-only import: the model picker (UI) and the * server-side resolution (`lib/server/agent/model.ts`) both import it. */ export interface AgentModelOption { /** id OpenRouter au format `provider/model`. */ id: string; /** label displayed in the picker. */ label: string; /** short note (cost / usage). */ hint?: string; } /** * Curated labels from a handful of top models. The picker (launch + * personal default) is NO LONGER limited to this list: it searches the entire index * OpenRouter (`/api/agent/models`) and formats the names via `formatModelName`. * This list is only used to provide nice known labels (cf. `model-display`) * for these specific ids. Ids to be confirmed against OpenRouter index `/models`. */ export const AGENT_ALLOWED_MODELS: AgentModelOption[] = [ { id: "deepseek/deepseek-v4-flash", label: "DeepSeek V4 Flash", hint: "Économique · défaut" }, { id: "anthropic/claude-sonnet-5", label: "Claude Sonnet 5", hint: "Équilibré, fort en code" }, { id: "anthropic/claude-opus-4.8", label: "Claude Opus 4.8", hint: "Qualité maximale" }, ]; // ── Context compaction (hardening, very long runs) ──────────────────── /** Beyond this estimation of tokens, we summarize the middle of the history. Conservative (proxy characters/4 which underestimates the code): safe on models with a wide window (DeepSeek, Claude…). */ export const AGENT_COMPACT_TOKEN_THRESHOLD = 70_000; /** * CALIBRATED VALUE of the compaction threshold: what it is worth at the reference price, and * the FLOOR under which it never falls (see `agentCompactThreshold`, which * transposes it to the price of the run model — it was a fixed ceiling until MIN-248). * * Window ≠ budget. The threshold was 75% of the window, and the models we * use have windows from 1M to 1.05M → effective threshold ~787,000 tokens, i.e. * 5× our largest checkpoint. Result: compaction never ran a single time in production (MIN-113). What caps a long session is not * the window, it is the COST PER ROUND: we return the entire history with each call * and — measured, not assumed — prompt caching does not amortize it (the breakpoints of * `caching.ts` are at the top of the history, so the block hidden remains frozen while * the history increases; the observed cost sticks to the full input rate to within 2%). * * 120,000 comes from the MIN-101 measurement of 814 agent `ai_usage` calls: * • p90 of real context = 86,815 tokens → a normal session never sees it; * • max observed = 158,301 → a cap at 180k would NEVER trigger, this which * would reproduce the bug that we are correcting here; * • summarizing is amortized in ~1.2 rounds from 60 k, so it is the quality (do not * harass the short sessions) which sets the floor, not the profitability. * To be reviewed if the distribution of contexts changes — not to the blind man. */ export const AGENT_COMPACT_BASELINE_TOKENS = 120_000; /** * Entry price AT WHICH the 120,000 above were calibrated: that of model * minddy's default at the time of measurement (`deepseek/deepseek-v4-flash`, * $0.14/Mtok). This is what makes the cap TRANSPOSABLE: at this price, the prompt * of a round costs $0.0168, and it is this COST PER ROUND — not the number of * tokens — that the cap actually limits. */ export const AGENT_COMPACT_REFERENCE_INPUT_USD_PER_MTOK = 0.14; /** * How high the cap can GO for a cheap model. Twice the value * calibrated, and the reason is that of MIN-113: the largest context ever * observed on the agent is 158,301 tokens. A threshold beyond 240,000 would therefore never trigger — that is, it would not exist. */ export const AGENT_COMPACT_MAX_TOKENS_CEILING = 240_000; /** * COMPACTION THRESHOLD of the run, derived from the model rather than written in hard copy. * * The 120,000 cap said a real thing — "a round should not cost * more than that to return" — but it said it in TOKENS, which does not mean la * same as for a model at the same price. Between `deepseek-v4-flash` * ($0.14/Mtok) and `claude-opus-4.8` ($5/Mtok), the same history of 120,000 * tokens costs $0.017 or $0.60 per round: thirty-six times the difference, for a * safeguard supposed to limit an expense. * * The threshold is therefore read in dollars and is converted into tokens at the price of the model. * Three terminals, and each repairs a known fault: * * - **floor to `AGENT_COMPACT_BASELINE_TOKENS`** — we never GO * below the calibrated value, even for a very expensive model. Compacting earlier * saves nothing: the model buys back in rereads what is taken from it, and * is exactly MIN-248. An expensive model therefore keeps the behavior * of today, down to the byte. * - **ceiling at `AGENT_COMPACT_MAX_TOKENS_CEILING`** — a threshold that runs * never reach has never been triggered (MIN-113). * - **and always ≤ 75% of the window**: the budget cannot exceed what * the model can read. * * `inputUsdPerMTok` unknown (BYOK provider outside the OpenRouter catalog) or null * (free model) → we fall back on the calibrated value. Ignoring the price is not * a license to move up: it is a reason not to move. */ export function agentCompactThreshold(opts: { contextWindow?: number | null; inputUsdPerMTok?: number | null; }): number { const fromWindow = opts.contextWindow ? Math.floor(opts.contextWindow * 0.75) : AGENT_COMPACT_TOKEN_THRESHOLD; const price = opts.inputUsdPerMTok; const affordable = typeof price === "number" && Number.isFinite(price) && price > 0 ? Math.floor( (AGENT_COMPACT_BASELINE_TOKENS * AGENT_COMPACT_REFERENCE_INPUT_USD_PER_MTOK) / price, ) : AGENT_COMPACT_BASELINE_TOKENS; const budget = Math.min( Math.max(affordable, AGENT_COMPACT_BASELINE_TOKENS), AGENT_COMPACT_MAX_TOKENS_CEILING, ); return Math.min(fromWindow, budget); }