Files
PaperClipAI/packages/adapters
8aaef59339 fix(pi-local): add stdout compaction to stop quadratic run-log growth (#10249)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The `pi_local` adapter runs the `pi` CLI in `--mode json` and
records its NDJSON stream as a run-log per turn
> - In JSON mode `pi` emits one `message_update` event per streaming
token, each carrying the **full cumulative assistant message**, so a
single turn's run-log grows quadratically with output length (measured
~77 MB for one thinking-model turn)
> - The root cause is in `pi`, but the Pi project declined to add a
compact mode (earendil-works/pi#3905, closed the same day), so Paperclip
has to filter on the consumer side
> - This PR adds a dedicated, config-gated compactor at the `pi_local`
stdout boundary (`stdoutLogMode`: `raw` | `compact`, default `compact`)
that strips the cumulative snapshots while preserving streaming deltas,
terminal events, and parser/UI output
> - The benefit is linear run-log growth (~150× smaller in practice)
with **no change** to `parsePiJsonl` results, token usage, or live
typing — keeping storage and replay bounded

## Linked Issues or Issue Description

- **Refs #5699** — same root cause ("full accumulated partial state" →
46 MB log, per that PR). This PR takes a **different, single-concern
approach**: a dedicated `stdout-compaction.ts` module with an explicit
`stdoutLogMode` config (default `compact`, fail-safe fallback to `raw`),
scoped to `pi_local` only. #5699 instead filters inline in
`buffered-on-log.ts`, bundles an unrelated `claude-local` change, and is
currently **conflicting** + stale (last updated 2026-05-14). Happy to
coordinate with @ukint-vs on a merged direction.
- **Refs earendil-works/pi#3905** — proposed adding a compact JSON mode
upstream in `pi`; closed by the maintainer, which is why this filters on
the Paperclip side rather than the producer.
- **Related #1846** (run-log/retention TTL) — complementary: this PR
reduces what's stored per run; #1846 addresses how long rows live.
- No public GitHub issue exists that is an exact duplicate of the
`pi_local` quadratic-growth symptom; the problem is tracked via #5699
above. (No internal/instance-local references used.)

## What Changed

- **`packages/adapters/pi-local/src/server/stdout-compaction.ts`** (new,
123 lines) — `createPiStdoutLogCompactor(mode)`. `raw` passes lines
through unchanged; `compact` strips the cumulative
`message`/`assistantMessageEvent.partial` from `message_update` events
while preserving streaming deltas, terminal events, and passthrough of
unknown event types. Default mode `compact`; an invalid explicit mode
fails safely to `raw`.
- **`packages/adapters/pi-local/src/server/execute.ts`** — read the mode
via `resolvePiStdoutLogMode(config)` and apply the compactor at the
stdout → `onLog` (run-log) boundary.
- **`packages/adapters/pi-local/src/index.ts`** — expose the
`stdoutLogMode` config option (documented inline: `string`, optional,
default `"compact"`).
- **`packages/adapters/pi-local/src/ui/parse-stdout.ts`** — confirm the
stdout parser consumes compacted output unchanged.
- **Tests** — new `stdout-compaction.test.ts` (359 lines) covering
modes, edge cases, and `parsePiJsonl` replay equivalence; updated
`execute.remote.test.ts`.

## Verification

- **Unit:** `pnpm --filter @paperclipai/adapter-pi-local exec vitest
run` → **121/121 passing (12 files)**.
- **Typecheck:** `pnpm --filter @paperclipai/adapter-pi-local exec tsc
--noEmit` → **clean, 0 errors**.
- **Real-data replay:** a real `pi --mode json` stream (`zai/glm-5.2`,
thinking=high) compacted line-by-line through this compactor:
- Raw **76.96 MB** → Compact **525.5 KB** = **99.33% reduction
(≈150×)**.
- 4503 lines in, 4503 out (**0 dropped**); **0** `message_update` with a
cumulative `.message` remaining; **4489** streaming deltas preserved.
- **Parser equivalence:** `parsePiJsonl(raw) ≡ parsePiJsonl(compact)` —
**0 errors** on both; identical `finalMessage` (13,369 chars); identical
`usage` (`inputTokens 2896, outputTokens 4491, cachedInputTokens 6848`).
Result, token accounting, and live-typing are unchanged.

## Risks

- **Behavioral shift:** default `compact` changes what is stored in
run-logs vs today. Operators who need the verbatim stream can set
`stdoutLogMode: "raw"`. Flagging for reviewer preference — happy to flip
the default to `raw` for upstream if that's preferred.
- **Ordering with secret redaction (#8027 / #9856):** compaction is
structural and runs at the stdout boundary **before** redaction; it only
removes cumulative duplicates and never alters token content, so it does
not change what redaction sees.
- **Unknown event types** pass through unchanged (forward-compatible
with future `pi` events).
- Low risk otherwise — the change is contained to the `pi_local`
adapter.

## Model Used

OpenAI `gpt-5.6-luna` — via pi's `openai-codex` provider (272K context
window). Used for implementation and for the real-data measurement in
Verification.

Reasoning/thinking: extended thinking enabled at level `high` (pi
`--thinking high`; maps to OpenAI reasoning effort). `gpt-5.6-luna` is a
reasoning-capable model.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
Co-authored-by: commitperclip[bot] <284812584+commitperclip[bot]@users.noreply.github.com>
2026-10-15 16:24:06 -07:00
..