mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-16 11:03:44 +00:00
Implement tool result truncation with actionable notices (#134)
- read: actionable notices with offset for continuation - First line > 30KB: return empty + bash command suggestion - Hit limit: '[Showing lines X-Y of Z. Use offset=N to continue]' - bash: tail truncation with temp file - Notice includes line range + temp file path - Edge case: last line > 30KB shows partial - grep: pre-truncate match lines to 500 chars - '[... truncated]' suffix on long lines - Notice for match limit and line truncation - find/ls: result/entry limit notices - '[N results limit reached. Use limit=M for more]' - All notices now in text content (LLM sees them) - TUI simplified (notices render as part of output) - Never return partial lines (except bash edge case)
This commit is contained in:
parent
de77cd1419
commit
b813a8b92b
9 changed files with 465 additions and 164 deletions
235
packages/coding-agent/docs/truncation.md
Normal file
235
packages/coding-agent/docs/truncation.md
Normal file
|
|
@ -0,0 +1,235 @@
|
|||
# Tool Output Truncation
|
||||
|
||||
## Limits
|
||||
|
||||
- **Line limit**: 2000 lines
|
||||
- **Byte limit**: 30KB
|
||||
- **Grep line limit**: 500 chars per match line
|
||||
|
||||
Whichever limit is hit first wins. **Never return partial lines** (except bash edge case).
|
||||
|
||||
---
|
||||
|
||||
## read
|
||||
|
||||
Head truncation (first N lines). Has offset/limit params for continuation.
|
||||
|
||||
### Scenarios
|
||||
|
||||
**First line > 30KB:**
|
||||
```
|
||||
LLM sees:
|
||||
[Line 1 is 50KB, exceeds 30KB limit. Use bash to read: head -c 30000 path/to/file]
|
||||
|
||||
Details:
|
||||
{ truncation: { truncated: true, truncatedBy: "bytes", outputLines: 0, ... } }
|
||||
```
|
||||
|
||||
**Hit line limit (2000 lines, < 30KB):**
|
||||
```
|
||||
LLM sees:
|
||||
[lines 1-2000 content]
|
||||
|
||||
[Showing lines 1-2000 of 5000. Use offset=2001 to continue]
|
||||
|
||||
Details:
|
||||
{ truncation: { truncated: true, truncatedBy: "lines", outputLines: 2000, totalLines: 5000 } }
|
||||
```
|
||||
|
||||
**Hit byte limit (< 2000 lines, 30KB):**
|
||||
```
|
||||
LLM sees:
|
||||
[lines 1-500 content]
|
||||
|
||||
[Showing lines 1-500 of 5000 (30KB limit). Use offset=501 to continue]
|
||||
|
||||
Details:
|
||||
{ truncation: { truncated: true, truncatedBy: "bytes", outputLines: 500, totalLines: 5000 } }
|
||||
```
|
||||
|
||||
**With offset, hit line limit (e.g., offset=1000):**
|
||||
```
|
||||
LLM sees:
|
||||
[lines 1000-2999 content]
|
||||
|
||||
[Showing lines 1000-2999 of 5000. Use offset=3000 to continue]
|
||||
|
||||
Details:
|
||||
{ truncation: { truncatedBy: "lines", ... } }
|
||||
```
|
||||
|
||||
**With offset, hit byte limit (e.g., offset=1000, 30KB after 500 lines):**
|
||||
```
|
||||
LLM sees:
|
||||
[lines 1000-1499 content]
|
||||
|
||||
[Showing lines 1000-1499 of 5000 (30KB limit). Use offset=1500 to continue]
|
||||
|
||||
Details:
|
||||
{ truncation: { truncatedBy: "bytes", outputLines: 500, ... } }
|
||||
```
|
||||
|
||||
**With offset, first line at offset > 30KB (e.g., offset=1000, line 1000 is 50KB):**
|
||||
```
|
||||
LLM sees:
|
||||
[Line 1000 is 50KB, exceeds 30KB limit. Use bash: sed -n '1000p' file | head -c 30000]
|
||||
|
||||
Details:
|
||||
{ truncation: { truncated: true, truncatedBy: "bytes", outputLines: 0 } }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## bash
|
||||
|
||||
Tail truncation (last N lines). Writes full output to temp file if truncated.
|
||||
|
||||
### Scenarios
|
||||
|
||||
**Hit line limit (2000 lines):**
|
||||
```
|
||||
LLM sees:
|
||||
[lines 48001-50000 content]
|
||||
|
||||
[Showing lines 48001-50000 of 50000. Full output: /tmp/pi-bash-xxx.log]
|
||||
|
||||
Details:
|
||||
{ truncation: { truncated: true, truncatedBy: "lines", outputLines: 2000, totalLines: 50000 }, fullOutputPath: "/tmp/..." }
|
||||
```
|
||||
|
||||
**Hit byte limit (< 2000 lines, 30KB):**
|
||||
```
|
||||
LLM sees:
|
||||
[lines 49501-50000 content]
|
||||
|
||||
[Showing lines 49501-50000 of 50000 (30KB limit). Full output: /tmp/pi-bash-xxx.log]
|
||||
|
||||
Details:
|
||||
{ truncation: { truncatedBy: "bytes", ... }, fullOutputPath: "/tmp/..." }
|
||||
```
|
||||
|
||||
**Last line alone > 30KB (edge case, partial OK here):**
|
||||
```
|
||||
LLM sees:
|
||||
[last 30KB of final line]
|
||||
|
||||
[Showing last 30KB of line 50000 (line is 100KB). Full output: /tmp/pi-bash-xxx.log]
|
||||
|
||||
Details:
|
||||
{ truncation: { truncatedBy: "bytes", lastLinePartial: true }, fullOutputPath: "/tmp/..." }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## grep
|
||||
|
||||
Head truncation. Primary limit: 100 matches. Each match line truncated to 500 chars.
|
||||
|
||||
### Scenarios
|
||||
|
||||
**Hit match limit (100 matches):**
|
||||
```
|
||||
LLM sees:
|
||||
file.ts:10: matching content here...
|
||||
file.ts:25: another match...
|
||||
...
|
||||
|
||||
[100 matches limit reached. Use limit=200 for more, or refine pattern]
|
||||
|
||||
Details:
|
||||
{ matchLimitReached: 100 }
|
||||
```
|
||||
|
||||
**Hit byte limit (< 100 matches, 30KB):**
|
||||
```
|
||||
LLM sees:
|
||||
[matches that fit in 30KB]
|
||||
|
||||
[30KB limit reached (50 of 100+ matches shown)]
|
||||
|
||||
Details:
|
||||
{ truncation: { truncatedBy: "bytes", ... } }
|
||||
```
|
||||
|
||||
**Match lines truncated (any line > 500 chars):**
|
||||
```
|
||||
LLM sees:
|
||||
file.ts:10: very long matching content that exceeds 500 chars gets cut off here... [truncated]
|
||||
file.ts:25: normal match
|
||||
|
||||
[Some lines truncated to 500 chars. Use read tool to see full lines]
|
||||
|
||||
Details:
|
||||
{ linesTruncated: true }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## find
|
||||
|
||||
Head truncation. Primary limit: 1000 results. File paths only (never > 30KB each).
|
||||
|
||||
### Scenarios
|
||||
|
||||
**Hit result limit (1000 results):**
|
||||
```
|
||||
LLM sees:
|
||||
src/file1.ts
|
||||
src/file2.ts
|
||||
[998 more paths]
|
||||
|
||||
[1000 results limit reached. Use limit=2000 for more, or refine pattern]
|
||||
|
||||
Details:
|
||||
{ resultLimitReached: 1000 }
|
||||
```
|
||||
|
||||
**Hit byte limit (unlikely, < 1000 results, 30KB):**
|
||||
```
|
||||
LLM sees:
|
||||
[paths that fit]
|
||||
|
||||
[30KB limit reached]
|
||||
|
||||
Details:
|
||||
{ truncation: { truncatedBy: "bytes", ... } }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ls
|
||||
|
||||
Head truncation. Primary limit: 500 entries. Entry names only (never > 30KB each).
|
||||
|
||||
### Scenarios
|
||||
|
||||
**Hit entry limit (500 entries):**
|
||||
```
|
||||
LLM sees:
|
||||
.gitignore
|
||||
README.md
|
||||
src/
|
||||
[497 more entries]
|
||||
|
||||
[500 entries limit reached. Use limit=1000 for more]
|
||||
|
||||
Details:
|
||||
{ entryLimitReached: 500 }
|
||||
```
|
||||
|
||||
**Hit byte limit (unlikely):**
|
||||
```
|
||||
LLM sees:
|
||||
[entries that fit]
|
||||
|
||||
[30KB limit reached]
|
||||
|
||||
Details:
|
||||
{ truncation: { truncatedBy: "bytes", ... } }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## TUI Display
|
||||
|
||||
`tool-execution.ts` reads `details.truncation` and related fields to display truncation notices in warning color. The LLM text content and TUI display show the same information.
|
||||
Loading…
Add table
Add a link
Reference in a new issue