import type { IdeAdapter } from '../ide-types.js'; import { resolveClaudeTranscriptPath } from './claude-code-local-state.js'; import { modelContextWindowTokens, resolveClaudeModelFromEnv } from './claude-code-model-window.js'; export { resolveClaudeTranscriptPath }; export { modelContextWindowTokens, resolveClaudeModelFromEnv }; /** * the context-window size in tokens. Third-party / proxied models whose id * carries no `[1M]` suffix (and is absent from the hardcoded allowlist) are * otherwise stuck at the 200K default, which inflates `ratio` up to 5×. * Same spirit as `PEAKS_CALLER_ID`: an env var the user can export, plus a * machine-wide `peaks config set --key context.windowTokens` twin. */ export declare const CONTEXT_WINDOW_TOKENS_ENV_VAR = "PEAKS_CONTEXT_WINDOW_TOKENS"; /** * Which layer produced a resolved context window: * - `env-override` — `PEAKS_CONTEXT_WINDOW_TOKENS` * - `harness-env` — the window peaks-loop itself wrote into the * harness's machine-local settings * (`CLAUDE_CODE_AUTO_COMPACT_WINDOW`, declared by * the adapter as `autoCompactWindowEnvVar`); see * `harness-window-config.ts` * - `config` — `context.windowTokens` (`peaks config set`) * - `model-heuristic` — `[1M]` suffix / `ONE_MILLION_CONTEXT_MODELS` * - `default` — 200K safe default */ export type ContextWindowSource = 'env-override' | 'harness-env' | 'config' | 'model-heuristic' | 'default'; export interface ContextWindowResolution { readonly tokens: number; readonly source: ContextWindowSource; } /** Explicit override inputs (both optional; env wins over config). */ export interface ContextWindowOverrides { readonly env?: NodeJS.ProcessEnv | undefined; /** Raw `context.windowTokens` value (unvalidated — validated here). */ readonly configWindowTokens?: unknown; /** * The window peaks-loop itself configured for the harness — raw value read * from the harness's machine-local settings `env` block (or the process env * the harness populated from it). Resolved by the caller * (`auto-compact-reader.ts` via `harness-window-config.ts`), NOT here: the * settings path and the key name are per-IDE declarations. * * the ratio peaks-loop computes and the window the harness compacts against * THE SAME NUMBER. Without it they are two independent resolutions that can * drift 5× apart on a 1M-window model the model-name heuristic misses. */ readonly harnessWindowTokens?: unknown; /** * Provenance of `harnessWindowTokens`: true when peaks-loop wrote that value * itself, false/omitted when a human set it. Only a peaks-written value may * be overruled by the late 1M rescue — see `resolveContextWindowTokens`. */ readonly harnessWindowPeakWritten?: boolean; /** Warning sink for an invalid override (defaults to `console.warn`). */ readonly onInvalidOverride?: ((message: string) => void) | undefined; /** * Warning sink for the E2 notice: a HUMAN pin (`env-override` / `config`) * larger than peaks-loop's own model-window estimate. Deliberately separate * from `onInvalidOverride` — that sink means "your value could not be read", * this one means "your value was read, and peaks-loop cannot promise the * harness will use it". Two meanings, two sinks. Defaults to `console.warn`. * * See `describeWindowAboveModelEstimate` for what the notice does and does * not claim, and why it is a notice rather than a refusal. */ readonly onAboveModelEstimate?: ((message: string) => void) | undefined; } /** * peaks-loop's model-window estimate. `null` when there is nothing to say. * * WHY THIS EXISTS. The harness does not take its auto-compact window on faith: * it reduces the configured value with `Math.min(native, override)`, where * `native` is the model's own context size. A pin ABOVE `native` is therefore * accepted by peaks-loop, written to the harness's settings, and silently * reduced by the harness — so peaks-loop's ratio divides by a window the * harness is not compacting on. That is the A1 drift (two independent * resolutions, one number meaning two things), arriving from the other side * from E1: E1 is a value the harness refuses, E2 a value it quietly lowers. * * CAN WE READ THE NATIVE WINDOW? No, and the notice does not pretend otherwise. * There is no API for it and no environment variable that carries it; the only * local knowledge is `modelContextWindowTokens`'s NAME HEURISTIC (200_000, or * 1_000_000 when the id says `1m` or matches the allowlist) — the very layer * this slice demoted, because it is wrong for precisely the proxied / * third-party models the pin exists for. It is used here ONLY as a disagreement * DETECTOR, never as an authority, and the uncertainty that buys is one-sided: * this notice means "peaks-loop's best guess of your model is smaller than the * window you pinned", which a genuinely larger model ALSO produces. It cannot * mean "the harness will definitely cap you" — only the harness knows that. * * WHY A NOTICE AND NOT A REFUSAL. The pin is the documented escape hatch for a * model id the heuristic cannot see, so refusing would delete the feature. The * notice keeps it and states the risk. * * WHY ONLY THE HUMAN PINS. The `harness-env` layer is a value peaks-loop FOUND * in the harness's own file, not one this call is setting; its disagreements are * already reported by the writer (`harnessWindowSyncWarning`), and warning here * would fire on every probe of a project whose file legitimately says 1000000. */ export declare function describeWindowAboveModelEstimate(input: { readonly model: string; readonly tokens: number; /** Human-readable name of the pin, e.g. `PEAKS_CONTEXT_WINDOW_TOKENS="500000"`. */ readonly pin: string; }): string | null; /** * Parse an explicit context-window override. Accepts a positive finite * integer only (number, or a numeric string so an env var works); anything * else — `0`, negative, `NaN`, `Infinity`, `1.5`, `'abc'`, `''` — returns * null so the caller can ignore it and fall through to the next layer. */ export declare function parseContextWindowOverride(raw: unknown): number | null; /** * Resolve the context window in tokens AND report which layer won * (first hit wins): * 1. env `PEAKS_CONTEXT_WINDOW_TOKENS` * 2. config `context.windowTokens` * 3. `harnessWindowTokens` — the window peaks-loop configured for the * harness (`autoCompactWindowEnvVar`) * 4. model-name heuristic (`modelContextWindowTokens`) * 5. `DEFAULT_CONTEXT_WINDOW_TOKENS` (200_000) * * An invalid explicit override is ignored with a warning and falls through * to the next layer — a typo must never crash or silently win the probe. * * The hard constraint is that the window peaks-loop divides by must BE the * window it configured for the harness. Layer 3 is peaks-loop's own output * read back from the harness's settings file, so the two sides reference one * artifact rather than two independent resolutions. It sits ABOVE the model * heuristic because a heuristic contradicting the number both sides already * use is precisely how the 5× drift arose (a 1M-window model measured * against a 200K guess). * * Why it sits BELOW the two explicit pins. Both are human declarations of * intent, and a pin that loses to a value peaks-loop wrote earlier is a * silently ignored setting: the user changes `context.windowTokens`, the * stale harness key shadows it, and nothing appears to happen. Conflict is * instead resolved by PROPAGATION — the probe that resolves the pin also * syncs it into the harness (`syncHarnessWindowForProject`), so peaks-loop's * ratio and the harness's window are back on one number before that command * returns. The conflict is therefore transient and self-healing rather than * either silent shadowing or permanent drift. * * Why layer 3 is not simply authoritative (the ratchet). Reading back a value * peaks-loop wrote makes peaks-loop trust its own earlier resolution, so a * first-time mis-resolution (an unrecognised model defaulting to 200_000) * would be written to disk and then believed forever: the ratio stays * saturated at 1.0 while the session grows past the pin, and the correction * that exists for exactly that case — the late 1M rescue — was disabled for * this layer. Self-locking. See `resolveContextWindowTokens` for how the * rescue is let back in without letting it overwrite a human's own setting. */ export declare function resolveContextWindow(model: string, overrides?: ContextWindowOverrides): ContextWindowResolution; export declare const CLAUDE_CODE_ADAPTER: IdeAdapter;