Commit Graph
2 Commits
Author SHA1 Message Date
roboomp a39dbc9b44 fix(tools): guarantee spill sink close when tail replay fails
#finalizeFile marked the sink finalized then ran the capped-artifact tail
replay before closing. A write error during that replay threw before
sink.end(), and because #finalized was already set the executor finally
paths calling dispose() could not retry the close, leaking the descriptor
and masking the original tool error.

Moved sink.end() into a finally around the tail replay and swallowed both
the replay and close errors so the descriptor is always released and
dispose() never throws.
2026-07-24 03:53:21 +00:00
roboomp 31098f9248 fix(tools): closed spilled output descriptors on error/abort paths
OutputSink.dump() was the only path that closed the spill Bun.FileSink.
The bash and Python executors re-throw on failure and their finally
blocks never closed the sink, so any large-output command that errored
leaked the artifact descriptor until an unrelated read (e.g. a SKILL.md
load) hit EMFILE.

Added an idempotent OutputSink.dispose() that closes the sink exactly
once (awaiting any in-flight sink creation, guarding post-finalize
resurrection) and wired it into every executor's finally block.

Fixes #6463
2026-07-24 03:44:12 +00:00