Skip to content

Commit d361eab

Browse files
committed
test(agents): cover exact tool-result UTF-16 cuts
1 parent e050e01 commit d361eab

2 files changed

Lines changed: 23 additions & 32 deletions

File tree

src/agents/embedded-agent-runner/tool-result-truncation.test.ts

Lines changed: 22 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -167,42 +167,33 @@ describe("truncateToolResultText", () => {
167167
expect(result.length).toBeGreaterThan(250);
168168
});
169169

170-
it("does not split surrogate pairs when truncating at a char boundary", () => {
171-
// Emoji 😀 is U+1F600, a surrogate pair (2 UTF-16 code units).
172-
const text = "a".repeat(100) + "😀".repeat(10) + "b".repeat(100);
173-
const result = truncateToolResultText(text, 50);
174-
expect(result.length).toBeLessThan(text.length);
175-
expect(hasLoneSurrogate(result)).toBe(false);
170+
it("keeps direct and suffix-only cuts on complete code points", () => {
171+
expect(
172+
truncateToolResultText("aaa😀z", 5, {
173+
suffix: "!",
174+
minKeepChars: 0,
175+
}),
176+
).toBe("aaa!");
177+
expect(
178+
truncateToolResultText("abcdef", 1, {
179+
suffix: "😀",
180+
minKeepChars: 0,
181+
}),
182+
).toBe("");
176183
});
177184

178-
it("does not split surrogate pairs in head+tail error-preserving truncation", () => {
179-
const head = "line\n".repeat(500);
180-
const middle = "x".repeat(10_000);
181-
// Place an emoji so it sits right at the tail budget boundary.
182-
const tail = "\nError: process failed 😀\n";
183-
const text = head + middle + tail;
184-
const result = truncateToolResultText(text, 5_000);
185-
expect(result).toContain("Error: process failed");
186-
expect(hasLoneSurrogate(result)).toBe(false);
185+
it("keeps both head and tail cuts on complete code points", () => {
186+
const marker = "\n\n⚠️ [... middle content omitted — showing head and tail ...]\n\n";
187+
const text = `${"a".repeat(6)}😀${"m".repeat(100)}😀${"x".repeat(22)} Error`;
188+
expect(
189+
truncateToolResultText(text, 100, {
190+
suffix: "!",
191+
minKeepChars: 1,
192+
}),
193+
).toBe(`${"a".repeat(6)}${marker}${"x".repeat(22)} Error!`);
187194
});
188195
});
189196

190-
function hasLoneSurrogate(value: string): boolean {
191-
for (let index = 0; index < value.length; index++) {
192-
const code = value.charCodeAt(index);
193-
if (code >= 0xd800 && code <= 0xdbff) {
194-
const next = value.charCodeAt(index + 1);
195-
if (next < 0xdc00 || next > 0xdfff) {
196-
return true;
197-
}
198-
index++;
199-
} else if (code >= 0xdc00 && code <= 0xdfff) {
200-
return true;
201-
}
202-
}
203-
return false;
204-
}
205-
206197
describe("getToolResultTextLength", () => {
207198
it("sums all text blocks in tool results", () => {
208199
const msg: ToolResultMessage = {

src/agents/embedded-agent-runner/tool-result-truncation.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -516,7 +516,7 @@ function formatAggregateElisionText(
516516
if (spillMarkers?.compact && spillMarkers.compact.length <= remainingTextBudget) {
517517
return spillMarkers.compact;
518518
}
519-
return truncateUtf16Safe(AGGREGATE_ELISION_MARKER, remainingTextBudget);
519+
return AGGREGATE_ELISION_MARKER.slice(0, remainingTextBudget);
520520
}
521521

522522
/**

0 commit comments

Comments
 (0)