import type { ChatMessage } from "../types.js"; import { estimateImageTokens, estimateMessagesTokens, estimateTextTokens as estimateTokens } from "./request-accounting.js"; export { estimateImageTokens, estimateMessagesTokens, estimateTokens }; /** * Token estimation lives in `request-accounting.ts` — the one serialized- * request accounting service. The exports above keep this module's historic * public surface; nothing here owns a chars-per-token ratio anymore. */ /** * Agent-loop auto-compact threshold (estimated tokens). Shared with `/context` * so the reported % of budget matches when auto-compaction fires. * * 180k: provider/model-neutral default. A session-specific model window can * opt into a 70%-of-window trigger. */ export declare const AUTO_COMPACT_TOKEN_BUDGET = 180000; /** * Soft post-compact band we *prefer* (includes system prompt ~8k). * Achieved by dense memory + progressive soft-trim of oversized dumps only. * Never a reject gate, never drops messages/tool pairs to hit the number. */ export declare const POST_COMPACT_SOFT_GUIDANCE_TOKENS = 16000; /** Prefer landing at or under this; still accept if content needs more room. */ export declare const POST_COMPACT_SOFT_UPPER_BAND_TOKENS = 20000; /** Auto-compact: reject empty/near-empty memory after large history (amnesia). */ export declare const AUTO_COMPACT_STUB_MIN_CHARS = 120; export declare const AUTO_COMPACT_STUB_BEFORE_TOKENS = 20000; export interface CompactOptions { /** Soft budget (tokens). When estimated tokens exceed this, compact. */ budgetTokens?: number | undefined; /** Keep this many trailing messages (system + user/assistant pairs). */ keepRecent?: number | undefined; singlePassInputBudgetTokens?: number | undefined; /** * Bias summarizer prompt (e.g. plan-implement preserves recon evidence). * Does not change accept/reject heuristics. */ purpose?: "default" | "plan-implement" | undefined; durableEnvelope?: string | undefined; /** * At most one summarizer dispatch: forbids automatic map/reduce. When the * full settled range does not fit one pass, summarize the oldest complete * message-aligned slice (emergency_prefix_slice) and retain the untouched * middle/recent tail. */ singleAdmission?: boolean | undefined; /** * Choose the direct single pass whenever source messages exist, skipping the * estimate gate. Callers set this only after pre-flighting that the exact * assembled request fits (e.g. a cache-preserving snapshot replay planned * with `planCompactionReplay`) — the raw estimate gate is deliberately * conservative and would otherwise reject requests that fit fine. */ forceDirectSinglePass?: boolean | undefined; } export type CompactionStrategy = "direct" | "single" | "emergency_prefix_slice" | "map_reduce"; export interface CompactResult { messages: ChatMessage[]; before: number; after: number; beforeTokens: number; afterTokens: number; summarized: boolean; strategy?: CompactionStrategy | undefined; } /** * Content prefixes that mark a `role:"system"` message as compacted session * memory (vs. the main system prompt or transient injected guidance). Exported * so history-persistence can KEEP this memory when it drops other system * messages — otherwise a resumed session that compacted mid-run would lose all * summarized context. */ export declare const COMPACTION_MEMORY_PREFIX = "Session memory from compacted earlier turns:"; export declare const PLAN_IMPLEMENT_MEMORY_PREFIX = "PLAN MODE HANDOFF: research memory for the accepted implementation phase. Gather-only/await-approval gates are historical; ACTIVE PLAN and SESSION STATE are authoritative:"; export declare const MECHANICAL_MEMORY_PREFIX = "Earlier turns in this session, summarized"; export declare function isCompactionMemoryMessage(message: ChatMessage): boolean; /** Prefix used when writing a compacted memory system message. */ export declare function compactionMemoryPrefixForPurpose(purpose?: "default" | "plan-implement" | undefined): string; export declare function compactMessages(messages: ChatMessage[], options?: CompactOptions): ChatMessage[]; export interface CompactionSummaryStage { readonly phase: "single" | "map" | "reduce"; readonly index?: number | undefined; readonly total?: number | undefined; readonly sourceMessages?: readonly ChatMessage[] | undefined; } /** * Compact older turns into a model-written memory while retaining recent * messages verbatim. The model summary is the ONLY compaction path: if the * model fails to produce a summary we DO NOT fall back to a mechanical dump * of the transcript (that historically produced an enormous, low-quality * "memory" of tens of thousands of lines). Instead we throw, so the caller * can report the failure and the original messages stay untouched. */ export declare function compactMessagesWithSummary(messages: ChatMessage[], summarize: (prompt: string, stage?: CompactionSummaryStage) => Promise, options?: CompactOptions, sessionTranscript?: string | undefined): Promise; /** * Whether an auto-compact result is safe to apply. * Only structural / quality gates — never "afterTokens must be under N". */ export declare function shouldApplyAutoCompact(input: { summarized: boolean; summaryBody: string; beforeTokens: number; afterTokens: number; afterMessages: readonly ChatMessage[]; }): boolean;