From ccb8714a711e4bdf8247fa0da1abcfb4fd0bd1d9 Mon Sep 17 00:00:00 2001 From: Matt Cowger Date: Thu, 18 Jun 2026 20:20:26 -0700 Subject: [PATCH] fix(opencode): trust discovered modes, don't inject hardcoded defaults (#1366) * fix(opencode): trust discovered modes instead of always injecting defaults When OpenCode reports its available agents, respect that list exactly rather than seeding from DEFAULT_MODES first. This means users who have intentionally disabled modes (e.g. Plan) in their OpenCode config will no longer see those modes surfaced in Paseo. DEFAULT_MODES is still used as a fallback when discovery returns nothing (e.g. OpenCode unreachable or no agents configured). * test(opencode): update mode discovery expectations * test(opencode): expect discovered custom modes only --- .../providers/opencode-agent.full-access.test.ts | 13 +++++++++++-- .../agent/providers/opencode-agent.test.ts | 3 +-- .../src/server/agent/providers/opencode-agent.ts | 16 +++++++++------- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/packages/server/src/server/agent/providers/opencode-agent.full-access.test.ts b/packages/server/src/server/agent/providers/opencode-agent.full-access.test.ts index 11b45dced..4550b55a3 100644 --- a/packages/server/src/server/agent/providers/opencode-agent.full-access.test.ts +++ b/packages/server/src/server/agent/providers/opencode-agent.full-access.test.ts @@ -63,7 +63,7 @@ function questionEvent(questionOverrides: Record = {}): unknown } describe("OpenCode auto_accept feature", () => { - test("lists OpenCode modes without the legacy virtual full-access mode", async () => { + test("lists discovered OpenCode modes without injecting defaults", async () => { const { runtime } = mockOpenCodeClient({ agents: [ { name: "build", mode: "primary", hidden: false, description: "Build agent" }, @@ -74,7 +74,16 @@ describe("OpenCode auto_accept feature", () => { const client = new OpenCodeAgentClient(createTestLogger(), undefined, { runtime }); const modes = await client.listModes({ cwd: "/tmp/project", force: false }); - expect(modes.map((mode) => mode.id)).toEqual(["build", "plan", "paseo-custom"]); + expect(modes.map((mode) => mode.id)).toEqual(["build", "paseo-custom"]); + }); + + test("falls back to default OpenCode modes when discovery returns no modes", async () => { + const { runtime } = mockOpenCodeClient({ agents: [] }); + + const client = new OpenCodeAgentClient(createTestLogger(), undefined, { runtime }); + const modes = await client.listModes({ cwd: "/tmp/project", force: false }); + + expect(modes.map((mode) => mode.id)).toEqual(["build", "plan"]); }); test("lists auto accept as a provider feature", async () => { diff --git a/packages/server/src/server/agent/providers/opencode-agent.test.ts b/packages/server/src/server/agent/providers/opencode-agent.test.ts index 19c1428ba..b08bc2081 100644 --- a/packages/server/src/server/agent/providers/opencode-agent.test.ts +++ b/packages/server/src/server/agent/providers/opencode-agent.test.ts @@ -404,8 +404,7 @@ describe("OpenCodeAgentClient adapter smoke tests", () => { const modes = await session.getAvailableModes(); - expect(modes.some((mode) => mode.id === "build")).toBe(true); - expect(modes.some((mode) => mode.id === "plan")).toBe(true); + expect(modes.map((mode) => mode.id)).toEqual(["paseo-test-custom"]); const custom = modes.find((mode) => mode.id === "paseo-test-custom"); expect(custom).toBeDefined(); diff --git a/packages/server/src/server/agent/providers/opencode-agent.ts b/packages/server/src/server/agent/providers/opencode-agent.ts index 2e7d0db22..fb935e7c6 100644 --- a/packages/server/src/server/agent/providers/opencode-agent.ts +++ b/packages/server/src/server/agent/providers/opencode-agent.ts @@ -591,14 +591,16 @@ function mapOpenCodeAgentToMode(agent: { } function mergeOpenCodeModes(discoveredModes: AgentMode[]): AgentMode[] { - const modesById = new Map(DEFAULT_MODES.map((mode) => [mode.id, mode])); - for (const mode of discoveredModes) { - if (mode.id === OPENCODE_LEGACY_FULL_ACCESS_MODE_ID) { - continue; - } - modesById.set(mode.id, mode); + const filtered = discoveredModes.filter( + (mode) => mode.id !== OPENCODE_LEGACY_FULL_ACCESS_MODE_ID, + ); + // When discovery returns results, trust them exactly — don't inject hardcoded + // defaults that the user may have intentionally disabled in their OpenCode config. + // Fall back to DEFAULT_MODES only when discovery produced nothing. + if (filtered.length > 0) { + return sortOpenCodeModes(filtered); } - return sortOpenCodeModes(Array.from(modesById.values())); + return sortOpenCodeModes([...DEFAULT_MODES]); } function sortOpenCodeModes(modes: AgentMode[]): AgentMode[] {