From 346a72ddb913168cdaa48f4e6b9bf3bc0d057bcc Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Sat, 25 Apr 2026 12:42:23 -0700 Subject: [PATCH] fix(codex): require authorized inbound claims for bound turns (#71702) * fix(codex): require authorized inbound claims for bound turns * fix(codex): consume unauthorized bound turns --- CHANGELOG.md | 1 + .../codex/src/conversation-binding.test.ts | 35 ++++++++++++++++++- extensions/codex/src/conversation-binding.ts | 3 ++ 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b5c55cca63c..b0b263a1cba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -188,6 +188,7 @@ Docs: https://docs.openclaw.ai - Agents/Claude CLI: pass the OpenClaw system prompt through Claude's prompt-file flag so Windows runs avoid argv length failures without changing system prompt semantics. Fixes #69158. (#69211) Thanks @skylee-01, @cassioanorte, @Syu0, and @Stache73. - Agents/CLI sessions: bind `google-gemini-cli` session auth-epoch to the Google account identity in `~/.gemini/oauth_creds.json`, so Gemini-backed agents resume their conversation after gateway restart instead of minting a fresh session, and stale bindings are invalidated when the authenticated Google account changes. Fixes #70973. (#71076) Thanks @openperf. - Slack: stop treating user mentions in assistant-authored message edit blocks as sender attribution, preventing edited bot messages from spoofing a mentioned DM user. (#71700) Thanks @vincentkoc. +- Codex: consume unauthorized bound conversation inbound claims before they can fall through to other claim handlers or enqueue Codex turns. (#71702) Thanks @vincentkoc. ## 2026.4.24 diff --git a/extensions/codex/src/conversation-binding.test.ts b/extensions/codex/src/conversation-binding.test.ts index d1c8f5b9d92..c58e47f1485 100644 --- a/extensions/codex/src/conversation-binding.test.ts +++ b/extensions/codex/src/conversation-binding.test.ts @@ -2,7 +2,10 @@ import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import { afterEach, beforeEach, describe, expect, it } from "vitest"; -import { handleCodexConversationBindingResolved } from "./conversation-binding.js"; +import { + handleCodexConversationBindingResolved, + handleCodexConversationInboundClaim, +} from "./conversation-binding.js"; let tempDir: string; @@ -40,4 +43,34 @@ describe("codex conversation binding", () => { await expect(fs.stat(sidecar)).rejects.toMatchObject({ code: "ENOENT" }); }); + + it("consumes inbound bound messages when command authorization is absent", async () => { + const result = await handleCodexConversationInboundClaim( + { + content: "run this", + channel: "discord", + isGroup: true, + }, + { + channelId: "discord", + pluginBinding: { + bindingId: "binding-1", + pluginId: "codex", + pluginRoot: tempDir, + channel: "discord", + accountId: "default", + conversationId: "channel-1", + boundAt: Date.now(), + data: { + kind: "codex-app-server-session", + version: 1, + sessionFile: path.join(tempDir, "session.jsonl"), + workspaceDir: tempDir, + }, + }, + }, + ); + + expect(result).toEqual({ handled: true }); + }); }); diff --git a/extensions/codex/src/conversation-binding.ts b/extensions/codex/src/conversation-binding.ts index 8ddcf182375..be0407ff4c2 100644 --- a/extensions/codex/src/conversation-binding.ts +++ b/extensions/codex/src/conversation-binding.ts @@ -113,6 +113,9 @@ export async function handleCodexConversationInboundClaim( if (!data) { return undefined; } + if (event.commandAuthorized !== true) { + return { handled: true }; + } const prompt = (event.bodyForAgent ?? event.content ?? "").trim(); if (!prompt) { return { handled: true };