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)
The Kimi Code subscription link had drifted across the 5 README
translations: the English README pointed to https://www.kimi.com/kimiplus/sale
at $0.99, while the Japanese, Korean, Russian, and Simplified Chinese
versions pointed to https://www.kimi.com/membership/pricing with a
referral tracking ID, also at $0.99 with an "only this month" qualifier.
Normalized all 5 to the canonical https://www.kimi.com/code at $19,
preserving each language's localized "Kimi Code Subscription" wording.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>