/** * Smart Read Tool - 80% token reduction through intelligent caching and diff-based updates * * Features: * - Diff-based updates (send only changes) * - Automatic chunking for large files * - Syntax-aware truncation * - Cache integration with git awareness * - Metrics and cache provenance * * WHAT A CALL SAVED IS NOT COUNTED HERE, AND NEVER COULD BE. This tool used to * publish four figures about its own saving -- `tokensSaved`, `tokenCount`, * `originalTokenCount` and `compressionRatio` -- every one of them counted from * `finalContent`, the string the method returns. What a caller pays for is the * serialised reply built around that string after the tool has returned, with * its metadata block and its report text, and no code in this file can see it. * Measured on a 5,416-character source file at the 4,000-character chunk * default, the reply cost 1,765 tokens against the file's own 1,270 -- a 39% * LOSS -- and reported `tokensSaved: 316`. * * So both halves are counted by the one party that sees both: the recorder * reads the file named in the `path` argument for itself, and the server counts * the text it actually returns. This tool declares nothing either, because its * own argument already names its baseline. */ import { CacheEngine } from '../../core/cache-engine.js'; import { TokenCounter } from '../../core/token-counter.js'; import { MetricsCollector } from '../../core/metrics.js'; export interface SmartReadOptions { enableCache?: boolean; ttl?: number; diffMode?: boolean; maxSize?: number; chunkSize?: number; /** * Which chunk to return for a chunked file (0-based). * * Advertised in the tool schema from the start, but absent from this * interface and never read, so asking for chunk 2 silently returned chunk 1. */ chunkIndex?: number; preserveStructure?: boolean; includeMetadata?: boolean; encoding?: BufferEncoding; } /** * What a read reports about the file, alongside the content itself. * * EVERY FIELD HERE IS BILLED TO THE CALLER, which is why so few of them are * sent unasked. Measured on this repository's own `tool-profile.ts`, the header * cost 127 tokens of a 1167-token reply, against a first-read overhead of about * 150 -- so the header was very nearly the whole of this tool's loss on a first * read. Of those 127, `hash` was 41 (a sha256 in hex is close to the worst case * a BPE tokenizer has), `path` 29 restating the argument the caller had just * sent, `fileType` and `encoding` 7 each -- and nothing in this repository, * production or test, read any of the four. The four flags added 22 more while * all four were false. * * So `size` is unconditional, the four descriptive fields are sent when asked * for, and a flag appears only when it is TRUE: the false case is the ordinary * one and omission says it for nothing. `getMetadata` fills the whole shape, * because a caller that wants metadata and no content wants exactly these. */ export interface SmartReadMetadata { /** Present when asked for, or on an explicit metadata read. */ path?: string; size: number; /** Present when asked for, or when the file was not read as utf-8. */ encoding?: string; /** Present when asked for, or on an explicit metadata read. */ fileType?: string; /** Content hash. Present when asked for, or on an explicit metadata read. */ hash?: string; /** Present only when the content came from the cache. */ fromCache?: boolean; /** Present only when the content is a diff against an earlier read. */ isDiff?: boolean; /** Present only when the file was split into chunks. */ chunked?: boolean; /** Present only when the content was cut short. */ truncated?: boolean; /** How many chunks the file was split into; present only when chunked. */ chunkCount?: number; /** Which chunk this response carries; present only when chunked. */ chunkIndex?: number; } export interface SmartReadResult { content: string; metadata: SmartReadMetadata; diff?: { added: string[]; removed: string[]; unchanged: number; }; } export declare class SmartReadTool { private cache; private metrics; constructor(cache: CacheEngine, _tokenCounter: TokenCounter, metrics: MetricsCollector); /** * Smart read with aggressive token optimization */ read(filePath: string, options?: SmartReadOptions): Promise; /** * Read a specific chunk from a chunked file */ readChunk(filePath: string, chunkIndex: number, chunkSize?: number): Promise; /** * Get file metadata without reading content (minimal tokens) */ getMetadata(filePath: string): Promise; } export declare function getSmartReadTool(cache: CacheEngine, tokenCounter: TokenCounter, metrics: MetricsCollector): SmartReadTool; /** * CLI function - Creates resources and uses factory */ export declare function runSmartRead(filePath: string, options?: SmartReadOptions): Promise; export declare const SMART_READ_TOOL_DEFINITION: { name: string; description: string; annotations: { title: string; readOnlyHint: boolean; destructiveHint: boolean; idempotentHint: boolean; openWorldHint: boolean; }; inputSchema: { type: string; properties: { path: { type: string; description: string; }; diffMode: { type: string; description: string; default: boolean; }; maxSize: { type: string; description: string; default: number; }; chunkSize: { type: string; description: string; default: number; }; chunkIndex: { type: string; description: string; }; enableCache: { type: string; description: string; default: boolean; }; ttl: { type: string; description: string; default: number; }; preserveStructure: { type: string; description: string; default: boolean; }; includeMetadata: { type: string; description: string; default: boolean; }; encoding: { type: string; description: string; default: string; }; }; required: string[]; }; }; //# sourceMappingURL=smart-read.d.ts.map