/** * Images, which until now were invisible to every part of this. * * THE HOLE. `mapBlocks` visits a block only when `typeof block.text === * 'string'`, so an image block was never classified, never compressed, never * deduplicated and never even counted. That is not a small omission: an * Anthropic image costs roughly `width * height / 750` tokens, so one * 1456x816 screenshot is about 1,585 tokens -- more than the entire knowledge * block -- and it is re-sent as conversation history on every subsequent turn * for the rest of the session. * * A BROWSER-DRIVING AGENT SENDS THE SAME SCREENSHOT REPEATEDLY, and that is * the case worth catching. Take a screenshot, act, take another, compare; * navigate back, screenshot again. Identical bytes, paid for each time. Text * dedup already handles the analogous case for tool output, and the argument * transfers exactly: the referent is inside the same request, so a * back-reference cannot miss the way a hash into an external cache can. * * WHAT IS NOT DONE HERE, and why. Re-encoding or downscaling an image needs a * codec, and every usable one is a heavy native dependency -- which is * precisely the "incompatible with restricted sandboxes" problem this project * exists to avoid having. So resizing is an OPTIONAL hook a caller may supply, * and the default is to leave the pixels alone. Dedup and accounting need no * dependency at all, and they are where the repeated-screenshot money is. * * DIMENSIONS ARE READ FROM THE HEADER, not by decoding. PNG puts width and * height in the IHDR chunk at a fixed offset; JPEG carries them in the first * SOFn marker. Both are a few dozen bytes in, so the cost is parsing a header * rather than rasterising an image, and an unrecognised format simply declines * to estimate rather than guessing. */ /** An image found in a request, with where it was and what it costs. */ export interface ImageBlock { /** The base64 payload, which is also its identity. */ readonly data: string; readonly mediaType: string; /** Estimated tokens, or null when the header was not recognised. */ readonly tokens: number | null; readonly width: number | null; readonly height: number | null; } /** Below this an image is not worth a back-reference. */ export declare const MIN_DEDUP_IMAGE_CHARS = 1000; /** Is this content block an image, in either shape a provider accepts? */ export declare function isImageBlock(block: unknown): boolean; /** * Reads an image's dimensions from its header. * * Only the first kilobyte is decoded, which is far more than any of these * headers needs and avoids materialising a multi-megabyte buffer to read six * bytes out of it. */ export declare function imageSize(data: string): { width: number; height: number; } | null; /** What one image block costs, as far as we can tell. */ export declare function describeImage(block: unknown): ImageBlock | null; /** The back-reference that replaces a repeated image. */ export declare function imageBackReference(image: ImageBlock, ordinal: number): string; export interface ImageDedupResult { /** One entry per input block: the replacement, or null to keep as-is. */ readonly replacements: readonly (string | null)[]; /** Estimated tokens removed, for reporting. */ readonly tokensSaved: number; /** How many repeats were collapsed. */ readonly collapsed: number; } /** * Replaces repeated images with a reference to the copy already in the request. * * Same rules as text dedup, for the same reasons. The FIRST occurrence is * always kept whole because it is what every later reference points at, and an * untouchable block -- signed, or behind the cache frontier -- is never * rewritten but is still the strongest referent there is. * * Lossless: the image is still in the request, above, and the reference names * which one. There is nothing to look up and nothing to miss. */ export declare function dedupImages(blocks: readonly { readonly block: unknown; readonly touchable: boolean; }[]): ImageDedupResult; //# sourceMappingURL=images.d.ts.map