fix: prune expired model cooldowns from session state#2
Merged
Conversation
state.failedUntil accumulated one entry per failed model for the lifetime of a session and was never cleaned up. Expired cooldowns are now pruned on each session.error, keeping the map bounded. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR addresses a small session-state memory leak in the OpenCode model-fallback plugin by pruning expired model cooldown entries from state.failedUntil during error-driven fallback selection.
Changes:
- Added
pruneExpiredCooldowns(state, now)to remove expired entries from the per-session cooldown map. - Invoked pruning on each
session.errorbefore recording the newly-failed model and selecting the next fallback. - Added a test covering that a model becomes selectable again after
cooldown_mselapses.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
src/plugin.ts |
Adds and uses pruneExpiredCooldowns during session.error handling to prevent failedUntil from growing unbounded. |
test/index.test.ts |
Adds a cooldown-expiration scenario to validate that previously cooled-down models can be reused. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| expect(prompts).toHaveLength(2) | ||
| }) | ||
|
|
||
| test("#given a model cooldown has expired #when a later error fires #then the model is reusable and stale cooldowns are pruned", async () => { |
This was referenced Jul 13, 2026
Merged
ShutovKS
added a commit
that referenced
this pull request
Jul 14, 2026
This was referenced Jul 14, 2026
ShutovKS
added a commit
that referenced
this pull request
Jul 14, 2026
PR #3 (suppressed comment): errorText only walked `cause` for Error instances; plain-object wrappers fell through to JSON.stringify, which serializes a nested Error to {} and drops retryable text. Walk `cause` explicitly for records too, with the same depth cap. PR #2 (inline comment): the prune test only proved an expired cooldown made the model selectable again — pruning itself was unobservable. Expose active cooldowns as `cooling` in model_fallback_control status and assert the expired entries are gone. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Merged
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.
Проблема
state.failedUntil(Map провайдер→время) накапливал по записи на каждую упавшую модель на всё время жизни сессии и никогда не очищался — микроутечка памяти в долгих сессиях.Решение
Добавлен
pruneExpiredCooldowns(state, now), который удаляет просроченные записи. Вызывается на каждомsession.errorперед выбором следующей модели.Тесты
cooldown_msмодель снова доступна как fallback, а просроченные записи удаляются.🤖 Generated with Claude Code