Previously, loadPluginHooksConfigs() loaded plugin hooks from marketplace
plugins (hookify, superpowers, zzcommon, zzfe, etc.) into
pluginComponents.hooksConfigs, but config-handler.ts never consumed them.
This meant plugin hooks were discovered but never merged into the runtime
hooks dispatch system.
Changes:
- Extend ClaudeHookEvent and ClaudeHooksConfig to support all 12 event
types (PostToolUseFailure, PermissionRequest, Notification,
SubagentStart, SubagentStop, SessionStart, SessionEnd) in addition to
the existing 5
- Add ALL_HOOK_EVENT_TYPES constant as single source of truth for event
type iteration
- Add mergePluginHooksConfigs() to unwrap plugin HooksConfig (with hooks
wrapper) into flat ClaudeHooksConfig, filtering out unsupported
prompt/agent hook types
- Add setPluginHooksConfigs() to store pending plugin configs and
invalidate the config cache
- Create applyHookConfig() handler following existing applyXxxConfig
pattern, wired into config-handler after loadPluginComponents()
- Extend DisabledHooksConfig and mergeDisabledHooks for all 12 events
Closes#4179
Mirrors the agent-config-handler change: command-config-handler now also discovers skills from host config.skills.paths set by other plugins, making them available as slash commands.
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
When another plugin (e.g. superpowers) injects skill directories via config.skills.paths in the config hook, the agent-config-handler now discovers those skills via a second discoverConfigSourceSkills call using the adapted host config. Fixes#3396.
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
The previous code read params.config.default_agent which is always populated to
"Sisyphus - Ultraworker" by existing logic (lines 196-204) even when the user
never configured default_agent. This silently overrode any custom agent_order.
Now we gate on configuredDefaultAgent (the user's explicit value) so the sort
shim rank map is only mutated when the user actually set default_agent.
Add regression test: agent_order without default_agent must preserve order.
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
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
Document the canonical ordering mechanism (Array.prototype.toSorted/sort shim in src/shared/agent-sort-shim.ts) and forbid ZWSP, U+2060, U+00AD, ANSI escape, and ASCII space prefixes. Reference sst/opencode#19127 as the upstream fix that will obsolete the shim.
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
The sort shim from the previous commit enforces canonical core ordering at runtime, so ZWSP prefixes are no longer needed. Removing them eliminates the Bun.stringWidth vs terminal-width drift that broke the TUI status bar (#3259).
Drop AGENT_LIST_SORT_PREFIXES and getAgentRuntimeName from agent-display-names; switch all call sites to getAgentDisplayName. getAgentListDisplayName stays as a thin alias for external importers.
Keep stripInvisibleAgentCharacters and the ZWSP regex paths so legacy session state and configs from v3.14.0-v3.16.0 still resolve.
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
The ZWSP-based core agent sort prefixes silently failed to produce the
canonical sisyphus -> hephaestus -> prometheus -> atlas order.
Empirical testing of OpenCode's Agent.list() sort behavior shows that
Unicode collation treats zero-width characters as ignorable at the primary
level, so ZWSP-prefixed names sorted alphabetically with non-core agents
interleaved (e.g. Sisyphus, athena, Atlas, explore, Hephaestus, ...).
This commit replaces the ZWSP prefixes with leading ASCII spaces in
descending lengths (sisyphus=4, hephaestus=3, prometheus=2, atlas=1).
ASCII spaces sort reliably before alphabetic characters in localeCompare
under all locales and render correctly in every terminal.
Changes:
- AGENT_LIST_SORT_PREFIXES: ZWSP -> leading spaces (4-3-2-1 descending)
- stripAgentListSortPrefix: now strips both legacy ZWSP and new leading
whitespace, preserving backward compatibility with existing sessions
- normalizeStoredAgentName / normalizeRegisteredAgentName: extract a
shared stripSortPrefix helper that handles both prefix formats
- agent-config-handler: resolve user-provided default_agent display
names through getAgentConfigKey before applying the runtime prefix,
so configs like default_agent="Hephaestus - Deep Agent" are normalized
- agent-runtime-name-sort.test.ts: new regression test simulating
OpenCode's exact sortBy logic (default_agent desc + name asc localeCompare)
to verify canonical core agent order under randomised input permutations
- AGENTS.md: document the empirical finding that ZWSP was broken, why
ASCII spaces work, and the descending prefix-length contract
Existing strip functions retain ZWSP support so legacy session state and
configs continue to resolve correctly without migration.
Commit 708891dab fixed most test expectations after the gpt-5.5 model
promotion but missed 13 tests across 6 files that still expected
openai/gpt-5.4 in DEFAULT_CATEGORIES and AGENT_MODEL_REQUIREMENTS.
Updates all remaining stale expectations to openai/gpt-5.5:
- agents/utils.test.ts: atlas/metis resolution, buildAgent category,
override.category expansion (5 tests)
- plugin-handlers/config-handler.test.ts: ultrabrain config resolution
and fallback (2 tests)
- shared/agent-variant.test.ts: sisyphus chain variant and category
fallback (2 tests)
- shared/model-capability-guardrails.test.ts: built-in requirement
model ID assertion (1 test)
- tools/look-at/multimodal-fallback-chain.test.ts: multimodal-looker
hardcoded variant metadata (1 test)
- cli/config-manager/generate-omo-config.test.ts: sisyphus model and
fallback_models expectations (2 tests)
Updates test expectations across agent, cli, shared, plugin, and tools tests
to match gpt-5.5 as the new default for oracle, hephaestus, and deep agents.
Includes snapshot updates for model-fallback tests.
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Updates the canonical Anthropic Opus model in every fallback chain
(sisyphus, oracle, prometheus, metis, momus, visual-engineering,
ultrabrain, deep, artistry, unspecified-high), the unspecified-high
category default, the think-mode HIGH_VARIANT_MAP, the Claude Code
alias map, the claude-thinking legacy alias, the context-limit GA
regex, and event.ts fallback strings.
Widens supportsCachedAnthropicLimit to accept both claude-*-4-6 and
claude-*-4-7 so the 1M context cache still applies across the bump.
Regenerates the bundled model-capabilities snapshot from models.dev
and the model-fallback snapshot to match the new source output.
When replace_plan is true (default), the native plan agent was demoted to
subagent but remained visible in the agent picker. This caused Sisyphus to
route planning to the native plan agent instead of Prometheus.
Add hidden: true to buildPlanDemoteConfig(), consistent with how the build
agent is hidden when default_builder_enabled is false.
- Modified agent-config-handler.ts to load and integrate both new agent sources
- Added loadAgentDefinitions() and readOpencodeConfigAgents() calls in loading phase
- Integrated both sources into agent precedence chains (both Sisyphus-enabled and disabled paths)
- Added detailed logging for new agent sources
- Added filtering logic to respect disabled_agents configuration
- Extended agent-config-handler.test.ts with 7 new integration tests
- All tests passing (18/18 integration, 65/65 loader suite)
Wave 3 of agent definitions enhancement complete.
Reverts getAgentDisplayName back to getAgentRuntimeName in the remapper
and default_agent assignment. OpenCode sorts agents by name via
localeCompare, so ZWSP prefixes in the name field are required to
preserve canonical core agent order (sisyphus > hephaestus > prometheus > atlas).
agent-key-remapper now unconditionally injects getAgentRuntimeName()
into the name field, ensuring OpenCode's localeCompare sort preserves
canonical core agent order even when builtin configs omit name.
Also fixes 3 pre-existing test failures where builtinSisyphusConfig
expected the original name to survive remapping.
- Replace getAgentRuntimeName with getAgentDisplayName for consistency
- Add stripAgentListSortPrefix helper to normalize agent names
- Strip sort prefixes in subagent-resolver and sync-executor
- Backfill canonical names for core agents when builtin configs omit name
- Update tests to match new behavior
The canonical agent order is: sisyphus → hephaestus → prometheus → atlas
Changes:
- Export CANONICAL_CORE_AGENT_ORDER as single source of truth
- Sort non-core agents alphabetically for deterministic ordering
- Add 12 regression tests including 100-permutation stability test
This permanently resolves the agent ordering saga that caused 15+ commits,
8+ PRs, and multiple reverts due to ZWSP prefixes, Object.entries() order
dependencies, and inconsistent merge sequences.
Closes#3281
Remap the core agent keys, default agent, and command routing back to\nlist display names so OpenCode's name-based sorting keeps the\ncanonical Sisyphus -> Hephaestus -> Prometheus -> Atlas order.\n\nAlso teach tool config lookups to resolve the prefixed list keys and\nadd regression tests that exercise the real ordering and routing path.
model-resolver.test.ts and prometheus-agent-config-builder.test.ts use
spyOn(shared, 'log') but do not own the logger module. When other test
files in the same bun test process call mock.module('../shared/logger'),
the import cache is poisoned and the spyOn targets a stale binding.
Add a lightweight mock.module call at the top of each file so the
auto-detection in run-ci-tests.ts picks them up as isolated targets.
This ensures each file gets its own module instance and the spy
captures all calls correctly.
Fixes the flaky CI failure pattern where resolveModelWithFallback and
buildPrometheusAgentConfig tests pass locally (separate bun process)
but fail in the shared CI batch.
Agent names in the config.agent object (which becomes the /agent API
response) contained invisible Zero-Width Space (U+200B) characters
baked in by getAgentListDisplayName(). These ZWSP prefixes were used
for TUI sort ordering, but they leaked into the public API surface.
Impact: any prompt_async consumer that discovered agent names via the
/agent endpoint and passed them back to prompt_async without manual
ZWSP stripping got silent message drops — the agent name didn't match.
hy-pony's feishu-bridge integration went dark after upgrading to 3.16.0
with no error, no warning, and no indication that invisible Unicode
characters in agent names were the cause.
Fix: switch all four callsites from getAgentListDisplayName() (which
prepends \u200B×N) to getAgentDisplayName() (clean names):
- agent-key-remapper.ts: config keys → display names (was the primary
injection point)
- agent-priority-order.ts: CORE_AGENT_ORDER lookup (must agree with
the keys emitted by the remapper)
- command-config-handler.ts: command agent field normalization
- tool-config-handler.ts: agent config lookup (simplified fallback
chain since the primary lookup is now clean)
Sort ordering is preserved by:
1. JS object insertion order from reorderAgentsByPriority()
2. The injected `order` field (1-4) added by injectOrderField()
getAgentListDisplayName() is marked @deprecated with a link to #3238.
AGENT_LIST_SORT_PREFIXES and stripAgentListSortPrefix() are kept for
any internal callers that strip prefixes from legacy data.
Closes#3238