fix: honor acp allowed agents for acpx probe

This commit is contained in:
Peter Steinberger
2026-04-25 05:10:46 +01:00
parent 14eab13ab4
commit 97fd45a8c1
4 changed files with 79 additions and 4 deletions

View File

@@ -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

View File

@@ -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": {

View File

@@ -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);

View File

@@ -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,
});