mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 17:50:45 +00:00
extensions/acpx: expose probeAgent config so non-codex ACP stacks stay available
Add optional probeAgent field to acpx plugin config, carry through resolveAcpxPluginConfig, forward to AcpxRuntime constructor so users can set plugins.entries.acpx.config.probeAgent to any configured agent id instead of hardcoding codex. Refs #68409
This commit is contained in:
committed by
Peter Steinberger
parent
f5f0235bb1
commit
eab26aca9b
@@ -40,6 +40,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Providers/OpenAI: lock the auth picker wording for OpenAI API key, Codex browser login, and Codex device pairing so the setup choices no longer imply a mixed Codex/API-key auth path. (#67848) Thanks @tmlxrd.
|
||||
- Agents/BTW: route `/btw` side questions through provider stream registration with the session workspace, so Ollama provider URL construction and workspace-scoped hooks apply correctly. Fixes #68336. (#70413) Thanks @suboss87.
|
||||
- Agents/sessions: make session transcript write locks non-reentrant by default, so same-process transcript writers contend unless a helper explicitly opts into nested lock ownership.
|
||||
- ACPX/probe: expose an optional `probeAgent` plugin config field so the embedded ACP runtime health probe can target a configured agent (for example `opencode` or `claude`) instead of hardcoding `codex`, and stop marking the entire ACP runtime backend unavailable when the default probe agent is simply not installed or not authenticated. (#68409) Thanks @lyfuci.
|
||||
- Memory search: use sqlite-vec KNN for vector recall while preserving full post-filter result limits in multi-model indexes. Fixes #69666. (#69680) Thanks @aalekh-sarvam.
|
||||
- Providers/OpenAI Codex: stop stale per-agent `openai-codex:default` OAuth profiles from shadowing a newer main-agent identity-scoped profile, and let `openclaw doctor` offer the matching cleanup. (#70393) Thanks @pashpashpash.
|
||||
- ACPX: route OpenClaw ACP bridge commands through the MCP-free runtime path even when the command is wrapped with `env`, has bridge flags, or is resumed from persisted session state, so documented `acpx openclaw` setups no longer fail on per-session MCP injection. (#68741) Thanks @alexlomt.
|
||||
|
||||
@@ -46,6 +46,10 @@
|
||||
"type": "number",
|
||||
"minimum": 0
|
||||
},
|
||||
"probeAgent": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"mcpServers": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
@@ -132,6 +136,11 @@
|
||||
"help": "Reserved compatibility field for the older embedded ACPX queue-owner path. Accepted for compatibility and logged as ignored.",
|
||||
"advanced": true
|
||||
},
|
||||
"probeAgent": {
|
||||
"label": "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.",
|
||||
"advanced": true
|
||||
},
|
||||
"mcpServers": {
|
||||
"label": "MCP Servers",
|
||||
"help": "Named MCP server definitions to inject into embedded ACP session bootstrap. Each entry needs a command and can include args and env.",
|
||||
|
||||
@@ -34,6 +34,7 @@ export type AcpxPluginConfig = {
|
||||
strictWindowsCmdWrapper?: boolean;
|
||||
timeoutSeconds?: number;
|
||||
queueOwnerTtlSeconds?: number;
|
||||
probeAgent?: string;
|
||||
mcpServers?: Record<string, McpServerConfig>;
|
||||
agents?: Record<string, { command: string }>;
|
||||
};
|
||||
@@ -49,6 +50,7 @@ export type ResolvedAcpxPluginConfig = {
|
||||
strictWindowsCmdWrapper: boolean;
|
||||
timeoutSeconds?: number;
|
||||
queueOwnerTtlSeconds: number;
|
||||
probeAgent?: string;
|
||||
legacyCompatibilityConfig: {
|
||||
strictWindowsCmdWrapper?: boolean;
|
||||
queueOwnerTtlSeconds?: number;
|
||||
@@ -107,6 +109,7 @@ export const AcpxPluginConfigSchema = z.strictObject({
|
||||
.number({ error: "queueOwnerTtlSeconds must be a number >= 0" })
|
||||
.min(0, { error: "queueOwnerTtlSeconds must be a number >= 0" })
|
||||
.optional(),
|
||||
probeAgent: nonEmptyTrimmedString("probeAgent must be a non-empty string").optional(),
|
||||
mcpServers: z.record(z.string(), McpServerConfigSchema).optional(),
|
||||
agents: z
|
||||
.record(
|
||||
|
||||
@@ -58,6 +58,37 @@ describe("embedded acpx plugin config", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("leaves probeAgent undefined by default so the runtime picks its built-in probe agent", () => {
|
||||
const resolved = resolveAcpxPluginConfig({
|
||||
rawConfig: undefined,
|
||||
workspaceDir: "/tmp/openclaw-acpx",
|
||||
});
|
||||
|
||||
expect(resolved.probeAgent).toBeUndefined();
|
||||
});
|
||||
|
||||
it("carries an explicit probeAgent through to the resolved plugin config, trimmed", () => {
|
||||
const resolved = resolveAcpxPluginConfig({
|
||||
rawConfig: {
|
||||
probeAgent: " opencode ",
|
||||
},
|
||||
workspaceDir: "/tmp/openclaw-acpx",
|
||||
});
|
||||
|
||||
expect(resolved.probeAgent).toBe("opencode");
|
||||
});
|
||||
|
||||
it("rejects an empty probeAgent string", () => {
|
||||
expect(() =>
|
||||
resolveAcpxPluginConfig({
|
||||
rawConfig: {
|
||||
probeAgent: "",
|
||||
},
|
||||
workspaceDir: "/tmp/openclaw-acpx",
|
||||
}),
|
||||
).toThrow(/probeAgent must be a non-empty string/);
|
||||
});
|
||||
|
||||
it("injects the built-in plugin-tools MCP server only when explicitly enabled", () => {
|
||||
const resolved = resolveAcpxPluginConfig({
|
||||
rawConfig: {
|
||||
|
||||
@@ -260,6 +260,8 @@ export function resolveAcpxPluginConfig(params: {
|
||||
]),
|
||||
);
|
||||
|
||||
const probeAgent = normalized.probeAgent?.trim();
|
||||
|
||||
return {
|
||||
cwd,
|
||||
stateDir,
|
||||
@@ -273,6 +275,7 @@ export function resolveAcpxPluginConfig(params: {
|
||||
normalized.strictWindowsCmdWrapper ?? DEFAULT_STRICT_WINDOWS_CMD_WRAPPER,
|
||||
timeoutSeconds: normalized.timeoutSeconds ?? DEFAULT_ACPX_TIMEOUT_SECONDS,
|
||||
queueOwnerTtlSeconds: normalized.queueOwnerTtlSeconds ?? DEFAULT_QUEUE_OWNER_TTL_SECONDS,
|
||||
probeAgent: probeAgent && probeAgent.length > 0 ? probeAgent : undefined,
|
||||
legacyCompatibilityConfig: {
|
||||
strictWindowsCmdWrapper: normalized.strictWindowsCmdWrapper,
|
||||
queueOwnerTtlSeconds: normalized.queueOwnerTtlSeconds,
|
||||
|
||||
@@ -145,6 +145,37 @@ describe("createAcpxRuntimeService", () => {
|
||||
await service.stop?.(ctx);
|
||||
});
|
||||
|
||||
it("forwards a configured probeAgent to the runtime factory so the probe does not hardcode the default", async () => {
|
||||
const workspaceDir = await makeTempDir();
|
||||
const ctx = createServiceContext(workspaceDir);
|
||||
const runtime = {
|
||||
ensureSession: vi.fn(),
|
||||
runTurn: vi.fn(),
|
||||
cancel: vi.fn(),
|
||||
close: vi.fn(),
|
||||
probeAvailability: vi.fn(async () => {}),
|
||||
isHealthy: vi.fn(() => true),
|
||||
doctor: vi.fn(async () => ({ ok: true, message: "ok" })),
|
||||
};
|
||||
const runtimeFactory = vi.fn(() => runtime as never);
|
||||
const service = createAcpxRuntimeService({
|
||||
pluginConfig: { probeAgent: "opencode" },
|
||||
runtimeFactory,
|
||||
});
|
||||
|
||||
await service.start(ctx);
|
||||
|
||||
expect(runtimeFactory).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
pluginConfig: expect.objectContaining({
|
||||
probeAgent: "opencode",
|
||||
}),
|
||||
}),
|
||||
);
|
||||
|
||||
await service.stop?.(ctx);
|
||||
});
|
||||
|
||||
it("warns when legacy compatibility config is explicitly ignored", async () => {
|
||||
const workspaceDir = await makeTempDir();
|
||||
const ctx = createServiceContext(workspaceDir);
|
||||
|
||||
@@ -53,6 +53,7 @@ function createDefaultRuntime(params: AcpxRuntimeFactoryParams): AcpxRuntimeLike
|
||||
mcpServers: toAcpMcpServers(params.pluginConfig.mcpServers),
|
||||
permissionMode: params.pluginConfig.permissionMode,
|
||||
nonInteractivePermissions: params.pluginConfig.nonInteractivePermissions,
|
||||
probeAgent: params.pluginConfig.probeAgent,
|
||||
timeoutMs:
|
||||
params.pluginConfig.timeoutSeconds != null
|
||||
? params.pluginConfig.timeoutSeconds * 1_000
|
||||
|
||||
Reference in New Issue
Block a user