feat(core): keep a head preview in elided tool results

A bare placeholder tells the model only the tool name and size; the
first 400 characters tell it what the output actually was (a skill
guide reads very differently from a fetched page), which is what it
needs to judge whether re-running the tool is worth it. The preview is
capped at the threshold so small thresholds still shrink content, and
the transform stays a pure per-message function (byte-stable prefixes).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Ramnique Singh 2026-07-07 20:22:44 +05:30
parent 4fbca2dbad
commit 04348dcc41
2 changed files with 23 additions and 1 deletions

View file

@ -179,6 +179,18 @@ describe("elideHistoricToolResults", () => {
expect(elided[0].content).toContain("101");
});
it("keeps a head preview ahead of the placeholder", () => {
const content = `SKILL GUIDE${"y".repeat(5000)}`;
const [elided] = elideHistoricToolResults([toolMsg(content)], 2500);
// Starts with the first 400 chars verbatim, then the marker.
expect(elided.content.startsWith(content.slice(0, 400))).toBe(true);
expect(elided.content).toContain("Rest of tool result elided");
expect(elided.content.length).toBeLessThan(700);
// Preview never exceeds a tiny threshold.
const [tiny] = elideHistoricToolResults([toolMsg(content)], 50);
expect(tiny.content.startsWith(`${content.slice(0, 50)}\n[Rest`)).toBe(true);
});
it("keeps tool results at or below the threshold verbatim", () => {
const exact = toolMsg("x".repeat(100));
expect(elideHistoricToolResults([exact], 100)).toEqual([exact]);

View file

@ -54,6 +54,9 @@ export const DEFAULT_ELISION_POLICY: ElisionPolicy = {
// placeholder saves nothing and a short note may carry useful context.
const MIDDLE_PANE_CONTENT_FLOOR_CHARS = 500;
// Head of an elided tool result kept verbatim ahead of the placeholder.
const TOOL_RESULT_PREVIEW_CHARS = 400;
const ContextConfig = z.object({
elideHistoricToolResults: z.boolean().optional(),
elideHistoricToolResultsThresholdChars: z.number().int().min(0).optional(),
@ -102,9 +105,16 @@ export function elideHistoricToolResults(
) {
return message;
}
// Keep a head preview so the model knows what it is declining to
// re-fetch (a skill body reads very differently from a web page).
// Capped at the threshold so tiny thresholds still shrink content.
const preview = message.content.slice(
0,
Math.min(TOOL_RESULT_PREVIEW_CHARS, thresholdChars),
);
return {
...message,
content: `[Tool result elided to save context: "${message.toolName}" returned ${message.content.length} characters in an earlier turn. Call the tool again if you need this output now.]`,
content: `${preview}\n[Rest of tool result elided to save context: "${message.toolName}" returned ${message.content.length} characters in an earlier turn. Call the tool again if you need the full output now.]`,
};
});
}