improvement(sandbox): exempt caller-consumed streams from the output retention budget - #6353
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
PR SummaryMedium Risk Overview Retention policy: If a stream handler is present, that stream skips 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 SummaryThe 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.
Confidence Score: 5/5The PR appears safe to merge. No blocking failure remains.
|
| 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
Reviews (3): Last reviewed commit: "fix(sandbox): cut Daytona's retained tai..." | Re-trigger Greptile
81fdc6d to
f636df3
Compare
|
@cursor review |
…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>
f636df3 to
922b5fa
Compare
|
@cursor review |
There was a problem hiding this comment.
✅ 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.
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:
handleChunkparses 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
Testing
Tested manually
Checklist