From 0ca670e6dc81584a888dd2cb98b37ca0223678fe Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Fri, 10 Jul 2026 23:45:37 +0000 Subject: [PATCH] fix(core): cap session output tokens --- packages/core/src/session/runner/llm.ts | 5 +++++ packages/core/test/session-runner.test.ts | 21 +++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/packages/core/src/session/runner/llm.ts b/packages/core/src/session/runner/llm.ts index 72c761e10d93..6fe8112348b6 100644 --- a/packages/core/src/session/runner/llm.ts +++ b/packages/core/src/session/runner/llm.ts @@ -40,6 +40,8 @@ import { Snapshot } from "../../snapshot" import { makeLocationNode } from "../../effect/app-node" import { llmClient } from "../../effect/app-node-platform" +const MAX_OUTPUT_TOKENS = 32_000 + /** * Runs one durable coding-agent Session until it settles. * @@ -210,6 +212,9 @@ const layer = Layer.effect( .map(SystemPart.make), messages: [...toLLMMessages(context, model), ...(isLastStep ? [Message.assistant(MAX_STEPS_PROMPT)] : [])], tools: toolMaterialization?.definitions ?? [], + generation: { + maxTokens: Math.min(model.route.defaults.limits?.output ?? 0, MAX_OUTPUT_TOKENS) || MAX_OUTPUT_TOKENS, + }, toolChoice: isLastStep ? "none" : undefined, }) if (yield* compaction.compactIfNeeded({ sessionID: session.id, entries, model, request })) diff --git a/packages/core/test/session-runner.test.ts b/packages/core/test/session-runner.test.ts index 0515d55cf5be..2a73c9ca8f01 100644 --- a/packages/core/test/session-runner.test.ts +++ b/packages/core/test/session-runner.test.ts @@ -108,6 +108,11 @@ const recoveryModel = Model.make({ provider: "fake", route: OpenAIChat.route.with({ limits: { context: 20_000, output: 1_000 } }), }) +const fullContextOutputModel = Model.make({ + id: "full-context-output", + provider: "fake", + route: OpenAIChat.route.with({ limits: { context: 500_000, output: 500_000 } }), +}) const authorizations: Tool.Context[] = [] const executions: string[] = [] const permission = Layer.succeed( @@ -655,6 +660,22 @@ describe("SessionRunnerLLM", () => { }), ) + it.effect("caps output when the catalog output limit consumes the full context window", () => + Effect.gen(function* () { + yield* setup + const session = yield* SessionV2.Service + currentModel = fullContextOutputModel + yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "hi" }), resume: false }) + requests.length = 0 + response = [] + + yield* session.resume(sessionID) + + expect(requests).toHaveLength(1) + expect(requests[0]?.generation).toEqual({ maxTokens: 32_000 }) + }), + ) + it.effect("retries the first provider turn after system context becomes available", () => Effect.gen(function* () { yield* setup