/** * A FILE'S TEXT, SENT AS TEXT RATHER THAN AS AN ESCAPED JSON STRING. * * Every tool answers with one object, serialised once by the dispatch, so a * tool that returns a file returns that file as a JSON string value: each * newline costs two characters instead of one, each quote two, each backslash * two. Measured through smart_read on three thousand characters of this * repository's own source -- under the disclosure threshold, so the reply is * exactly what the dispatch built -- the escape of the `content` field alone * cost 255, 162 and 243 tokens on replies of 1,089, 965 and 1,021. That is * between a sixth and a quarter of the whole reply spent on nothing: the caller * learns not one thing from a doubled newline. * * MCP replies are a LIST of parts, and the text parts are joined. So the escape * is avoidable without withholding anything: the envelope goes in one part as * JSON, the field's text goes in the next as itself, and the envelope names * where it went. `restoreTextPart` below puts it back, and a round-trip test * holds the two halves to each other -- that function is the whole claim that * the caller's information is unchanged. * * WHY HERE AND NOT IN THE DISPATCH. Progressive disclosure re-renders a large * reply into a preview, and its `parseShape` already routes a long string field * through a nested shape pass -- which de-escapes it as a side effect. So a * disclosed reply has no escape tax to recover and splitting it first would * hand disclosure a body that is no longer JSON. This runs only where * disclosure declined, which is every reply too small to be disclosed. */ /** The key the envelope carries to say which field moved, and where. */ export declare const TEXT_PART_KEY = "_textPart"; /** What the envelope says about the part that follows it. */ export interface TextPartNote { /** The field the text belongs to, as a dotted path from the payload's root. */ readonly path: string; /** Which text part of this reply holds it. */ readonly index: number; } /** A reply's text parts, which is all this module reads or writes. */ export interface TextParts { readonly content: Array<{ type: string; text: string; }>; } /** * Sends the payload's largest string field as text instead of as a JSON string. * * Returns the parts unchanged whenever the split would not plainly pay: a reply * that is not a single JSON object, a payload already carrying the envelope key, * no string field, or a saving under the threshold. BY LENGTH, MEASURED on both * renderings rather than estimated from the field -- the envelope keeps its own * braces and gains a key, and only the difference between the two complete * renderings says whether the caller is better off. */ export declare function liftTextPart(parts: TextParts['content']): { content: Array<{ type: string; text: string; }>; lifted: boolean; }; /** * The payload a caller would have received as one object, rebuilt from the parts. * * THIS IS THE CLAIM, NOT A CONVENIENCE. Splitting a reply is only honest if the * caller can get back exactly what it would have had, so the inverse ships with * the transform and a round-trip test holds them to each other. A reply that was * never lifted parses straight through, so a caller can route every reply * through this and never ask which kind it has. * * EQUAL AS DATA, NOT BYTE FOR BYTE. Removing a key and putting it back leaves it * last rather than where it was, so the rebuilt object deep-equals the original * while its serialisation may order two keys differently. Key order carries no * information in JSON, and preserving it would mean spending characters in the * envelope to record a position -- which is the cost this whole module exists to * remove. */ export declare function restoreTextPart(parts: TextParts['content'] | undefined): unknown; //# sourceMappingURL=text-part.d.ts.map