Skip to content

Commit a79fec1

Browse files
committed
fix(bash): drain output fiber before scope closes to prevent data loss
Fast-exiting commands (e.g. echo test) could produce (no output) because Effect.forkScoped interrupted the stream consumer fiber before it had a chance to process chunks buffered in the Node.js readable pipe. Fix by joining the fiber after the exit/abort/timeout race so all buffered stdout/stderr is flushed into the accumulator list before Effect.scoped disposes the scope.
1 parent c082a13 commit a79fec1

1 file changed

Lines changed: 7 additions & 2 deletions

File tree

packages/opencode/src/tool/bash.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import { Shell } from "@/shell/shell"
1717
import { BashArity } from "@/permission/arity"
1818
import * as Truncate from "./truncate"
1919
import { Plugin } from "@/plugin"
20-
import { Effect, Stream } from "effect"
20+
import { Effect, Fiber, Stream } from "effect"
2121
import { ChildProcess } from "effect/unstable/process"
2222
import { ChildProcessSpawner } from "effect/unstable/process/ChildProcessSpawner"
2323

@@ -444,7 +444,7 @@ export const BashTool = Tool.define(
444444
Effect.gen(function* () {
445445
const handle = yield* spawner.spawn(cmd(input.shell, input.name, input.command, input.cwd, input.env))
446446

447-
yield* Effect.forkScoped(
447+
const outputFiber = yield* Effect.forkScoped(
448448
Stream.runForEach(Stream.decodeText(handle.all), (chunk) => {
449449
const size = Buffer.byteLength(chunk, "utf-8")
450450
list.push({ text: chunk, size })
@@ -517,6 +517,11 @@ export const BashTool = Tool.define(
517517
yield* handle.kill({ forceKillAfter: "3 seconds" }).pipe(Effect.orDie)
518518
}
519519

520+
// Drain any remaining buffered output before the scope closes.
521+
// Without this, the stream consumer fiber can be interrupted before
522+
// it processes chunks that were buffered when the process exited.
523+
yield* Fiber.join(outputFiber).pipe(Effect.ignore)
524+
520525
return exit.kind === "exit" ? exit.code : null
521526
}),
522527
).pipe(Effect.orDie)

0 commit comments

Comments
 (0)