/** * Agent config keys to display names mapping. * Config keys are lowercase (e.g., "sisyphus", "atlas"). * Display names include suffixes for UI/logs (e.g., "Sisyphus - Ultraworker"). * * IMPORTANT: Display names MUST NOT contain parentheses or other characters * that are invalid in HTTP header values per RFC 7230. OpenCode passes the * agent name in the `x-opencode-agent-name` header, and parentheses cause * header validation failures that prevent agents from appearing in the UI * type selector dropdown. Use ` - ` (space-dash-space) instead of `(...)`. */ export const AGENT_DISPLAY_NAMES: Record = { sisyphus: "Sisyphus - Ultraworker", hephaestus: "Hephaestus - Deep Agent", prometheus: "Prometheus - Plan Builder", atlas: "Atlas - Plan Executor", "sisyphus-junior": "Sisyphus-Junior", metis: "Metis - Plan Consultant", momus: "Momus - Plan Critic", athena: "Athena - Council", "athena-junior": "Athena-Junior - Council", oracle: "oracle", librarian: "librarian", explore: "explore", "multimodal-looker": "multimodal-looker", "council-member": "council-member", } const AGENT_LIST_SORT_PREFIXES: Record = { sisyphus: "\u200B", hephaestus: "\u200B\u200B", prometheus: "\u200B\u200B\u200B", atlas: "\u200B\u200B\u200B\u200B", } function stripAgentListSortPrefix(agentName: string): string { return agentName.replace(/^\u200B+/, "") } /** * 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 } export function getAgentListDisplayName(configKey: string): string { const displayName = getAgentDisplayName(configKey) const prefix = AGENT_LIST_SORT_PREFIXES[configKey.toLowerCase()] return prefix ? `${prefix}${displayName}` : displayName } const REVERSE_DISPLAY_NAMES: Record = Object.fromEntries( Object.entries(AGENT_DISPLAY_NAMES).map(([key, displayName]) => [displayName.toLowerCase(), key]), ) // Legacy parenthesized display names for backward compatibility. // Old configs/sessions may reference these names; resolve them to config keys. const LEGACY_DISPLAY_NAMES: Record = { "sisyphus (ultraworker)": "sisyphus", "hephaestus (deep agent)": "hephaestus", "prometheus (plan builder)": "prometheus", "atlas (plan executor)": "atlas", "metis (plan consultant)": "metis", "momus (plan critic)": "momus", "athena (council)": "athena", "athena-junior (council)": "athena-junior", } /** * Resolve an agent name (display name or config key) to its lowercase config key. * "Atlas - Plan Executor" -> "atlas", "Atlas (Plan Executor)" -> "atlas", "atlas" -> "atlas" */ export function getAgentConfigKey(agentName: string): string { const lower = stripAgentListSortPrefix(agentName).toLowerCase() const reversed = REVERSE_DISPLAY_NAMES[lower] if (reversed !== undefined) return reversed const legacy = LEGACY_DISPLAY_NAMES[lower] if (legacy !== undefined) return legacy if (AGENT_DISPLAY_NAMES[lower] !== undefined) return lower return lower } /** * Normalize an agent name for prompt APIs. * - Known display names -> canonical display names * - Known config keys (any case) -> canonical display names * - Unknown/custom names -> preserved as-is (trimmed) */ export function normalizeAgentForPrompt(agentName: string | undefined): string | undefined { if (typeof agentName !== "string") { return undefined } const trimmed = stripAgentListSortPrefix(agentName.trim()) if (!trimmed) { return undefined } const lower = trimmed.toLowerCase() const reversed = REVERSE_DISPLAY_NAMES[lower] if (reversed !== undefined) { return AGENT_DISPLAY_NAMES[reversed] ?? trimmed } const legacy = LEGACY_DISPLAY_NAMES[lower] if (legacy !== undefined) { return AGENT_DISPLAY_NAMES[legacy] ?? trimmed } if (AGENT_DISPLAY_NAMES[lower] !== undefined) { return AGENT_DISPLAY_NAMES[lower] } return trimmed } export function normalizeAgentForPromptKey(agentName: string | undefined): string | undefined { if (typeof agentName !== "string") { return undefined } const trimmed = stripAgentListSortPrefix(agentName.trim()) if (!trimmed) { return undefined } const lower = trimmed.toLowerCase() const reversed = REVERSE_DISPLAY_NAMES[lower] if (reversed !== undefined) { return reversed } const legacy = LEGACY_DISPLAY_NAMES[lower] if (legacy !== undefined) { return legacy } if (AGENT_DISPLAY_NAMES[lower] !== undefined) { return lower } return trimmed }