Three-Layer Tool Result Flow

Description

The Three-Layer Tool Result Flow is the system Claude Code uses to manage tool output of varying sizes without overwhelming the context window. Every tool declares a maxResultSizeChars property on its ToolDef, which is clamped against the system-wide DEFAULT_MAX_RESULT_SIZE_CHARS (50,000 characters) via getPersistenceThreshold(). When a tool result exceeds this threshold, the full content is persisted to a session-local file on disk, and the model receives only a compact reference containing a 2,000-byte preview and the file path.

Layer 1 (Index) is the compact reference the model always sees inline: a <persisted-output> XML-wrapped message containing the file size, the disk path, and a truncated preview (first ~2KB, cut at a newline boundary). Layer 2 (Detailed) is the full tool result persisted to {projectDir}/{sessionId}/tool-results/{toolUseId}.{txt|json}, which the model can read on demand using the FileRead tool. Layer 3 is the aggregate per-message budget enforcement: when multiple parallel tool calls in a single turn collectively exceed MAX_TOOL_RESULTS_PER_MESSAGE_CHARS (200,000 characters), the largest blocks are evicted to disk even if each individually was under the per-tool threshold. This prevents N parallel tools from producing, e.g., 10 x 40K = 400K in one turn.

A ContentReplacementState object tracks which tool_use_ids have been replaced across the entire conversation, ensuring that budget decisions are stable (idempotent) across turns to preserve prompt cache prefixes. Replacement records are written to the transcript so they survive session resume. GrowthBook feature flags (tengu_satin_quoll for per-tool threshold overrides, tengu_hawthorn_window for the aggregate budget, tengu_hawthorn_steeple for enabling the aggregate feature) allow runtime tuning without code deploys.

Key claims

Relations

Sources