Revert "Merge pull request #3657 from code-yeongyu/refactor/replace-zwsp-with-real-spaces"

This reverts commit f1a11f2c92, reversing
changes made to 62c19ce0ef.
This commit is contained in:
YeonGyu-Kim
2026-04-27 15:53:46 +09:00
parent 9823462994
commit 1da7df1aee
6 changed files with 35 additions and 213 deletions
+6 -19
View File
@@ -1,5 +1,5 @@
import { describe, it, expect } from "bun:test"
import { AGENT_DISPLAY_NAMES, getAgentConfigKey, getAgentDisplayName, getAgentListDisplayName, getAgentRuntimeName, normalizeAgentForPrompt, normalizeAgentForPromptKey } from "./agent-display-names"
import { AGENT_DISPLAY_NAMES, getAgentConfigKey, getAgentDisplayName, getAgentListDisplayName, normalizeAgentForPrompt, normalizeAgentForPromptKey } from "./agent-display-names"
describe("getAgentDisplayName", () => {
it("returns display name for lowercase config key (new format)", () => {
@@ -194,11 +194,11 @@ describe("getAgentConfigKey", () => {
})
describe("getAgentListDisplayName", () => {
it("returns clean display names for object keys (no leading whitespace, RFC 7230 safe)", () => {
expect(getAgentListDisplayName("sisyphus")).toBe("Sisyphus - Ultraworker")
expect(getAgentListDisplayName("hephaestus")).toBe("Hephaestus - Deep Agent")
expect(getAgentListDisplayName("prometheus")).toBe("Prometheus - Plan Builder")
expect(getAgentListDisplayName("atlas")).toBe("Atlas - Plan Executor")
it("applies invisible stable-sort prefixes to the core agent list", () => {
expect(getAgentListDisplayName("sisyphus")).toBe("\u200BSisyphus - Ultraworker")
expect(getAgentListDisplayName("hephaestus")).toBe("\u200B\u200BHephaestus - Deep Agent")
expect(getAgentListDisplayName("prometheus")).toBe("\u200B\u200B\u200BPrometheus - Plan Builder")
expect(getAgentListDisplayName("atlas")).toBe("\u200B\u200B\u200B\u200BAtlas - Plan Executor")
})
it("keeps non-core agents unprefixed for list display", () => {
@@ -206,19 +206,6 @@ describe("getAgentListDisplayName", () => {
})
})
describe("getAgentRuntimeName", () => {
it("applies leading-space stable-sort prefixes so OpenCode localeCompare yields canonical order", () => {
expect(getAgentRuntimeName("sisyphus")).toBe(" Sisyphus - Ultraworker")
expect(getAgentRuntimeName("hephaestus")).toBe(" Hephaestus - Deep Agent")
expect(getAgentRuntimeName("prometheus")).toBe(" Prometheus - Plan Builder")
expect(getAgentRuntimeName("atlas")).toBe(" Atlas - Plan Executor")
})
it("keeps non-core agents unprefixed (no entry in AGENT_LIST_SORT_PREFIXES)", () => {
expect(getAgentRuntimeName("oracle")).toBe("oracle")
})
})
describe("normalizeAgentForPrompt", () => {
it("strips core UI ordering prefixes back to canonical display names", () => {
expect(normalizeAgentForPrompt(getAgentListDisplayName("sisyphus"))).toBe("Sisyphus - Ultraworker")
+19 -8
View File
@@ -27,10 +27,10 @@ export const AGENT_DISPLAY_NAMES: Record<string, string> = {
}
const AGENT_LIST_SORT_PREFIXES: Record<string, string> = {
sisyphus: " ",
hephaestus: " ",
prometheus: " ",
atlas: " ",
sisyphus: "\u200B",
hephaestus: "\u200B\u200B",
prometheus: "\u200B\u200B\u200B",
atlas: "\u200B\u200B\u200B\u200B",
}
const INVISIBLE_AGENT_CHARACTERS_REGEX = /[\u200B\u200C\u200D\uFEFF]/g
@@ -40,7 +40,7 @@ export function stripInvisibleAgentCharacters(agentName: string): string {
}
export function stripAgentListSortPrefix(agentName: string): string {
return stripInvisibleAgentCharacters(agentName).replace(/^\s+/, "")
return stripInvisibleAgentCharacters(agentName)
}
export function getAgentRuntimeName(configKey: string): string {
@@ -50,20 +50,31 @@ export function getAgentRuntimeName(configKey: string): string {
return prefix ? `${prefix}${displayName}` : displayName
}
/**
* Get display name for an agent config key.
* Uses case-insensitive lookup for backward compatibility.
* Returns original key if not found.
*/
export function getAgentDisplayName(configKey: string): string {
// Try exact match first
const exactMatch = AGENT_DISPLAY_NAMES[configKey]
if (exactMatch !== undefined) return exactMatch
// Fall back to case-insensitive search
const lowerKey = configKey.toLowerCase()
for (const [k, v] of Object.entries(AGENT_DISPLAY_NAMES)) {
if (k.toLowerCase() === lowerKey) return v
}
// Unknown agent: return original key
return configKey
}
/**
* Runtime-facing agent name used for OpenCode list ordering.
*/
export function getAgentListDisplayName(configKey: string): string {
return getAgentDisplayName(configKey)
return getAgentRuntimeName(configKey)
}
const REVERSE_DISPLAY_NAMES: Record<string, string> = Object.fromEntries(
-152
View File
@@ -1,152 +0,0 @@
/// <reference types="bun-types" />
import { describe, expect, it, test } from "bun:test"
import {
AGENT_DISPLAY_NAMES,
getAgentRuntimeName,
normalizeAgentForPromptKey,
} from "./agent-display-names"
// OpenCode Agent.list() sorts via remeda sortBy: default_agent desc, then name asc localeCompare.
// Reference: ../opencode/packages/opencode/src/agent/agent.ts:284-293.
// Earlier ZWSP prefixes silently failed: Unicode collation treats zero-width chars as ignorable.
function simulateOpencodeSort(agentNames: string[], defaultName: string): string[] {
return [...agentNames].sort((a, b) => {
const aIsDefault = a === defaultName ? 1 : 0
const bIsDefault = b === defaultName ? 1 : 0
if (aIsDefault !== bIsDefault) return bIsDefault - aIsDefault
return a.localeCompare(b)
})
}
describe("OpenCode Agent.list() sort with runtime-name prefixes", () => {
describe("#given the four core agents and a mix of non-core agents", () => {
test("#when sorted using opencode-style sortBy #then core agents come first in canonical order", () => {
const sisyphus = getAgentRuntimeName("sisyphus")
const hephaestus = getAgentRuntimeName("hephaestus")
const prometheus = getAgentRuntimeName("prometheus")
const atlas = getAgentRuntimeName("atlas")
const allAgents = [
sisyphus,
hephaestus,
prometheus,
atlas,
"athena",
"explore",
"metis",
"oracle",
]
const sorted = simulateOpencodeSort(allAgents, sisyphus)
const orderedConfigKeys = sorted.map((name) => normalizeAgentForPromptKey(name))
expect(orderedConfigKeys).toEqual([
"sisyphus",
"hephaestus",
"prometheus",
"atlas",
"athena",
"explore",
"metis",
"oracle",
])
})
test("#when default_agent is unset #then canonical core order still holds via prefix alone", () => {
const sisyphus = getAgentRuntimeName("sisyphus")
const hephaestus = getAgentRuntimeName("hephaestus")
const prometheus = getAgentRuntimeName("prometheus")
const atlas = getAgentRuntimeName("atlas")
const allAgents = [hephaestus, prometheus, atlas, sisyphus, "athena", "oracle"]
const sorted = simulateOpencodeSort(allAgents, "no-such-default-agent")
const orderedConfigKeys = sorted.map((name) => normalizeAgentForPromptKey(name))
expect(orderedConfigKeys.slice(0, 4)).toEqual([
"sisyphus",
"hephaestus",
"prometheus",
"atlas",
])
})
})
describe("#given input array in random order", () => {
test("#when sorted with opencode comparator #then result is always canonical", () => {
const sisyphus = getAgentRuntimeName("sisyphus")
const hephaestus = getAgentRuntimeName("hephaestus")
const prometheus = getAgentRuntimeName("prometheus")
const atlas = getAgentRuntimeName("atlas")
const nonCore = ["athena", "explore", "librarian", "metis", "oracle"]
const allAgents = [...nonCore, atlas, prometheus, hephaestus, sisyphus]
for (let attempt = 0; attempt < 25; attempt += 1) {
const shuffled = [...allAgents]
for (let i = shuffled.length - 1; i > 0; i -= 1) {
const j = Math.floor(Math.random() * (i + 1))
;[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]]
}
const sorted = simulateOpencodeSort(shuffled, sisyphus)
const orderedConfigKeys = sorted.map((name) => normalizeAgentForPromptKey(name))
expect(orderedConfigKeys).toEqual([
"sisyphus",
"hephaestus",
"prometheus",
"atlas",
"athena",
"explore",
"librarian",
"metis",
"oracle",
])
}
})
})
describe("#given runtime names containing only core agents", () => {
test("#when sorted #then sisyphus, hephaestus, prometheus, atlas in that order", () => {
const sisyphus = getAgentRuntimeName("sisyphus")
const hephaestus = getAgentRuntimeName("hephaestus")
const prometheus = getAgentRuntimeName("prometheus")
const atlas = getAgentRuntimeName("atlas")
const sorted = simulateOpencodeSort([atlas, prometheus, hephaestus, sisyphus], sisyphus)
const orderedConfigKeys = sorted.map((name) => normalizeAgentForPromptKey(name))
expect(orderedConfigKeys).toEqual([
"sisyphus",
"hephaestus",
"prometheus",
"atlas",
])
})
})
describe("#given the prefix is meant to render in OpenCode TUI", () => {
it("uses ASCII whitespace so terminals render the prefix without character corruption", () => {
const runtimeNames = Object.keys(AGENT_DISPLAY_NAMES).map(getAgentRuntimeName)
const invisibleCharsRegex = /[\u200B\u200C\u200D\uFEFF]/
for (const name of runtimeNames) {
expect(invisibleCharsRegex.test(name)).toBe(false)
}
})
it("only adds leading whitespace, never trailing or interior whitespace beyond the display name", () => {
const sisyphus = getAgentRuntimeName("sisyphus")
const hephaestus = getAgentRuntimeName("hephaestus")
const prometheus = getAgentRuntimeName("prometheus")
const atlas = getAgentRuntimeName("atlas")
for (const name of [sisyphus, hephaestus, prometheus, atlas]) {
const trimmed = name.trimStart()
expect(name.length).toBeGreaterThanOrEqual(trimmed.length)
expect(trimmed.endsWith(" ")).toBe(false)
}
})
})
})