From ba5723d38b1b818ed4f34dd285bfab4b137b26d1 Mon Sep 17 00:00:00 2001 From: Mariano Date: Sat, 2 May 2026 20:34:00 +0200 Subject: [PATCH] [codex] Fix Codex OAuth status auth label (#76197) Merged via squash. Prepared head SHA: a0168232b506d6121cbbcf9ff2e9d144259c3cb5 Co-authored-by: mbelinky <132747814+mbelinky@users.noreply.github.com> Co-authored-by: mbelinky <132747814+mbelinky@users.noreply.github.com> Reviewed-by: @mbelinky --- CHANGELOG.md | 8 ++ src/auto-reply/reply/commands-status.test.ts | 85 ++++++++++++++++++++ src/cli/plugins-command-helpers.ts | 2 +- src/infra/clawhub.ts | 12 +-- src/status/status-text.ts | 42 +++++++--- 5 files changed, 130 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 42d44219bb7..dc11acc1884 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ Docs: https://docs.openclaw.ai +## Unreleased + +### Changes + +### Fixes + +- Status: show the `openai-codex` OAuth profile for `openai/gpt-*` sessions running through the native Codex runtime instead of reporting auth as unknown. (#76197) Thanks @mbelinky. + ## 2026.5.2 ### Highlights diff --git a/src/auto-reply/reply/commands-status.test.ts b/src/auto-reply/reply/commands-status.test.ts index e47ca00406c..6017991bede 100644 --- a/src/auto-reply/reply/commands-status.test.ts +++ b/src/auto-reply/reply/commands-status.test.ts @@ -534,6 +534,91 @@ describe("buildStatusReply subagent summary", () => { expect(normalized).not.toContain("Fast ยท codex"); }); + it("uses Codex OAuth auth labels for openai models running on the Codex harness", async () => { + registerStatusCodexHarness(); + + await withTempHome( + async (dir) => { + const authPath = path.join( + dir, + ".openclaw", + "agents", + "main", + "agent", + "auth-profiles.json", + ); + fs.mkdirSync(path.dirname(authPath), { recursive: true }); + fs.writeFileSync( + authPath, + JSON.stringify({ + version: 1, + profiles: { + "openai-codex:status": { + type: "oauth", + provider: "openai-codex", + access: "access-token", + refresh: "refresh-token", + expires: Date.now() + 60_000, + }, + }, + }), + "utf8", + ); + + const commonParams = { + sessionEntry: { + sessionId: "sess-status-codex-oauth", + updatedAt: 0, + }, + sessionKey: "agent:main:main", + parentSessionKey: "agent:main:main", + sessionScope: "per-sender" as const, + statusChannel: "mobilechat", + provider: "openai", + model: "gpt-5.5", + contextTokens: 32_000, + resolvedFastMode: false, + resolvedVerboseLevel: "off" as const, + resolvedReasoningLevel: "off" as const, + resolveDefaultThinkingLevel: async () => undefined, + isGroup: false, + defaultGroupActivation: () => "mention" as const, + }; + + const codexText = await buildStatusText({ + cfg: { + ...baseCfg, + agents: { + defaults: { + agentRuntime: { id: "codex", fallback: "none" }, + }, + }, + }, + ...commonParams, + }); + const piText = await buildStatusText({ + cfg: baseCfg, + ...commonParams, + }); + + const normalizedCodex = normalizeTestText(codexText); + const normalizedPi = normalizeTestText(piText); + expect(normalizedCodex).toContain("Model: openai/gpt-5.5"); + expect(normalizedCodex).toContain("oauth (openai-codex:status)"); + expect(normalizedCodex).toContain("openai-codex:status"); + expect(normalizedPi).toContain("Model: openai/gpt-5.5"); + expect(normalizedPi).toContain("unknown"); + expect(normalizedPi).not.toContain("openai-codex:status"); + }, + { + env: { + OPENAI_API_KEY: undefined, + OPENAI_OAUTH_TOKEN: undefined, + }, + }, + ); + }); + it("uses workspace-scoped auth evidence in /status auth labels", async () => { const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-status-auth-label-")); const workspaceDir = path.join(tempRoot, "workspace"); diff --git a/src/cli/plugins-command-helpers.ts b/src/cli/plugins-command-helpers.ts index 844b17e9461..d87fc45d82a 100644 --- a/src/cli/plugins-command-helpers.ts +++ b/src/cli/plugins-command-helpers.ts @@ -213,7 +213,7 @@ export function buildPreferredClawHubSpec(raw: string): string | null { } function normalizeReadinessPhase(readiness: ClawHubPackageReadiness): string { - return normalizeLowercaseStringOrEmpty(String(readiness.phase ?? readiness.status ?? "")); + return normalizeLowercaseStringOrEmpty(readiness.phase ?? readiness.status ?? ""); } export function isClawHubReadinessInstallReady( diff --git a/src/infra/clawhub.ts b/src/infra/clawhub.ts index 8f226f99ba1..74025101374 100644 --- a/src/infra/clawhub.ts +++ b/src/infra/clawhub.ts @@ -60,8 +60,8 @@ export type ClawHubArtifactScanState = | "suspicious" | "malicious" | "not-run" - | string; -export type ClawHubArtifactModerationState = "approved" | "quarantined" | "revoked" | string; + | (string & {}); +export type ClawHubArtifactModerationState = "approved" | "quarantined" | "revoked" | (string & {}); export type ClawHubResolvedArtifact = | { source: "clawhub"; @@ -115,7 +115,7 @@ export type ClawHubPackageReadinessPhase = | "metadata-ready" | "blocked" | "ready-for-openclaw" - | string; + | (string & {}); export type ClawHubPackageReadiness = { ready?: boolean | null; readyForOpenClaw?: boolean | null; @@ -124,12 +124,12 @@ export type ClawHubPackageReadiness = { status?: ClawHubPackageReadinessPhase | null; package?: { name?: string | null; - family?: ClawHubPackageFamily | string | null; - channel?: ClawHubPackageChannel | string | null; + family?: ClawHubPackageFamily | (string & {}) | null; + channel?: ClawHubPackageChannel | (string & {}) | null; isOfficial?: boolean | null; } | null; packageName?: string | null; - artifactKind?: ClawHubArtifactKind | string | null; + artifactKind?: ClawHubArtifactKind | (string & {}) | null; blockers?: string[]; scanState?: ClawHubArtifactScanState | null; moderationState?: ClawHubArtifactModerationState | null; diff --git a/src/status/status-text.ts b/src/status/status-text.ts index 9c73e1d1c7d..c4573396386 100644 --- a/src/status/status-text.ts +++ b/src/status/status-text.ts @@ -136,6 +136,18 @@ async function resolveStatusHarnessId(params: { } } +function resolveStatusAuthProvider(params: { + provider: string; + effectiveHarness?: string; +}): string { + const harness = normalizeOptionalLowercaseString(params.effectiveHarness); + const provider = normalizeOptionalLowercaseString(params.provider); + if (harness === "codex" && provider === "openai") { + return "openai-codex"; + } + return params.provider; +} + function formatAgentTaskCountsLine(agentId: string): string | undefined { const snapshot = buildTaskStatusSnapshot(listTasksForAgentIdForStatus(agentId)); if (snapshot.totalCount === 0) { @@ -178,10 +190,23 @@ export async function buildStatusText(params: BuildStatusTextParams): Promise