diff --git a/packages/opencode/src/effect/runner.ts b/packages/opencode/src/effect/runner.ts index cb12b4c52b..38c45a6342 100644 --- a/packages/opencode/src/effect/runner.ts +++ b/packages/opencode/src/effect/runner.ts @@ -1,10 +1,10 @@ -import { Cause, Deferred, Effect, Exit, Fiber, Option, Schema, Scope, SynchronizedRef } from "effect" +import { Cause, Deferred, Effect, Exit, Fiber, Schema, Scope, SynchronizedRef } from "effect" export interface Runner { readonly state: Runner.State readonly busy: boolean readonly ensureRunning: (work: Effect.Effect) => Effect.Effect - readonly startShell: (work: (signal: AbortSignal) => Effect.Effect) => Effect.Effect + readonly startShell: (work: Effect.Effect) => Effect.Effect readonly cancel: Effect.Effect } @@ -20,7 +20,6 @@ export namespace Runner { interface ShellHandle { id: number fiber: Fiber.Fiber - abort: AbortController } interface PendingHandle { @@ -100,13 +99,7 @@ export namespace Runner { }), ).pipe(Effect.flatten) - const stopShell = (shell: ShellHandle) => - Effect.gen(function* () { - shell.abort.abort() - const exit = yield* Fiber.await(shell.fiber).pipe(Effect.timeoutOption("100 millis")) - if (Option.isNone(exit)) yield* Fiber.interrupt(shell.fiber) - yield* Fiber.await(shell.fiber).pipe(Effect.exit, Effect.asVoid) - }) + const stopShell = (shell: ShellHandle) => Fiber.interrupt(shell.fiber) const ensureRunning = (work: Effect.Effect) => SynchronizedRef.modifyEffect( @@ -138,7 +131,7 @@ export namespace Runner { ), ) - const startShell = (work: (signal: AbortSignal) => Effect.Effect) => + const startShell = (work: Effect.Effect) => SynchronizedRef.modifyEffect( ref, Effect.fnUntraced(function* (st) { @@ -153,9 +146,8 @@ export namespace Runner { } yield* busy const id = next() - const abort = new AbortController() - const fiber = yield* work(abort.signal).pipe(Effect.ensuring(finishShell(id)), Effect.forkChild) - const shell = { id, fiber, abort } satisfies ShellHandle + const fiber = yield* work.pipe(Effect.ensuring(finishShell(id)), Effect.forkChild) + const shell = { id, fiber } satisfies ShellHandle return [ Effect.gen(function* () { const exit = yield* Fiber.await(fiber) diff --git a/packages/opencode/src/session/prompt.ts b/packages/opencode/src/session/prompt.ts index dc75efcdc9..19f0850ff4 100644 --- a/packages/opencode/src/session/prompt.ts +++ b/packages/opencode/src/session/prompt.ts @@ -743,7 +743,7 @@ NOTE: At any point in time through this workflow you should feel free to ask the } satisfies MessageV2.TextPart) }) - const shellImpl = Effect.fn("SessionPrompt.shellImpl")(function* (input: ShellInput, signal: AbortSignal) { + const shellImpl = Effect.fn("SessionPrompt.shellImpl")(function* (input: ShellInput) { const ctx = yield* InstanceState.context const session = yield* sessions.get(input.sessionID) if (session.revert) { @@ -1577,7 +1577,7 @@ NOTE: At any point in time through this workflow you should feel free to ask the function* (input: ShellInput) { const s = yield* InstanceState.get(state) const runner = getRunner(s.runners, input.sessionID) - return yield* runner.startShell((signal) => shellImpl(input, signal)) + return yield* runner.startShell(shellImpl(input)) }, ) diff --git a/packages/opencode/test/effect/runner.test.ts b/packages/opencode/test/effect/runner.test.ts index 9dc395876e..a91df76ebf 100644 --- a/packages/opencode/test/effect/runner.test.ts +++ b/packages/opencode/test/effect/runner.test.ts @@ -250,7 +250,7 @@ describe("Runner", () => { Effect.gen(function* () { const s = yield* Scope.Scope const runner = Runner.make(s) - const result = yield* runner.startShell((_signal) => Effect.succeed("shell-done")) + const result = yield* runner.startShell(Effect.succeed("shell-done")) expect(result).toBe("shell-done") expect(runner.busy).toBe(false) }), @@ -264,7 +264,7 @@ describe("Runner", () => { const fiber = yield* runner.ensureRunning(Effect.never.pipe(Effect.as("x"))).pipe(Effect.forkChild) yield* Effect.sleep("10 millis") - const exit = yield* runner.startShell((_s) => Effect.succeed("nope")).pipe(Effect.exit) + const exit = yield* runner.startShell(Effect.succeed("nope")).pipe(Effect.exit) expect(Exit.isFailure(exit)).toBe(true) yield* runner.cancel @@ -279,12 +279,10 @@ describe("Runner", () => { const runner = Runner.make(s) const gate = yield* Deferred.make() - const sh = yield* runner - .startShell((_signal) => Deferred.await(gate).pipe(Effect.as("first"))) - .pipe(Effect.forkChild) + const sh = yield* runner.startShell(Deferred.await(gate).pipe(Effect.as("first"))).pipe(Effect.forkChild) yield* Effect.sleep("10 millis") - const exit = yield* runner.startShell((_s) => Effect.succeed("second")).pipe(Effect.exit) + const exit = yield* runner.startShell(Effect.succeed("second")).pipe(Effect.exit) expect(Exit.isFailure(exit)).toBe(true) yield* Deferred.succeed(gate, undefined) @@ -302,37 +300,26 @@ describe("Runner", () => { }, }) - const sh = yield* runner - .startShell((signal) => - Effect.promise( - () => - new Promise((resolve) => { - signal.addEventListener("abort", () => resolve("aborted"), { once: true }) - }), - ), - ) - .pipe(Effect.forkChild) + const sh = yield* runner.startShell(Effect.never.pipe(Effect.as("aborted"))).pipe(Effect.forkChild) yield* Effect.sleep("10 millis") - const exit = yield* runner.startShell((_s) => Effect.succeed("second")).pipe(Effect.exit) + const exit = yield* runner.startShell(Effect.succeed("second")).pipe(Effect.exit) expect(Exit.isFailure(exit)).toBe(true) yield* runner.cancel const done = yield* Fiber.await(sh) - expect(Exit.isSuccess(done)).toBe(true) + expect(Exit.isFailure(done)).toBe(true) }), ) it.live( - "cancel interrupts shell that ignores abort signal", + "cancel interrupts shell", Effect.gen(function* () { const s = yield* Scope.Scope const runner = Runner.make(s) const gate = yield* Deferred.make() - const sh = yield* runner - .startShell((_signal) => Deferred.await(gate).pipe(Effect.as("ignored"))) - .pipe(Effect.forkChild) + const sh = yield* runner.startShell(Deferred.await(gate).pipe(Effect.as("ignored"))).pipe(Effect.forkChild) yield* Effect.sleep("10 millis") const stop = yield* runner.cancel.pipe(Effect.forkChild) @@ -356,9 +343,7 @@ describe("Runner", () => { const runner = Runner.make(s) const gate = yield* Deferred.make() - const sh = yield* runner - .startShell((_signal) => Deferred.await(gate).pipe(Effect.as("shell-result"))) - .pipe(Effect.forkChild) + const sh = yield* runner.startShell(Deferred.await(gate).pipe(Effect.as("shell-result"))).pipe(Effect.forkChild) yield* Effect.sleep("10 millis") expect(runner.state._tag).toBe("Shell") @@ -384,9 +369,7 @@ describe("Runner", () => { const calls = yield* Ref.make(0) const gate = yield* Deferred.make() - const sh = yield* runner - .startShell((_signal) => Deferred.await(gate).pipe(Effect.as("shell"))) - .pipe(Effect.forkChild) + const sh = yield* runner.startShell(Deferred.await(gate).pipe(Effect.as("shell"))).pipe(Effect.forkChild) yield* Effect.sleep("10 millis") const work = Effect.gen(function* () { @@ -414,16 +397,7 @@ describe("Runner", () => { const runner = Runner.make(s) const gate = yield* Deferred.make() - const sh = yield* runner - .startShell((signal) => - Effect.promise( - () => - new Promise((resolve) => { - signal.addEventListener("abort", () => resolve("aborted"), { once: true }) - }), - ), - ) - .pipe(Effect.forkChild) + const sh = yield* runner.startShell(Effect.never.pipe(Effect.as("aborted"))).pipe(Effect.forkChild) yield* Effect.sleep("10 millis") const run = yield* runner.ensureRunning(Effect.succeed("y")).pipe(Effect.forkChild) @@ -478,7 +452,7 @@ describe("Runner", () => { const runner = Runner.make(s, { onBusy: Ref.update(count, (n) => n + 1), }) - yield* runner.startShell((_signal) => Effect.succeed("done")) + yield* runner.startShell(Effect.succeed("done")) expect(yield* Ref.get(count)).toBe(1) }), ) @@ -509,9 +483,7 @@ describe("Runner", () => { const runner = Runner.make(s) const gate = yield* Deferred.make() - const fiber = yield* runner - .startShell((_signal) => Deferred.await(gate).pipe(Effect.as("ok"))) - .pipe(Effect.forkChild) + const fiber = yield* runner.startShell(Deferred.await(gate).pipe(Effect.as("ok"))).pipe(Effect.forkChild) yield* Effect.sleep("10 millis") expect(runner.busy).toBe(true)