diff --git a/docs/tools/acp-agents-setup.md b/docs/tools/acp-agents-setup.md index 24e17d7675b..592be83264b 100644 --- a/docs/tools/acp-agents-setup.md +++ b/docs/tools/acp-agents-setup.md @@ -235,8 +235,9 @@ Restart the gateway after changing this value. ### Health probe agent configuration The bundled `acpx` plugin probes one harness agent while deciding whether the -embedded runtime backend is ready. It defaults to `codex`. If your deployment -uses a different default ACP agent, set the probe agent to the same id: +embedded runtime backend is ready. If `acp.allowedAgents` is set, it defaults to +the first allowed agent; otherwise it defaults to `codex`. If your deployment +needs a different ACP agent for health checks, set the probe agent explicitly: ```bash openclaw config set plugins.entries.acpx.config.probeAgent claude diff --git a/extensions/acpx/openclaw.plugin.json b/extensions/acpx/openclaw.plugin.json index 2addb54516c..55bd1756011 100644 --- a/extensions/acpx/openclaw.plugin.json +++ b/extensions/acpx/openclaw.plugin.json @@ -133,7 +133,7 @@ }, "probeAgent": { "label": "Health Probe Agent", - "help": "Agent id used for the embedded ACP runtime health probe. Defaults to the runtime built-in probe agent (codex). Set this to another configured ACP agent id (for example `opencode` or `claude`) when the default probe agent is not installed or not authenticated, so the whole embedded ACP backend does not get marked unavailable.", + "help": "Agent id used for the embedded ACP runtime health probe. Defaults to the first `acp.allowedAgents` entry when that allowlist is set, otherwise to the runtime built-in probe agent (codex). Set this explicitly (for example `opencode` or `claude`) when the default probe agent is not installed or not authenticated, so the whole embedded ACP backend does not get marked unavailable.", "advanced": true }, "mcpServers": { diff --git a/extensions/acpx/src/service.test.ts b/extensions/acpx/src/service.test.ts index 63ef39887d7..7cbd5c74945 100644 --- a/extensions/acpx/src/service.test.ts +++ b/extensions/acpx/src/service.test.ts @@ -176,6 +176,61 @@ describe("createAcpxRuntimeService", () => { await service.stop?.(ctx); }); + it("uses the first allowed ACP agent as the default probe agent", async () => { + const workspaceDir = await makeTempDir(); + const ctx = createServiceContext(workspaceDir); + ctx.config = { + acp: { + allowedAgents: [" OpenCode ", "codex"], + }, + }; + const runtime = createMockRuntime(); + const runtimeFactory = vi.fn(() => runtime as never); + const service = createAcpxRuntimeService({ + runtimeFactory, + }); + + await service.start(ctx); + + expect(runtimeFactory).toHaveBeenCalledWith( + expect.objectContaining({ + pluginConfig: expect.objectContaining({ + probeAgent: "opencode", + }), + }), + ); + + await service.stop?.(ctx); + }); + + it("keeps explicit probeAgent ahead of acp.allowedAgents", async () => { + const workspaceDir = await makeTempDir(); + const ctx = createServiceContext(workspaceDir); + ctx.config = { + acp: { + allowedAgents: ["opencode"], + }, + }; + const runtime = createMockRuntime(); + const runtimeFactory = vi.fn(() => runtime as never); + const service = createAcpxRuntimeService({ + pluginConfig: { probeAgent: "codex" }, + runtimeFactory, + }); + + await service.start(ctx); + + expect(runtimeFactory).toHaveBeenCalledWith( + expect.objectContaining({ + pluginConfig: expect.objectContaining({ + probeAgent: "codex", + }), + }), + ); + + await service.stop?.(ctx); + }); + it("warns when legacy compatibility config is explicitly ignored", async () => { const workspaceDir = await makeTempDir(); const ctx = createServiceContext(workspaceDir); diff --git a/extensions/acpx/src/service.ts b/extensions/acpx/src/service.ts index 27175434b9a..c7065a174fe 100644 --- a/extensions/acpx/src/service.ts +++ b/extensions/acpx/src/service.ts @@ -84,6 +84,21 @@ function formatDoctorFailureMessage(report: { message: string; details?: string[ return detailText ? `${report.message} (${detailText})` : report.message; } +function normalizeProbeAgent(value: string | undefined): string | undefined { + const normalized = value?.trim().toLowerCase(); + return normalized ? normalized : undefined; +} + +function resolveAllowedAgentsProbeAgent(ctx: OpenClawPluginServiceContext): string | undefined { + for (const agent of ctx.config.acp?.allowedAgents ?? []) { + const normalized = normalizeProbeAgent(agent); + if (normalized) { + return normalized; + } + } + return undefined; +} + export function createAcpxRuntimeService( params: CreateAcpxRuntimeServiceParams = {}, ): OpenClawPluginService { @@ -102,8 +117,12 @@ export function createAcpxRuntimeService( rawConfig: params.pluginConfig, workspaceDir: ctx.workspaceDir, }); + const effectiveBasePluginConfig: ResolvedAcpxPluginConfig = { + ...basePluginConfig, + probeAgent: basePluginConfig.probeAgent ?? resolveAllowedAgentsProbeAgent(ctx), + }; const pluginConfig = await prepareAcpxCodexAuthConfig({ - pluginConfig: basePluginConfig, + pluginConfig: effectiveBasePluginConfig, stateDir: ctx.stateDir, logger: ctx.logger, });