# Large tool results and usefulness

A tool returns 30,000 log lines. Only three explain the failure, but the model cannot know which three without inspecting the evidence. A harness must decide what to retain, what to put in the next request, and how the agent can recover omitted material. Cutting the output to fit is one operation; judging whether it answers the task is another.

This chapter distinguishes verified output-handling mechanisms from proposed quality checks. The inspected paths below do not establish a general semantic usefulness classifier across these harnesses. Tool-specific validation, hooks, and the model's subsequent reasoning can add behavior, but should not be silently counted as a built-in result-quality judge.

## Four different limits

| Boundary | What it protects | What can be lost |
| --- | --- | --- |
| Collection | Process memory or transport size | Bytes never retained by the collector |
| Tool response | The immediate message sent back to the model | Content outside a preview or truncated region |
| Durable storage | Transcript or recovery-record size | Material unavailable after a restart |
| Context projection | Space in later model requests | Older evidence omitted from the current view |

These budgets are independent. A file path can recover omitted response text only if the file exists and an allowed tool can read it. A long-lived transcript can preserve evidence that is absent from the next model request. A recovery payload may be smaller than the original result.

## What the four harnesses do

| Harness | Inspected mechanism | Qualification |
| --- | --- | --- |
| Codex | MCP results are truncated for model history using an output policy; code mode retains the raw MCP result. Unified process output has requested/model budgets and omission markers. | Logging, model output, and code-mode representations have different limits. This path does not promise an artifact containing every omitted byte. [MCP output][c-mcp], [process output][c-exec] |
| pi | Built-in read and bash tools use line/byte limits. Read provides continuation offsets; bash reports a full-output temporary path when truncated. | These are inspected built-in tool contracts, not a guarantee that every extension result receives identical treatment. [Limits][p-limits], [read][p-read], [bash][p-bash] |
| Goose | Oversized text blocks in ordinary tool results are written to temporary files and replaced with references. | The threshold is per text block. Non-text content passes through, and a file-write failure returns full text with a warning. [Handler][g-large], [call site][g-call] |
| Sidekick | An optional native context processor replaces older, large tool results with placeholders; durable tool-intent payloads have a separate storage bound. | Age/size trimming is neither a semantic relevance verdict nor full-history summary compaction. Private-source links require access. [Projection][s-trim], [storage][s-store] |

Sidekick's findings concern its native runtime. An external harness wrapped through an Outpost may handle output differently. The [design comparison](../comparison.md) links to the reviewed harness versions and execution scopes.

## Preserve a way to inspect the evidence

Pi's shared truncation defaults are 2,000 lines and 50 KiB, whichever is reached first. Its read tool keeps a bounded head of the selected content and reports the next offset. Bash instead exposes the tail and a path to fuller output when truncation occurs. That makes the response actionable: the agent can search the artifact or read a narrower region instead of repeating the original operation. [Pi limits][p-limits], [read continuation][p-read], [bash result][p-bash]

Goose's general large-response handler defaults to 200,000 characters per text block, configurable through its response-size setting. It writes the block to a temporary file and returns a notice pointing to it. Because file-write failure preserves the text with a warning, this handler is not a hard upper bound on total response size. Several smaller blocks or non-text content also require separate accounting. [Goose large-response handler][g-large]

The design lesson is to specify both the preview and retrieval contracts. An artifact reference should identify what was saved, how to read it, and its lifetime. A preview should say whether it is a head, tail, sampled excerpt, or summary. Otherwise an agent can mistake “not shown” for “not present.” These are builder recommendations, not claims that every implementation provides all these fields.

## Keep model context separate from raw output

Codex's MCP output adapter gives model history a truncated response while preserving the underlying result for code mode. A script can inspect that result and emit selected evidence. Unified process output separately accounts for bytes omitted during collection and applies model-facing limits; increasing the final response budget cannot restore bytes already discarded upstream. [MCP response projection][c-mcp], [process omission markers][c-exec]

[Code mode](code-mode.md) can reduce a large result to relevant records before it enters the model conversation. That shifts selection into a program, rather than proving the selection is correct. For example, filtering logs only for “error” may miss a failed assertion reported as a warning. Retain the original evidence or a reproducible retrieval path, and make the filter explicit.

