From 6a16c41e8f5726c8bd5fab068c104fb6cb4402a3 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Fri, 26 Jun 2026 22:02:30 -0400 Subject: [PATCH] fix(core): continue after tool defects --- packages/core/src/session/runner/llm.ts | 3 ++- packages/core/test/session-runner.test.ts | 16 +++++++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/packages/core/src/session/runner/llm.ts b/packages/core/src/session/runner/llm.ts index 9e29ae71ee..6a123cf6d7 100644 --- a/packages/core/src/session/runner/llm.ts +++ b/packages/core/src/session/runner/llm.ts @@ -333,7 +333,8 @@ export const layer = Layer.effect( if (stream._tag === "Success" && !publisher.hasProviderError()) yield* withPublication(publisher.failUnsettledTools("Provider did not return a tool result", true)) if (stream._tag === "Failure") return yield* Effect.failCause(stream.cause) - if (settled._tag === "Failure") return yield* Effect.failCause(settled.cause) + if (settled._tag === "Failure" && Cause.hasInterrupts(settled.cause)) + return yield* Effect.failCause(settled.cause) return { needsContinuation: !publisher.hasProviderError() && needsContinuation, step: currentStep } }), ) diff --git a/packages/core/test/session-runner.test.ts b/packages/core/test/session-runner.test.ts index c9594e8244..2001e22757 100644 --- a/packages/core/test/session-runner.test.ts +++ b/packages/core/test/session-runner.test.ts @@ -2565,7 +2565,7 @@ describe("SessionRunnerLLM", () => { }), ) - it.effect("propagates unexpected local tool defects operationally", () => + it.effect("returns unexpected local tool defects to the model and continues", () => Effect.gen(function* () { yield* setup const session = yield* SessionV2.Service @@ -2579,11 +2579,20 @@ describe("SessionRunnerLLM", () => { LLMEvent.stepFinish({ index: 0, reason: "tool-calls" }), LLMEvent.finish({ reason: "tool-calls" }), ], + [ + LLMEvent.stepStart({ index: 0 }), + LLMEvent.textStart({ id: "text-after-defect" }), + LLMEvent.textDelta({ id: "text-after-defect", text: "Recovered" }), + LLMEvent.textEnd({ id: "text-after-defect" }), + LLMEvent.stepFinish({ index: 0, reason: "stop" }), + LLMEvent.finish({ reason: "stop" }), + ], ] - expect(yield* session.resume(sessionID).pipe(Effect.catchDefect(Effect.succeed))).toBe("unexpected tool defect") + yield* session.resume(sessionID) - expect(requests).toHaveLength(1) + expect(requests).toHaveLength(2) + expect(requests[1]?.messages.map((message) => message.role)).toEqual(["user", "assistant", "tool"]) expect(yield* session.context(sessionID)).toMatchObject([ { type: "user", text: "Call defect" }, { @@ -2599,6 +2608,7 @@ describe("SessionRunnerLLM", () => { }, ], }, + { type: "assistant", finish: "stop", content: [{ type: "text", text: "Recovered" }] }, ]) }), )