refactor(agents): drop ZWSP prefixes from agent display names
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>
This commit is contained in:
@@ -9,7 +9,7 @@ import type { OhMyOpenCodeConfig } from "../config"
|
||||
import * as agentLoader from "../features/claude-code-agent-loader"
|
||||
import * as skillLoader from "../features/opencode-skill-loader"
|
||||
import type { LoadedSkill } from "../features/opencode-skill-loader"
|
||||
import { getAgentListDisplayName, getAgentRuntimeName } from "../shared/agent-display-names"
|
||||
import { getAgentDisplayName, getAgentListDisplayName } from "../shared/agent-display-names"
|
||||
import { applyAgentConfig } from "./agent-config-handler"
|
||||
import type { PluginComponents } from "./plugin-components-loader"
|
||||
|
||||
@@ -205,7 +205,7 @@ describe("applyAgentConfig builtin override protection", () => {
|
||||
})
|
||||
|
||||
// then
|
||||
expect(config.default_agent).toBe(getAgentRuntimeName("sisyphus"))
|
||||
expect(config.default_agent).toBe(getAgentDisplayName("sisyphus"))
|
||||
})
|
||||
|
||||
test("keeps config-key default_agent behavior unchanged", async () => {
|
||||
@@ -222,7 +222,7 @@ describe("applyAgentConfig builtin override protection", () => {
|
||||
})
|
||||
|
||||
// then
|
||||
expect(config.default_agent).toBe(getAgentRuntimeName("sisyphus"))
|
||||
expect(config.default_agent).toBe(getAgentDisplayName("sisyphus"))
|
||||
})
|
||||
|
||||
test("keeps fallback default_agent behavior unchanged", async () => {
|
||||
@@ -238,7 +238,25 @@ describe("applyAgentConfig builtin override protection", () => {
|
||||
})
|
||||
|
||||
// then
|
||||
expect(config.default_agent).toBe(getAgentRuntimeName("sisyphus"))
|
||||
expect(config.default_agent).toBe(getAgentDisplayName("sisyphus"))
|
||||
})
|
||||
|
||||
test("resolved default_agent contains no zero-width invisible characters", async () => {
|
||||
// given canonical core ordering is now enforced by the agent sort shim, so
|
||||
// default_agent must not carry the legacy ZWSP prefix that earlier biased
|
||||
// OpenCode's localeCompare sort.
|
||||
const config = createBaseConfig()
|
||||
|
||||
// when applyAgentConfig resolves the default agent
|
||||
await applyAgentConfig({
|
||||
config,
|
||||
pluginConfig: createPluginConfig(),
|
||||
ctx: { directory: "/tmp" },
|
||||
pluginComponents: createPluginComponents(),
|
||||
})
|
||||
|
||||
// then the persisted default_agent is the clean display name
|
||||
expect(config.default_agent).not.toMatch(/[\u200B\u200C\u200D\uFEFF]/)
|
||||
})
|
||||
|
||||
test("filters user agents whose key matches the builtin display-name alias", async () => {
|
||||
@@ -262,7 +280,7 @@ describe("applyAgentConfig builtin override protection", () => {
|
||||
// then
|
||||
expect(result[BUILTIN_SISYPHUS_DISPLAY_NAME]).toEqual({
|
||||
...builtinSisyphusConfig,
|
||||
name: getAgentRuntimeName("sisyphus"),
|
||||
name: getAgentDisplayName("sisyphus"),
|
||||
})
|
||||
})
|
||||
|
||||
@@ -287,7 +305,7 @@ describe("applyAgentConfig builtin override protection", () => {
|
||||
// then
|
||||
expect(result[BUILTIN_SISYPHUS_DISPLAY_NAME]).toEqual({
|
||||
...builtinSisyphusConfig,
|
||||
name: getAgentRuntimeName("sisyphus"),
|
||||
name: getAgentDisplayName("sisyphus"),
|
||||
})
|
||||
expect(result.SiSyPhUs).toBeUndefined()
|
||||
})
|
||||
@@ -314,7 +332,7 @@ describe("applyAgentConfig builtin override protection", () => {
|
||||
// then
|
||||
expect(result[BUILTIN_SISYPHUS_DISPLAY_NAME]).toEqual({
|
||||
...builtinSisyphusConfig,
|
||||
name: getAgentRuntimeName("sisyphus"),
|
||||
name: getAgentDisplayName("sisyphus"),
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import type { OhMyOpenCodeConfig } from "../config";
|
||||
import { isTaskSystemEnabled, log, migrateAgentConfig } from "../shared";
|
||||
import {
|
||||
getAgentConfigKey,
|
||||
getAgentRuntimeName,
|
||||
getAgentDisplayName,
|
||||
normalizeAgentForPromptKey,
|
||||
} from "../shared/agent-display-names";
|
||||
import { AGENT_NAME_MAP } from "../shared/migration";
|
||||
@@ -196,10 +196,10 @@ export async function applyAgentConfig(params: {
|
||||
const configKey = getAgentConfigKey(configuredDefaultAgent);
|
||||
const runtimeConfigKey = normalizeAgentForPromptKey(configuredDefaultAgent) ?? configKey;
|
||||
(params.config as { default_agent?: string }).default_agent =
|
||||
getAgentRuntimeName(runtimeConfigKey);
|
||||
getAgentDisplayName(runtimeConfigKey);
|
||||
} else {
|
||||
(params.config as { default_agent?: string }).default_agent =
|
||||
getAgentRuntimeName("sisyphus");
|
||||
getAgentDisplayName("sisyphus");
|
||||
}
|
||||
|
||||
// Assembly order: Sisyphus -> Hephaestus -> Prometheus -> Atlas
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, it, expect } from "bun:test"
|
||||
import { remapAgentKeysToDisplayNames } from "./agent-key-remapper"
|
||||
import { getAgentDisplayName, getAgentListDisplayName, getAgentRuntimeName } from "../shared/agent-display-names"
|
||||
import { getAgentDisplayName, getAgentListDisplayName } from "../shared/agent-display-names"
|
||||
|
||||
describe("remapAgentKeysToDisplayNames", () => {
|
||||
it("remaps known agent keys to display names", () => {
|
||||
@@ -124,22 +124,22 @@ describe("remapAgentKeysToDisplayNames", () => {
|
||||
getAgentListDisplayName("atlas"),
|
||||
])
|
||||
expect(result[getAgentListDisplayName("sisyphus")]).toEqual({
|
||||
name: getAgentRuntimeName("sisyphus"),
|
||||
name: getAgentListDisplayName("sisyphus"),
|
||||
prompt: "test",
|
||||
mode: "primary",
|
||||
})
|
||||
expect(result[getAgentListDisplayName("hephaestus")]).toEqual({
|
||||
name: getAgentRuntimeName("hephaestus"),
|
||||
name: getAgentListDisplayName("hephaestus"),
|
||||
prompt: "test",
|
||||
mode: "primary",
|
||||
})
|
||||
expect(result[getAgentListDisplayName("prometheus")]).toEqual({
|
||||
name: getAgentRuntimeName("prometheus"),
|
||||
name: getAgentListDisplayName("prometheus"),
|
||||
prompt: "test",
|
||||
mode: "primary",
|
||||
})
|
||||
expect(result[getAgentListDisplayName("atlas")]).toEqual({
|
||||
name: getAgentRuntimeName("atlas"),
|
||||
name: getAgentListDisplayName("atlas"),
|
||||
prompt: "test",
|
||||
mode: "primary",
|
||||
})
|
||||
@@ -160,24 +160,41 @@ describe("remapAgentKeysToDisplayNames", () => {
|
||||
|
||||
// then runtime-facing names stay aligned even when builtin configs omit name
|
||||
expect(result[getAgentListDisplayName("sisyphus")]).toEqual({
|
||||
name: getAgentRuntimeName("sisyphus"),
|
||||
name: getAgentListDisplayName("sisyphus"),
|
||||
prompt: "test",
|
||||
mode: "primary",
|
||||
})
|
||||
expect(result[getAgentListDisplayName("hephaestus")]).toEqual({
|
||||
name: getAgentRuntimeName("hephaestus"),
|
||||
name: getAgentListDisplayName("hephaestus"),
|
||||
prompt: "test",
|
||||
mode: "primary",
|
||||
})
|
||||
expect(result[getAgentListDisplayName("prometheus")]).toEqual({
|
||||
name: getAgentRuntimeName("prometheus"),
|
||||
name: getAgentListDisplayName("prometheus"),
|
||||
prompt: "test",
|
||||
mode: "primary",
|
||||
})
|
||||
expect(result[getAgentListDisplayName("atlas")]).toEqual({
|
||||
name: getAgentRuntimeName("atlas"),
|
||||
name: getAgentListDisplayName("atlas"),
|
||||
prompt: "test",
|
||||
mode: "primary",
|
||||
})
|
||||
})
|
||||
|
||||
it("emits a single literal display-name row with no ZWSP for a single core agent", () => {
|
||||
// given a single core agent input
|
||||
const agents = {
|
||||
sisyphus: { foo: "bar" },
|
||||
}
|
||||
|
||||
// when remapping
|
||||
const result = remapAgentKeysToDisplayNames(agents)
|
||||
|
||||
// then exactly one row is emitted under the clean literal display name
|
||||
expect(Object.keys(result)).toEqual(["Sisyphus - Ultraworker"])
|
||||
expect(result["Sisyphus - Ultraworker"]).toEqual({
|
||||
name: "Sisyphus - Ultraworker",
|
||||
foo: "bar",
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { getAgentListDisplayName, getAgentRuntimeName } from "../shared/agent-display-names"
|
||||
import { getAgentListDisplayName } from "../shared/agent-display-names"
|
||||
|
||||
function rewriteAgentNameForListDisplay(
|
||||
key: string,
|
||||
@@ -11,7 +11,7 @@ function rewriteAgentNameForListDisplay(
|
||||
const agent = value as Record<string, unknown>
|
||||
return {
|
||||
...agent,
|
||||
name: getAgentRuntimeName(key),
|
||||
name: getAgentListDisplayName(key),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { describe, test, expect, spyOn, beforeEach, afterEach, mock } from "bun:test"
|
||||
import type { CategoryConfig } from "../config/schema"
|
||||
import type { OhMyOpenCodeConfig } from "../config"
|
||||
import { getAgentDisplayName, getAgentListDisplayName, getAgentRuntimeName } from "../shared/agent-display-names"
|
||||
import { getAgentDisplayName, getAgentListDisplayName } from "../shared/agent-display-names"
|
||||
import { resolveCategoryConfig } from "./category-config-resolver"
|
||||
|
||||
import * as agents from "../agents"
|
||||
@@ -359,19 +359,19 @@ describe("Plan agent demote behavior", () => {
|
||||
expect(emittedCoreEntries).toEqual([
|
||||
[
|
||||
getAgentListDisplayName("sisyphus"),
|
||||
expect.objectContaining({ name: getAgentRuntimeName("sisyphus") }),
|
||||
expect.objectContaining({ name: getAgentListDisplayName("sisyphus") }),
|
||||
],
|
||||
[
|
||||
getAgentListDisplayName("hephaestus"),
|
||||
expect.objectContaining({ name: getAgentRuntimeName("hephaestus") }),
|
||||
expect.objectContaining({ name: getAgentListDisplayName("hephaestus") }),
|
||||
],
|
||||
[
|
||||
getAgentListDisplayName("prometheus"),
|
||||
expect.objectContaining({ name: getAgentRuntimeName("prometheus") }),
|
||||
expect.objectContaining({ name: getAgentListDisplayName("prometheus") }),
|
||||
],
|
||||
[
|
||||
getAgentListDisplayName("atlas"),
|
||||
expect.objectContaining({ name: getAgentRuntimeName("atlas") }),
|
||||
expect.objectContaining({ name: getAgentListDisplayName("atlas") }),
|
||||
],
|
||||
])
|
||||
})
|
||||
@@ -540,7 +540,7 @@ describe("default_agent behavior with Sisyphus orchestration", () => {
|
||||
await handler(config)
|
||||
|
||||
// then
|
||||
expect(config.default_agent).toBe(getAgentRuntimeName("hephaestus"))
|
||||
expect(config.default_agent).toBe(getAgentDisplayName("hephaestus"))
|
||||
})
|
||||
|
||||
test("canonicalizes configured default_agent when key uses mixed case", async () => {
|
||||
@@ -564,7 +564,7 @@ describe("default_agent behavior with Sisyphus orchestration", () => {
|
||||
await handler(config)
|
||||
|
||||
// then
|
||||
expect(config.default_agent).toBe(getAgentRuntimeName("hephaestus"))
|
||||
expect(config.default_agent).toBe(getAgentDisplayName("hephaestus"))
|
||||
})
|
||||
|
||||
test("canonicalizes configured default_agent key to display name", async () => {
|
||||
@@ -588,7 +588,7 @@ describe("default_agent behavior with Sisyphus orchestration", () => {
|
||||
await handler(config)
|
||||
|
||||
// #then
|
||||
expect(config.default_agent).toBe(getAgentRuntimeName("hephaestus"))
|
||||
expect(config.default_agent).toBe(getAgentDisplayName("hephaestus"))
|
||||
})
|
||||
|
||||
test("preserves existing display-name default_agent", async () => {
|
||||
@@ -613,7 +613,7 @@ describe("default_agent behavior with Sisyphus orchestration", () => {
|
||||
await handler(config)
|
||||
|
||||
// #then
|
||||
expect(config.default_agent).toBe(getAgentRuntimeName("hephaestus"))
|
||||
expect(config.default_agent).toBe(getAgentDisplayName("hephaestus"))
|
||||
})
|
||||
|
||||
test("sets default_agent to sisyphus when missing", async () => {
|
||||
@@ -636,7 +636,7 @@ describe("default_agent behavior with Sisyphus orchestration", () => {
|
||||
await handler(config)
|
||||
|
||||
// #then
|
||||
expect(config.default_agent).toBe(getAgentRuntimeName("sisyphus"))
|
||||
expect(config.default_agent).toBe(getAgentDisplayName("sisyphus"))
|
||||
})
|
||||
|
||||
test("uses canonical default_agent display name so OpenCode lookups match emitted agent keys", async () => {
|
||||
@@ -660,7 +660,7 @@ describe("default_agent behavior with Sisyphus orchestration", () => {
|
||||
await handler(config)
|
||||
|
||||
// then
|
||||
expect(config.default_agent).toBe(getAgentRuntimeName("hephaestus"))
|
||||
expect(config.default_agent).toBe(getAgentDisplayName("hephaestus"))
|
||||
})
|
||||
|
||||
test("sets default_agent to sisyphus when configured default_agent is empty after trim", async () => {
|
||||
@@ -684,7 +684,7 @@ describe("default_agent behavior with Sisyphus orchestration", () => {
|
||||
await handler(config)
|
||||
|
||||
// then
|
||||
expect(config.default_agent).toBe(getAgentRuntimeName("sisyphus"))
|
||||
expect(config.default_agent).toBe(getAgentDisplayName("sisyphus"))
|
||||
})
|
||||
|
||||
test("preserves custom default_agent names while trimming whitespace", async () => {
|
||||
|
||||
Reference in New Issue
Block a user