Compare commits

...
Author SHA1 Message Date
Aiden Cline 134411857d fix(core): prevent shell eviction loop 2026-08-20 09:53:54 -05:00
2 changed files with 26 additions and 2 deletions
+2 -2
View File
@@ -148,10 +148,10 @@ const layer = () =>
const removeSession = Effect.fnUntraced(function* (id: Shell.ID) {
const session = sessions.get(id)
if (!session) return
sessions.delete(id)
const index = exitOrder.indexOf(id)
if (index !== -1) exitOrder.splice(index, 1)
if (!session) return
sessions.delete(id)
if (session.timeoutFiber) yield* Fiber.interrupt(session.timeoutFiber)
// Unblock any wait still pending when the command is removed before it terminated.
yield* Deferred.fail(session.done, new NotFoundError({ id }))
+24
View File
@@ -814,6 +814,30 @@ describe("ShellTool", () => {
{ timeout: 15_000 },
)
it.live("does not retain removed running shells in exit order", () =>
Effect.acquireUseRelease(
Effect.promise(() => tmpdir()),
(tmp) =>
withSession(tmp.path, () =>
Effect.gen(function* () {
const shell = yield* Shell.Service
yield* Effect.forEach(Array.from({ length: 26 }), () =>
Effect.gen(function* () {
const info = yield* shell.create({ command: idleCommand, timeout: 0 })
yield* shell.remove(info.id)
yield* Effect.sleep(Duration.millis(10))
}),
)
const info = yield* shell.create({ command: helloCommand, timeout: 0 })
const settled = yield* shell.wait(info.id).pipe(Effect.timeoutOption(Duration.seconds(2)))
expect(settled._tag).toBe("Some")
}),
),
(tmp) => Effect.promise(() => tmp[Symbol.asyncDispose]().then(() => undefined)),
),
)
if (!isWindows) {
it.live("settles a shell terminated by an external signal", () =>
Effect.acquireUseRelease(