feat(pi-overflow): 新增统一的新工具结果展示截断扩展

此前截断分散在多层且互相冲突:Pi bash 保留尾部 50KB,RTK 兜底截断又只留
开头 12000 字符(砍掉报错与完整输出路径),pi-chrome 截开头 30K,web_fetch
各 provider 不一致。新扩展统一负责"新结果给模型看多少":
- capture.ts 最先加载暂存原文,index.ts 最后加载(system-prompt 之前)
- 超过 24K 字符时原文落盘 <agentDir>/my-pi/pi-overflow/(700/600、原子写),
  优先取 bash fullOutputPath 完整文件;模型看到 头 + 中段信号行 + 尾 的有界
  视图,行号基于落盘原文、结尾永远可见
- overflow_query:按 id 正则搜索、行区间、jsonPath 只读查询,id 校验防穿越
- 收编:RTK truncate 默认关闭、删除 pi-chrome truncateText、web_fetch 默认
  每页 20000(上限 100000)、web_search max_results 限定 1–20
- pi-compaction 遮蔽带 details.overflow.id 的结果时在占位符提示 overflow_query
- 登记 tools.jsonc / routing / 权限 allow / tool-search 常驻与分组

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
云服务部-叶林立
2026-10-21 20:25:44 -07:00
co-authored by Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
parent a6d8416c76
commit 2a6bf8c929
44 changed files with 1882 additions and 34 deletions
+66
View File
@@ -0,0 +1,66 @@
# 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
`<agentDir>/my-pi/pi-overflow/<id>.txt` + `<id>.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.