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
Bug fixes:
1. extractStatusCode: handle nested data.statusCode (Anthropic error structure)
2. Error regex: relax credit.*balance.*too.*low pattern for multi-char gaps
3. Zod schema: bump max_fallback_attempts from 10 to 20 (config rejected silently)
4. getFallbackModelsForSession: fallback to sisyphus/any agent when session.error lacks agent
5. Model detection: derive model from agent config when session.error lacks model info
6. Auto-retry: resend last user message with fallback model via promptAsync
7. Persistent fallback: override model on every chat.message (not just pendingFallbackModel)
8. Manual model change: detect UI model changes and reset fallback state
9. Agent preservation: include agent in promptAsync body to prevent defaulting to sisyphus
Additional:
- Add sessionRetryInFlight guard to prevent double-retries
- Add resolveAgentForSession with 3-tier resolution (event → session memory → session ID)
- Add normalizeAgentName for display names like "Prometheus (Planner)" → "prometheus"
- Add resolveAgentForSessionFromContext to fetch agent from session messages
- Move AGENT_NAMES and agentPattern to module scope for reuse
- Register runtime-fallback hooks in event.ts and chat-message.ts
- Remove diagnostic debug logging from isRetryableError
- Add 400 to default retry_on_errors and credit/balance patterns to RETRYABLE_ERROR_PATTERNS
The \b word boundary regex treats '-' as a boundary, causing
'sisyphus-junior-session-123' to incorrectly match 'sisyphus'
instead of 'sisyphus-junior'.
Sorting agent names by length (descending) ensures longer names
are matched first, fixing the hyphenated agent detection issue.
Fixes cubic-dev-ai review issue #8
- Add normalizeFallbackModels helper to centralize string/array normalization (P3)
- Export RuntimeFallbackConfig and FallbackModels types from config/index.ts
- Fix agent detection regex to use word boundaries for sessionID matching
- Improve tests to verify actual fallback switching logic (not just log paths)
- Add SessionCategoryRegistry cleanup in executeSyncTask on completion/error (P2)
- All 24 runtime-fallback tests pass, 115 delegate-task tests pass
Replace word-boundary regex with stricter patterns that match
status codes only at start/end of string or surrounded by whitespace.
Prevents false matches like '1429' or '4290'.
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Implement full fallback_models support across all integration points:
1. Model Resolution Pipeline (src/shared/model-resolution-pipeline.ts)
- Add userFallbackModels to ModelResolutionRequest
- Process user fallback_models before hardcoded fallback chain
- Support both connected provider and availability checking modes
2. Agent Utils (src/agents/utils.ts)
- Update applyModelResolution to accept userFallbackModels
- Inject fallback_models for all builtin agents (sisyphus, oracle, etc.)
- Support both single string and array formats
3. Model Resolver (src/shared/model-resolver.ts)
- Add userFallbackModels to ExtendedModelResolutionInput type
- Pass through to resolveModelPipeline
4. Delegate Task Executor (src/tools/delegate-task/executor.ts)
- Extract category fallback_models configuration
- Pass to model resolution pipeline
- Register session category for runtime-fallback hook
5. Session Category Registry (src/shared/session-category-registry.ts)
- New module: maps sessionID -> category
- Used by runtime-fallback to lookup category fallback_models
- Auto-cleanup support
6. Runtime Fallback Hook (src/hooks/runtime-fallback/index.ts)
- Check SessionCategoryRegistry first for category fallback_models
- Fallback to agent-level configuration
- Import and use SessionCategoryRegistry
Test Results:
- runtime-fallback: 24/24 tests passing
- model-resolver: 46/46 tests passing
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
- Add Category-level fallback_models support in getFallbackModelsForSession()
- Try agent-level fallback_models first
- Then try agent's category fallback_models
- Support all builtin agents including hephaestus, sisyphus-junior, build, plan
- Expand agent name recognition regex to include:
- hephaestus, sisyphus-junior, build, plan, multimodal-looker
- Add comprehensive test coverage (6 new tests, total 24):
- Model switching via chat.message hook
- Agent-level fallback_models configuration
- SessionID agent pattern detection
- Cooldown mechanism validation
- Max attempts limit enforcement
All 24 tests passing
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Add configuration schemas for runtime model fallback feature:
- RuntimeFallbackConfigSchema with enabled, retry_on_errors,
max_fallback_attempts, cooldown_seconds, notify_on_fallback
- FallbackModelsSchema for init-time fallback model selection
- Add fallback_models to AgentOverrideConfigSchema and CategoryConfigSchema
- Export types and schemas from config/index.ts
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
- applyReplaceLines: use stripped array directly instead of restoreLeadingIndent
- applySetLine: keep restoreLeadingIndent (1:1 replacement needs indent preservation)
- Added test case for replace_lines preserving new line indentation
- All 3025 tests pass
🤖 Generated with OhMyOpenCode assistance
- keyword-detector: always set variant to 'max' when ultrawork/ulw keyword detected
- chat-message: remove variant resolution logic to passthrough TUI variant unchanged
- Tests updated to reflect new behavior
🤖 Generated with OhMyOpenCode assistance
Add dedicated '## Hashline Edit' section to configurations.md explaining the hash-anchored Edit tool, its default-on behavior, and how to disable it or its companion hooks. Update src/config/AGENTS.md to reflect hashline_edit moved out of experimental and into root schema (27 fields).
Move hashline_edit out of experimental so it is a stable top-level config with default-on runtime behavior and explicit disable support. Add migration and tests to preserve existing experimental.hashline_edit users without breaking configs.
Breaking Changes:
- Change hashline format from 'lineNum:hex|content' to 'lineNum#CID:content'
- Replace hex-based hashing (00-ff) with CID-based hashing (ZPMQVRWSNKTXJBYH nibbles)
- Simplify constants: HASH_DICT → NIBBLE_STR + HASHLINE_DICT
- Update patterns: HASHLINE_PATTERN → HASHLINE_REF_PATTERN + HASHLINE_OUTPUT_PATTERN
Benefits:
- More compact and memorable CID identifiers
- Better alignment with LSP line reference format (lineNum#ID)
- Improved error messages and diff metadata clarity
- Remove unused toHashlineContent from diff-enhancer hook
Updates:
- Refactor hash-computation for CID generation
- Update all diff-utils to use new format
- Update hook to use raw content instead of hashline format
- Update tests to match new expectations
🤖 Generated with assistance of [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)
- Add no-hephaestus-non-gpt to hook list for schema validation
- Add disable_omo_env to experimental features schema
- Sync schema with existing hook and feature implementations
🤖 Generated with assistance of [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)
background_output auto-enabled full_session when the task was still
running, returning the entire session transcript on every poll. When
the parent agent had no other work and polled in a tight loop, this
caused massive token waste because each response dumped thousands of
tokens into the conversation history.
Default full_session to false so running-task checks return a compact
status table (~200 tokens). Callers can still pass full_session=true
explicitly when they need the full transcript.