/** * Observation Masker — Phase 1 of Advanced Token Optimization * * Masks old tool results (observations) in conversation history to reduce token usage. * Based on JetBrains Research (Dec 2025): observation masking outperforms LLM summarization, * achieving 52% cost reduction with +2.6% higher solve rates. * * Strategy: In-place masking of conversationHistory after N turns. * The agent can re-read from the environment if needed (files, git, etc.). */ import type { Message, ImageBlock, TextBlock } from '../providers/types.js'; /** * Defines which input fields to keep when compacting a tool_use input. * All other fields are removed. */ export interface InputCompactionRule { /** Fields to preserve in the compacted input */ keepFields: string[]; } export interface ObservationMaskConfig { /** Turns after which tool results are masked (default: 6) */ maskAfterTurns: number; /** Minimum content length (chars) to mask — skip tiny results (default: 400 ≈ 100 tokens) */ minCharsToMask: number; /** Tool names to NEVER mask (e.g., recall tools, state queries) */ neverMask: string[]; /** Tool names to mask after just 1 turn (large reads, bash output) */ alwaysMaskEarly: string[]; /** * Tool input compaction rules (Phase 1b). Keys are tool names. * After the turn threshold, large input fields are removed, keeping only the fields listed. * Set to false to disable input compaction entirely. * Default: compact edit (keep filePath) and write_file (keep path, mode). */ inputCompaction: Map | false; } export declare const DEFAULT_INPUT_COMPACTION: Map; export declare const DEFAULT_MASK_CONFIG: ObservationMaskConfig; interface TurnStamp { turn: number; toolName: string; /** Short summary of input for mask text (e.g., file path, command) */ inputSummary: string; /** Original content length in chars */ contentLength: number; } export interface MaskResult { maskedCount: number; tokensSaved: number; } export interface ObservationMaskStats { /** Total observations masked this session */ maskedCount: number; /** Estimated tokens saved this session */ tokensSaved: number; /** Active stamps (pending masking) */ activeStamps: number; /** Total tool_use inputs compacted this session (Phase 1b) */ inputsCompacted: number; } export declare class ObservationMasker { private readonly stamps; private readonly config; private stats; /** Tracks which image blocks have been stamped (by identity) to avoid re-stamping */ private readonly stampedImages; constructor(config?: Partial); /** * Register a tool result with its turn number and input context. * Called immediately after a tool result is added to the messages array. */ stamp(toolUseId: string, toolName: string, input: Record, contentLength: number, turn: number): void; /** * Stamp all image blocks in a message array with the current turn. * Call this after adding user messages that may contain images. */ stampImages(messages: Message[], turn: number): void; /** Turn at which each image block was first seen */ private readonly imageStamps; /** * Mask old tool results, images, and compact old tool_use inputs in-place. * - tool_result: replaces content with compact mask text (Phase 1) * - image: replaces with text placeholder after maskAfterTurns (Phase 2) * - tool_use input: strips large fields, keeping only identifying fields (Phase 1b) */ maskHistory(messages: Message[], currentTurn: number): MaskResult; getStats(): ObservationMaskStats; /** * Reset all state (stamps and stats). Used when clearing history. */ reset(): void; /** * Get current configuration (for testing/inspection). */ getConfig(): ObservationMaskConfig; /** * Compact a tool_use input in-place by stripping large fields. * Returns estimated tokens saved (0 if no compaction performed). */ private compactInput; } /** * Extract a short summary from tool input for the mask text. */ export declare function extractInputSummary(toolName: string, input: Record): string; /** * Build the compact mask text that replaces the original content. */ export declare function buildMaskText(stamp: TurnStamp): string; /** * Check if a tool result content string is already masked. */ export declare function isMasked(content: string): boolean; /** * Replace an image content block with a text placeholder. * Preserves filename and dimensions for context. */ export declare function maskImageBlock(block: ImageBlock, turn: number): TextBlock; export {};