Skip to content

Commit fad98e3

Browse files
committed
test(session): cover same-drain stopping reset, correct crash comment
1 parent 8c38173 commit fad98e3

2 files changed

Lines changed: 36 additions & 1 deletion

File tree

‎packages/core/src/session/runner/llm.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ const layer = Layer.effect(
7777
// and compactions carry it when the turn continues across the handoff,
7878
// and hook continuations loop in-drain so they can never leak across
7979
// turns. A crash loses the count while an already-admitted steer
80-
// survives — one free steer per crash, bounded and self-limiting.
80+
// survives — up to two fresh grants per crash, bounded and self-limiting.
8181
let stoppingContinuations = input.continuation?.stoppingContinuations ?? 0
8282
let entering = true
8383
const promotable = input.promotable ?? "input"

‎packages/core/test/session-runner.test.ts‎

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7045,6 +7045,41 @@ describe("SessionRunnerLLM", () => {
70457045
expect(seen).toEqual([0, 1, 0])
70467046
})
70477047

7048+
scenario("resets the stopping counter when fresh input lands mid-drain", function* (s) {
7049+
const seen: number[] = []
7050+
let grants = 0
7051+
const hooks = yield* PluginHooks.Service
7052+
yield* hooks.register("session", "experimental.stopping", (event) =>
7053+
Effect.sync(() => {
7054+
seen.push(event.continuations)
7055+
if (grants >= 1) return
7056+
grants++
7057+
event.decision = { shouldContinue: true, text: "One more" }
7058+
}),
7059+
)
7060+
yield* s.admit("First turn")
7061+
yield* s.llm.push(
7062+
TestLLM.text("First", "text-first"),
7063+
TestLLM.tool("call-blocked", "echo", { text: "granted steer work" }),
7064+
TestLLM.text("Second", "text-second"),
7065+
TestLLM.text("Third", "text-third"),
7066+
)
7067+
const tools = yield* s.blockTools()
7068+
const runner = yield* SessionRunner.Service
7069+
const run = yield* runner.drain({ sessionID, force: false }).pipe(Effect.forkChild)
7070+
yield* tools.started
7071+
yield* s.session.prompt({ sessionID, text: "Second turn", resume: false, delivery: "queue" })
7072+
yield* tools.release
7073+
const result = yield* Fiber.join(run)
7074+
7075+
// Grant at the end of turn A, then fresh input arrives mid-drain while the
7076+
// granted steer runs. The same drain promotes it as a new turn with a
7077+
// reset counter — without the in-drain reset the second vote would see 1.
7078+
expect(result._tag).toBe("Complete")
7079+
expect(s.requests).toHaveLength(4)
7080+
expect(seen).toEqual([0, 0])
7081+
})
7082+
70487083
scenario("never writes the stopping counter into session metadata", function* (s) {
70497084
const hooks = yield* PluginHooks.Service
70507085
yield* hooks.register("session", "experimental.stopping", (event) =>

0 commit comments

Comments
 (0)