From e26abd8da9d28d0e79a481a1e0c0d6b0a70f05ee Mon Sep 17 00:00:00 2001 From: Shoubhit Dash Date: Thu, 14 May 2026 16:34:42 +0530 Subject: [PATCH] fix(tool): close shell truncation stream (#27517) --- packages/opencode/src/tool/shell.ts | 37 ++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/packages/opencode/src/tool/shell.ts b/packages/opencode/src/tool/shell.ts index d3ca542684..fcfd59f353 100644 --- a/packages/opencode/src/tool/shell.ts +++ b/packages/opencode/src/tool/shell.ts @@ -443,6 +443,31 @@ export const ShellTool = Tool.define( let expired = false let aborted = false + const closeSink = Effect.fnUntraced(function* () { + const stream = sink + if (!stream) return + sink = undefined + if (stream.destroyed || stream.closed) return + yield* Effect.promise( + () => + new Promise((resolve) => { + let settled = false + const done = () => { + if (settled) return + settled = true + stream.off("close", done) + stream.off("error", done) + stream.off("finish", done) + resolve() + } + stream.once("close", done) + stream.once("error", done) + stream.once("finish", done) + stream.end(done) + }), + ).pipe(Effect.catch(() => Effect.void)) + }) + yield* ctx.metadata({ metadata: { output: "", @@ -452,6 +477,7 @@ export const ShellTool = Tool.define( const code: number | null = yield* Effect.scoped( Effect.gen(function* () { + yield* Effect.addFinalizer(closeSink) const handle = yield* spawner.spawn(cmd(input.shell, input.command, input.cwd, input.env)) yield* Effect.forkScoped( @@ -555,17 +581,6 @@ export const ShellTool = Tool.define( if (meta.length > 0) { output += "\n\n\n" + meta.join("\n") + "\n" } - if (sink) { - const stream = sink - yield* Effect.promise( - () => - new Promise((resolve) => { - stream.end(() => resolve()) - stream.on("error", () => resolve()) - }), - ) - } - return { title: input.description, metadata: {