fix(core): respect automatic compaction opt-out on overflow (#45036)

Co-authored-by: rekram1-node <rekram1-node@users.noreply.github.com>
This commit is contained in:
opencode-agent[bot] 2026-08-25 16:58:39 -05:00 committed by GitHub
parent 61dd296161
commit b79cad5ec8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 35 additions and 2 deletions

View file

@ -103,6 +103,7 @@ export type Outcome =
| Pick<SessionMessage.CompactionFailed, "status" | "error">
export interface Interface extends State.Transformable<Draft> {
readonly enabled: () => boolean
readonly required: (input: RequiredInput) => boolean
readonly compact: (input: AutoInput) => Effect.Effect<Outcome>
readonly compactManual: (input: ManualInput) => Effect.Effect<Outcome>
@ -405,6 +406,7 @@ const make = (dependencies: Dependencies) => {
return Service.of({
transform: state.transform,
reload: state.reload,
enabled: () => state.get().auto,
required,
compact,
compactManual,

View file

@ -508,6 +508,7 @@ const layer = Layer.effect(
// restart the step instead of surfacing the provider error.
if (
recoverOverflow &&
compaction.enabled() &&
!publisher.record().outputStarted &&
isContextOverflowFailure(overflowFailure ?? streamFailure) &&
(yield* restore(compaction.compact(compactionInput))).status === "completed"

View file

@ -33,6 +33,7 @@ import { Session } from "@opencode-ai/core/session"
import { Snapshot } from "@opencode-ai/core/snapshot"
import { SessionEvent } from "@opencode-ai/core/session/event"
import { SessionContext } from "@opencode-ai/core/session/context"
import { SessionCompaction } from "@opencode-ai/core/session/compaction"
import { SessionInbox } from "@opencode-ai/core/session/inbox"
import { SessionMessage } from "@opencode-ai/core/session/message"
import { SessionModelRequest } from "@opencode-ai/core/session/model-request"
@ -458,6 +459,7 @@ const it = testEffect(
Config.node,
Snapshot.node,
SessionContext.node,
SessionCompaction.node,
SessionModelRequest.node,
SessionRunnerLLM.node,
SessionExecution.node,
@ -2560,6 +2562,34 @@ describe("SessionRunnerLLM", () => {
}),
)
it.effect("does not recover provider context overflow when automatic compaction is disabled", () =>
Effect.gen(function* () {
const session = yield* setupOverflowRecovery
const compaction = yield* SessionCompaction.Service
yield* compaction.transform((draft) => draft.configure({ auto: false }))
yield* TestLLM.push(
[LLMEvent.providerError({ message: "prompt too long", classification: "context-overflow" })],
TestLLM.text("Must not compact", "text-unexpected-summary"),
TestLLM.text("Must not retry", "text-unexpected-retry"),
)
yield* admit(session, "Continue")
expect((yield* session.resume(sessionID).pipe(Effect.flip)).message).toBe("prompt too long")
expect(requests).toHaveLength(1)
expect(yield* session.context(sessionID)).toContainEqual(
expect.objectContaining({
type: "assistant",
finish: "error",
error: expect.objectContaining({ message: "prompt too long" }),
}),
)
expect(yield* session.context(sessionID)).not.toContainEqual(expect.objectContaining({ type: "compaction" }))
expect(yield* recordedEventTypes(sessionID)).not.toContain(
Bus.versionedType(SessionEvent.Compaction.Started.type, 1),
)
}),
)
it.effect("recovers from provider context overflow without a configured context limit", () =>
Effect.gen(function* () {
const session = yield* setupOverflowRecovery

View file

@ -67,7 +67,7 @@ Add `compaction` to any [OpenCode configuration file](/config):
| Field | Default | V2 behavior |
| ------------- | ------: | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `auto` | `true` | Runs the preflight context-size check. It does not disable manual compaction or one-shot provider-overflow recovery. |
| `auto` | `true` | Enables preflight context-size checks and one-shot provider-overflow recovery. Disabling it does not affect manual compaction. |
| `keep.tokens` | `15000` | Approximate number of tokens from the newest serialized conversation context to retain beside the summary. |
| `buffer` | `20000` | Safety reserve below an explicit input limit. Without one, it is the minimum context reserve and the model output allowance wins when larger. |

View file

@ -73,7 +73,7 @@ Before each Step, the runner estimates the complete model-visible request agains
The full transcript remains durable. Active model history after the compaction boundary contains the summary and retained recent context; provider-native continuation state does not cross that boundary.
If the provider reports context overflow before durable assistant output or tool execution, the runner may perform one overflow-triggered compaction and rebuild the same logical Step. A second overflow or any overflow after durable output is terminal.
If automatic compaction is enabled and the provider reports context overflow before durable assistant output or tool execution, the runner may perform one overflow-triggered compaction and rebuild the same logical Step. A second overflow, any overflow after durable output, or an overflow when automatic compaction is disabled is terminal.
## Durable Events Are Session-Scoped