From 67df7f5014fd98dda7e8033dbc23cf6e006e4b89 Mon Sep 17 00:00:00 2001 From: skizophanes <88134772+pysol-dev@users.noreply.github.com> Date: Mon, 11 May 2026 12:17:13 -0600 Subject: [PATCH] fix: resolve display-named opencode planning agents (#700) Co-authored-by: thales <88134772+xenothales@users.noreply.github.com> --- apps/opencode-plugin/workflow.test.ts | 32 +++++++++++++++++++++++++ apps/opencode-plugin/workflow.ts | 34 ++++++++++++++++++++++++--- 2 files changed, 63 insertions(+), 3 deletions(-) diff --git a/apps/opencode-plugin/workflow.test.ts b/apps/opencode-plugin/workflow.test.ts index b74c71de..bef37cfc 100644 --- a/apps/opencode-plugin/workflow.test.ts +++ b/apps/opencode-plugin/workflow.test.ts @@ -186,6 +186,38 @@ describe("applyWorkflowConfig", () => { expect(config.agent.build.permission.submit_plan).toBe("deny"); }); + test("plan-agent mode resolves planningAgents to existing display-named agents", () => { + const prometheusKey = "\u200B\u200B\u200BPrometheus - Plan Builder"; + const sisyphusKey = "Sisyphus (Ultraworker)"; + const config: any = { + agent: { + [prometheusKey]: { + mode: "primary", + permission: {}, + }, + [sisyphusKey]: { + mode: "primary", + permission: {}, + }, + }, + }; + + applyWorkflowConfig( + config, + normalizeWorkflowOptions({ + workflow: "plan-agent", + planningAgents: ["prometheus", "sisyphus"], + }), + false, + ); + + expect(config.agent[prometheusKey].permission.submit_plan).toBe("allow"); + expect(config.agent[sisyphusKey].permission.submit_plan).toBe("allow"); + expect(config.agent.prometheus).toBeUndefined(); + expect(config.agent.sisyphus).toBeUndefined(); + expect(config.agent.build.permission.submit_plan).toBe("deny"); + }); + test("plan-agent mode denies user-configured non-planning primary agents", () => { const config: any = { agent: { diff --git a/apps/opencode-plugin/workflow.ts b/apps/opencode-plugin/workflow.ts index 6a2e4647..5acb1235 100644 --- a/apps/opencode-plugin/workflow.ts +++ b/apps/opencode-plugin/workflow.ts @@ -132,7 +132,9 @@ export function applyWorkflowConfig( opencodeConfig.agent ??= {}; + const planningAgentConfigKeys = new Set(); for (const agentName of options.planningAgents) { + planningAgentConfigKeys.add(resolveAgentConfigKey(opencodeConfig, agentName)); allowPlanningAgent(opencodeConfig, agentName); } @@ -143,7 +145,7 @@ export function applyWorkflowConfig( } for (const [agentName, agentConfig] of Object.entries(opencodeConfig.agent)) { - if (options.planningAgentSet.has(agentName)) { + if (options.planningAgentSet.has(agentName) || planningAgentConfigKeys.has(agentName)) { allowPlanningAgent(opencodeConfig, agentName); continue; } @@ -169,10 +171,36 @@ function denySubmitPlan(opencodeConfig: OpenCodeConfig, agentName: string): void ensurePermission(agent).submit_plan = "deny"; } +function normalizeAgentLookupKey(value: string): string { + return value.replace(/[\u200B-\u200D\uFEFF]/g, "").trim().toLowerCase(); +} + +function resolveAgentConfigKey(opencodeConfig: OpenCodeConfig, agentName: string): string { + opencodeConfig.agent ??= {}; + + if (Object.prototype.hasOwnProperty.call(opencodeConfig.agent, agentName)) { + return agentName; + } + + const normalizedTarget = normalizeAgentLookupKey(agentName); + for (const existingKey of Object.keys(opencodeConfig.agent)) { + const normalizedExisting = normalizeAgentLookupKey(existingKey); + if (normalizedExisting === normalizedTarget) { + return existingKey; + } + if (normalizedExisting.startsWith(`${normalizedTarget} -`) || normalizedExisting.startsWith(`${normalizedTarget} (`)) { + return existingKey; + } + } + + return agentName; +} + function ensureAgentConfig(opencodeConfig: OpenCodeConfig, agentName: string): AgentConfig { opencodeConfig.agent ??= {}; - opencodeConfig.agent[agentName] ??= {}; - return opencodeConfig.agent[agentName]; + const resolvedAgentName = resolveAgentConfigKey(opencodeConfig, agentName); + opencodeConfig.agent[resolvedAgentName] ??= {}; + return opencodeConfig.agent[resolvedAgentName]; } function ensurePermission(agent: AgentConfig): Record {