fix(core): clamp compaction output budget (#36745)

Co-authored-by: Dax Raad <thdxr@users.noreply.github.com>
This commit is contained in:
opencode-agent[bot]
2026-07-13 17:50:20 -04:00
committed by GitHub
co-authored by Dax Raad
parent ecb5754f4c
commit fbe7f26e71
2 changed files with 26 additions and 1 deletions
+2 -1
View File
@@ -16,6 +16,7 @@ import { Token } from "../util/token"
const DEFAULT_BUFFER = 20_000
const DEFAULT_KEEP_TOKENS = 8_000
const OUTPUT_TOKEN_MAX = 32_000
const TOOL_OUTPUT_MAX_CHARS = 2_000
const SUMMARY_TEMPLATE = `Output exactly the Markdown structure shown inside <template> and keep the section order unchanged. Do not include the <template> tags in your response.
<template>
@@ -320,7 +321,7 @@ const make = (dependencies: Dependencies) => {
message.type === "assistant" && message.tokens !== undefined,
)
if (!last) return false
const output = input.model.route.defaults.limits?.output ?? 0
const output = Math.min(input.model.route.defaults.limits?.output ?? 0, OUTPUT_TOKEN_MAX)
const used =
last.tokens.input + last.tokens.output + last.tokens.reasoning + last.tokens.cache.read + last.tokens.cache.write
if (used <= 0) return false
+24
View File
@@ -139,6 +139,11 @@ const compactModel = Model.make({
provider: "fake",
route: OpenAIChat.route.with({ limits: { context: 4_000, output: 50 } }),
})
const fullOutputModel = Model.make({
id: "full-output",
provider: "fake",
route: OpenAIChat.route.with({ limits: { context: 262_144, output: 262_144 } }),
})
const undersizedContextModel = Model.make({
id: "undersized-context",
provider: "fake",
@@ -1865,6 +1870,25 @@ describe("SessionRunnerLLM", () => {
}),
)
it.effect("does not compact immediately when the advertised output limit fills the context", () =>
Effect.gen(function* () {
const session = yield* setup
currentModel = fullOutputModel
response = reply.textWithUsage("Earlier answer", "text-full-output-first", 9_500)
yield* admit(session, "Earlier question")
yield* session.resume(sessionID)
requests.length = 0
response = reply.text("Continued", "text-full-output-final")
yield* admit(session, "Continue")
yield* session.resume(sessionID)
expect(requests).toHaveLength(1)
expect(userTexts(requests[0])).toContain("Continue")
expect(yield* session.context(sessionID)).not.toContainEqual(expect.objectContaining({ type: "compaction" }))
}),
)
it.effect("stops after required automatic compaction fails", () =>
Effect.gen(function* () {
const session = yield* setup