diff --git a/packages/server/src/server/agent/providers/acp-agent.test.ts b/packages/server/src/server/agent/providers/acp-agent.test.ts index e51f57c47..b686497b5 100644 --- a/packages/server/src/server/agent/providers/acp-agent.test.ts +++ b/packages/server/src/server/agent/providers/acp-agent.test.ts @@ -747,4 +747,55 @@ describe("ACPAgentSession", () => { }); expect((session as any).activeForegroundTurnId).toBeNull(); }); + + test("auto-approves Copilot ACP permissions in autopilot mode without emitting prompt events", async () => { + const session = new ACPAgentSession( + { + provider: "copilot", + cwd: "/tmp/paseo-acp-test", + modeId: "https://agentclientprotocol.com/protocol/session-modes#autopilot", + }, + { + provider: "copilot", + logger: createTestLogger(), + defaultCommand: ["copilot", "--acp"], + defaultModes: [], + capabilities: { + supportsStreaming: true, + supportsSessionPersistence: true, + supportsDynamicModes: true, + supportsMcpServers: true, + supportsReasoningStream: true, + supportsToolInvocations: true, + }, + }, + ); + + const events: Array<{ type: string }> = []; + session.subscribe((event) => { + events.push(event as { type: string }); + }); + + const response = await session.requestPermission({ + toolCall: { + toolCallId: "tool-1", + title: "Edit file", + kind: "edit", + status: "pending", + } as any, + options: [ + { optionId: "allow-once", name: "Allow Once", kind: "allow_once" }, + { optionId: "reject-once", name: "Reject Once", kind: "reject_once" }, + ], + } as any); + + expect(response).toEqual({ + outcome: { + outcome: "selected", + optionId: "allow-once", + }, + }); + expect(session.getPendingPermissions()).toEqual([]); + expect(events.find((event) => event.type === "permission_requested")).toBeUndefined(); + }); }); diff --git a/packages/server/src/server/agent/providers/acp-agent.ts b/packages/server/src/server/agent/providers/acp-agent.ts index 269d8ced2..6b41a3825 100644 --- a/packages/server/src/server/agent/providers/acp-agent.ts +++ b/packages/server/src/server/agent/providers/acp-agent.ts @@ -107,6 +107,9 @@ const ACP_CLIENT_CAPABILITIES: ACPClientCapabilities = { terminal: true, }; +const COPILOT_AUTOPILOT_MODE = + "https://agentclientprotocol.com/protocol/session-modes#autopilot"; + type ACPAgentClientOptions = { provider: string; logger: Logger; @@ -1084,6 +1087,18 @@ export class ACPAgentSession implements AgentSession, ACPClient { async requestPermission( params: RequestPermissionRequest, ): Promise { + if (shouldAutoApprovePermissionRequest(this.provider, this.currentMode)) { + const selectedOption = selectPermissionOption(params.options, { behavior: "allow" }); + return selectedOption + ? { + outcome: { + outcome: "selected", + optionId: selectedOption.optionId, + }, + } + : { outcome: { outcome: "cancelled" } }; + } + const requestId = randomUUID(); let toolSnapshot = this.toolCalls.get(params.toolCall.toolCallId) ?? @@ -1939,6 +1954,10 @@ function mapPermissionRequest( }; } +function shouldAutoApprovePermissionRequest(provider: string, currentMode: string | null): boolean { + return provider === "copilot" && currentMode === COPILOT_AUTOPILOT_MODE; +} + function selectPermissionOption( options: PermissionOption[], response: AgentPermissionResponse,