Repository navigation
Description
Claude (anthropic-primary/claude-opus-5-5), assisting Nathan:
Before submitting
- I searched existing issues and did not find a duplicate.
- I included enough detail to reproduce or investigate the problem.
Area
apps/server
Steps to reproduce
In a Pi thread, have the agent run this with its bash tool (15 seconds, 300 lines, 213 KB of output):
python3 -c "import time for i in range(300): print(f'line {i:04d} ' + 'x' * 700, flush=True) time.sleep(0.05) "Then, on a copy of
statev2.sqlite:SELECT json_extract(payload_json, '$.id') AS item, count(*) AS rows, sum(length(payload_json)) AS bytes, max(length(payload_json)) AS largest FROM orchestration_events WHERE event_type = 'turn-item.updated' AND json_extract(payload_json, '$.type') = 'command_execution' GROUP BY item ORDER BY bytes DESC LIMIT 5;Expected behavior
A running Pi tool stores a bounded number of rows per item (like Claude and Codex command output), so storage follows the final output size, not how long the output streams.
Actual behavior
That one command item has 154
turn-item.updatedrows totalling 7.0 MB, growing to 53 KB each, written over 15.1 seconds (about 10 a second). Its final projection row is 52.6 KB, so the log holds about 130 times the final size. On an ordinary day the same server stores about 1.8 times the final size across all turn items (19,406 rows, 100 MB for 55 MB of final content), so the problem shows up with long, chatty commands and with many of them at once.What we saw on a real server:
- A self-hosted, headless
t3 serveon Linux. All threads use the Pi provider.- Nov 5 to Nov 8, 2026: agents on this server ran about 315,000 bash commands and 95,000 other tool calls in Pi threads (many of them delegated tasks).
statev2.sqlitegrew to 106 GB (99.05 GiB). The finished turn items from those runs total only about 3 GB.orchestration_eventstook up to about 132,000 rows an hour, against 1,400 to 5,000 an hour normally. Most wereturn-item.updatedrows for running tool items, and long commands carried about 50 KB of output in every row.- The background prune (every six hours, at 10% duty, keeping at least a day of events) later deleted about 2.2 million superseded rows (about 39 GB). Our database predated incremental auto vacuum, so the file stayed at 106 GB until
t3 doctor --fixtook the server offline for 30 minutes and printedDone: 99.05 GiB -> 10.06 GiB.Cause in source:
- Pi's bash tool calls
onUpdatewith the whole output so far, cut to its last 2,000 lines or 50 KB, at most every 100 ms (BASH_UPDATE_THROTTLE_MS = 100inpi-coding-agent1.3.0,dist/core/tools/renderers/bash.js, limits indist/core/tools/truncate.js, repo https://github.com/earendil-works/pi).- The Pi adapter turns every
tool_execution_updateinto aturn_item.updatedcarrying that full output, and each is appended toorchestration_eventsas its own row:
(readst3code/packages/provider-pi/src/server/adapter.ts
Line 1215 in 36f011d
const resultRecord = completed ? event["result"] : event["partialResult"]; partialResult) (t3code/packages/provider-pi/src/server/adapter.ts
Line 2046 in 36f011d
case "tool_execution_update": tool_execution_updategoes toemitToolItemwith phase"update")- Claude and Codex threads avoid this for command output: their adapters send it as
command_output.delta, which the ingestor drops before persistence andCommandOutputHubserves live:
type: "command_output.delta",
type: "command_output.delta",
case "command_output.delta": - At about 10 rows a second of up to 53 KB, one command that keeps printing stores roughly 1.9 GB an hour until the prune removes it a day or more later.
Related
- fix(server): a streamed reply or reasoning block no longer stores every snapshot of its text #36362 collapses consecutive streaming snapshots of assistant messages and reasoning into one stored row in
OrchestrationEventStore.appendAgentEvents. Running tool items are not in its scope. In our repro the 154 command rows were nearly back to back on the thread (onenode.updatedand onecodemoderow in between), so the same collapse applied to running tool items would leave a handful of rows instead of 154.- [Bug]: Paragraph streaming stores every full reasoning snapshot as a permanent event (OpenCode: 93k rows, 2.2 GB in one day) #23365 is the same pattern for OpenCode reasoning in paragraph mode.
- orchestration_events grows without bound: streaming updates are stored as permanent events and never compacted #21154 and [Bug]: Intermediate turn-item snapshots remain outside event compaction (78.5 MB on one install) #19780 cover compacting superseded snapshots after the fact. That works here too. This report is about the write volume and how big the database gets before the prune catches up.
Possible fixes
- Extend the collapse in fix(server): a streamed reply or reasoning block no longer stores every snapshot of its text #36362 to running tool items (
command_execution,dynamic_tooland the like while status is running) so only the newest running snapshot row is kept per item.- In the Pi adapter, send bash and powershell output as
command_output.deltalike Claude and Codex, and store the item only when it starts and ends.We are happy to send a PR for either if that would help.
Impact
Major degradation or frequent failure
Version or commit
0.1.10, built from main @ 21b0b7a057. The same code is on main @ 36f011dfb7.
Environment
Ubuntu 24.04.5 LTS x86_64, Node 24.21.0, headless
t3 serveas a systemd user service, Pi (pi-coding-agent) 1.3.0. We build from a fork that tracks main daily and does not change the Pi adapter or persistence code.Workaround
None for the write volume. After the prune,
t3 doctor --fix(with the server stopped) returns the space on databases created before incremental auto vacuum. Newer databases return it on their own.
Activity