From ec5015924c791d618699446c1c8af8275321fb78 Mon Sep 17 00:00:00 2001 From: Lucenx9 <185146821+Lucenx9@users.noreply.github.com> Date: Wed, 22 Apr 2026 23:50:20 +0200 Subject: [PATCH] fix(codex): fail closed for unknown approvals --- .../src/app-server/approval-bridge.test.ts | 27 +++++++++++++++++++ .../codex/src/app-server/approval-bridge.ts | 22 ++++++++++++--- 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/extensions/codex/src/app-server/approval-bridge.test.ts b/extensions/codex/src/app-server/approval-bridge.test.ts index e4d42b39bfb..941360910f5 100644 --- a/extensions/codex/src/app-server/approval-bridge.test.ts +++ b/extensions/codex/src/app-server/approval-bridge.test.ts @@ -106,6 +106,29 @@ describe("Codex app-server approval bridge", () => { ); }); + it("fails closed for unsupported native approval methods without requesting plugin approval", async () => { + const params = createParams(); + + const result = await handleCodexAppServerApprovalRequest({ + method: "future/requestApproval", + requestParams: { + threadId: "thread-1", + turnId: "turn-1", + itemId: "future-1", + }, + paramsForRun: params, + threadId: "thread-1", + turnId: "turn-1", + }); + + expect(result).toEqual({ + decision: "decline", + reason: "OpenClaw codex app-server bridge does not grant native approvals yet.", + }); + expect(mockCallGatewayTool).not.toHaveBeenCalled(); + expect(params.onAgentEvent).not.toHaveBeenCalled(); + }); + it("maps app-server approval response families separately", () => { expect( buildApprovalResponse( @@ -134,5 +157,9 @@ describe("Codex app-server approval bridge", () => { permissions: { network: { allowHosts: ["example.com"] } }, scope: "turn", }); + expect(buildApprovalResponse("future/requestApproval", undefined, "approved-once")).toEqual({ + decision: "decline", + reason: "OpenClaw codex app-server bridge does not grant native approvals yet.", + }); }); }); diff --git a/extensions/codex/src/app-server/approval-bridge.ts b/extensions/codex/src/app-server/approval-bridge.ts index 2985e395734..3415780db0b 100644 --- a/extensions/codex/src/app-server/approval-bridge.ts +++ b/extensions/codex/src/app-server/approval-bridge.ts @@ -38,6 +38,9 @@ export async function handleCodexAppServerApprovalRequest(params: { if (!matchesCurrentTurn(requestParams, params.threadId, params.turnId)) { return undefined; } + if (!isSupportedAppServerApprovalMethod(params.method)) { + return unsupportedApprovalResponse(); + } const context = buildApprovalContext({ method: params.method, @@ -161,9 +164,7 @@ export function buildApprovalResponse( } return { permissions: {}, scope: "turn" }; } - return { - decision: outcome === "approved-once" || outcome === "approved-session" ? "accept" : "decline", - }; + return unsupportedApprovalResponse(); } function matchesCurrentTurn( @@ -299,6 +300,13 @@ function requestedPermissions(requestParams: JsonObject | undefined): JsonObject return granted; } +function unsupportedApprovalResponse(): JsonValue { + return { + decision: "decline", + reason: "OpenClaw codex app-server bridge does not grant native approvals yet.", + }; +} + function hasAvailableDecision(requestParams: JsonObject | undefined, decision: string): boolean { const available = requestParams?.availableDecisions; if (!Array.isArray(available)) { @@ -348,6 +356,14 @@ function approvalKindForMethod(method: string): AgentApprovalEventData["kind"] { return "unknown"; } +function isSupportedAppServerApprovalMethod(method: string): boolean { + return ( + method === "item/commandExecution/requestApproval" || + method === "item/fileChange/requestApproval" || + method === "item/permissions/requestApproval" + ); +} + function emitApprovalEvent(params: EmbeddedRunAttemptParams, data: AgentApprovalEventData): void { params.onAgentEvent?.({ stream: "approval", data: data as unknown as Record }); }