From a97e80ca37b60ecaaa1ff7a23a9627b3d08654be Mon Sep 17 00:00:00 2001 From: JackChen Date: Thu, 16 Apr 2026 17:36:51 +0800 Subject: [PATCH] fix: truncateToolOutput may exceed maxChars when limit < marker overhead - Fall back to hard slice when maxChars is too small to fit the marker - Fix misplaced JSDoc for outputSchema in AgentConfig - Tighten test assertion to verify length <= maxChars --- src/tool/executor.ts | 8 +++++++- src/types.ts | 10 +++++----- tests/tool-executor.test.ts | 4 ++-- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/tool/executor.ts b/src/tool/executor.ts index 2a48db1..ec5ee82 100644 --- a/src/tool/executor.ts +++ b/src/tool/executor.ts @@ -220,7 +220,13 @@ export function truncateToolOutput(data: string, maxChars: number): string { const markerTemplate = '\n\n[...truncated characters...]\n\n' const markerOverhead = markerTemplate.length + String(data.length).length - const available = Math.max(0, maxChars - markerOverhead) + // When maxChars is too small to fit any content alongside the marker, + // fall back to a hard slice so the result never exceeds maxChars. + if (maxChars <= markerOverhead) { + return data.slice(0, maxChars) + } + + const available = maxChars - markerOverhead const headChars = Math.floor(available * 0.7) const tailChars = available - headChars const truncatedCount = data.length - headChars - tailChars diff --git a/src/types.ts b/src/types.ts index 3f42995..1ef7e92 100644 --- a/src/types.ts +++ b/src/types.ts @@ -253,11 +253,6 @@ export interface AgentConfig { * calls and text outputs to detect stuck loops before `maxTurns` is reached. */ readonly loopDetection?: LoopDetectionConfig - /** - * Optional Zod schema for structured output. When set, the agent's final - * output is parsed as JSON and validated against this schema. A single - * retry with error feedback is attempted on validation failure. - */ /** * Maximum tool output length in characters for all tools used by this agent. * When set, tool outputs exceeding this limit are truncated (head + tail @@ -265,6 +260,11 @@ export interface AgentConfig { * takes priority over this value. */ readonly maxToolOutputChars?: number + /** + * Optional Zod schema for structured output. When set, the agent's final + * output is parsed as JSON and validated against this schema. A single + * retry with error feedback is attempted on validation failure. + */ readonly outputSchema?: ZodSchema /** * Called before each agent run. Receives the prompt and agent config. diff --git a/tests/tool-executor.test.ts b/tests/tool-executor.test.ts index 40b909a..945e2f8 100644 --- a/tests/tool-executor.test.ts +++ b/tests/tool-executor.test.ts @@ -231,9 +231,9 @@ describe('truncateToolOutput', () => { it('handles very small maxChars gracefully', () => { const data = 'x'.repeat(100) - // With maxChars=1, the marker alone exceeds the budget, but it should not crash + // With maxChars=1, the marker alone exceeds the budget — falls back to hard slice const result = truncateToolOutput(data, 1) - expect(result).toContain('[...truncated') + expect(result.length).toBeLessThanOrEqual(1) }) })