Discovered via post-implementation review (Oracle goal verification): the
prior commit's prefix swap (ZWSP -> ASCII spaces) inherited a pre-existing
architectural bug from the ZWSP era. `getAgentListDisplayName()` was an
alias for `getAgentRuntimeName()`, which meant every callsite that used
the "list display" name as an OBJECT KEY (config.agent keys, lookup keys,
HTTP-header-bound paths) ended up carrying the sort prefix.
This worked silently with ZWSP because zero-width characters are visually
invisible. With ASCII space prefixes, the same bug becomes user-visible
and violates the explicit RFC 7230 constraint documented in AGENTS.md:
"ZWSP MUST NOT appear in object keys (used as HTTP header values)."
Fix: separate the two concepts that were conflated.
- `getAgentListDisplayName(key)` now returns the CLEAN display name
(alias of `getAgentDisplayName`). Used for object keys, config keys,
and any path where the name will be sent over HTTP.
- `getAgentRuntimeName(key)` keeps its prefixed return value. Used ONLY
for the `.name` field that OpenCode reads for `localeCompare` sort.
`agent-key-remapper.ts` was already correct: it uses `getAgentRuntimeName`
for the `.name` field. The bug was that `getAgentListDisplayName` (used
as the object key) also returned the prefix.
Test updates:
- agent-display-names.test.ts splits the assertions: getAgentListDisplayName
asserts clean names, new getAgentRuntimeName describe asserts prefixes
- All other tests using getAgentListDisplayName as an expected object key
continue to pass because they always wanted clean names
Verification:
- bun test: 5769 pass / 10 pre-existing failures (unchanged)
- bun run typecheck: clean
- Manual: agent-key-remapper output keys verified RFC 7230 safe (no
leading whitespace, no ZWSP); name fields preserve descending-space
prefix for canonical core agent ordering
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>
Adds missing capability entry for gpt-5.5 to supplemental-entries.ts.
This model was promoted as default for oracle, hephaestus, and deep agents,
but the capability snapshot was missing, causing resolution failures.
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Now that oracle, hephaestus, and the deep category ship gpt-5.5-native
prompts and default to gpt-5.5 in their fallback chains, user configs
that pin openai/gpt-5.4 should follow the same upgrade on next load.
The existing migration pipeline (migrateModelVersions) picks this up
automatically from MODEL_VERSION_MAP, records the upgrade in the
sidecar file to stay idempotent, and leaves gpt-5.4-mini / -mini-fast
/ -nano alone since those cost-tier variants are intentional picks
and do not have direct gpt-5.5 counterparts yet.
Now that each of these agents ships a native gpt-5.5 prompt variant,
their default OpenAI model is bumped from gpt-5.4 to gpt-5.5. The
gpt-5.4 entries are removed from the same-provider position in each
fallback chain (a single provider doesn't need both version entries -
if gpt-5.5 isn't available there, cross-provider fallback kicks in).
- oracle.fallbackChain: gpt-5.5 high (openai) -> gemini-3.1-pro -> claude -> glm
- hephaestus.fallbackChain: gpt-5.5 medium (openai only, requiresProvider)
- deep category.fallbackChain: gpt-5.5 medium -> claude -> gemini
- deep category.config: openai/gpt-5.5 medium
No changes to sisyphus, sisyphus-junior, ultrabrain, momus, or other
agents that still point at gpt-5.4; those stay on the shared regex-
based routing in the gpt-5-4 prompt family.
Inline ORACLE_GPT_5_5_PROMPT constant added to oracle.ts (Oracle is
a single-file agent, no sub-directory variant split).
Distinctive elements over ORACLE_GPT_PROMPT:
- Confidence signaling (high/medium/low) added as a required field
alongside the existing effort estimate (borrowed from Codex's
review_prompt.md)
- Codex-style section headers (# General, ## Decision framework,
## Response structure, etc.) replacing the XML-tagged structure
- Prose-first output more explicitly encouraged
- Three-tier response structure (Essential / Expanded / Edge cases)
preserved with the same hard numerical limits
- Follow-up session behavior explicitly documented
createOracleAgent() branches on isGpt5_5Model first, then isGptModel,
falling back to the thinking-enabled claude default.
The base prompt is category-agnostic; the actual category context (deep,
quick, ultrabrain, writing) layers on top at runtime via the
promptAppend parameter resolved by resolvePromptAppend.
Distinctive elements:
- Closing '# Category context' section explicitly telling the agent
to read the appended block as overriding defaults on conflict
- Orchestrator-facing final-answer structure (What changed / Key
decisions / Verification / Observations / Blockers) instead of a
user-facing conversational close
- Sparse commentary cadence; the orchestrator synthesizes progress
for the user, so mid-task narration is mostly noise
getSisyphusJuniorPromptSource() checks gpt-5-5 before the gpt-5.4 /
gpt-5.3-codex path so the new prompt takes precedence for gpt-5.5
deployments.
Ground-up rewrite that follows the same Codex-style section structure
as the new gpt-5-5 sisyphus prompt, tuned for Hephaestus's autonomous
deep-worker role.
Distinctive elements:
- 'Autonomy and Persistence' section with named 'Forbidden stops' list
(replaces gpt-5-4's FORBIDDEN/CORRECT table rhetoric)
- 'Three-attempt failure protocol' codified
- 'Exploration-first approach' with explicit 5-15 minute expectation
- 'Dig deeper' subsection for root-cause bias
- 'Ambition vs precision' distinction for greenfield vs existing
codebase work
getHephaestusPromptSource() now checks gpt-5-5 before gpt-5-4; the
regex-based gpt-5-4 path stays as the catch-all for other native
versions.
Ground-up rewrite styled after OpenAI Codex's gpt-5.4 prompt
architecture: '# General' -> '## Autonomy and Persistence' -> '## Task
execution' -> '## Validating your work' -> '# Working with the user' ->
'# Tool Guidelines' section hierarchy.
Key differences from the gpt-5-4 variant:
- Prose-first output, bullets only when content is list-shaped
- Contract frames replace threat frames (GPT-5.5 follows instructions
well; NEVER/FORBIDDEN rhetoric adds entropy without compliance gain)
- Explicit opener blacklist for 'Done -', 'Got it', 'Great question'
- '{{ personality }}' slot reserved for future persona substitution
- '{{ taskSystemGuide }}' slot switches todo/task tools per harness cfg
- Codex-compatible clickable file reference format
Sisyphus factory now checks isGpt5_5Model before isGptNativeSisyphusModel,
so gpt-5.5 models route to the new prompt while gpt-5.4, gpt-5.6+, and
other matches stay on the existing gpt-5-4 prompt.
The GPT_NATIVE_SISYPHUS_RE regex already matches gpt-5.5 (and future
5.6+), which is correct for shared behavior. However, gpt-5.5 now has
its own prompt family separate from gpt-5.4, so we need a narrower
check to route exclusively to the gpt-5-5 variants before falling
through to the regex-matched gpt-5-4 path.
The regex stays as the catch-all for future versions; isGpt5_5Model
is the precise-match guard for the current release.