fix(agent-names): use HTTP-header-safe display names and config keys for API calls (#3138)

Display names with parentheses like 'Atlas (Plan Executor)' cause HTTP
header validation errors in x-opencode-agent-name. This was blocking
Atlas/Prometheus from working via /start-work and auto-retry.

Changes:
- Display names: parens -> dashes ('Atlas - Plan Executor')
- Hooks (start-work, no-hephaestus-non-gpt, no-sisyphus-gpt): use
  config keys ('atlas', 'sisyphus', 'hephaestus') for agent API fields
- auto-retry: use config key instead of display name for promptAsync
- agent-override-protection: handle dash-suffix normalization
- Updated all test expectations to match new format

Closes #3138
This commit is contained in:
YeonGyu-Kim
2026-04-07 10:08:04 +09:00
parent ebfd4a41ee
commit f8c626086e
26 changed files with 146 additions and 144 deletions
@@ -40,13 +40,13 @@ describe("claude-code-session-state", () => {
test("should strip zero-width ordering prefixes before storing agent for session", () => {
// given
const sessionID = "test-session-prefixed"
const agent = "\u200B\u200B\u200BPrometheus (Plan Builder)"
const agent = "\u200B\u200B\u200BPrometheus - Plan Builder"
// when
setSessionAgent(sessionID, agent)
// then
expect(getSessionAgent(sessionID)).toBe("Prometheus (Plan Builder)")
expect(getSessionAgent(sessionID)).toBe("Prometheus - Plan Builder")
})
test("should NOT overwrite existing agent (first-write wins)", () => {
@@ -88,10 +88,10 @@ describe("claude-code-session-state", () => {
setSessionAgent(sessionID, "sisyphus")
// when
updateSessionAgent(sessionID, "\u200B\u200BHephaestus (Deep Agent)")
updateSessionAgent(sessionID, "\u200B\u200BHephaestus - Deep Agent")
// then
expect(getSessionAgent(sessionID)).toBe("Hephaestus (Deep Agent)")
expect(getSessionAgent(sessionID)).toBe("Hephaestus - Deep Agent")
})
})
@@ -133,21 +133,21 @@ describe("claude-code-session-state", () => {
describe("agent registration", () => {
test("should register config-key lookup when given a display name", () => {
// given
registerAgentName("Atlas (Plan Executor)")
registerAgentName("Atlas - Plan Executor")
// when / then
expect(isAgentRegistered("atlas")).toBe(true)
expect(isAgentRegistered("Atlas (Plan Executor)")).toBe(true)
expect(isAgentRegistered("Atlas - Plan Executor")).toBe(true)
})
describe("#given atlas display name with zero-width prefix", () => {
describe("#when checking registration without the zero-width prefix", () => {
test("#then it treats the display name as registered", () => {
// given
registerAgentName("\u200BAtlas (Plan Executor)")
registerAgentName("\u200BAtlas - Plan Executor")
// when
const isRegistered = isAgentRegistered("Atlas (Plan Executor)")
const isRegistered = isAgentRegistered("Atlas - Plan Executor")
// then
expect(isRegistered).toBe(true)