Sidekick's optional context processor uses message age and approximate size. Its defaults target tool results at least ten messages behind the tail and roughly 1,000 tokens or larger. It replaces eligible contents with a placeholder while preserving tool-call identity in the request projection. The current factory enables this only through configured context preferences. Old content is not necessarily irrelevant: age is a heuristic for space management. [Processor][s-trim], [activation][s-factory]

Sidekick also bounds the payload retained in a durable tool-intent record. It can drop inline image data and trim bulky result or metadata fields, recording truncation markers. That protects recovery storage but means a redelivered payload need not contain the complete original evidence. Re-running a mutation merely to obtain missing output would be a separate authorization and replay-safety decision. [Bounded recovery payload][s-store], [persistence and recovery](persistence.md).

## Size, validity, success, and usefulness differ

Consider four responses from an invoice tool:

- Valid JSON containing the wrong customer's invoice is structurally valid but irrelevant.
- An empty result can be a correct answer or a failed query, depending on the contract.
- A successful HTTP response containing a login page is not necessarily a successful retrieval.
- A truncated log may contain the decisive error, or omit it entirely.

Schema checks assess shape. Exit codes and error flags report execution outcomes. Truncation markers describe omitted content. None alone establishes task relevance, factual correctness, or sufficient evidence. The inspected generic handlers use budgets, content types, age, and status; this review did not locate an always-on semantic usefulness classifier in those paths. This is a scoped finding, not proof that no tool or optional extension can perform quality evaluation. [Codex budgeting][c-mcp], [pi status handling][p-bash], [Goose content handling][g-large], [Sidekick age/size logic][s-trim]

## Builder exercise: keep useful evidence discoverable

Create fixture results containing a decisive line at the beginning, middle, and end, plus valid-but-irrelevant JSON and an explicit tool error. Apply your output policy and inspect what the next model request receives.

Check that truncation is visible, call/result identifiers survive, artifact retrieval works, and retries do not repeat side effects just to recover missing text. Record original size, retained size, representation type, and retrieval reference separately from execution status.

If you add a semantic evaluator, give it a concrete task contract and preserve its uncertainty. Measure missed evidence and false rejection of useful results, not just token reduction. Keep that evaluator distinct from size handling, tool authorization, and [conversation compaction](compaction.md); each changes a different part of the agent's reasoning environment.

[c-mcp]: https://github.com/openai/codex/blob/8c076942cb70fba8e5c4732c23f1faabc4aed484/codex-rs/core/src/tools/context.rs#L136-L214
[c-exec]: https://github.com/openai/codex/blob/8c076942cb70fba8e5c4732c23f1faabc4aed484/codex-rs/core/src/tools/context.rs#L379-L563
[p-limits]: https://github.com/earendil-works/pi/blob/482c1381a00d8d00bc81697e4a980c593db6803e/packages/coding-agent/src/core/tools/truncate.ts#L1-L45
[p-read]: https://github.com/earendil-works/pi/blob/482c1381a00d8d00bc81697e4a980c593db6803e/packages/coding-agent/src/core/tools/read.ts#L154-L204
[p-bash]: https://github.com/earendil-works/pi/blob/482c1381a00d8d00bc81697e4a980c593db6803e/packages/coding-agent/src/core/tools/bash.ts#L328-L408
[g-large]: https://github.com/aaif-goose/goose/blob/34a222e3f2b95d8b4a43482b53c2cc957ecce26d/crates/goose/src/agents/large_response_handler.rs#L5-L76
[g-call]: https://github.com/aaif-goose/goose/blob/34a222e3f2b95d8b4a43482b53c2cc957ecce26d/crates/goose/src/agents/state_machine/ops_toolcalling.rs#L265-L314
[s-trim]: https://github.com/datex/sidekick/blob/463d653124e1c2cda46a3f14f01f3d6f384326b1/backend/src/sidekick/services/context_pipeline/processors/limits.py#L33-L157
[s-factory]: https://github.com/datex/sidekick/blob/463d653124e1c2cda46a3f14f01f3d6f384326b1/backend/src/sidekick/services/context_pipeline/factory.py#L92-L117
[s-store]: https://github.com/datex/sidekick/blob/463d653124e1c2cda46a3f14f01f3d6f384326b1/backend/src/sidekick/services/tool_intents.py#L210-L266
