Files
openclaw/src/agents/openai-codex-routing.ts
pashpashpash 02fe0d8978 Keep OpenAI Codex migrations on automatic runtime routing (#79238)
* fix: keep migrated openai codex routes automatic

* scope runtime policy to providers and models

* fix runtime policy surfaces

* fix ci runtime policy checks

* fix doctor stale session runtime pins
2026-05-08 16:05:35 +09:00

152 lines
4.6 KiB
TypeScript

import type { OpenClawConfig } from "../config/types.openclaw.js";
import { normalizeOptionalLowercaseString } from "../shared/string-coerce.js";
import { normalizeEmbeddedAgentRuntime } from "./pi-embedded-runner/runtime.js";
import { resolveProviderIdForAuth } from "./provider-auth-aliases.js";
import { normalizeProviderId } from "./provider-id.js";
export const OPENAI_PROVIDER_ID = "openai";
export const OPENAI_CODEX_PROVIDER_ID = "openai-codex";
function isOfficialOpenAIBaseUrl(baseUrl: unknown): boolean {
if (typeof baseUrl !== "string" || !baseUrl.trim()) {
return true;
}
try {
const url = new URL(baseUrl.trim());
return (
url.protocol === "https:" &&
url.hostname.toLowerCase() === "api.openai.com" &&
(url.pathname === "" ||
url.pathname === "/" ||
url.pathname === "/v1" ||
url.pathname === "/v1/")
);
} catch {
return false;
}
}
function openAIProviderUsesCustomBaseUrl(config: OpenClawConfig | undefined): boolean {
return !isOfficialOpenAIBaseUrl(config?.models?.providers?.openai?.baseUrl);
}
export function isOpenAIProvider(provider: string | undefined): boolean {
return normalizeProviderId(provider ?? "") === OPENAI_PROVIDER_ID;
}
export function isOpenAICodexProvider(provider: string | undefined): boolean {
return normalizeProviderId(provider ?? "") === OPENAI_CODEX_PROVIDER_ID;
}
export function openAIProviderUsesCodexRuntimeByDefault(params: {
provider?: string;
config?: OpenClawConfig;
}): boolean {
return isOpenAIProvider(params.provider) && !openAIProviderUsesCustomBaseUrl(params.config);
}
export function parseModelRefProvider(value: unknown): string | undefined {
if (typeof value !== "string") {
return undefined;
}
const slashIndex = value.trim().indexOf("/");
if (slashIndex <= 0) {
return undefined;
}
return normalizeProviderId(value.trim().slice(0, slashIndex));
}
export function modelRefUsesOpenAIProvider(value: unknown): boolean {
return parseModelRefProvider(value) === OPENAI_PROVIDER_ID;
}
export function modelSelectionShouldEnsureCodexPlugin(params: {
model?: string;
config?: OpenClawConfig;
}): boolean {
const provider = parseModelRefProvider(params.model);
if (provider === OPENAI_CODEX_PROVIDER_ID) {
return true;
}
return provider === OPENAI_PROVIDER_ID && !openAIProviderUsesCustomBaseUrl(params.config);
}
export function hasOpenAICodexAuthProfileOverride(value: unknown): boolean {
return (
typeof value === "string" &&
normalizeOptionalLowercaseString(value)?.startsWith(`${OPENAI_CODEX_PROVIDER_ID}:`) === true
);
}
export function shouldRouteOpenAIPiThroughCodexAuthProvider(params: {
provider: string;
harnessRuntime?: string;
agentHarnessId?: string;
authProfileProvider?: string;
authProfileId?: string;
config?: OpenClawConfig;
workspaceDir?: string;
}): boolean {
if (
!isOpenAIProvider(params.provider) ||
!hasOpenAICodexAuthProfileOverride(params.authProfileId)
) {
return false;
}
const runtime = normalizeEmbeddedAgentRuntime(params.agentHarnessId ?? params.harnessRuntime);
if (runtime !== "pi") {
return false;
}
const aliasLookupParams = {
config: params.config,
workspaceDir: params.workspaceDir,
};
const authProfileProvider = resolveProviderIdForAuth(
params.authProfileProvider ?? params.authProfileId?.split(":", 1)[0] ?? "",
aliasLookupParams,
);
return authProfileProvider === OPENAI_CODEX_PROVIDER_ID;
}
export function listOpenAIAuthProfileProvidersForAgentRuntime(params: {
provider: string;
harnessRuntime?: string;
agentHarnessId?: string;
}): string[] {
if (!isOpenAIProvider(params.provider)) {
return [params.provider];
}
const runtime = normalizeEmbeddedAgentRuntime(
normalizeExplicitRuntimePin(params.agentHarnessId) ?? params.harnessRuntime,
);
if (runtime === "codex") {
return [OPENAI_CODEX_PROVIDER_ID];
}
if (runtime === "pi") {
return [OPENAI_PROVIDER_ID, OPENAI_CODEX_PROVIDER_ID];
}
return [params.provider];
}
function normalizeExplicitRuntimePin(value: unknown): string | undefined {
if (typeof value !== "string" || !value.trim()) {
return undefined;
}
const runtime = normalizeEmbeddedAgentRuntime(value);
return runtime === "auto" || runtime === "default" ? undefined : runtime;
}
export function resolveOpenAIRuntimeProviderForPi(params: {
provider: string;
harnessRuntime?: string;
agentHarnessId?: string;
authProfileProvider?: string;
authProfileId?: string;
config?: OpenClawConfig;
workspaceDir?: string;
}): string {
return shouldRouteOpenAIPiThroughCodexAuthProvider(params)
? OPENAI_CODEX_PROVIDER_ID
: params.provider;
}