# pi-overflow Bounded display for oversized tool results. When a tool result exceeds `thresholdChars`, pi-overflow persists the full original to disk and shows the model a deterministic view — head lines, signal lines (error/fail/warn/…), and tail lines — plus an `overflow_query` tool to search or read the stored original by id. ## Two-stage load order - `capture.ts` — **first** in `pi.extensions`. A `tool_result` handler that never modifies anything; it stashes the untouched original text in `globalThis[Symbol.for("my-pi.pi-overflow.stash.v1")]` (capacity 64) so the bound stage can still reach the pre-RTK original. - `index.ts` — **last** content extension, right before `system-prompt.ts`. Its `tool_result` handler sees the final text after every other handler (RTK semantic compaction included) has run, then: - resolves the *original*: `details.fullOutputPath` file (streamed, capped at `maxEntryBytes` head+tail) → stashed raw text → current text; - if the current text still exceeds `thresholdChars`, replaces it with the bounded view **built from the stored file** — line numbers and totals in the view are exactly what `overflow_query` will read, even when RTK or bash already cut the in-context text (file sources are scanned line-by-line in a stream, never held in memory); - if the current text fits but the original did not (an earlier extension already cut it), keeps the text and appends a one-line `[pi-overflow id=…]` recovery marker; - preserves image parts and `structuredContent`, and merges `details.overflow = { id, totalLines, totalBytes, truncated }` into the existing details. - Exempt tools (`read`, `edit`, `write`, `overflow_query`, `ask_user_question`) and nested calls (`parentToolCallId`) are never touched. ## Stored originals `/my-pi/pi-overflow/.txt` + `.json` metadata, where `id = sha1(sessionId + "\0" + toolCallId)[0:12]`. Directories are `0700`, files `0600`, writes are atomic. `session_start` cleans entries older than `retentionDays` and evicts oldest-first past `maxStoreBytes`. ## `overflow_query` - `id` (required, `^[0-9a-f]{12}$` — path traversal safe) - `search` — case-insensitive regex; hits come back with `context` lines and adjacent hits merge; capped by `maxResults` - `lines: { start, end }` — 1-based inclusive line range - `jsonPath` — dot/bracket path into stored JSON (`a.b[3].c`) - No selector → metadata + first-screen view. Output is always bounded by `thresholdChars` and reports whether it was truncated. ## Configuration `config/pi-overflow.json` inside the installed bundle. Every field is validated independently and falls back to its default; an uncompilable `signalPattern` falls back to the default pattern (case-insensitive). ## Development ```sh npm test # node --test test/*.test.ts npm run check # strip-types syntax check + tests ``` Tests inject a temporary store root — they never write to a real agent directory.