From fbd16ef93ef0cbfe7a5ec20b38faefb577476d30 Mon Sep 17 00:00:00 2001 From: Aiden Cline Date: Thu, 24 Sep 2026 18:59:41 -0500 Subject: [PATCH] fix(core): fit model limits and recover compaction overflow --- packages/core/src/plugin/provider/openai.ts | 8 + packages/core/src/session/compaction.ts | 346 +++++++++++++----- packages/core/src/session/model-request.ts | 28 +- packages/core/src/session/runner/llm.ts | 5 + packages/core/test/config/compaction.test.ts | 2 +- .../core/test/plugin/provider-openai.test.ts | 25 ++ packages/core/test/session-compaction.test.ts | 34 +- .../test/session-model-request-hooks.test.ts | 58 +++ .../core/test/session-model-request.test.ts | 33 +- .../test/session-native-compaction.test.ts | 6 +- packages/core/test/session-runner.test.ts | 129 ++++++- services/www/src/docs/content/compaction.mdx | 28 +- 12 files changed, 560 insertions(+), 142 deletions(-) diff --git a/packages/core/src/plugin/provider/openai.ts b/packages/core/src/plugin/provider/openai.ts index 255cbb2cfcc..5c7bdc38aff 100644 --- a/packages/core/src/plugin/provider/openai.ts +++ b/packages/core/src/plugin/provider/openai.ts @@ -1,5 +1,6 @@ import type { IntegrationOAuthMethodRegistration } from "@opencode/plugin/effect/integration" import { define } from "@opencode/plugin/effect/plugin" +import type { SessionRequest } from "@opencode/plugin/effect/session" import { Deferred, Effect, Option, Schema, Semaphore, Stream } from "effect" import type { Server } from "node:http" import { App } from "../../app.js" @@ -307,6 +308,13 @@ export const OpenAIPlugin = define({ }), { providerID: Provider.ID.openai }, ) + // The ChatGPT backend rejects a requested output limit, and OpenAI counts one against rate limits. + const omitOutputLimit = (evt: SessionRequest) => + Effect.sync(() => { + delete evt.options.maxTokens + }) + for (const name of ["context", "compaction"] as const) + yield* ctx.session.hook(name, omitOutputLimit, { providerID: Provider.ID.openai }) const refresh = () => loading.withPermit(load().pipe(Effect.andThen(ctx.provider.reload()))) yield* bus.subscribe(Credential.Event.Switched).pipe( Stream.filter((event) => event.data.integrationID === Integration.ID.make("openai")), diff --git a/packages/core/src/session/compaction.ts b/packages/core/src/session/compaction.ts index fc9d16799a0..aed56665326 100644 --- a/packages/core/src/session/compaction.ts +++ b/packages/core/src/session/compaction.ts @@ -37,12 +37,17 @@ import { toLLMMessages } from "./runner/to-llm-message.js" import type { AgentNotFoundError } from "./error.js" import type { Instructions } from "../instructions/index.js" -const DEFAULT_BUFFER = 20_000 +const AUTO_THRESHOLD = 0.9 const DEFAULT_KEEP_TOKENS = 15_000 -const OUTPUT_TOKEN_MAX = 32_000 const TOOL_OUTPUT_MAX_CHARS = 2_000 +const FALLBACK_TOOL_CHARS = 1_000 +const FALLBACK_OVERFLOW_RETRIES = 3 const IMAGE_TOKEN_ESTIMATE = 1_500 const PDF_TOKEN_ESTIMATE = 2_000 +const usableWindow = (limit: SessionRunnerModel.Resolved["limit"]) => + Math.min(limit.context > 0 ? limit.context : (limit.input ?? 0), limit.input ?? Number.POSITIVE_INFINITY) +const compactionCeiling = (usable: number, buffer?: number) => + buffer === undefined ? Math.floor(usable * AUTO_THRESHOLD) : usable - buffer const SUMMARY_TEMPLATE = `You MUST use this format for your response (you may omit sections that aren't applicable). Do not include the