Skip to content

improvement(sandbox): exempt caller-consumed streams from the output retention budget - #6353

Merged
icecrasher321 merged 3 commits into
stagingfrom
fix/pi-streamed-output-retention
Aug 7, 2026
Merged

improvement(sandbox): exempt caller-consumed streams from the output retention budget#6353
icecrasher321 merged 3 commits into
stagingfrom
fix/pi-streamed-output-retention

Conversation

@icecrasher321

Copy link
Copy Markdown
Collaborator

Summary

A Pi agent turn emits one JSONL event per step and passes the 10 MB process output budget on an ordinary session, killing the run. The bytes were never a result: handleChunk parses every chunk as it arrives and keeps none of it, and the accumulated copy is only ever read back to build an error message.

The budget bounds what Sim RETAINS, so a stream the caller consumes itself is exempt and only a 64 KB diagnostic tail is kept. The limit is unchanged for everything else.

Gated per stream, not per command: a caller that streams stdout but not stderr still has stderr fully bounded. Both adapters gate on the handler's presence, so the calls that parse markers out of stdout (Pi's clone/prepare/push, which do not stream) keep full retention and full budgeting — the case daytona.ts already warns about.

E2B's SDK still accumulates internally, so this bounds what Sim retains rather than the provider's peak; Daytona accumulates locally and is bounded outright.

Type of Change

  • Other: UX Improvement

Testing

Tested manually

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

@vercel

vercel Bot commented Aug 7, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped Aug 7, 2026 12:25am

Request Review

@cursor

cursor Bot commented Aug 7, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Changes sandbox output limit enforcement on a critical Pi execution path; behavior is scoped to caller-consumed streams but both E2B and Daytona command runners were updated and must stay aligned.

Overview
Fixes Pi agent turns dying on sandbox_output_limit_exceeded when JSONL step events exceed the 10 MB process retention cap even though the caller already consumed the stream via onStdout/onStderr.

Retention policy: If a stream handler is present, that stream skips SandboxProcessOutputBudget billing; Sim keeps only a 64 KB diagnostic tail (with a truncation note), via tailStreamedSandboxOutput and appendStreamedSandboxOutput. Exemption is per stream—streaming stdout alone still fully bounds stderr. Paths without handlers (e.g. marker parsing) keep full retention and budgeting.

Provider parity: E2B and Daytona adapters apply the same gating and final tail cut so failover does not change behavior when streams end between one and two tail lengths.

Conformance tests cover oversized streamed stdout, mid-band tail sizing, and non-exempt stderr.

Reviewed by Cursor Bugbot for commit 922b5fa. Configure here.

@greptile-apps

greptile-apps Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR exempts caller-consumed sandbox streams from retained-output budgeting while preserving a bounded diagnostic tail and continuing to enforce limits independently on unconsumed streams.

  • Adds byte-aware helpers for retaining the final 64 KB of streamed output.
  • Applies per-stream retention behavior consistently across the Daytona and E2B adapters.
  • Adds cross-provider conformance coverage for oversized consumed streams, final-tail sizing, and unconsumed-stream limits.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
apps/sim/lib/execution/remote-sandbox/output-limits.ts Adds UTF-8-aware streamed-output tailing and bounded incremental accumulation helpers.
apps/sim/lib/execution/remote-sandbox/daytona.ts Exempts callback-consumed streams from budgeting, bounds their local diagnostic copies, and normalizes final tails on every result path.
apps/sim/lib/execution/remote-sandbox/e2b.ts Applies per-stream retention decisions to normal, timeout, and non-zero-exit results while continuing to budget unconsumed output.
apps/sim/lib/execution/remote-sandbox/conformance.test.ts Adds provider-conformance tests and fixes the previously reported mock typing issue through signature inference.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  Output[Sandbox output chunk] --> Handler{Caller supplied handler?}
  Handler -- No --> Budget[Charge retained-output budget]
  Budget --> Full[Retain full stream]
  Handler -- Yes --> Callback[Deliver chunk to caller]
  Callback --> Tail[Retain bounded diagnostic tail]
  Full --> Result[Command result]
  Tail --> Result
Loading

Reviews (3): Last reviewed commit: "fix(sandbox): cut Daytona's retained tai..." | Re-trigger Greptile

Comment thread apps/sim/lib/execution/remote-sandbox/conformance.test.ts
@icecrasher321
icecrasher321 force-pushed the fix/pi-streamed-output-retention branch from 81fdc6d to f636df3 Compare August 7, 2026 00:18
@icecrasher321

Copy link
Copy Markdown
Collaborator Author

@greptile

@icecrasher321

Copy link
Copy Markdown
Collaborator Author

@cursor review

Comment thread apps/sim/lib/execution/remote-sandbox/daytona.ts
icecrasher321 and others added 3 commits August 6, 2026 17:24
…n budget

A Pi agent turn emits one JSONL event per step and passes the 10 MB process
output budget on an ordinary session, killing the run. The bytes were never a
result: `handleChunk` parses every chunk as it arrives and keeps none of it,
and the accumulated copy is only ever read back to build an error message.

The budget bounds what Sim RETAINS, so a stream the caller consumes itself is
exempt and only a 64 KB diagnostic tail is kept. The limit is unchanged for
everything else.

Gated per stream, not per command: a caller that streams stdout but not stderr
still has stderr fully bounded. Both adapters gate on the handler's presence, so
the calls that parse markers out of stdout (Pi's clone/prepare/push, which do
not stream) keep full retention and full budgeting — the case daytona.ts already
warns about.

E2B's SDK still accumulates internally, so this bounds what Sim retains rather
than the provider's peak; Daytona accumulates locally and is bounded outright.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The two new E2B mocks annotated their arguments as `any`, which both violates
the repo's no-`any` rule and defeats the point of a mock: an invalid SDK shape
would type-check.

Matches the sibling mock a few lines above (`async (_code, options) =>`) and
infers from the `vi.fn()` signature instead of naming a type, so the mock stays
bound to whatever the adapter actually calls.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`appendStreamedSandboxOutput` deliberately lets the accumulator grow to twice
the tail before collapsing, so a single re-cut is amortized across chunks rather
than paid on every one. That leaves it anywhere inside that band when the stream
ends. E2B tails the value it returns, Daytona returned the accumulator as-is, so
a stream finishing between one and two tails came back roughly 96 KB on Daytona
and 64 KB on E2B.

The two adapters must agree — a divergence here surfaces as changed behavior
during a failover, which is the one moment nobody wants surprises. Daytona now
takes the same final cut on every return path.

The conformance test that should have caught this asserted the bound as
`tail * 2`, which is satisfied by both the correct and the incorrect value. It
now asserts the tail plus the truncation note, and a second case exercises the
band between one and two tails where the two providers could disagree.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@icecrasher321
icecrasher321 force-pushed the fix/pi-streamed-output-retention branch from f636df3 to 922b5fa Compare August 7, 2026 00:25
@icecrasher321

Copy link
Copy Markdown
Collaborator Author

@greptile

@icecrasher321

Copy link
Copy Markdown
Collaborator Author

@cursor review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 922b5fa. Configure here.

@icecrasher321
icecrasher321 merged commit f340ad9 into staging Aug 7, 2026
30 checks passed
@icecrasher321
icecrasher321 deleted the fix/pi-streamed-output-retention branch August 7, 2026 00:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant