From 316eeee9ca4c1d0261f114bfb6470030ff53161e Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Wed, 13 May 2026 21:54:34 +0800 Subject: [PATCH] Fix ACP diagnostic model counting (#991) --- .../generic-acp-agent.diagnostic.test.ts | 57 +++++++++++++++++++ .../agent/providers/generic-acp-agent.ts | 18 ++++-- 2 files changed, 70 insertions(+), 5 deletions(-) diff --git a/packages/server/src/server/agent/providers/generic-acp-agent.diagnostic.test.ts b/packages/server/src/server/agent/providers/generic-acp-agent.diagnostic.test.ts index 2632c2d85..d70819b7d 100644 --- a/packages/server/src/server/agent/providers/generic-acp-agent.diagnostic.test.ts +++ b/packages/server/src/server/agent/providers/generic-acp-agent.diagnostic.test.ts @@ -76,6 +76,63 @@ describe("GenericACPAgentClient diagnostics", () => { expect(diagnostic).toContain("Status: Available"); }); + test("counts models and modes exposed as ACP config options", async () => { + class ConfigOptionGenericACPAgentClient extends GenericACPAgentClient { + protected override async spawnProcess(): Promise { + return { + child: { kill: vi.fn(), exitCode: 0, signalCode: null, once: vi.fn() }, + initialize: { + protocolVersion: 1, + agentInfo: { name: "Devin", version: "2026.5.6" }, + agentCapabilities: {}, + }, + connection: { + newSession: vi.fn().mockResolvedValue({ + sessionId: "session-1", + models: null, + modes: null, + configOptions: [ + { + id: "mode", + name: "Session Mode", + category: "mode", + type: "select", + currentValue: "ask", + options: [ + { value: "accept-edits", name: "Code" }, + { value: "ask", name: "Ask" }, + ], + }, + { + id: "model", + name: "Model", + category: "model", + type: "select", + currentValue: "swe-1-6-slow", + options: [{ value: "swe-1-6-slow", name: "SWE-1.6 Slow" }], + }, + ], + }), + }, + } as SpawnedACPProcess; + } + + protected override async closeProbe(): Promise {} + } + + const client = new ConfigOptionGenericACPAgentClient({ + logger: createTestLogger(), + command: [process.execPath, "acp"], + providerId: "devin", + label: "Devin", + }); + + const { diagnostic } = await client.getDiagnostic(); + + expect(diagnostic).toContain("Models: 1"); + expect(diagnostic).toContain("Modes: Code, Ask"); + }); + test("reports ACP probe failures instead of falling back to no diagnostic", async () => { class FailingGenericACPAgentClient extends GenericACPAgentClient { protected override async spawnProcess(): Promise { diff --git a/packages/server/src/server/agent/providers/generic-acp-agent.ts b/packages/server/src/server/agent/providers/generic-acp-agent.ts index 0c2d07a47..8eb3ea5cc 100644 --- a/packages/server/src/server/agent/providers/generic-acp-agent.ts +++ b/packages/server/src/server/agent/providers/generic-acp-agent.ts @@ -3,8 +3,14 @@ import type { Logger } from "pino"; import { findExecutable, isCommandAvailable } from "../../../utils/executable.js"; import { execCommand } from "../../../utils/spawn.js"; +import type { AgentProvider } from "../agent-sdk-types.js"; import { createProviderEnvSpec } from "../provider-launch-config.js"; -import { ACPAgentClient, type SessionStateResponse } from "./acp-agent.js"; +import { + ACPAgentClient, + deriveModelDefinitionsFromACP, + deriveModesFromACP, + type SessionStateResponse, +} from "./acp-agent.js"; import { formatDiagnosticStatus, formatProviderDiagnostic, @@ -127,11 +133,12 @@ export class GenericACPAgentClient extends ACPAgentClient { "ACP session/new", ); sessionValue = response.sessionId ? `ok (${response.sessionId})` : "ok"; + const transformed = this.transformSessionResponse(response); return { status: formatDiagnosticStatus(true), initialize: initializeValue, session: sessionValue, - ...summarizeSessionState(this.transformSessionResponse(response)), + ...summarizeSessionState(this.provider, transformed), }; } finally { await this.closeProbe(probe); @@ -247,14 +254,15 @@ function isAgentInfo(value: unknown): value is { name: string; version?: string } function summarizeSessionState( + provider: AgentProvider, response: SessionStateResponse, ): Pick { - const models = response.models?.availableModels ?? []; - const modes = response.modes?.availableModes ?? []; + const models = deriveModelDefinitionsFromACP(provider, response.models, response.configOptions); + const { modes } = deriveModesFromACP([], response.modes, response.configOptions); return { models: `${models.length}`, modes: - modes.length > 0 ? modes.map((mode) => mode.name || mode.id).join(", ") : "none reported", + modes.length > 0 ? modes.map((mode) => mode.label || mode.id).join(", ") : "none reported", }; }