Volcano Engine sends quota exceeded errors with the words in reverse
order: 'You have exceeded the 5-hour usage quota'. The existing
patterns required 'quota' to precede 'exceeded', so they never matched.
- Add /exceeded.*quota/i and /usage.?quota/i to RETRYABLE_ERROR_PATTERNS
- Add exceeded.*quota and usage\s*quota to AUTO_RETRY_PATTERNS
- Add regression tests for both detection paths
Fixes: runtime-fallback not triggering on Volcano Engine quota errors
The deep and artistry category fallback chains had no opencode-go provider
entries, causing them to fall through to the ultimate fallback
(opencode/gpt-5-nano) which is not available to opencode-go users.
Add opencode-go entries:
- deep: deepseek-v4-pro -> kimi-k2.6 -> glm-5.1
- artistry: kimi-k2.6 -> glm-5.1
Fixes#3924
The same output.trim() bug fixed in PR #3909 for doctor exists in the
installer's opencode-binary.ts. Without this fix, `bunx oh-my-opencode
install` would store polluted Electron stdout (e.g., `00:24:25.202 >
app starting { version: '1.14.33', packaged: true }`) as the OpenCode
version in config, breaking downstream version-dependent logic.
- Extract extractSemverFromOutput to src/shared/extract-semver.ts
(precedent: spawn-with-windows-hide is in shared because used by
both doctor and installer)
- src/cli/doctor/checks/system-binary.ts now imports from shared and
re-exports for backward compat
- src/cli/config-manager/opencode-binary.ts uses the shared helper
with `?? output.trim()` fallback to preserve legacy behavior on
non-semver-shaped successful outputs (e.g., custom builds)
- Add 4 installer regression tests covering: clean semver, polluted
Electron stdout (regression for #3765 installer caller), fallback
for non-semver, null when no binary on PATH
Refs #3765
MessageAbortedError/worker shutdown could race with scheduled removeTask, leaving background_output's manager.getTask returning 'Task not found' even though the task had completed cleanly.
Fix: add completedTaskArchive (max 500, FIFO eviction). On removeTask, archive non-running/pending tasks with sessionId. getTask falls back to archive on active-map miss. addTask clears stale archive entries on re-registration.
Fixes#3895
When session.status(idle) is converted to synthetic session.idle and recorded in recentAnyIdles, a real session.idle arriving within 500ms was being dropped by the dedup logic. recentSyntheticIdles was cleared but recentAnyIdles persisted, causing TODO-DIAG to red-alert with 'no todossession.idle event'.
Fix: when real session.idle arrives, also clear recentAnyIdles entry so dedup does not drop it. Test renamed and expected dispatchCalls updated 1 → 2.
Fixes#2667
openSync with read-only mode fails fsync on Windows because FlushFileBuffers requires write-permission FD. This caused atomic writes to fail silently, leaving migrated config unwritten and triggering repeated migration + .bak.<timestamp> generation on every startup.
Same root cause as PR #3644 (#3643). Hyperplan disappear is a secondary symptom of plugin load instability.
Fixes#3877
The Electron-based OpenCode build leaks log lines like
`00:24:25.202 > app starting { version: '1.14.33', packaged: true }`
into stdout, so `getOpenCodeVersion` was returning the entire log
line as the 'version'. `compareVersions` then split that string on
'.' and produced a nonsensical numeric array (e.g., `[0, 0, 14, 0]`),
which it judged as < the minimum 1.4.0. Result: doctor incorrectly
flagged OpenCode 1.14.x as below the minimum required version.
Replace the raw `stdout.trim()` return with a small
`extractSemverFromOutput` helper that runs a semver-shaped regex
across the output. A negative lookbehind `(?<![\d:])` skips the
milliseconds segment of timestamps (e.g., `25.202` in
`00:24:25.202`), so the parser locks onto the real version token.
Adds 12 unit tests covering plain semver, v-prefix, pre-release,
build metadata, the Electron regression, timestamp-only stdout,
and various invalid inputs.
Fixes#3765
Keep ordinary delegated subagents from seeing team tools while preserving communication tools for real team-mode member sessions, including fallback retry sessions.
Sub-agent sessions created via the task tool or call_omo_agent tool were
being created without a model. The resolved model was only passed as a
promptAsync body override, which opencode core ignores, causing fallback
to the client's system default model (often a reasoning model that hangs).
OpenCode's session.create API supports model: { id, providerID, variant }
at creation time. This fix ensures the resolved category/agent model is
passed during session creation across all paths:
- createSyncSession() (delegate task / category-based)
- createOrGetSession() (call_omo_agent / direct agent calls)
- BackgroundManager.startTask() (background tasks)
This guarantees each sub-agent session is created with the correct model
regardless of whether promptAsync honors its model override.
Atlas was delegating tasks one-by-one because the workflow framed parallel
as a conditional ("if tasks can run in parallel..."), letting models default
to the safer sequential path. The new shared ATLAS_PARALLEL_BY_DEFAULT block
flips the default: parallel is mandatory; sequential requires a NAMED
blocking dependency (input dependency or file conflict).
Adds two new prompt variants — kimi (K2.6 thinking-mode calibration:
commitment framing + concrete budgets) and opus-4-7 (counters 4.7's lower
default subagent count and literal-following bias). Recalibrates default
(Claude 4.6 family), gpt (GPT-5.5 outcome-first / decision rules over
absolutes), and gemini (preserves TOOL_CALL_MANDATE; replaces stale
session_id with task_id). All five variants share the parallel mandate
positioned BEFORE the workflow so "mandate above" references resolve.
Strengthens the orchestrator-direct-edit reminder hooks
(ORCHESTRATOR_DELEGATION_REQUIRED + DIRECT_WORK_REMINDER) with the central
challenge "Do you ACTUALLY need to be the one doing this?" — replacing the
previous bullet-heavy framing.
Tests now parametrized over all 5 variants. Adds prompt-routing.test
covering GPT/Gemini/Kimi/Opus 4.7/default routing and edge cases, plus a
session_id rejection test (every variant must use task_id for retries).
Replaces fsyncSync(tempFileDescriptor) with tolerantFsyncSync, allowing
EPERM/EACCES/ENOTSUP/EINVAL during fsync while still propagating real
errors. Adds an optional deps.fsyncSync injection point used solely by
the new EPERM tolerance regression tests.
Without this fix, plugin startup itself can fail on synced folders
because writeFileAtomically is used by config migrations and posthog
activity state — the same EPERM-on-fsync failure pattern reported for
team_create.
Replaces direct fileHandle.sync() calls in acquireLock and atomicWrite
with tolerantFsync. Users on iCloud Drive / OneDrive / Desktop sync
folders were hitting 'EPERM: operation not permitted, fsync' during
team_create, which propagated up and aborted the entire team_create
flow even though the actual write+rename had succeeded.
Reported on Discord (omo 4.0.0, opencode desktop 1.14.41, project on
synced Desktop). atomicity is preserved by the temp-file rename; only
the durability hint is now best-effort on filesystems that disallow
fsync.
Adds isToleratedFsyncError, tolerantFsync (async, FileHandle), and
tolerantFsyncSync (sync, fd) helpers that swallow filesystem-limitation
errors during fsync (EPERM, EACCES, ENOTSUP, EINVAL) while still
propagating real errors (EIO, ENOSPC, EBADF, etc.). Synced folders
like iCloud Drive, OneDrive, and antivirus-locked files reject fsync
with EPERM even though the underlying write+rename succeeded; for the
runtime data this codebase persists, losing the durability hint is
acceptable in exchange for not blocking the operation entirely.
The helper is intentionally not barrel-exported (consumers import the
file directly), matching the existing convention for write-file-atomically.
Commit 2dfa6336f changed Metis primary model from claude-opus-4-7 max
to claude-sonnet-4-6 in src/shared/model-requirements.ts but only
updated the unit test in model-requirements.test.ts. The CLI installer
end-to-end snapshot test (src/cli/model-fallback.test.ts) generates
configs from those same fallback chains via generateModelConfig and
captures them as snapshots, so 17 snapshot assertions broke.
Snapshots regenerated with `bun test src/cli/model-fallback.test.ts -u`.
Verified 86/86 pass across model-requirements + model-fallback +
openai-only-model-catalog test files.
The change is a no-op for the web import that this PR is primarily
about — including it here to unblock CI on dev (where the breakage
landed) rather than spinning up a separate hotfix PR.
The previous commit (2dfa6336f) used 'git add -A' which swept up files that
prior commits had intentionally deleted but were still present untracked in
the local workspace. This commit re-deletes them to match the upstream intent.
Files removed (each was previously deleted in the cited commit):
- drafts/gpt-5-5/{README,deep,hephaestus,oracle,sisyphus-junior,sisyphus}.md
(deleted in 40ded422c chore(drafts): remove stale gpt-5.5 prompt drafts)
- src/plugin-dispose.{ts,test.ts}
(deleted in e2f5c0d36 refactor(plugin): remove orphaned createPluginDispose)
- src/features/tmux-subagent/{cleanup,session-created-handler,session-deleted-handler}.ts
(deleted in 7a7926f22 chore(tmux-subagent): remove dead event-handler modules)
- src/tools/delegate-task/{model-string-parser,resolve-call-id,resolve-call-id.test}.ts
(model-string-parser deleted in db056346d; resolve-call-id was scratch)
- src/__debug-test.test.ts (debug scratch never intended for git)
Typecheck + model-requirements tests still pass. The legitimate metis +
AGENTS.md edits from the previous commit remain on dev.
Source code change:
- src/shared/model-requirements.ts: prepend claude-sonnet-4-6 to metis fallback
chain so Sonnet becomes the default. Opus 4.7 max remains as the immediate
fallback for callers who want extra reasoning.
- src/shared/model-requirements.test.ts: update assertion to expect Sonnet
primary + Opus secondary.
AGENTS.md accuracy fixes (verified against source):
- Agent modes: Sisyphus/Hephaestus are 'primary' (not 'all'); Sisyphus-Junior
is 'subagent' (not 'all'). Confirmed via 'const MODE: AgentMode = ...' in
each agent file. Also clarified Prometheus has no agentSources factory and
is built via buildPrometheusAgentConfig.
- Sisyphus fallback chain: corrected order to kimi-k2.6 → k2p5 → kimi-k2.5
→ gpt-5.5 medium → glm-5 → big-pickle (was missing kimi-k2.5).
- Librarian/Explore: added missing minimax-m2.7 step between -highspeed and
claude-haiku-4-5.
- Metis chain: removed fictitious gemini-3.1-pro entry.
- Sisyphus-Junior chain: spelled out the actual fallback (was 'user-configurable').
- Temperatures: Sisyphus/Hephaestus do not set explicit temperature (model
default); Sisyphus-Junior is 0.1 via SISYPHUS_JUNIOR_DEFAULTS.
- Quick category default: gpt-5.4-mini (not gpt-5.4-mini-fast).
Team-mode corrections:
- Eligibility registry has 3 verdicts: eligible (sisyphus, atlas, sisyphus-junior),
conditional (hephaestus — needs D-36 teammate permission), hard-reject
(oracle, librarian, explore, multimodal-looker, metis, momus, prometheus).
- Schema has 11 fields, not 4: added max_messages_per_run, max_wall_clock_minutes,
max_member_turns, base_dir, message_payload_max_bytes, recipient_unread_max_bytes,
mailbox_poll_interval_ms.
- Hooks: 'team-session-events' is 4 sub-handlers in src/plugin/event.ts
(team-idle-wake-hint, team-lead-orphan-handler, team-member-error-handler,
team-member-status-handler), not a single Continuation-tier hook.
- Tier counts now show base + team-mode: ToolGuard 14/15, Transform 5/7.
- Total: 52 base hooks, 59 with team-mode.
Doc cascade for the Metis change:
- docs/guide/orchestration.md, agent-model-matching.md, installation.md
- docs/reference/configuration.md, features.md