feat: add custom agent display names via config#1728
feat: add custom agent display names via config#1728potb wants to merge 16 commits intocode-yeongyu:devfrom
Conversation
There was a problem hiding this comment.
1 issue found across 7 files
Confidence score: 4/5
- Test cleanup in
src/shared/agent-config-integration.test.tscan be skipped on assertion failure, which risks shared state leaking into later tests and causing flaky behavior. - Overall risk is low because the issue is limited to test isolation rather than production code paths.
- Pay close attention to
src/shared/agent-config-integration.test.ts- ensure cleanup runs via afterEach or try/finally.
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them.
<file name="src/shared/agent-config-integration.test.ts">
<violation number="1" location="src/shared/agent-config-integration.test.ts:244">
P2: Cleanup relies on the final line of the test body, so any failed assertion prevents reset and can leak shared state into subsequent tests. Use afterEach/try/finally to ensure reset always runs.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
|
@cubic-dev-ai recheck |
@potb I have started the AI code review. It will take a few minutes to complete. |
There was a problem hiding this comment.
3 issues found across 34 files
Confidence score: 4/5
- Safe to merge overall; the issues are low-severity and test-only, so risk to production behavior is minimal.
- Most notable risk is order-dependent test failures from global agent alias state not being reset in
src/tools/delegate-task/sync-continuation.test.tsandsrc/tools/delegate-task/sync-prompt-sender.test.ts, which can cause flaky CI. - Placeholder tests in
src/shared/session-utils.test.tsdon’t exerciseisCallerOrchestrator, so they don’t validate the intended behavior but won’t break runtime logic. - Pay close attention to
src/tools/delegate-task/sync-continuation.test.ts,src/tools/delegate-task/sync-prompt-sender.test.ts,src/shared/session-utils.test.ts- global alias state leakage and missing assertions.
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them.
<file name="src/tools/delegate-task/sync-continuation.test.ts">
<violation number="1" location="src/tools/delegate-task/sync-continuation.test.ts:371">
P2: Global agent alias state initialized in tests is never reset, which can leak into subsequent tests and make the suite order-dependent.</violation>
</file>
<file name="src/shared/session-utils.test.ts">
<violation number="1" location="src/shared/session-utils.test.ts:17">
P3: These tests are placeholders: they never call `isCallerOrchestrator` or assert its behavior, so they don’t validate the orchestrator alias logic described in the test names.</violation>
</file>
<file name="src/tools/delegate-task/sync-prompt-sender.test.ts">
<violation number="1" location="src/tools/delegate-task/sync-prompt-sender.test.ts:130">
P2: Tests initialize global agent alias state without resetting it, which can leak alias configuration across tests in the same Bun process and cause order-dependent failures. Add resetAgentNameAliases in an afterEach/beforeEach for this file.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
|
Tested locally, works properly |
There was a problem hiding this comment.
2 issues found across 3 files (changes from recent commits).
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them.
<file name="src/shared/session-utils.test.ts">
<violation number="1" location="src/shared/session-utils.test.ts:11">
P2: `spyOn(sessionUtils, "getMessageDir")` won’t affect `isCallerOrchestrator` because it calls the local `getMessageDir` binding directly. The tests may still hit the real filesystem, making the mock ineffective and the tests unreliable.</violation>
<violation number="2" location="src/shared/session-utils.test.ts:11">
P2: Spies created with `spyOn` are never restored, which can leak mocked implementations across tests in bun:test and cause test pollution. Add an `afterEach` (e.g., `mock.restore()` or `getMessageDirSpy.mockRestore()`) to clean up spies after each test.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
There was a problem hiding this comment.
1 issue found across 1 file (changes from recent commits).
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them.
<file name="src/shared/session-utils.test.ts">
<violation number="1" location="src/shared/session-utils.test.ts:7">
P2: Global fs mock makes existsSync always return true, so getMessageDir never returns null. The test "returns false when no message directory exists" therefore doesn’t exercise the missing-directory case and only passes because findNearestMessageWithFields returns null.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
|
@potb @code-yeongyu Actual amazing PR. Love the names but can be confusing at times. |
code-yeongyu
left a comment
There was a problem hiding this comment.
PR Review: feat: add custom agent display names via config
Summary
This PR adds an agent_display_names config property allowing users to rename agents to custom display names. It introduces:
- Schema:
agent_display_names: Record<string, string>inOhMyOpenCodeConfigSchema - New modules:
agent-name-aliases.ts(bidirectional alias mapping) +agent-display-name-rekeyer.ts(config re-keying) - Canonicalization:
toCanonical()/toRegistered()functions applied across ~15 call sites (delegate-task, call-omo-agent, look-at, background-task, session-utils, tool-registry, idle-event) - Tests: ~1200 lines of new tests across 9 test files
Positive Feedback
-
Architecture is sound. Two separate concerns are cleanly split:
agent-display-names.ts→ UI display name overrides (user-facing labels)agent-name-aliases.ts→ bidirectional canonical↔registered mapping (SDK routing)
This separation aligns with the existing codebase's SRP patterns.
-
Collision detection is well thought out.
initializeAgentNameAliasesvalidates for:- Unknown canonical names
- Duplicate aliases
- Aliases colliding with existing canonical names
All returning structured warnings rather than throwing — correct approach for config validation.
-
Test coverage is thorough. BDD-style comments (
//#given,//#when,//#then), round-trip tests, edge cases (empty config, case-insensitivity, reset), integration tests for config-handler and idle-event. -
Files stay under 200 LOC.
agent-name-aliases.ts(76 LOC),agent-display-name-rekeyer.ts(43 LOC) — well within limits.
Issues Requiring Changes
1. CRITICAL: Dual initialization creates a timing/state inconsistency
initializeAgentNameAliases is called in two separate places:
src/index.ts:27→initializeAgentDisplayNames(pluginConfig.agent_display_names)(sets UI overrides)src/plugin-handlers/agent-display-name-rekeyer.ts:18→initializeAgentNameAliases(displayNames, Object.keys(agentResult))(sets bidirectional mapping)
The problem: initializeAgentNameAliases resets its internal Maps on every call (lines 13-14 of agent-name-aliases.ts). So the first initialization in index.ts calls initializeAgentDisplayNames which does NOT call initializeAgentNameAliases. Then later the rekeyer calls initializeAgentNameAliases during the config hook.
But there's a timing gap: any toCanonical() calls that happen between plugin init and the config hook will operate without aliases. This is fragile. Consider either:
- Unifying initialization into one place (rekeyer), or
- Documenting clearly that
toCanonicalis not safe to call before config hook completes
2. CRITICAL: Schema field lacks JSDoc comment
Every field in OhMyOpenCodeConfigSchema has a /** ... */ JSDoc comment except the new agent_display_names:
/** Disable specific tools by name (e.g., ["todowrite", "todoread"]) */
disabled_tools: z.array(z.string()).optional(),
agent_display_names: z.record(z.string(), z.string()).optional(), // <-- no JSDocAdd: /** Map agent canonical names to custom display names (e.g., { "sisyphus": "Builder" }) */
3. Whitespace-only changes pollute the diff
Multiple files have indentation changes on lines that have no functional modifications (e.g., idle-event.ts, tools.ts, sync-executor.ts, todo-continuation-enforcer.test.ts, config-handler.test.ts). The entire try block in sync-executor.ts lines 30-42 is re-indented without functional changes.
This makes review harder and creates unnecessary merge conflicts. Please revert whitespace-only changes to keep the diff focused on the actual feature.
4. mergeConfigs doesn't follow existing pattern for agent_display_names
In plugin-config.ts, agent_display_names uses deepMerge but agents already uses deepMerge:
agent_display_names: deepMerge(base.agent_display_names ?? {}, override.agent_display_names ?? {}),For Record<string, string>, deepMerge is overkill — a simple spread { ...(base.agent_display_names ?? {}), ...(override.agent_display_names ?? {}) } is sufficient and more explicit. deepMerge is designed for nested objects with recursion, proto-pollution safety, and MAX_DEPTH=50 — none of which apply to a flat string-to-string map.
5. Missing newline at EOF in two files
Both agent-display-names.ts and agent-display-names.test.ts are missing trailing newlines (visible from \ No newline at end of file in the diff). This is a minor convention issue but should be fixed.
6. toCanonical in call-omo-agent/tools.ts normalizes then casts
const normalizedAgent = toCanonical(args.subagent_type).toLowerCase() as AllowedAgentTypeAfter toCanonical, the value is already lowercased (the function returns aliasToCanonical.get(lower) ?? lower). The .toLowerCase() is redundant but harmless. However, the as AllowedAgentType cast after canonicalization is unsafe — if the alias maps to something not in AllowedAgentType, this silently passes. Consider adding a runtime check.
7. Removed JSDoc comments from agent-display-names.ts
The PR removes the module-level doc comment and function-level JSDoc from agent-display-names.ts:
-/**
- * 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)").
- */These were helpful documentation. The new initializeAgentDisplayNames, resetAgentDisplayNames, and getAgentDisplayName functions also lack JSDoc. Please preserve or update the existing documentation.
Minor Observations (Non-blocking)
- The test comment style mixes
// givenand//#given— both are used in the codebase but this PR uses both in the same new test files. Consider consistency within each file. agent-display-name-rekeyer.test.tsusesspyOn(shared, "log" as any)— theas anyis technically an anti-pattern per project rules but is pragmatic for mocking.
Verdict: Request Changes
The core architecture is good and the test coverage is impressive. The main issues are:
- The dual initialization timing gap (critical for correctness)
- Missing JSDoc on schema field
- Whitespace pollution in the diff
Fix these and this is ready to merge.
- Implement aliasToCanonical and canonicalToAlias Maps for bidirectional agent name mapping - Add initializeAgentNameAliases() with collision detection and validation - Add toCanonical() and toRegistered() for case-insensitive name translation - Add getCanonicalToRegisteredMap() for re-keyer iteration - Add hasAliases() quick check and resetAgentNameAliases() for testing - Add comprehensive TDD test suite: 15 tests covering round-trip, identity, case-insensitivity, collision detection, and reset behavior - Export from shared barrel (index.ts) - 77 LOC implementation, 235 LOC tests - All tests pass: 15/15, 32 assertions
…stomization - Implement rekeyAgentsByDisplayNames() to post-process config.agent and agentResult - Re-key canonical agent names to user-chosen display names from agent_display_names config - Update config.default_agent if it references a re-keyed agent - No-op when displayNames is empty or undefined (no behavioral change) - Call initializeAgentNameAliases() to build bidirectional alias maps and get validation warnings - Log all warnings from alias initialization via log() - Check existence before mutating to avoid phantom entries - Comprehensive TDD test suite: 9 tests covering re-keying, default_agent updates, no-ops, multiple aliases, warning handling, and sync behavior - 44 LOC implementation, well under 120 LOC limit - Export from plugin-handlers barrel (index.ts) - All tests pass: 9/9, 31 assertions
…nicalize prompt sites Task 3: Integrate re-keyer into config pipeline - Import rekeyAgentsByDisplayNames from agent-display-name-rekeyer - Add re-keying call after applyToolConfig() and before applyMcpConfig() - Ensures hardcoded lookups in tool config complete with canonical keys before re-keying - Add 2 test cases: verify re-keying with display names, verify no-op when undefined - All 31 config-handler tests pass Task 4: Canonicalize look-at tool prompt site - Import toRegistered from shared barrel - Change line 114: agent: MULTIMODAL_LOOKER_AGENT -> agent: toRegistered(MULTIMODAL_LOOKER_AGENT) - This is the only hardcoded canonical constant in prompt call sites - Other sites receive registered names already (from SDK responses or resolved values) - All 24 look-at tests pass Wave 2 verification: - bun run typecheck: zero errors - bun test src/plugin-handlers/config-handler.test.ts: 31 pass - bun test src/tools/look-at/: 24 pass
Wave 3: Five-task parallel canonicalization of all reverse-flow agent name comparison sites Task 5: Canonicalize getAgentToolRestrictions() input - Add toCanonical() call at start of getAgentToolRestrictions() and hasAgentToolRestrictions() - Ensures tool restrictions resolve correctly when agents are renamed - Create agent-tool-restrictions.test.ts with 8 test cases - Verify both canonical and alias names resolve correctly Task 6: Canonicalize isCallerOrchestrator() and todo-continuation skipAgents - session-utils.ts line 27: canonicalize atlas detection - idle-event.ts lines 118, 137: canonicalize compaction and skip-agents checks - Ensure orchestrator detection and todo-continuation work with renamed agents - Add tests for renamed atlas and renamed prometheus Task 7: Canonicalize delegate-task comparison sites - subagent-resolver.ts: canonicalize SISYPHUS_JUNIOR_AGENT check (line 24) - subagent-resolver.ts: canonicalize isPlanFamily() checks (lines 34, 84, 86) - sync-continuation.ts: canonicalize isPlanFamily() check (line 79) - sync-prompt-sender.ts: canonicalize isPlanFamily() check (line 20) - Add 4 test cases for plan family guards with renamed agents Task 8: Canonicalize background-output, look-at metadata, and call-omo-agent sites - background-output.ts: canonicalize SISYPHUS_JUNIOR_AGENT check (line 33) - create-background-output.ts: canonicalize SISYPHUS_JUNIOR_AGENT check (line 30) - multimodal-agent-metadata.ts: canonicalize MULTIMODAL_LOOKER_AGENT check (line 45) - call-omo-agent/tools.ts: canonicalize ALLOWED_AGENTS validation (lines 37-38) - sync-executor.ts: toRegistered() for SDK agent names (line 34) - Full test suite: 2617 tests pass (all background-output and look-at paths) Task 9: Canonicalize disabled_agents check in tool-registry - tool-registry.ts: canonicalize disabled_agents multimodal-looker check (line 54) - Make check defensive against display names (official canonical-only, but defensive is safe) - Add tests to src/index.test.ts for disabled_agents canonicalization Summary: - 18 files modified/created - 25+ new tests added across shared, tools, hooks, plugin - All 2617 tests pass - Full typecheck: zero errors - Build succeeds: ESM + declarations + schema - All comparison sites now canonicalize agent names before lookups - Ensures custom agent names work correctly throughout entire SDK boundary
…and ensure consistent lowercase from toCanonical
…aceholder tests Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus <[email protected]>
260f709 to
9e71117
Compare
# Conflicts: # src/tools/delegate-task/sync-continuation.test.ts

Summary
Closes #1715
Adds
agent_display_namesconfig property so users can rename agents to custom display names.{ "agent_display_names": { "sisyphus": "Builder", "oracle": "Advisor", "prometheus": "Planner" } }Summary by cubic
Adds custom agent display names via agent_display_names and makes renamed agents work end-to-end. All comparisons use canonical names; SDK prompts send registered names.
New Features
Bug Fixes
Written for commit adffdf2. Summary will update on new commits.