Prevent cross-file mock.module leakage by restoring Bun mocks after recovery-hook test, so executor tests always run against the real module implementation.
Stop patching global timers in every lock-management test. Use scoped fake timers only in continuation tests so lock/notification assertions remain deterministic in CI.
Remove duplicated dispatchToHooks declaration that broke TypeScript parsing, and isolate chat-headers tests from marker cache collisions with unique message IDs.
Replace exact call count assertions with delta-based checks:
- capture errorSpy.mock.calls.length before processing events
- slice to only check calls made during this test's execution
- use try/finally to guarantee mockRestore() even on assertion failure
This prevents test pollution from cross-file spy leakage in CI batch runs.
Replace exact call count assertions with delta-based checks:
- capture errorSpy.mock.calls.length before processing events
- slice to only check calls made during this test's execution
- use try/finally to guarantee mockRestore() even on assertion failure
This prevents test pollution from cross-file spy leakage in CI batch runs.
Fixes#1804, fixes#1962
The migration entry 'gpt-5.2-codex → gpt-5.3-codex' caused the plugin to silently overwrite user configs on every startup with a model that doesn't exist in the OpenAI API. Users explicitly setting gpt-5.2-codex (the correct current model) were forced to revert their config manually every session.
- BUG-7: Add gpt-5-nano as final fallback in multimodal-looker model requirements
- BUG-14: Remove hardcoded LIBRARIAN_MODEL, let librarian resolve through normal fallback chain
- Update snapshots and tests to reflect new fallback behavior
When browserProvider is not set, agent-browser skill should NOT resolve.
Test assertions were inverted — expected 'Skills not found' but asserted the opposite.
Unhandled SQLite exceptions (SQLITE_BUSY, database locked, etc.) in queueMicrotask/setTimeout callbacks could crash the entire process. Added try/catch/finally to ensure db.close() is always called and errors are logged instead of crashing.
Split 1021-line index.ts into 10 focused modules per project conventions.
New structure:
- error-classifier.ts: error analysis with dynamic status code extraction
- agent-resolver.ts: agent detection utilities
- fallback-state.ts: state management and cooldown logic
- fallback-models.ts: model resolution from config
- auto-retry.ts: retry helpers with mutual recursion support
- event-handler.ts: session lifecycle events
- message-update-handler.ts: message.updated event handling
- chat-message-handler.ts: chat message interception
- hook.ts: main factory with proper cleanup
- types.ts: updated with HookDeps interface
- index.ts: 2-line barrel re-export
Embedded fixes:
- Fix setInterval leak with .unref()
- Replace require() with ESM import
- Add log warning on invalid model format
- Update sessionLastAccess on normal traffic
- Make extractStatusCode dynamic from config
- Remove unused SessionErrorInfo type
All 61 tests pass without modification.
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Update schema comment to match actual code default [400, 429, 503, 529].
Previously the comment omitted 400 which is included in the code default.
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Revert test name and assertion to original behavior per PR review feedback.
The test now correctly expects Atlas to respect uiSelectedModel instead of using its own fallback chain.
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Make provider auto-retry signal detection respect timeout_seconds setting:
- When timeout_seconds=0, disable quota-based fallback escalation
- Only treat auto-retry signals as errors when timeout is enabled
- Add test to verify behavior when timeout_seconds is disabled
- Update documentation to explain timeout_seconds=0 behavior
This allows users to disable timeout-based fallbacks while keeping
error-based fallback functionality intact.
Refactor retry signal detection to be provider-agnostic:
- Replace hardcoded Copilot/OpenAI checks with generic pattern matching
- Detect any provider message containing limit/quota keywords + [retrying in X]
- Add OpenAI pattern: 'usage limit has been reached [retrying in X]'
- Update logging to use generic 'provider' instead of specific names
- Add 'usage limit has been reached' to RETRYABLE_ERROR_PATTERNS
This enables fallback escalation for any provider that signals automatic
retries due to quota/rate limits, not just Copilot and OpenAI.
Closes PR discussion: generalize retry pattern detection
- Extract duplicated auto-retry logic (~40 lines each) from session.error and
message.updated handlers into shared autoRetryWithFallback() helper
- Fix userFallbackModels path in model-resolution-pipeline to respect
constraints.connectedProviders parameter instead of reading cache directly,
matching the behavior of categoryDefaultModel and fallbackChain paths