Adds an opt-in `disabled_providers` config field plus the `shared/disabled-providers`
helper (`isProviderDisabled`, `filterDisabledProviderModels`, `applyDisabledProviders`)
that the plugin-config layer applies to resolved providers.
The routing changes in team-mode (team-runtime, tools/lifecycle, plugin/tool-registry)
are NOT in this PR — those are entangled with the `MemberSelectionMode` /
`ShutdownActor` refactors from the closed PR #3871 and need their own extraction
once those refactors land independently. This PR isolates the parts that are
cleanly independent: schema, helper, and config-layer wiring.
Companion to #4024 (live-tail tailer), both extracted from the closed PR #3871.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Guard the tool-pair validator when transformed assistant messages belong to a tracked subagent session, while keeping normal main-session orphaned tool_use repair intact.
Constraint: code-yeongyu/oh-my-openagent#3996 reports background Oracle sessions hanging after synthetic placeholder tool_result insertion.
Rejected: disable tool-pair-validator globally | would regress main-session compaction/orphaned tool_use repair.
Confidence: high
Scope-risk: narrow
Directive: Keep subagent skip coverage and normal repair coverage together when changing tool-pair validation.
Tested: bun test src/hooks/tool-pair-validator/hook.test.ts src/plugin/messages-transform.test.ts
Tested: bun run typecheck
Not-tested: live Oracle background task e2e; no remote push or PR comment performed.
scheduleForcedExit was called without exitAfterCleanup=true for SIGTERM/SIGINT.
After cleanup completed, timeout cleared but process.exit() never called.
Process stayed alive waiting for event loop to drain.
Benchmark (systemctl stop):
- Before: 10s+ timeout (systemd had to SIGKILL)
- After: ~30ms instant shutdown
Test results (3 runs):
- Test 1: 30ms
- Test 2: 28ms
- Test 3: 27ms
The sort shim always placed core agents (sisyphus, hephaestus, prometheus,
atlas) before any custom agent, ignoring the user's configured default_agent
in opencode.json. This caused the TUI to always show Sisyphus as the selected
agent on startup regardless of default_agent.
Add setDefaultAgentForSort() which inserts the configured default_agent at
rank 0 in the sort shim's rank map. Called from applyAgentConfig after
resolving the effective default_agent value.
Fixes#3900
Bun's --frozen-lockfile failed on origin/dev because v4.1.2 (c75deee54)
bumped the platform binary versions in package.json without refreshing
bun.lock. Regenerated via `bun install` to unblock CI on dev-based PRs.
Required for `bun install --frozen-lockfile` (used by ci.yml test +
typecheck jobs) to succeed.
When many background tasks complete in rapid succession while the parent
session is idle, each completion fired its own promptAsync call, stacking
N consecutive `<system-reminder>` user messages with no assistant turn
between. Hyperplan + many parallel explore subagents made this very
visible to the user.
Route the idle-path through the existing pendingParentWakes queue with a
100ms debounce window. Notifications arriving during the debounce join
the same batch, the 150ms settle window also coalesces newcomers, and a
single batched prompt fires to the parent. Busy-path semantics are
unchanged (still 1s retry).
Prior attempts (1c05c60dc, ea55c385b, a337635e3) all coalesced only the
busy-defer path, leaving the idle-immediate-send path uncoalesced.
Reframe the skill's Lifecycle section so the lead treats teams as
ephemeral, one-per-phase units. The moment a phase ends or the shape
no longer fits, call team_delete and spawn a fresh team. Restructure
through delete-then-create, never in place.
Also fixes a misframing in old step 5: team_shutdown_request is a
per-session self-shutdown signal, not a 'wind down the team' command.
team_delete is what tears the whole team down.
- Add src/locales/ with en baseline and zh overrides (Partial<Record> fallback)
- Add src/shared/i18n.ts with initI18n/t/setLocale/getLocale (LANG env auto-detect)
- Add I18nConfigSchema with locale field to plugin config
- Internationalize 13 hardcoded strings in task-toast-manager
- Add 18 unit tests for i18n module
- Pin manager tests to en locale for determinism
Follow-up pass to catch drift my earlier docs commit missed:
docs/reference/features.md:
- 'Architecture Snapshot': 5-tier hook composition '52 base / 59 with team mode'
to '54 base / 61 with team mode' (matches actual ToolGuard +2 from
fsync-skip-warning + bash-file-read-guard).
docs/reference/cli.md:
- Add 'boulder' subcommand to Commands table (inspects boulder work-state).
docs/reference/configuration.md:
- Code comment example 'Kimi K2.5 work best' to 'Kimi K2.6 work best' to match
current primary Sisyphus fallback chain.
docs/guide/installation.md:
- Add 'Kimi K2.6 (opencode-go, vercel)' row to the provider availability table
above the existing K2.5 row.
- Selection priority footnote and 'Safe Overrides' chains updated to mention
K2.6 alongside K2.5 in Sisyphus and Atlas chains.
docs/guide/agent-model-matching.md:
- Provider table 'Kimi K2.5' row to 'Kimi K2.6 / K2.5'.
- Atlas fallback chain mentions K2.6 instead of K2.5 alone.
Sweep the same '25+ hooks' / '50+ hooks' drift that lingered in the localized
READMEs and the marketing site after the user-facing docs refresh:
- README.ja.md / README.ko.md / README.zh-cn.md / README.ru.md:
'25+ built-in hooks' to '54+ lifecycle hooks (61 with Team Mode)'.
- web/app/layout.tsx + web/app/_components/landing-page.tsx:
SEO description, OG description, Twitter description, JSON-LD description,
and HeroStats injected count: '50+ lifecycle hooks' to '54+ lifecycle hooks'.
Matches the canonical hook composition in src/plugin/hooks/ (24 Session +
16 ToolGuard + 5 Transform + 7 Continuation + 2 Skill = 54 base; +7 with
team_mode.enabled = 61 total).
Align README.md, CONTRIBUTING.md, and docs/ with the same ground truth used
in the hierarchical AGENTS.md regeneration commit 1e7a7600a:
- README.md: '25+ built-in hooks' was undercounted by ~30 hooks; now reads
'54+ lifecycle hooks (61 with Team Mode)' to match actual tier composition.
- CONTRIBUTING.md: src/hooks/ '52 hooks across 55 modules' -> '54 base (61 team) across 58 dirs';
src/tools/ '26 tools across 16 directories' -> '20-39 tools (config-gated)';
src/features/ '19 feature modules' -> '20 feature modules' (boulder-state, team-mode added).
- docs/reference/features.md: Core-agent order indices were off-by-one;
Sisyphus 1/Hephaestus 2/Prometheus 3/Atlas 4 -> 0/1/2/3 (matches installAgentSortShim).
- docs/guide/overview.md: Sisyphus model recommendations now mention Kimi K2.6 alongside K2.5
since K2.6 is the active default fallback in the primary Sisyphus chain
(see src/shared/model-requirements.ts).
When runtime-fallback aborts an in-flight request to swap in a fallback
model, opencode emits session.error{isAbort:true} as a consequence. The
existing event handler treated that as a user cancellation and called
resetRetryState — wiping attemptCount. Every subsequent provider
auto-retry signal then started over at attempt:1, never reaching
max_fallback_attempts, producing an infinite retry loop firing a new
fallback every ~2 seconds.
The bug only surfaces when the configured fallback target itself
silently fails (e.g. github-copilot quota exhausted): the original
model keeps re-emitting retry signals, our handler keeps "fixing"
them, the counter never advances. Reproducible on upstream/dev HEAD
(5ffbe0e24e).
Fix:
- New `internallyAbortedSessions: Set<string>` on HookDeps tracks
sessions whose abort we triggered ourselves.
- abortSessionRequest in auto-retry.ts adds the session to the set
when called with one of our internal sources:
"session.status.retry-signal", "message.updated.retry-signal",
"session.timeout". The "session.stop" source (user-initiated) is
intentionally NOT marked — that path must still wipe state.
- handleSessionError in event-handler.ts checks the set before the
cancellation branch. If the session is marked, consume the flag
(delete it so a later user-abort still gets the reset) and skip
resetRetryState. The state's attemptCount is preserved, so the
next iteration progresses 1→2→3→... until max_fallback_attempts.
- dispose() clears the new set alongside the other per-session maps.
Tests: 3 new event-handler integration tests cover the fix
(internal-abort preserves state, external-abort still resets,
consecutive internal-abort cycles advance attemptCount). Existing
tests pass: 7/7 on event-handler. Pre-existing 2 dispose-test flakes
on the full runtime-fallback suite were verified to exist on
upstream/dev without this patch — unrelated.
bun run build: pass. bun run typecheck: pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
mergeWithClaudeCodeAgents deduplicated by raw agent.name.toLowerCase() while
matchesRequestedAgent strips invisible characters, the numeric sort prefix,
and wrapper characters via stripAgentListSortPrefix. A project or user agent
named with a zero-width prefix, quote wrappers, or a sort prefix survived as
a visible duplicate of the hidden native build or demoted plan agent and
matched subagent_type="build" or "plan", which let an OMO orchestrator reach
the hidden execution agent the previous filter was meant to block.
Apply the same canonicalization to the dedup key so visible aliases of hidden
server agents collapse onto the hidden entry instead of bypassing the filter.
Adds three regression tests covering ZWSP, quote-wrapper, and sort-prefix
bypass paths.
bun.lock: refresh platform optionalDependencies to 4.1.1 so frozen-lockfile
install succeeds in CI.