diff --git a/docs/custom-providers.md b/docs/custom-providers.md index 924d1bc0664..60818b3b78e 100644 --- a/docs/custom-providers.md +++ b/docs/custom-providers.md @@ -532,6 +532,30 @@ Only enable capabilities Paseo should execute. When the agent and Paseo run in different environments, configure equivalent absolute workspace paths before delegating filesystem or terminal operations to Paseo. +Some ACP agents publish slash commands asynchronously after `session/new`. +Enable initial command waiting for those providers so the new-agent composer +does not read an empty command list before the update arrives: + +```json +{ + "agents": { + "providers": { + "my-agent": { + "extends": "acp", + "label": "My Agent", + "command": ["my-agent", "acp"], + "params": { + "waitForInitialCommands": true, + "initialCommandsWaitTimeoutMs": 1500 + } + } + } + } +} +``` + +Keep the default timeout unless the provider documents a longer command-discovery delay. + ### Generic ACP diagnostics Paseo diagnostics for `extends: "acp"` providers report the configured command, resolved launcher binary, version output, ACP `initialize`, ACP `session/new`, model count, modes, and final status. 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 74cd504c286..f5efb4078b3 100644 --- a/packages/server/src/server/agent/providers/acp-agent.test.ts +++ b/packages/server/src/server/agent/providers/acp-agent.test.ts @@ -2363,6 +2363,7 @@ describe("ACPAgentSession slash commands", () => { { name: "research_codebase", description: "Search the workspace for relevant files", + input: { hint: "query" }, }, { name: "create_plan", @@ -2375,7 +2376,7 @@ describe("ACPAgentSession slash commands", () => { { name: "research_codebase", description: "Search the workspace for relevant files", - argumentHint: "", + argumentHint: "query", kind: "command", }, { @@ -2390,7 +2391,7 @@ describe("ACPAgentSession slash commands", () => { { name: "research_codebase", description: "Search the workspace for relevant files", - argumentHint: "", + argumentHint: "query", kind: "command", }, { diff --git a/packages/server/src/server/agent/providers/acp-agent.ts b/packages/server/src/server/agent/providers/acp-agent.ts index 1a43ed5538b..0d0e3bb42d9 100644 --- a/packages/server/src/server/agent/providers/acp-agent.ts +++ b/packages/server/src/server/agent/providers/acp-agent.ts @@ -2687,7 +2687,7 @@ export class ACPAgentSession implements AgentSession, ACPClient { this.cachedCommands = update.availableCommands.map((command) => ({ name: command.name, description: command.description, - argumentHint: "", + argumentHint: command.input?.hint ?? "", kind: "command", })); this.settleCommandsReady(); diff --git a/packages/server/src/server/agent/providers/generic-acp-agent.test.ts b/packages/server/src/server/agent/providers/generic-acp-agent.test.ts index 19a1e9f8ee4..991208f586c 100644 --- a/packages/server/src/server/agent/providers/generic-acp-agent.test.ts +++ b/packages/server/src/server/agent/providers/generic-acp-agent.test.ts @@ -82,4 +82,21 @@ describe("GenericACPAgentClient", () => { }, }); }); + + test("uses provider params to wait for asynchronously published slash commands", () => { + const _client = new GenericACPAgentClient({ + logger: createTestLogger(), + command: ["zcode-acp-server"], + providerParams: { + waitForInitialCommands: true, + initialCommandsWaitTimeoutMs: 2_000, + }, + }); + void _client; + + expect(mockState.superConstructorOptions.at(-1)).toMatchObject({ + waitForInitialCommands: true, + initialCommandsWaitTimeoutMs: 2_000, + }); + }); }); 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 095d4e10a83..86150358827 100644 --- a/packages/server/src/server/agent/providers/generic-acp-agent.ts +++ b/packages/server/src/server/agent/providers/generic-acp-agent.ts @@ -21,6 +21,8 @@ import { export const GenericACPProviderParamsSchema = z .object({ supportsMcpServers: z.boolean().optional(), + waitForInitialCommands: z.boolean().optional(), + initialCommandsWaitTimeoutMs: z.number().int().positive().optional(), clientCapabilities: z .object({ fs: z @@ -69,8 +71,10 @@ export class GenericACPAgentClient extends ACPAgentClient { }, defaultCommand: options.command, capabilities: buildGenericACPCapabilities(providerParams), - waitForInitialCommands: options.waitForInitialCommands, - initialCommandsWaitTimeoutMs: options.initialCommandsWaitTimeoutMs, + waitForInitialCommands: + options.waitForInitialCommands ?? providerParams.waitForInitialCommands, + initialCommandsWaitTimeoutMs: + options.initialCommandsWaitTimeoutMs ?? providerParams.initialCommandsWaitTimeoutMs, clientCapabilities: providerParams.clientCapabilities, clientCapabilityMeta: options.clientCapabilityMeta, configFeatureOptions: options.configFeatureOptions,