import type { GenericMessage, TranscriptMessage } from '../../../shared/types.js'; import type { ToolResult } from '../../../shared/toolResult.js'; import { ConversationMessage, AssistantMessage, ToolRelayMessage } from '../ConversationMessage/index.js'; /** Looks up whether a tool's results go stale after a mutation. */ export type VolatileLookup = (toolName: string | undefined) => boolean; export type LiveProjectionOptions = { keepRecentTurns?: number; tokenBudget?: number; }; /** * The single, append-only source of truth for a conversation. * * Canonical messages are never mutated for pruning — the views fed to the LLM, * persistence, and the transcript are produced by **pure, deterministic** * projections that compress copies. This replaces the old in-place pruning of * `Session.messages` (and its `unprunedMessages` snapshot side-channel). * * The mutators here represent legitimate conversation construction (append a * turn, merge a system note, finalise a placeholder, rollback, restore); none * of them is pruning. */ export declare class ConversationHistory { /** Recent user turns kept uncompressed (age ceiling). */ static readonly KEEP_RECENT_TURNS = 8; /** Token budget for full tool-result content kept live (size ceiling). */ static readonly DEFAULT_LIVE_TOKEN_BUDGET = 6000; /** User turns kept when projecting for persistence. */ static readonly PERSISTENCE_KEEP_TURNS = 7; private messages; get length(): number; append(message: ConversationMessage): void; /** Append a synthetic assistant `tool_use` + the matching tool-result relay. */ appendToolExchange(assistant: AssistantMessage, relay: ToolRelayMessage): void; /** * Append user text, merging into the last message when it is already a * user-role message (the API rejects consecutive same-role messages). */ mergeOrAppendUserText(text: string): void; /** * Replace one result in the last relay (a server tool finalising a * placeholder it committed earlier — e.g. create_plan after approval). */ replaceLastResult(toolUseId: string, result: ToolResult): void; peekLast(): ConversationMessage | undefined; popLast(): void; /** * Remove the Nth real user turn (0-based) and everything after it. Throws if * the index is out of range. */ truncateAtUserTurn(index: number): void; /** Replace the whole history from a persisted wire snapshot (restore). */ replaceAll(wire: GenericMessage[]): void; clear(): void; countUserTurns(): number; /** Canonical messages as wire form — the "full" (uncompressed) view. */ toWireAll(): GenericMessage[]; /** * Pure, deterministic projection of the conversation for the LLM. Returns a * fresh `GenericMessage[]` exactly as long as the canonical history (1:1, so * tool_use/tool_result pairing, turn indexing, and the cache-stable prefix * are preserved). Never mutates canonical. * * Two ceilings combine (compress the union of both): * - age: user turns older than `keepRecentTurns` are compressed; * - size: tool-result rounds beyond `tokenBudget` (newest always kept full). * Plus eager volatile staleness: a volatile query result with a later * mutation collapses to `{ stale }` regardless of recency. */ projectForLlm(isVolatile: VolatileLookup, opts?: LiveProjectionOptions): GenericMessage[]; /** * Pure projection for persistence: trim to the last `keepTurns` user turns and * aggressively compress (all tool results compressed, aux + old tool_use inputs * stripped). No volatile metadata. Output is hashed + stored by the caller. */ projectForPersistence(opts?: { keepTurns?: number; }): GenericMessage[]; /** * Transcript rows: `seen` is the projected (compressed) content the LLM sees, * `full` the canonical content — included only when it differs. */ buildTranscriptMessages(isVolatile: VolatileLookup, opts?: LiveProjectionOptions): TranscriptMessage[]; private userTurnStartIndices; /** Index before which messages are compressed by the age ceiling (0 = none). */ private ageCompressBoundary; /** Index before which messages are compressed by the token-budget ceiling. */ private budgetCompressBoundary; /** * Tool-use ids of volatile query results that a later mutation made stale. * Single backward scan tracking whether a mutation has been seen yet. */ private computeStaleToolUseIds; }