Skip to content

Commit ecd0380

Browse files
committed
Merge branch 'main' into cl-6904-grokopenai-responses-adapters-omit-prompt_cache_key-xai
2 parents 7b53e34 + 7df3956 commit ecd0380

2 files changed

Lines changed: 70 additions & 2 deletions

File tree

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { describe, expect, test } from "bun:test";
2+
import { createSizeCapTransform } from "@intx/inference";
3+
import type { StrategyContext, ToolResult } from "@intx/types/runtime";
4+
import { MAX_RESULT_CHARS, truncateToolResultContent } from "./result-truncation-plugin.js";
5+
6+
describe("truncateToolResultContent", () => {
7+
test("within-cap content passes through unchanged", () => {
8+
const content = "x".repeat(100);
9+
expect(truncateToolResultContent(content)).toBe(content);
10+
});
11+
12+
test("oversized content gets a marker that never promises retrievable remainder", () => {
13+
const content = "x".repeat(MAX_RESULT_CHARS + 500);
14+
const truncated = truncateToolResultContent(content);
15+
16+
expect(truncated).toContain("[output truncated");
17+
expect(truncated).toContain("NOT retrievable");
18+
// The pre-cap discard must never be described as recoverable elsewhere.
19+
expect(truncated).not.toContain("see the rest");
20+
expect(truncated).not.toContain("Full output available");
21+
});
22+
23+
test("truncation marker survives the size-cap blob spill", async () => {
24+
// Reproduce the production pipeline for an output over MAX_RESULT_CHARS:
25+
// truncation runs first (at the tool), size-cap spills the already-cut
26+
// text to a blob and tells the model the blob holds the full output. The
27+
// blob's tail must therefore carry the honest "discarded, NOT retrievable"
28+
// marker so the model does not loop re-running the command.
29+
const original = "x".repeat(MAX_RESULT_CHARS + 500);
30+
const truncated = truncateToolResultContent(original);
31+
32+
const blobs = new Map<string, string>();
33+
const transform = createSizeCapTransform({
34+
maxChars: 10_000,
35+
contextStore: {
36+
writeBlob: async (key: string, bytes: Uint8Array) => {
37+
blobs.set(key, new TextDecoder().decode(bytes));
38+
},
39+
},
40+
});
41+
42+
const result: ToolResult = {
43+
callId: "call-1",
44+
content: truncated,
45+
isError: false,
46+
};
47+
const { output } = await transform.apply(
48+
{ call: { id: "call-1", name: "run_shell", arguments: {} }, result },
49+
{} as StrategyContext,
50+
);
51+
52+
const spilled = blobs.get("call-1");
53+
expect(spilled).toBe(truncated);
54+
// The blob's tail tells the truth about the pre-spill discard.
55+
expect(spilled).toContain("NOT retrievable");
56+
expect(spilled?.endsWith("Use offset/limit or a narrower query.]")).toBe(true);
57+
// The inline marker's blob promise is now genuine: the blob really does
58+
// hold everything that still exists.
59+
expect(output.content).toContain("tool-output:///call-1");
60+
});
61+
});

src/plugins/result-truncation-plugin.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,17 @@ export function truncateToolResultContent(
2020
if (content.length <= maxChars) return content;
2121

2222
const remaining = content.length - maxChars;
23+
// Truncation happens here, at the source — before the reactor's size-cap
24+
// transform spills to a tool-output:/// blob. The blob therefore holds only
25+
// this already-truncated text, so the marker must say the remainder is gone:
26+
// a "see the blob for the rest" promise would send the model chasing content
27+
// that does not exist and re-running the command in a loop.
2328
return (
2429
content.slice(0, maxChars) +
25-
`\n[output truncated — ${remaining.toLocaleString()} characters omitted. ` +
26-
`Use offset/limit params or a more targeted query to see the rest.]`
30+
`\n[output truncated at ${maxChars.toLocaleString()} chars — ` +
31+
`${remaining.toLocaleString()} chars discarded, NOT retrievable ` +
32+
`(no tool-output URI has them; re-running gives the same cut). ` +
33+
`Use offset/limit or a narrower query.]`
2734
);
2835
}
2936

0 commit comments

Comments
 (0)