fix: persist modelContextLimit, skip min-threshold when unknown, percentage defaults, keep blocks active across restarts - #607
Open
Neko-Yukari wants to merge 4 commits into
Conversation
…is missing compressMessageId (the assistant message executing compress) is marked ignored/synthetic and never persisted, so after an opencode restart the block was deactivated and the full original context was re-injected, causing premature compression reminders. Fall back to anchorMessageId (which persists): if the anchor exists, keep the block active so the compressed summary keeps being injected into the LLM context.
Absolute 100K/50K defaults trigger compression reminders far too early on modern large-context models (256K-1M). Use 85%/60% of the model context by default; falls back gracefully when the limit is unknown.
opencode runs the chat.message transform before the system.prompt hook, so on the first message after a restart state.modelContextLimit is not yet cached. resolveContextTokenLimit then fails for percentage limits and overMinLimit fell back to unconditional true, injecting compression nudges on every turn for a normal ~300K context on 1M models. Fix: overMinLimit now returns false when the limit cannot be resolved (skip nudge instead of unconditionally triggering). The model limit is a session constant: from the second turn onward the system.prompt hook has cached it and threshold checks work normally. The real model limit is still enforced by the API layer. Tests: 108/108 pass (added min-threshold skip and 300K no-false-alarm regressions).
Neko-Yukari
force-pushed
the
fix-restart-thresholds
branch
from
August 18, 2026 16:54
4e0a9cd to
275a36c
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Four related fixes for premature compression reminders on modern large-context models (256K-1M tokens), observed as "restart then immediately asked to compress" on a ~300K / 1M context.
1. Persist
modelContextLimit(state/persistence.ts,state/state.ts)opencode runs the
chat.messagetransform before thesystem.prompthook, so on the first message after a restartstate.modelContextLimitis not yet cached and is not persisted.resolveContextTokenLimitthen fails for percentage limits. Persisting the value restores it before the first transform.2. Skip min-threshold check when limit is unknown (
messages/inject/utils.ts)Previously
overMinLimit = minContextLimit === undefined ? true— with the limit unresolvable, a normal ~300K context on a 1M model triggered compression nudges on every turn. Now it skips the check (false); the real model limit is still enforced by the API layer. (max side already behaved this way.)3. Default limits as percentages (
config.ts)Absolute
100000/50000defaults fire far too early on 256K-1M models. Defaults are now85%/60%of the model context, falling back gracefully when the limit is unknown.4. Keep compress blocks active across restarts (
messages/sync.ts)compressMessageId(the assistant message executing compress) is marked ignored/synthetic and never persisted, so after a restartsyncCompressionBlocksdeactivated the block and the full original context was re-injected. When the origin message is missing but the anchor message still exists, the block is kept active so the summary keeps being injected.Tests
109/109 pass. Added:
modelContextLimitunknownmodelContextLimitpersistence round-trip