Filter host config agent aliases with the same protected builtin-name rules used for external agent sources so stale display-name entries cannot replace resolved user-configured models when team mode is enabled.
Fixes#4429
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
When a user explicitly configures a model for the multimodal-looker
agent (e.g. zhipuai-coding-plan/glm-5.1), treat that model as
vision-capable even when its provider config does not declare
modalities.input or capabilities.input.image. This unblocks
vision-capable models that the provider config does not advertise.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Apply mcp_env_allowlist to plugin hooks: intersect HTTP allowedEnvVars
with MCP allowlist, set command allowedEnvVars to full MCP allowlist
- Scrub process.env in executeHookCommand when allowedEnvVars provided
- Add PluginHooksState class with per-directory Map storage
- Add PluginHooksConfig interface for typed boundary layer
- Pass directory context through hook-config-handler
- Add 16 tests across 4 files (40 assertions) covering allowlist
filtering, env scrubbing, directory isolation, and edge cases
- Remove unnecessary 'as' type assertions, use discriminated union
narrowing instead
When all plugin hooks are removed (user disables/uninstalls plugins),
hooksConfigs becomes an empty array. The previous guard
(hooksConfigs.length > 0) skipped setPluginHooksConfigs(), leaving
stale plugin hooks active in pendingPluginHooksConfigs. Now we always
call setPluginHooksConfigs() so empty configs properly clear the
pending state and invalidate the cache.
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>
Add optional `displayName` field to AgentOverrideConfigSchema (next to
the existing `color` field) so users can specify localized agent names
in oh-my-openagent.json:
{ "agents": { "sisyphus": { "displayName": "总指挥" } } }
When set, the override takes precedence everywhere
AGENT_DISPLAY_NAMES[agentName] is used — TUI agent selector, the
agent list key, and the internal `name` field. When not set, behavior
is identical to before (hardcoded English names from AGENT_DISPLAY_NAMES).
Implementation touches:
- AgentOverrideConfigSchema: adds displayName?: z.string().optional()
- getAgentDisplayName / getAgentListDisplayName: accept optional overrides
map and check displayName before the hardcoded table
- remapAgentKeysToDisplayNames: forwards overrides map to name resolution
- agent-config-handler: passes pluginConfig.agents as the overrides map
Backward compatible — existing configs without displayName continue to
work unchanged.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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).