From 3786cf35693395c295dc208eae7ae97c05b8b767 Mon Sep 17 00:00:00 2001 From: Chris Banes Date: Wed, 20 May 2026 06:47:10 +0100 Subject: [PATCH] Wait for Cursor ACP slash commands before listing. (#1099) cursor-agent publishes commands asynchronously via available_commands_update, so listCommands() returned an empty cache when called right after session/new. Enable waitForInitialCommands only on CursorACPAgentClient with a 10s timeout; other ACP providers keep the immediate-return default. Co-authored-by: Cursor --- .../server/agent/providers/acp-agent.test.ts | 22 ++++++++++++++++++- .../agent/providers/cursor-acp-agent.ts | 12 +++++++++- .../agent/providers/generic-acp-agent.ts | 4 ++++ 3 files changed, 36 insertions(+), 2 deletions(-) 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 21b417172..1e13ef190 100644 --- a/packages/server/src/server/agent/providers/acp-agent.test.ts +++ b/packages/server/src/server/agent/providers/acp-agent.test.ts @@ -1416,7 +1416,27 @@ describe("transformPiModels", () => { describe("ACPAgentSession slash commands", () => { test("returns immediately for ACP sessions that do not wait for async command discovery", async () => { - const session = createSession(); + const session = new ACPAgentSession( + { + provider: "claude-acp", + cwd: "/tmp/paseo-acp-test", + }, + { + provider: "claude-acp", + logger: createTestLogger(), + defaultCommand: ["claude", "--acp"], + defaultModes: [], + capabilities: { + supportsStreaming: true, + supportsSessionPersistence: true, + supportsDynamicModes: true, + supportsMcpServers: true, + supportsReasoningStream: true, + supportsToolInvocations: true, + }, + waitForInitialCommands: false, + }, + ); await expect(session.listCommands()).resolves.toEqual([]); }); diff --git a/packages/server/src/server/agent/providers/cursor-acp-agent.ts b/packages/server/src/server/agent/providers/cursor-acp-agent.ts index 582d44c01..ad253a4d1 100644 --- a/packages/server/src/server/agent/providers/cursor-acp-agent.ts +++ b/packages/server/src/server/agent/providers/cursor-acp-agent.ts @@ -14,6 +14,7 @@ interface CursorACPAgentClientOptions { } const CURSOR_MODELS_TIMEOUT_MS = 10_000; +const CURSOR_INITIAL_COMMANDS_WAIT_TIMEOUT_MS = 10_000; const CURSOR_MODEL_MARKER_PATTERN = /\s+\((?:default|current)\)$/; export class CursorACPAgentClient extends GenericACPAgentClient { @@ -21,7 +22,16 @@ export class CursorACPAgentClient extends GenericACPAgentClient { private readonly env?: Record; constructor(options: CursorACPAgentClientOptions) { - super(options); + super({ + logger: options.logger, + command: options.command, + env: options.env, + providerId: options.providerId, + label: options.label, + // cursor-agent publishes slash commands asynchronously via available_commands_update. + waitForInitialCommands: true, + initialCommandsWaitTimeoutMs: CURSOR_INITIAL_COMMANDS_WAIT_TIMEOUT_MS, + }); this.cursorCommand = options.command; this.env = options.env; } 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 8eb3ea5cc..303f1e5d1 100644 --- a/packages/server/src/server/agent/providers/generic-acp-agent.ts +++ b/packages/server/src/server/agent/providers/generic-acp-agent.ts @@ -27,6 +27,8 @@ interface GenericACPAgentClientOptions { env?: Record; providerId?: string; label?: string; + waitForInitialCommands?: boolean; + initialCommandsWaitTimeoutMs?: number; } export class GenericACPAgentClient extends ACPAgentClient { @@ -42,6 +44,8 @@ export class GenericACPAgentClient extends ACPAgentClient { env: options.env, }, defaultCommand: options.command, + waitForInitialCommands: options.waitForInitialCommands, + initialCommandsWaitTimeoutMs: options.initialCommandsWaitTimeoutMs, }); this.command = options.command;