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 three levels:| Level | Limit | Variable / Override | Default |
|---|---|---|---|
| Per-tool (new) | Cap applied inside the dispatch before aggregation | TruncationAware.MaxResultChars() capability per plugin | 30,000 chars (global) |
| 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 |
Per-tool truncation (capability)
Plugins that implementplugins.TruncationAware declare their own cap ā useful when the tool has non-default context needs:
| Plugin | Cap | Rationale |
|---|---|---|
@read | 80,000 | Large files (~1500 lines) are the primary code-learning surface; a low cap blinds the model |
@search | 60,000 | Breadth-oriented structured output (file:line:match) ā the model needs reach |
@tree | 50,000 | Monorepo listings easily exceed 30k |
| Other plugins | 30,000 | Global default |
[TRUNCATED N chars omitted, M kept] marker).
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 inside the Session Workspace, instead of the legacy global/tmp/chatcli-tool-results/:
- Isolation between sessions. Multiple
chatcliinstances running in parallel on the same host no longer share the overflow pool. - On-demand reads by the agent. The scratch dir is on the agentās read allowlist, so when the model encounters the
[full output saved to ...]marker in the preview, it can open the file withread_file:
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
Session Workspace
Where overflow files live and how the agent reads them.
Subagent Delegation
Complementary strategy to avoid saturating context with raw data.
Context Recovery
What happens when even with budgeting the context overflows.
Cost Tracking
Monitor token consumption including tool results.