fix(sandbox+web-react): real stream errors, driveTurn delegation, skip unchanged skill re-writes#206
Merged
Merged
Conversation
…ocking box.prompt driveSandboxTurn was named/documented as the detached cron/mission-step helper but was built on box.prompt(), which awaits the WHOLE turn in process. Every autonomous caller therefore held its worker alive for the run's duration — the exact durability trap the helper is meant to avoid. The stale comment even claimed "the SDK SandboxInstance has no driveTurn", but the pinned SDK (@tangle-network/sandbox 0.10.5) ships box.driveTurn, box.dispatchPrompt, and box.findCompletedTurn. Route the helper through box.driveTurn: ONE settle -> poll -> dispatch pass that runs the turn fire-and-detached server-side and returns immediately with where it stands, never awaiting completion in process. The old Outcome<PromptResult> contract assumed a completed result in one call, which is blocking by construction and cannot represent a still-running turn. Change the return to Outcome<TurnDriveResult> (the SDK's discriminated running | completed | failed): the Outcome boundary now separates a retryable transport throw (fail) from a settled turn state (ok), and a deterministic `failed` turn is a state the caller inspects rather than a swallowed error. Expose turnId + wallCapMs so tick-based drivers get idempotent resume and unattended-run bounding. Callers: the two in-repo consumers are unit tests, updated here. The return-type change is a compile-time break that forces external autonomous callers to handle `running` — failing loud is correct, since their prior blocking usage was the bug. Add a test proving the helper returns promptly on state:'running' (box.prompt is never called), plus transport-throw and turnId/wallCapMs-forwarding coverage.
…ror'
The sandbox lane sends a failure as { type:'error', data:{ message } } (mirrored
by session.run.failed), but chat-stream only read top-level details/error, so
EVERY real error rendered as the useless 'Unknown stream error'. Read
data.message first — empty_visible_output, egress, model 402 etc. now reach the
operator. Fleet-wide (every product on streamChatTurn).
…s unchanged Reusing a box re-wrote the entire (large) skill corpus via box.fs.writeMany UNCONDITIONALLY — seconds of latency on every turn even when nothing changed. Stamp the corpus content hash into box metadata at create; on reuse, skip the re-write when the hash still matches. Fail-safe: a missing/mismatched hash (a redeploy with new skills, or a box predating this) writes as before — a stale skip is never risked. Metadata-only (no extra exec/round-trip), so existing deferred-write tests are unchanged; adds a skip test. 2520/2520 green.
tangletools
approved these changes
Jul 20, 2026
tangletools
left a comment
There was a problem hiding this comment.
✅ Auto-approved drewstone PR — da40046d
This PR was opened by the trusted drewstone account.
The full PR reviewer audit still runs separately and will publish findings if it detects issues.
tangletools · auto-approval · reason: drewstone_author · 2026-07-20T01:55:11Z
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Three shell/chat fixes, all proven against workcomp-agent live:
1. Surface real stream errors (
web-react/chat-stream.ts). The sandbox lane sends failures as{type:'error', data:{message}}but the reader only checked top-leveldetails/error, so every real error rendered as "Unknown stream error". Now readsdata.messagefirst — empty-output, egress, model 402 reach the operator. Fleet-wide.2. driveSandboxTurn delegates to the SDK (
sandbox/index.ts, from #205). Usesbox.driveTurninstead of a blockingbox.prompt().3. Skip the deferred skill re-write when unchanged (
sandbox/index.ts). Reusing a box re-wrote the whole (large) skill corpus viabox.fs.writeManyunconditionally — seconds of latency per turn. Stamp the corpus content hash into box metadata at create; on reuse, skip the re-write when the hash matches. Fail-safe: a missing/mismatched hash (redeploy with new skills, or a pre-existing box) writes as before — a stale skip is never risked. Metadata-only, so existing deferred-write tests are unchanged; adds a skip test.Verification
pnpm typecheckclean ·pnpm test2520/2520 · clean merge into main. Live-proven on workcomp-agent: (1) fixed the masked errors Drew saw; (3) targets the ~10s reuse latency behind the prewarm work.