fix(acpx): ignore Codex ACP timeout config

This commit is contained in:
Peter Steinberger
2026-04-28 00:12:27 +01:00
parent d74c8423c7
commit d3e4640bed
3 changed files with 94 additions and 26 deletions

View File

@@ -17,6 +17,7 @@ Docs: https://docs.openclaw.ai
### Fixes
- Agents/ACPX: stop forwarding Codex ACP timeout config controls that Codex rejects while preserving OpenClaw's run-timeout watchdog for ACP subagents. Fixes #73052. Thanks @pfrederiksen and @richa65.
- Docs/tools: clarify that `tools.profile: "messaging"` is intentionally narrow and that `tools.profile: "full"` is the unrestricted baseline for broader command/control access. Carries forward #39954. Thanks @posigit.
- Control UI/Agents: redact tool-call args, partial/final results, derived exec output, and configured custom secret patterns before streaming tool events to the Control UI, so tool output cannot expose provider or channel credentials. Fixes #72283. (#72319) Thanks @volcano303 and @BunsDev.
- Agents/sessions: keep `sessions_history` recall redaction enabled even when general log redaction is disabled, and clarify that safety-boundary UI/tool/diagnostic payloads still redact independently of `logging.redactSensitive`. Carries forward #72319. Thanks @volcano303 and @BunsDev.

View File

@@ -353,6 +353,68 @@ describe("AcpxRuntime fresh reset wrapper", () => {
});
});
it("ignores unsupported Codex ACP timeout config controls", async () => {
const baseStore: TestSessionStore = {
load: vi.fn(async () => ({
acpxRecordId: "agent:codex:acp:test",
agentCommand: CODEX_ACP_COMMAND,
})),
save: vi.fn(async () => {}),
};
const { runtime, delegate } = makeRuntime(baseStore);
const setConfigOption = vi.spyOn(delegate, "setConfigOption").mockResolvedValue(undefined);
const handle: Parameters<NonNullable<AcpRuntime["setConfigOption"]>>[0]["handle"] = {
sessionKey: "agent:codex:acp:test",
backend: "acpx",
runtimeSessionName: "agent:codex:acp:test",
acpxRecordId: "agent:codex:acp:test",
};
await runtime.setConfigOption({
handle,
key: "timeout",
value: "60000",
});
await runtime.setConfigOption({
handle,
key: "Timeout_Seconds",
value: "60",
});
expect(setConfigOption).not.toHaveBeenCalled();
});
it("forwards timeout config controls for non-Codex ACP agents", async () => {
const baseStore: TestSessionStore = {
load: vi.fn(async () => ({
acpxRecordId: "agent:claude:acp:test",
agentCommand: "npx @agentclientprotocol/claude-agent-acp",
})),
save: vi.fn(async () => {}),
};
const { runtime, delegate } = makeRuntime(baseStore);
const setConfigOption = vi.spyOn(delegate, "setConfigOption").mockResolvedValue(undefined);
const handle: Parameters<NonNullable<AcpRuntime["setConfigOption"]>>[0]["handle"] = {
sessionKey: "agent:claude:acp:test",
backend: "acpx",
runtimeSessionName: "agent:claude:acp:test",
acpxRecordId: "agent:claude:acp:test",
};
await runtime.setConfigOption({
handle,
key: "timeout",
value: "60",
});
expect(setConfigOption).toHaveBeenCalledOnce();
expect(setConfigOption).toHaveBeenCalledWith({
handle,
key: "timeout",
value: "60",
});
});
it("keeps stale persistent loads hidden until a fresh record is saved", async () => {
const baseStore: TestSessionStore = {
load: vi.fn(async () => ({ acpxRecordId: "stale" }) as never),

View File

@@ -510,36 +510,41 @@ export class AcpxRuntime implements AcpRuntime {
): Promise<void> {
const delegate = await this.resolveDelegateForHandle(input.handle);
const command = await this.resolveCommandForHandle(input.handle);
if (
(input.key === "model" ||
input.key === "thinking" ||
input.key === "thought_level" ||
input.key === "reasoning_effort") &&
isCodexAcpCommand(command)
) {
const override =
input.key === "model"
? normalizeCodexAcpModelOverride(input.value)
: normalizeCodexAcpModelOverride(undefined, input.value);
if (!override && input.key !== "model") {
const key = input.key.trim().toLowerCase();
if (isCodexAcpCommand(command)) {
if (key === "timeout" || key === "timeout_seconds") {
return;
}
if (override) {
if (override.model) {
await delegate.setConfigOption({
...input,
key: "model",
value: override.model,
});
if (
key === "model" ||
key === "thinking" ||
key === "thought_level" ||
key === "reasoning_effort"
) {
const override =
key === "model"
? normalizeCodexAcpModelOverride(input.value)
: normalizeCodexAcpModelOverride(undefined, input.value);
if (!override && key !== "model") {
return;
}
if (override.reasoningEffort) {
await delegate.setConfigOption({
...input,
key: "reasoning_effort",
value: override.reasoningEffort,
});
if (override) {
if (override.model) {
await delegate.setConfigOption({
...input,
key: "model",
value: override.model,
});
}
if (override.reasoningEffort) {
await delegate.setConfigOption({
...input,
key: "reasoning_effort",
value: override.reasoningEffort,
});
}
return;
}
return;
}
}
await delegate.setConfigOption(input);