From d434cbc38466a7f7742c68aa640efb9f532574dc Mon Sep 17 00:00:00 2001 From: jun Date: Wed, 2 Sep 2026 10:58:32 +0900 Subject: [PATCH] fix(agents): allow Codexless V2 task recovery Codexless's app-server sends originator=codexless_agent, which the encrypted V2 recovery admission allowlist rejected, so its child tasks failed as unreadable_encrypted_agent_task. Admit that originator. The issuer, client id, token, account and proxy-secret checks after admission are unchanged. Regression: the recovery request preserves originator=codexless_agent. Source from #3229. Co-authored-by: iamnomankazi <60215267+iamnomankazi@users.noreply.github.com> --- src/server/responses/agent-task-recovery.ts | 1 + tests/agent-task-recovery-security.test.ts | 23 +++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/src/server/responses/agent-task-recovery.ts b/src/server/responses/agent-task-recovery.ts index 8b409e175b..e1c35932ff 100644 --- a/src/server/responses/agent-task-recovery.ts +++ b/src/server/responses/agent-task-recovery.ts @@ -23,6 +23,7 @@ const CODEX_ORIGINATORS = new Set([ "Codex Desktop", "codex_app", "codex_work_desktop", + "codexless_agent", ]); const CODEX_OAUTH_CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann"; const OPENAI_TOKEN_ISSUERS = new Set(["https://auth.openai.com", "https://auth.openai.com/"]); diff --git a/tests/agent-task-recovery-security.test.ts b/tests/agent-task-recovery-security.test.ts index cf213aefbe..ab6ddb2f9e 100644 --- a/tests/agent-task-recovery-security.test.ts +++ b/tests/agent-task-recovery-security.test.ts @@ -413,4 +413,27 @@ describe("agent task recovery security", () => { expect(response.status).toBe(200); expect(recoveryOriginator).toBe("codex_work_desktop"); }); + + test("accepts the Codexless originator", async () => { + let recoveryOriginator = ""; + globalThis.fetch = (async (input, init) => { + if (String(input).includes("chatgpt.com")) { + recoveryOriginator = new Headers(init?.headers).get("originator") ?? ""; + return new Response(recoverySse("Recover the Codexless child task."), { status: 200 }); + } + return providerResponse(); + }) as typeof fetch; + const headers = codexHeaders(); + headers.set("originator", "codexless_agent"); + + const response = await post( + routedConfig(), + "xai/grok-4.5", + encryptedInput(), + headers, + ); + + expect(response.status).toBe(200); + expect(recoveryOriginator).toBe("codexless_agent"); + }); });