Tool Result Pairing
Everytool_use (a tool call made by the model) must have a corresponding tool_result in the conversation history. When this pairing breaks — due to interruption, timeout, or silent error — the API rejects the history.
The EnsureToolResultPairing system automatically validates and repairs:
| Problem | Repair Action |
|---|---|
tool_use without tool_result (orphan) | Injects synthetic error result |
tool_result without tool_use (orphan) | Removes from history |
Duplicate tool_use IDs | Keeps only the first occurrence |
Synthetic Results
When a tool_use has no corresponding result, ChatCLI injects:3-Phase Validation
ID Collection
Traverses the entire history collecting
tool_use IDs (from assistant messages) and tool_result IDs (from tool messages).Misalignment Detection
Compares the two sets of IDs. Tool uses without a result are “missing”. Tool results without a use are “orphans”. Duplicate IDs are flagged for deduplication.
Result Budget Enforcement
Tool results such as large file reads or command output can quickly consume the context window. The budget system limits the aggregate size at two levels:| Level | Limit | Environment Variable | Default |
|---|---|---|---|
| Per result | Maximum size of a single result | CHATCLI_TOOL_RESULT_MAX_CHARS | 20,000 chars |
| Per turn | Aggregate size of all results in the turn | CHATCLI_TOOL_RESULT_BUDGET_CHARS | 200,000 chars |
How Enforcement Works
The budget is applied in two passes:- Pass 1: Per Result
- Pass 2: Per Turn
Each individual result is checked against
DefaultPerResultMaxChars (20KB). If it exceeds the limit, the full content is saved to disk and replaced with a preview:Disk Persistence
Truncated results are saved as temporary files in/tmp/chatcli-tool-results/:
Preview: Head + Tail
The preview retains the beginning and end of the result to maximize usefulness:| Component | Size |
|---|---|
| Head (beginning) | 4,000 chars (cuts at the last line break) |
| Reference | File path on disk |
| Tail (end) | 1,000 chars (cuts at the first line break) |
Progressive Microcompaction
Microcompaction progressively reduces the size of old tool results as the conversation advances, without losing critical information:| Result Age | Action | Details |
|---|---|---|
| Current and previous turn | No change | Results preserved in full |
| 2+ turns ago | Truncated | Head (2,000 chars) + tail (500 chars) |
| 4+ turns ago | Summarized | One-line description: [Old tool result cleared -- 450 lines, 28K chars, Go source] |
Content Type Detection
The summary automatically identifies the content type for context:| Content | Detected Type |
|---|---|
Starts with { or [ | JSON |
Contains package | Go source |
Contains def | Python source |
Contains function | JavaScript source |
Starts with diff or --- | diff |
Starts with commit | git log |
| Other | text |
Microcompaction Configuration
| Variable | Description | Default |
|---|---|---|
CHATCLI_MICROCOMPACT_TRUNCATE_TURNS | Turns before truncating | 2 |
CHATCLI_MICROCOMPACT_SUMMARIZE_TURNS | Turns before summarizing | 4 |
Only results larger than 3,000 chars are compacted. Small results are always preserved. Results from write and execution tools are preserved as they contain critical error information.
Complete Flow
Tool result management is applied in this order during the agent loop:Complete Configuration
| Environment Variable | Description | Default |
|---|---|---|
CHATCLI_TOOL_RESULT_BUDGET_CHARS | Aggregate budget per turn | 200,000 |
CHATCLI_TOOL_RESULT_MAX_CHARS | Maximum size per result | 20,000 |
CHATCLI_MICROCOMPACT_TRUNCATE_TURNS | Turns before truncation starts | 2 |
CHATCLI_MICROCOMPACT_SUMMARIZE_TURNS | Turns before summarization starts | 4 |
Next Steps
Context Recovery
What happens when even with budgeting the context overflows.
JSON Recovery
How malformed arguments are fixed before execution.
Cost Tracking
Monitor token consumption including tool results.
Agent Mode
The ReAct loop that generates and processes tool calls.