diff --git a/packages/opencode/src/acp/agent.ts b/packages/opencode/src/acp/agent.ts index 665e3712be8a..2a5c461ca866 100644 --- a/packages/opencode/src/acp/agent.ts +++ b/packages/opencode/src/acp/agent.ts @@ -56,10 +56,21 @@ import { ShellID } from "@/tool/shell/id" type ModeOption = { id: string; name: string; description?: string } type ModelOption = { modelId: string; name: string } +export type CommandOption = { name: string; description: string } const decodeTodos = Schema.decodeUnknownResult(Schema.fromJsonString(Schema.Array(Todo.Info))) const DEFAULT_VARIANT_VALUE = "default" +export function addCommandFallbacks(commands: CommandOption[]) { + const names = new Set(commands.map((c) => c.name)) + const fallbacks: CommandOption[] = [ + { name: "compact", description: "compact the session" }, + { name: "model", description: "change the session model" }, + { name: "mode", description: "change the session mode" }, + ] + commands.push(...fallbacks.filter((command) => !names.has(command.name))) +} + const log = Log.create({ service: "acp-agent" }) async function getContextLimit( @@ -1137,12 +1148,7 @@ export class Agent implements ACPAgent { name: command.name, description: command.description ?? "", })) - const names = new Set(availableCommands.map((c) => c.name)) - if (!names.has("compact")) - availableCommands.push({ - name: "compact", - description: "compact the session", - }) + addCommandFallbacks(availableCommands) const mcpServers: Record = {} for (const server of params.mcpServers) { diff --git a/packages/opencode/test/acp/command-fallbacks.test.ts b/packages/opencode/test/acp/command-fallbacks.test.ts new file mode 100644 index 000000000000..f422521a91dd --- /dev/null +++ b/packages/opencode/test/acp/command-fallbacks.test.ts @@ -0,0 +1,21 @@ +import { describe, expect, test } from "bun:test" +import { ACP } from "../../src/acp/agent" + +describe("acp command fallbacks", () => { + test("registers built-in session commands when they are missing", () => { + const commands: ACP.CommandOption[] = [] + + ACP.addCommandFallbacks(commands) + + expect(commands.map((command) => command.name)).toEqual(["compact", "model", "mode"]) + }) + + test("does not duplicate commands returned by the server", () => { + const commands: ACP.CommandOption[] = [{ name: "model", description: "custom model command" }] + + ACP.addCommandFallbacks(commands) + + expect(commands.filter((command) => command.name === "model")).toHaveLength(1) + expect(commands.map((command) => command.name)).toEqual(["model", "compact", "mode"]) + }) +})