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
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
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.
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.
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
- Remove plugin_loaded telemetry from index.ts (was 46.5% of all events, ~2.83M/month)
- Add enableLocalEvaluation: false to prevent feature flag polling/decide calls
- Add strictLocalEvaluation: true to prevent server fallback requests
- Add disableRemoteConfig: true to prevent remote config network requests
- Remove 'plugin_loaded' from PostHogActivityReason type
- Update tests: remove stale mocks, add SDK options verification test
- enableExceptionAutocapture: false already present (kept)
Estimated billing reduction: ~$960+/month from feature flag requests,
plus ~2.83M fewer events/month from plugin_loaded removal.
Capture delegated child-session retry context before the first prompt so fallback recovery still works when session history is empty. Align background and sync launch paths around the same bootstrap contract, clear session-scoped fallback state on every terminal path, and lock the behavior with regression coverage for first-prompt retries, exhaustion, isolation, and cleanup.
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Agent names like \hephaestus\ cause 'Agent not found' errors because
sanitizeSubagentType() was only called in subagent-resolver.ts but not
in the direct manager.launch() path or background-executor.ts.
- manager.ts: strip leading/trailing backslash/quote chars from input.agent
before validation so \hephaestus\ → hephaestus
- background-executor.ts: call sanitizeSubagentType(args.subagent_type)
instead of passing raw value to manager.launch()
- agent-display-names.ts: reuse sanitizeSubagentType in stripAgentListSortPrefix
- Add unit tests for all three fix points
Fixes: sessions dying with 'Agent not found: \hephaestus\'
Restore consistent assertion style in librarian and explore tests.
Multi-provider entries like ["opencode-go", "vercel"] should use
toContain() not providers[0].toBe() to match the established pattern.
kimi-k2.6 is referenced in fallback chains but missing from the bundled snapshot (which only has kimi-k2.5). Added supplemental entry with specs matching models.dev data.
Walks up the directory tree from a start directory looking for
.opencode/oh-my-openagent.json[c] (or legacy basename) files. Returns
detected paths in nearest-first order, optionally stopping at a
caller-provided directory.
Reuses detectPluginConfigFile so canonical/legacy basename detection,
caching, and JSONC vs JSON precedence stay consistent with existing
config loading. Foundation for #417 hierarchical config discovery.
Address cubic and oracle review feedback to make the Node/Electron
fallback faithful to Bun.spawn behavior so cross-runtime callers
behave identically.
Changes:
- resolveStdio() default stdio is now ["ignore", "pipe", "inherit"]
to match Bun.spawn defaults (was ["pipe", "pipe", "pipe"]).
Prevents hangs in dispatcher and on-complete-hook callers that
did not explicitly set stdin and would otherwise wait forever
for input on Node.
- spawn-with-windows-hide.ts uses the same defaults so the Windows
Node helper aligns with the rest of the shim.
- wrapNodeProcess now rejects proc.exited with the original error
on "error" events (previously swallowed the error and resolved
to exit code 1, hiding ENOENT and friends).
- spawnSync result returns the real result.pid instead of -1 and
exposes stdout/stderr as Buffer | undefined to match Node's
spawnSync result shape when those streams are not piped.
Tests cover the new defaults, real pid surfacing, and missing
executable error propagation.
Refs cubic review and oracle audit on #3798.