openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
1,412 lines • 57.4 kB
JavaScript
import { A as OPENAI_GPT_56_TERRA_MODEL_ID, B as OPENAI_CODEX_RESPONSES_BASE_URL, C as OPENAI_GPT_54_NANO_MODEL_ID, D as OPENAI_GPT_56_LUNA_MODEL_ID, E as OPENAI_GPT_55_PRO_MODEL_ID, F as isOpenAIPlatformOnlyRouteModelId, H as isOpenAIApiBaseUrl, I as isOpenAISubscriptionOnlyRouteModelId, L as normalizeOpenAIModelRouteId, M as OPENAI_GPT_6_MODEL_IDS, N as OPENAI_PROVIDER_MODERN_MODEL_IDS, O as OPENAI_GPT_56_MODEL_ID, P as buildOpenAIUnknownModelHint, R as resolveOpenAICodexReasoningEfforts, S as OPENAI_GPT_54_MODEL_ID, T as OPENAI_GPT_55_MODEL_ID, U as isOpenAICodexBaseUrl, V as classifyOpenAIBaseUrl, W as resolveOpenAIDefaultBaseUrl, _ as OPENAI_CHAT_LATEST_MODEL_ID, g as OPENAI_CHATGPT_MODERN_MODEL_IDS, j as OPENAI_GPT_56_VARIANT_MODEL_IDS, k as OPENAI_GPT_56_SOL_MODEL_ID, n as resolveAuthoredOpenAIProviderConfig, o as resolveUnifiedOpenAIThinkingProfile, s as modelCatalog, v as OPENAI_DAYBREAK_MODEL_IDS, w as OPENAI_GPT_54_PRO_MODEL_ID, x as OPENAI_GPT_54_MINI_MODEL_ID, y as OPENAI_GPT_53_CODEX_SPARK_MODEL_ID, z as OPENAI_CODEX_MODELS_ENDPOINT } from "./provider-policy-api-CZZ7QgUx.mjs";
import { l as isSIWCAuthFlow, s as TOKEN_SHARING_RESOURCE } from "./token-sharing-CmBELw8t.mjs";
import { t as createOpenAIProvider } from "./provider-contract-api-Bv-IcdNg.mjs";
import { asOptionalRecord, normalizeLowercaseStringOrEmpty, normalizeOptionalString, readStringValue, uniqueValues } from "openclaw/plugin-sdk/string-coerce-runtime";
import { DEFAULT_CONTEXT_TOKENS, buildFamilyForwardCompatModel, buildFirstTemplateModel, buildManifestModelProviderConfig, findCatalogTemplate, matchesExactOrPrefix, normalizeProviderId } from "openclaw/plugin-sdk/provider-model-metadata";
import { resolveOpenAICodexAuthIdentity } from "openclaw/plugin-sdk/provider-oauth-runtime";
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
import { createLazyRuntimeModule, createLazyRuntimeSurface } from "openclaw/plugin-sdk/lazy-runtime";
import { createProviderApiKeyAuthMethod } from "openclaw/plugin-sdk/provider-entry";
import { applyAgentDefaultModelPrimary, ensureModelAllowlistEntry, resolveAgentModelPrimaryValue } from "openclaw/plugin-sdk/provider-onboard";
import { addProviderUsageModel, asProviderUsageObject, buildProviderUsageHistorySnapshot, buildUsageHttpErrorSnapshot, cleanProviderUsageCredential, createProviderUsageDailyAccumulator, decodeProviderUsageAdminToken, encodeProviderUsageAdminToken, fetchCodexUsage, fetchProviderUsagePages, parseProviderUsageNonNegativeInteger, parseProviderUsageNumber, resolveProviderUsageDailyPeriod, resolveProviderUsageDisplayName } from "openclaw/plugin-sdk/provider-usage";
//#region extensions/openai/account-models.ts
const OPENAI_UNKNOWN_MODEL_COST = {
input: 0,
output: 0,
cacheRead: 0,
cacheWrite: 0
};
const OPENAI_RESPONSES_UNSUPPORTED_MODEL_ID_PATTERN = /(?:^|-)(?:search|live|cyber|exp|alpha)(?:-|$)/;
const OPENAI_PRE_GPT5_MODEL_ID_PATTERN = /^(?:ft:)?(?:gpt-3\.5|gpt-4|o[134])/;
/** Conservative Responses metadata for an OpenAI chat id without a catalog row. */
function buildOpenAIUncataloguedModel(id, baseUrl) {
return {
id,
name: id,
api: "openai-responses",
baseUrl,
reasoning: /^(?:gpt-(?:[5-9]|\d{2})|o\d)/.test(id),
input: ["text"],
cost: OPENAI_UNKNOWN_MODEL_COST,
contextWindow: 128e3,
maxTokens: 16384
};
}
/**
* Keeps catalog rows for listed ids and gives every other runnable listed chat id
* conservative Responses metadata. Shared prefix templates are not used here: every
* OpenAI id shares "gpt-", and GPT-5 reasoning settings make GPT-4-family requests fail.
*/
function projectOpenAIAccountModels(params) {
const catalogIds = new Set(params.catalogModels.map((model) => model.id));
return params.listedModels.flatMap((model) => {
if (OPENAI_PRE_GPT5_MODEL_ID_PATTERN.test(model.id)) return [];
if (catalogIds.has(model.id)) return [model];
if (OPENAI_RESPONSES_UNSUPPORTED_MODEL_ID_PATTERN.test(model.id) || isOpenAISubscriptionOnlyRouteModelId(model.id)) return [];
return [buildOpenAIUncataloguedModel(model.id, params.baseUrl)];
});
}
//#endregion
//#region extensions/openai/codex-model-rows.ts
function readCodexReasoningLevels(row) {
const record = asOptionalRecord(row);
const value = record?.supported_reasoning_levels ?? record?.supportedReasoningLevels;
if (!Array.isArray(value)) return;
return value.flatMap((entry) => {
if (typeof entry === "string" && entry.trim().length > 0) return [entry.trim()];
const effort = asOptionalRecord(entry)?.effort;
return typeof effort === "string" && effort.trim().length > 0 ? [effort.trim()] : [];
});
}
function readCodexModelRows(body) {
const models = asOptionalRecord(body)?.models;
if (!Array.isArray(models)) throw new Error("OpenAI Codex model discovery response must be { models: [] }");
return models;
}
function shouldIncludeCodexModelRow(row, readers) {
const { readLiveModelCatalogStringField, readLiveModelCatalogBooleanField } = readers;
const visibility = normalizeLowercaseStringOrEmpty(readLiveModelCatalogStringField(row, "visibility") ?? "");
if (visibility && visibility !== "list") return false;
return (readLiveModelCatalogBooleanField(row, "show_in_picker") ?? readLiveModelCatalogBooleanField(row, "showInPicker")) !== false;
}
function resolveCodexModelInput(row, fallback) {
const record = asOptionalRecord(row);
const rawModalities = [record?.input_modalities, record?.inputModalities].find(Array.isArray)?.filter((entry) => typeof entry === "string") ?? [];
if (rawModalities.length === 0) return fallback?.input ?? ["text", "image"];
const modalities = new Set(rawModalities.map((modality) => normalizeLowercaseStringOrEmpty(modality)));
const input = [
"text",
"image",
"audio",
"video"
].filter((modality) => modalities.has(modality) || modality === "image" && modalities.has("vision"));
return input.length > 0 ? input : fallback?.input ?? ["text", "image"];
}
//#endregion
//#region extensions/openai/default-models.ts
const OPENAI_DEFAULT_MODEL = "openai/gpt-6-astra";
const OPENAI_CODEX_DEFAULT_MODEL = "openai/gpt-6-astra";
function applyOpenAIProviderConfig(cfg) {
const configuredModel = cfg.agents?.defaults?.model;
const withConfiguredRefs = [resolveAgentModelPrimaryValue(configuredModel), ...typeof configuredModel === "object" ? configuredModel.fallbacks ?? [] : []].filter((ref) => typeof ref === "string" && ref.trim().length > 0).reduce((next, modelRef) => ensureModelAllowlistEntry({
cfg: next,
modelRef
}), cfg);
const models = { ...withConfiguredRefs.agents?.defaults?.models };
const gptAliasClaimed = Object.entries(models).some(([modelRef, model]) => modelRef !== "openai/gpt-6-astra" && model?.alias?.trim().toLowerCase() === "gpt");
models[OPENAI_DEFAULT_MODEL] = {
...models[OPENAI_DEFAULT_MODEL],
...models["openai/gpt-6-astra"]?.alias === void 0 && !gptAliasClaimed ? { alias: "GPT" } : {}
};
return {
...withConfiguredRefs,
agents: {
...withConfiguredRefs.agents,
defaults: {
...withConfiguredRefs.agents?.defaults,
models
}
}
};
}
function applyOpenAIConfig(cfg) {
const next = applyOpenAIProviderConfig(cfg);
return resolveAgentModelPrimaryValue(cfg.agents?.defaults?.model) === void 0 ? applyAgentDefaultModelPrimary(next, OPENAI_DEFAULT_MODEL) : next;
}
//#endregion
//#region extensions/openai/model-service-tiers.ts
/** Only authenticated remote rows may supply this private observation, never fallback seeds. */
function readOpenAICodexServiceTiers(rows) {
return rows.flatMap((row) => {
const record = asOptionalRecord(row);
const modelId = normalizeOptionalString(record?.slug) ?? normalizeOptionalString(record?.id);
const tiers = record?.service_tiers;
if (!modelId || !Array.isArray(tiers)) return [];
const serviceTiers = [];
for (const tier of tiers) {
const id = normalizeOptionalString(asOptionalRecord(tier)?.id);
if (!id) return [];
serviceTiers.push(id);
}
return [{
modelId,
runtimeId: "codex",
api: "openai-chatgpt-responses",
baseUrl: OPENAI_CODEX_RESPONSES_BASE_URL,
serviceTiers: [...new Set(serviceTiers)]
}];
});
}
/**
* A successful account-scoped listing is authoritative even when every row is hidden; static
* hints must not invent subscription access. Hidden rows stay listed: the account is entitled.
*/
function buildOpenAICodexReadyOutcome(rows) {
const modelServiceTiers = readOpenAICodexServiceTiers(rows);
return {
provider: "openai",
status: "ready",
...modelServiceTiers.length ? { modelServiceTiers } : {},
listedModelIds: rows.flatMap((row) => {
const record = asOptionalRecord(row);
return normalizeOptionalString(record?.slug) ?? normalizeOptionalString(record?.id) ?? [];
})
};
}
function projectOpenAICatalog(catalog, profileId) {
const scopedProfileId = profileId?.trim();
return {
providers: { openai: catalog.provider },
...catalog.outcome ? { outcomes: [scopedProfileId ? {
...catalog.outcome,
profileId: scopedProfileId
} : catalog.outcome] } : {}
};
}
//#endregion
//#region extensions/openai/replay-policy.ts
const RESPONSES_FAMILY_APIS = /* @__PURE__ */ new Set([
"openai-responses",
"openai-chatgpt-responses",
"azure-openai-responses"
]);
/**
* Returns the provider-owned replay policy for OpenAI-family transports.
*/
function buildOpenAIReplayPolicy(ctx) {
return {
sanitizeMode: "images-only",
applyAssistantFirstOrderingFix: false,
validateGeminiTurns: false,
validateAnthropicTurns: false,
...RESPONSES_FAMILY_APIS.has(ctx.modelApi ?? "") ? {
allowSyntheticToolResults: true,
appendOnlyRuntimeContext: true,
...ctx.inHistorySystemUpdates ? { inHistorySystemUpdates: true } : {}
} : {},
...ctx.modelApi === "openai-completions" ? {
sanitizeToolCallIds: true,
toolCallIdMode: "strict"
} : { sanitizeToolCallIds: false }
};
}
//#endregion
//#region extensions/openai/transport-policy.ts
const DEFAULT_OPENAI_WS_DEGRADE_COOLDOWN_MS = 6e4;
const AZURE_PROVIDER_IDS = /* @__PURE__ */ new Set(["azure-openai", "azure-openai-responses"]);
function isAzureOpenAIBaseUrl(baseUrl) {
return normalizeLowercaseStringOrEmpty(URL.parse(baseUrl?.trim() ?? "")?.hostname).endsWith(".openai.azure.com");
}
function normalizeIdentityValue(value, maxLength = 160) {
const trimmed = value.trim().replace(/[\r\n]+/g, " ");
return trimmed.length > maxLength ? trimmed.slice(0, maxLength) : trimmed;
}
function usesKnownNativeOpenAIRoute(provider, baseUrl) {
const normalizedProvider = normalizeProviderId(provider);
if (!normalizedProvider) return false;
if (normalizedProvider === "openai") return !baseUrl || isOpenAIApiBaseUrl(baseUrl) || isOpenAICodexBaseUrl(baseUrl);
if (AZURE_PROVIDER_IDS.has(normalizedProvider)) return !baseUrl || isAzureOpenAIBaseUrl(baseUrl);
return false;
}
function resolveSessionHeaders(sessionIdValue) {
if (!sessionIdValue) return;
const sessionId = normalizeIdentityValue(sessionIdValue);
if (!sessionId) return;
return {
"x-client-request-id": sessionId,
"x-openclaw-session-id": sessionId
};
}
function resolveOpenAITransportTurnState(ctx) {
if (!usesKnownNativeOpenAIRoute(ctx.provider, ctx.model?.baseUrl)) return;
const sessionHeaders = resolveSessionHeaders(ctx.sessionId);
if (!sessionHeaders) return ctx.transport === "websocket" ? { websocket: { degradeCooldownMs: DEFAULT_OPENAI_WS_DEGRADE_COOLDOWN_MS } } : void 0;
const turnId = normalizeIdentityValue(ctx.turnId);
const attempt = String(Math.max(1, ctx.attempt));
return {
headers: {
...sessionHeaders,
"x-openclaw-turn-id": turnId,
"x-openclaw-turn-attempt": attempt
},
metadata: {
openclaw_session_id: sessionHeaders["x-openclaw-session-id"] ?? "",
openclaw_turn_id: turnId,
openclaw_turn_attempt: attempt,
openclaw_transport: ctx.transport
},
...ctx.transport === "websocket" ? { websocket: {
headers: sessionHeaders,
degradeCooldownMs: DEFAULT_OPENAI_WS_DEGRADE_COOLDOWN_MS
} } : {}
};
}
//#endregion
//#region extensions/openai/shared.ts
const OPENAI_DEFAULT_RUNTIME_CONTEXT_TOKENS = 272e3;
const loadResponsesStream = createLazyRuntimeModule(() => import("./responses-stream.runtime-xo4asOqb.mjs"));
const wrapOpenAIResponsesProviderStreamFn = (ctx) => {
const loadStream = createLazyRuntimeSurface(loadResponsesStream, (runtime) => runtime.wrapOpenAIResponsesStream(ctx));
return async (...args) => (await loadStream())(...args);
};
function buildOpenAIResponsesProviderHooks(options) {
return {
isCacheTtlEligible: ({ provider, baseUrl, supportsPromptCacheKey }) => normalizeProviderId(provider) === "openai" && (supportsPromptCacheKey ?? (classifyOpenAIBaseUrl(baseUrl) === "platform" || isOpenAICodexBaseUrl(baseUrl))),
buildReplayPolicy: buildOpenAIReplayPolicy,
prepareExtraParams: ({ extraParams }) => {
const transport = extraParams?.transport;
return [
"auto",
"sse",
"websocket",
"websocket-cached"
].some((candidate) => candidate === transport) ? extraParams : {
...extraParams,
transport: options?.transport ?? "auto"
};
},
wrapStreamFn: wrapOpenAIResponsesProviderStreamFn,
wrapSimpleCompletionStreamFn: (ctx) => ctx.auth?.mode === "oauth" && ctx.auth.authFlow === "chatgpt-token-sharing" ? wrapOpenAIResponsesProviderStreamFn({
...ctx,
nativeWebSearchAllowedByToolPolicy: false
}) : void 0,
resolveTransportTurnState: resolveOpenAITransportTurnState
};
}
//#endregion
//#region extensions/openai/usage.ts
const OPENAI_COSTS_URL = "https://api.openai.com/v1/organization/costs";
const OPENAI_COMPLETIONS_USAGE_URL = "https://api.openai.com/v1/organization/usage/completions";
const OPENAI_ADMIN_TOKEN_PREFIX = "openclaw:openai-admin:v1:";
const OPENAI_USAGE_RESPONSE_MAX_BYTES = 4194304;
const OPENAI_USAGE_HISTORY_DAYS = 30;
function utcDay(epochSeconds) {
return (/* @__PURE__ */ new Date(epochSeconds * 1e3)).toISOString().slice(0, 10);
}
async function fetchPages(params) {
return await fetchProviderUsagePages({
responseLabel: "OpenAI usage",
responseMaxBytes: OPENAI_USAGE_RESPONSE_MAX_BYTES,
timeoutMs: params.timeoutMs,
fetchFn: params.fetchFn,
buildRequest: (page) => {
const url = new URL(params.baseUrl);
url.searchParams.set("start_time", String(params.startTime));
url.searchParams.set("end_time", String(params.endTime));
url.searchParams.set("bucket_width", "1d");
url.searchParams.set("limit", String(params.periodDays));
url.searchParams.set("group_by", params.groupBy);
if (params.projectId) url.searchParams.set("project_ids", params.projectId);
if (page) url.searchParams.set("page", page);
return {
url,
headers: {
Accept: "application/json",
Authorization: `Bearer ${params.apiKey}`
}
};
}
});
}
function aggregateHistory(params) {
const daily = /* @__PURE__ */ new Map();
for (const kind of ["costs", "completions"]) for (const rawBucket of params[kind]) {
const bucket = asProviderUsageObject(rawBucket);
const startTime = parseProviderUsageNumber(bucket?.start_time);
if (startTime === void 0 || !Array.isArray(bucket?.results)) continue;
const accumulator = daily.get(startTime) ?? createProviderUsageDailyAccumulator(utcDay(startTime), true);
daily.set(startTime, accumulator);
for (const rawResult of bucket.results) {
const result = asProviderUsageObject(rawResult);
if (kind === "costs") {
const amount = parseProviderUsageNumber(asProviderUsageObject(result?.amount)?.value) ?? 0;
const category = resolveProviderUsageDisplayName(result?.line_item, "API");
accumulator.amount += amount;
accumulator.categories.set(category, (accumulator.categories.get(category) ?? 0) + amount);
continue;
}
if (!result) continue;
const requests = parseProviderUsageNonNegativeInteger(result.num_model_requests);
const textInputTokens = parseProviderUsageNonNegativeInteger(result.input_tokens);
const audioInputTokens = parseProviderUsageNonNegativeInteger(result.input_audio_tokens);
const cacheReadTokens = parseProviderUsageNonNegativeInteger(result.input_cached_tokens);
const inputTokens = Math.max(0, textInputTokens - cacheReadTokens) + audioInputTokens;
const outputTokens = parseProviderUsageNonNegativeInteger(result.output_tokens) + parseProviderUsageNonNegativeInteger(result.output_audio_tokens);
const totalTokens = textInputTokens + audioInputTokens + outputTokens;
accumulator.requests = (accumulator.requests ?? 0) + requests;
accumulator.inputTokens += inputTokens;
accumulator.cacheReadTokens += cacheReadTokens;
accumulator.outputTokens += outputTokens;
accumulator.totalTokens += totalTokens;
addProviderUsageModel(accumulator, resolveProviderUsageDisplayName(result.model, "Responses and Chat Completions"), {
requests,
inputTokens,
cacheReadTokens,
cacheWriteTokens: 0,
outputTokens,
totalTokens
});
}
}
return buildProviderUsageHistorySnapshot({
provider: "openai",
displayName: "OpenAI",
plan: params.projectId ? `Admin API · ${params.projectId}` : "Admin API",
periodDays: params.periodDays,
unit: "USD",
...params.projectId ? { scope: `Project ${params.projectId}` } : {},
daily: [...daily.entries()].toSorted(([a], [b]) => a - b).map(([, entry]) => entry),
formatSummary: ({ requests, totalTokens }) => `${requests.toLocaleString("en-US")} requests · ${totalTokens.toLocaleString("en-US")} tokens`
});
}
async function fetchOpenAIAdminUsage(params) {
const period = resolveProviderUsageDailyPeriod({
now: params.now ?? Date.now(),
periodDays: params.periodDays,
defaultPeriodDays: OPENAI_USAGE_HISTORY_DAYS
});
const common = {
apiKey: params.apiKey,
projectId: params.projectId,
startTime: Math.floor(period.startMs / 1e3),
endTime: Math.floor(period.endMs / 1e3),
periodDays: period.periodDays,
timeoutMs: params.timeoutMs,
fetchFn: params.fetchFn
};
const [costs, completions] = await Promise.all([fetchPages({
...common,
baseUrl: OPENAI_COSTS_URL,
groupBy: "line_item"
}), fetchPages({
...common,
baseUrl: OPENAI_COMPLETIONS_USAGE_URL,
groupBy: "model"
})]);
if (!costs.ok || !completions.ok) {
const failedStatus = !costs.ok ? costs.status : !completions.ok ? completions.status : void 0;
if (failedStatus === 401 || failedStatus === 403) return {
provider: "openai",
displayName: "OpenAI",
windows: [],
error: "Admin API key required"
};
return failedStatus ? buildUsageHttpErrorSnapshot({
provider: "openai",
status: failedStatus
}) : {
provider: "openai",
displayName: "OpenAI",
windows: [],
error: "Usage unavailable"
};
}
return aggregateHistory({
costs: costs.data,
completions: completions.data,
periodDays: period.periodDays,
projectId: params.projectId
});
}
async function resolveOpenAIUsageAuth(ctx) {
const explicitAdminKey = cleanProviderUsageCredential(ctx.env.OPENAI_ADMIN_KEY);
if (explicitAdminKey) return { token: encodeProviderUsageAdminToken(OPENAI_ADMIN_TOKEN_PREFIX, explicitAdminKey) };
const oauth = await ctx.resolveOAuthToken();
if (oauth && isSIWCAuthFlow(oauth.authFlow)) return { handled: true };
if (oauth) return oauth;
return { handled: true };
}
async function fetchOpenAIUsage(ctx) {
const adminKey = decodeProviderUsageAdminToken(OPENAI_ADMIN_TOKEN_PREFIX, ctx.token);
if (!adminKey) {
const snapshot = await fetchCodexUsage(ctx.token, ctx.accountId, ctx.timeoutMs, ctx.fetchFn);
if (snapshot.error) return snapshot;
const { token: accessToken, email: profileEmail } = ctx;
const accountEmail = resolveOpenAICodexAuthIdentity({
access: accessToken,
email: profileEmail
}).email ?? profileEmail;
return accountEmail ? {
...snapshot,
accountEmail
} : snapshot;
}
return await fetchOpenAIAdminUsage({
apiKey: adminKey,
projectId: cleanProviderUsageCredential(ctx.env.OPENAI_PROJECT_ID),
timeoutMs: ctx.timeoutMs,
fetchFn: ctx.fetchFn
});
}
//#endregion
//#region extensions/openai/openai-chatgpt-provider.ts
const PROVIDER_ID$1 = "openai";
const OPENAI_MANIFEST_MODELS = buildManifestModelProviderConfig({
providerId: PROVIDER_ID$1,
catalog: modelCatalog.providers.openai
}).models;
const OPENAI_CODEX_GPT_56_THINKING_LEVEL_MAP = {
off: null,
xhigh: "xhigh",
max: "max"
};
const OPENAI_CODEX_GPT_56_NATIVE_CONTEXT_TOKENS = 372e3;
const OPENAI_CODEX_GPT_55_CODEX_CONTEXT_TOKENS = 4e5;
const OPENAI_CODEX_GPT_54_MAX_TOKENS = 128e3;
const OPENAI_CODEX_GPT_54_MINI_COST = {
input: .75,
output: 4.5,
cacheRead: .075,
cacheWrite: 0
};
const OPENAI_CODEX_FORWARD_COMPAT_PATCHES = /* @__PURE__ */ new Map([
[OPENAI_GPT_55_PRO_MODEL_ID, {
contextWindow: 1e6,
cost: {
input: 30,
output: 180,
cacheRead: 0,
cacheWrite: 0
}
}],
[OPENAI_GPT_54_MODEL_ID, {
contextWindow: 105e4,
cost: {
input: 2.5,
output: 15,
cacheRead: .25,
cacheWrite: 0
}
}],
[OPENAI_GPT_54_PRO_MODEL_ID, {
contextWindow: 105e4,
cost: {
input: 30,
output: 180,
cacheRead: 0,
cacheWrite: 0
}
}],
[OPENAI_GPT_54_MINI_MODEL_ID, {
contextWindow: 4e5,
cost: OPENAI_CODEX_GPT_54_MINI_COST
}],
[OPENAI_GPT_53_CODEX_SPARK_MODEL_ID, {
input: ["text"],
contextWindow: 128e3,
contextTokens: 128e3,
cost: OPENAI_CODEX_GPT_54_MINI_COST
}]
]);
const OPENAI_CODEX_GPT_54_TEMPLATE_MODEL_IDS = ["gpt-5.3-codex"];
/** Legacy codex rows first; fall back to catalog `gpt-5.4` when the API omits 5.3/5.2. */
const OPENAI_CODEX_GPT_54_CATALOG_SYNTH_TEMPLATE_MODEL_IDS = [...OPENAI_CODEX_GPT_54_TEMPLATE_MODEL_IDS, OPENAI_GPT_54_MODEL_ID];
const OPENAI_CODEX_GPT_55_PRO_TEMPLATE_MODEL_IDS = [
OPENAI_GPT_54_MODEL_ID,
OPENAI_GPT_54_PRO_MODEL_ID,
...OPENAI_CODEX_GPT_54_TEMPLATE_MODEL_IDS
];
const OPENAI_CODEX_IMAGE_CAPABLE_MODEL_IDS = [
...OPENAI_GPT_6_MODEL_IDS,
...OPENAI_GPT_56_VARIANT_MODEL_IDS,
OPENAI_GPT_55_MODEL_ID,
OPENAI_GPT_55_PRO_MODEL_ID,
OPENAI_GPT_54_MODEL_ID,
OPENAI_GPT_54_PRO_MODEL_ID,
OPENAI_GPT_54_MINI_MODEL_ID
];
function isOpenAIProvider(provider) {
return normalizeProviderId(provider ?? "") === PROVIDER_ID$1;
}
function normalizeCodexTransportFields(params) {
const useCodexTransport = !params.baseUrl || isOpenAIApiBaseUrl(params.baseUrl) || isOpenAICodexBaseUrl(params.baseUrl);
const api = useCodexTransport && (!params.api || params.api === "openai-responses" || params.api === "openai-completions") ? "openai-chatgpt-responses" : params.api ?? void 0;
return {
api,
baseUrl: api === "openai-chatgpt-responses" && useCodexTransport ? OPENAI_CODEX_RESPONSES_BASE_URL : params.baseUrl
};
}
function normalizeCodexTransport(model) {
const canonicalModelId = normalizeLowercaseStringOrEmpty(model.id) === "gpt-5.4-codex" ? OPENAI_GPT_54_MODEL_ID : model.id;
const canonicalName = normalizeLowercaseStringOrEmpty(model.name) === "gpt-5.4-codex" ? OPENAI_GPT_54_MODEL_ID : model.name;
const normalizedTransport = normalizeCodexTransportFields({
api: model.api,
baseUrl: model.baseUrl
});
const api = normalizedTransport.api ?? model.api;
const baseUrl = normalizedTransport.baseUrl ?? model.baseUrl;
if (api === model.api && baseUrl === model.baseUrl && canonicalModelId === model.id && canonicalName === model.name) return model;
return {
...model,
id: canonicalModelId,
name: canonicalName,
api,
baseUrl
};
}
function resolveCodexForwardCompatModel(ctx) {
const trimmedModelId = ctx.modelId.trim();
const lower = normalizeLowercaseStringOrEmpty(trimmedModelId);
const synthBaseUrl = ctx.providerConfig?.baseUrl ?? "https://chatgpt.com/backend-api/codex";
if (OPENAI_GPT_6_MODEL_IDS.some((modelId) => modelId === lower)) {
const catalogModel = OPENAI_MANIFEST_MODELS.find((model) => model.id === lower);
if (!catalogModel || catalogModel.contextWindow === void 0) return;
return {
...catalogModel,
contextWindow: catalogModel.contextWindow,
input: catalogModel.input.filter((item) => item === "text" || item === "image"),
...ctx.modelRegistry.find(PROVIDER_ID$1, trimmedModelId),
id: trimmedModelId,
provider: PROVIDER_ID$1,
api: "openai-chatgpt-responses",
baseUrl: synthBaseUrl
};
}
const isGpt56 = OPENAI_GPT_56_VARIANT_MODEL_IDS.some((modelId) => modelId === lower);
if (isGpt56 || lower === "gpt-5.5") {
const contextWindow = isGpt56 ? OPENAI_CODEX_GPT_56_NATIVE_CONTEXT_TOKENS : OPENAI_CODEX_GPT_55_CODEX_CONTEXT_TOKENS;
const registeredModel = withDefaultCodexContextMetadata({
model: ctx.modelRegistry.find(PROVIDER_ID$1, trimmedModelId),
baseUrl: synthBaseUrl,
contextWindow,
contextTokens: OPENAI_DEFAULT_RUNTIME_CONTEXT_TOKENS
});
const model = registeredModel ?? {
id: trimmedModelId,
name: trimmedModelId,
api: "openai-chatgpt-responses",
provider: PROVIDER_ID$1,
baseUrl: synthBaseUrl,
reasoning: true,
input: ["text", "image"],
cost: {
input: 0,
output: 0,
cacheRead: 0,
cacheWrite: 0
},
contextWindow,
contextTokens: 272e3,
maxTokens: OPENAI_CODEX_GPT_54_MAX_TOKENS
};
return isGpt56 ? {
...model,
thinkingLevelMap: registeredModel ? {
...OPENAI_CODEX_GPT_56_THINKING_LEVEL_MAP,
...registeredModel.thinkingLevelMap
} : OPENAI_CODEX_GPT_56_THINKING_LEVEL_MAP
} : model;
}
let templateIds;
let patch;
const knownPatch = OPENAI_CODEX_FORWARD_COMPAT_PATCHES.get(lower === "gpt-5.4-codex" ? OPENAI_GPT_54_MODEL_ID : lower);
if (knownPatch) {
templateIds = lower === "gpt-5.5-pro" ? OPENAI_CODEX_GPT_55_PRO_TEMPLATE_MODEL_IDS : OPENAI_CODEX_GPT_54_CATALOG_SYNTH_TEMPLATE_MODEL_IDS;
patch = {
contextTokens: OPENAI_DEFAULT_RUNTIME_CONTEXT_TOKENS,
maxTokens: OPENAI_CODEX_GPT_54_MAX_TOKENS,
...knownPatch,
...knownPatch.input ? { input: [...knownPatch.input] } : {}
};
} else if (ctx.agentRuntimeId === "codex" && ctx.authProfileId === void 0 && ctx.authProfileMode === void 0 && ctx.providerConfig?.auth === void 0) {
templateIds = OPENAI_GPT_56_VARIANT_MODEL_IDS;
patch = {
reasoning: true,
input: ["text", "image"],
thinkingLevelMap: OPENAI_CODEX_GPT_56_THINKING_LEVEL_MAP,
compat: {
supportsReasoningEffort: true,
supportedReasoningEfforts: [
"low",
"medium",
"high",
"xhigh",
"max"
]
}
};
} else return;
patch = {
...patch,
api: "openai-chatgpt-responses",
baseUrl: synthBaseUrl
};
const canonicalModelId = lower === "gpt-5.4-codex" ? OPENAI_GPT_54_MODEL_ID : trimmedModelId;
return buildFirstTemplateModel({
providerId: PROVIDER_ID$1,
modelId: canonicalModelId,
templateIds,
ctx,
patch
}) ?? {
id: canonicalModelId,
name: canonicalModelId,
api: "openai-chatgpt-responses",
provider: PROVIDER_ID$1,
baseUrl: synthBaseUrl,
reasoning: true,
input: patch?.input ?? ["text", "image"],
cost: {
input: 0,
output: 0,
cacheRead: 0,
cacheWrite: 0
},
contextWindow: patch?.contextWindow ?? DEFAULT_CONTEXT_TOKENS,
contextTokens: patch?.contextTokens,
maxTokens: patch?.maxTokens ?? DEFAULT_CONTEXT_TOKENS,
...patch?.thinkingLevelMap ? { thinkingLevelMap: patch.thinkingLevelMap } : {},
...patch?.compat ? { compat: patch.compat } : {}
};
}
function withDefaultCodexContextMetadata(params) {
if (!params.model) return;
const contextTokens = typeof params.model.contextTokens === "number" ? params.model.contextTokens : typeof params.model.contextWindow === "number" && params.model.contextWindow > 0 ? Math.min(params.contextTokens, params.model.contextWindow) : params.contextTokens;
const input = params.model.input?.includes("image") ? params.model.input : uniqueValues([...params.model.input ?? ["text"], "image"]);
return {
...params.model,
api: "openai-chatgpt-responses",
baseUrl: params.baseUrl,
input,
contextWindow: params.contextWindow,
contextTokens
};
}
function buildCodexCredentialExtra(identity) {
const extra = {
...identity.accountId ? { accountId: identity.accountId } : {},
...identity.chatgptPlanType ? { chatgptPlanType: identity.chatgptPlanType } : {}
};
return Object.keys(extra).length > 0 ? extra : void 0;
}
async function refreshOpenAICodexOAuthCredential(cred) {
try {
const { refreshOpenAICodexToken } = await import("./openai-chatgpt-provider.runtime-eoOkddCA.mjs");
const refreshed = await refreshOpenAICodexToken(cred.refresh);
const identity = resolveOpenAICodexAuthIdentity({
access: refreshed.access,
email: cred.email
});
return {
...cred,
...refreshed,
type: "oauth",
provider: PROVIDER_ID$1,
email: identity.email ?? cred.email,
displayName: cred.displayName,
...buildCodexCredentialExtra(identity)
};
} catch (error) {
const message = formatErrorMessage(error);
if (/extract\s+accountid\s+from\s+token/i.test(message) && typeof cred.access === "string" && cred.access.trim().length > 0) return cred;
throw error;
}
}
function buildOpenAICodexAuthResult(buildResult, creds) {
const identity = resolveOpenAICodexAuthIdentity({
access: creds.access,
email: readStringValue(creds.email)
});
return buildResult({
providerId: PROVIDER_ID$1,
defaultModel: OPENAI_CODEX_DEFAULT_MODEL,
configPatch: { agents: { defaults: { models: { [OPENAI_CODEX_DEFAULT_MODEL]: {} } } } },
access: creds.access,
refresh: creds.refresh,
expires: creds.expires,
email: identity.email,
profileName: identity.profileName,
credentialExtra: buildCodexCredentialExtra(identity)
});
}
async function runOpenAICodexOAuth(ctx) {
const [{ loginOpenAICodexOAuth }, { buildOauthProviderAuthResult }] = await Promise.all([import("./openai-chatgpt-oauth.runtime-CAab2pQ6.mjs"), import("openclaw/plugin-sdk/provider-auth-result")]);
const creds = await loginOpenAICodexOAuth({
prompter: ctx.prompter,
runtime: ctx.runtime,
oauth: ctx.oauth,
isRemote: ctx.isRemote,
openUrl: ctx.openUrl,
signal: ctx.signal,
assertCurrent: ctx.assertCurrent,
onManualCodeInput: ctx.onManualCodeInput,
localBrowserMessage: "Complete sign-in in browser…"
});
if (!creds) return { profiles: [] };
return buildOpenAICodexAuthResult(buildOauthProviderAuthResult, creds);
}
async function runOpenAICodexDeviceCode(ctx) {
const spin = ctx.prompter.progress("Starting device code flow…");
try {
const [{ loginOpenAICodexDeviceCode }, { buildOauthProviderAuthResult }] = await Promise.all([import("./openai-chatgpt-device-code-CCs2lw9A.mjs"), import("openclaw/plugin-sdk/provider-auth-result")]);
const creds = await loginOpenAICodexDeviceCode({
...ctx.signal ? { signal: ctx.signal } : {},
assertCurrent: ctx.assertCurrent,
onProgress: (message) => spin.update(message),
onVerification: async ({ verificationUrl, userCode, expiresInMs }) => {
const expiresInMinutes = Math.max(1, Math.round(expiresInMs / 6e4));
const deviceCodeMessage = [ctx.isRemote ? "Open this URL in your LOCAL browser and enter the code below." : "Open this URL in your browser and enter the code below.", `URL: <${verificationUrl}>`].join("\n");
if (ctx.isRemote) await ctx.openUrl(verificationUrl);
if (ctx.prompter.deviceCode) await ctx.prompter.deviceCode({
title: "OpenAI Codex device code",
code: userCode,
expiresInMinutes,
message: "Enter this one-time code on the sign-in page."
});
else await ctx.prompter.note([
deviceCodeMessage,
`Code: ${userCode}`,
`Code expires in ${expiresInMinutes} minutes. Never share it.`
].join("\n"), "OpenAI Codex device code");
if (ctx.isRemote) {
ctx.runtime.log(`\nOpen this URL in your LOCAL browser:\n\n${verificationUrl}\n`);
return;
}
try {
await ctx.openUrl(verificationUrl);
ctx.runtime.log(`Open: ${verificationUrl}`);
} catch {
ctx.runtime.log(`Open manually: ${verificationUrl}`);
}
}
});
spin.stop("OpenAI device code complete");
return buildOpenAICodexAuthResult(buildOauthProviderAuthResult, creds);
} catch (error) {
spin.stop("OpenAI device code failed");
ctx.runtime.error(formatErrorMessage(error));
await ctx.prompter.note("Trouble with device code login? See https://docs.openclaw.ai/start/faq", "OAuth help");
throw error;
}
}
function buildOpenAIChatGPTAuthMethodRuns() {
return {
oauth: runOpenAICodexOAuth,
"device-code": runOpenAICodexDeviceCode
};
}
function buildOpenAICodexProviderHooks() {
return {
resolveDynamicModel: resolveCodexForwardCompatModel,
preferRuntimeResolvedModel: (ctx) => {
if (!isOpenAIProvider(ctx.provider)) return false;
const id = ctx.modelId.trim().toLowerCase();
return OPENAI_CHATGPT_MODERN_MODEL_IDS.some((modelId) => modelId === id);
},
normalizeResolvedModel: (ctx) => {
if (!isOpenAIProvider(ctx.provider)) return;
let model = normalizeCodexTransport(ctx.model);
if (!(Array.isArray(model.input) && model.input.includes("image")) && [ctx.modelId, model.name].some((candidate) => typeof candidate === "string" && matchesExactOrPrefix(candidate, OPENAI_CODEX_IMAGE_CAPABLE_MODEL_IDS))) model = {
...model,
input: ["text", "image"]
};
return model === ctx.model ? void 0 : model;
},
normalizeTransport: ({ provider, api, baseUrl }) => {
if (!isOpenAIProvider(provider)) return;
const normalized = normalizeCodexTransportFields({
api,
baseUrl
});
if (normalized.api === api && normalized.baseUrl === baseUrl) return;
return normalized;
},
resolveUsageAuth: resolveOpenAIUsageAuth,
fetchUsageSnapshot: fetchOpenAIUsage,
refreshOAuth: refreshOpenAICodexOAuthCredential
};
}
//#endregion
//#region extensions/openai/openai-provider.ts
const PROVIDER_ID = "openai";
function classifyOpenAiFailoverCode(code) {
switch (code?.trim().toUpperCase()) {
case "SERVER_ERROR": return "server_error";
case "INSUFFICIENT_QUOTA": return "billing";
default: return;
}
}
const OPENAI_MODELS_ENDPOINT = "https://api.openai.com/v1/models";
const OPENAI_MODELS_CACHE_TTL_MS = 6e4;
const OPENAI_CODEX_MODELS_CACHE_TTL_MS = 6e4;
const OPENAI_CODEX_GPT_56_CONTEXT_WINDOW = 372e3;
const OPENAI_GPT_54_MAX_TOKENS = 128e3;
const OPENAI_CHAT_LATEST_COST = {
input: 5,
output: 30,
cacheRead: .5,
cacheWrite: 0
};
const OPENAI_CATALOG_AUGMENTATIONS = [
{
id: OPENAI_GPT_55_PRO_MODEL_ID,
templateIds: [OPENAI_GPT_54_PRO_MODEL_ID, OPENAI_GPT_54_MODEL_ID],
contextWindow: 105e4,
contextTokens: OPENAI_DEFAULT_RUNTIME_CONTEXT_TOKENS
},
{
id: OPENAI_GPT_54_MODEL_ID,
templateIds: [OPENAI_GPT_54_MODEL_ID, OPENAI_GPT_55_MODEL_ID],
contextWindow: 105e4
},
{
id: OPENAI_GPT_54_PRO_MODEL_ID,
templateIds: [OPENAI_GPT_54_PRO_MODEL_ID, OPENAI_GPT_55_PRO_MODEL_ID],
contextWindow: 105e4
},
{
id: OPENAI_GPT_54_MINI_MODEL_ID,
templateIds: [OPENAI_GPT_54_MINI_MODEL_ID, "gpt-5-mini"],
contextWindow: 4e5
},
{
id: OPENAI_GPT_54_NANO_MODEL_ID,
templateIds: [
OPENAI_GPT_54_NANO_MODEL_ID,
"gpt-5-nano",
"gpt-5-mini"
],
contextWindow: 4e5
}
];
const OPENAI_MANIFEST_PROVIDER = buildManifestModelProviderConfig({
providerId: PROVIDER_ID,
catalog: modelCatalog.providers.openai
});
function buildOpenAIManifestModelsForBaseUrl(baseUrl) {
return OPENAI_MANIFEST_PROVIDER.models.map((model) => model.api === "openai-chatgpt-responses" || isOpenAICodexBaseUrl(model.baseUrl) ? { ...model } : {
...model,
api: model.api ?? OPENAI_MANIFEST_PROVIDER.api ?? "openai-responses",
baseUrl
});
}
function buildOpenAIStaticPlatformProviderConfig(apiKey, baseUrl = resolveOpenAIDefaultBaseUrl()) {
return {
baseUrl,
api: "openai-responses",
...apiKey ? { apiKey } : {},
models: buildOpenAIManifestModelsForBaseUrl(baseUrl)
};
}
async function buildOpenAILiveProviderConfig(params) {
const baseUrl = normalizeOptionalString(params.baseUrl) ?? resolveOpenAIDefaultBaseUrl(params.env);
const fallback = buildOpenAIStaticPlatformProviderConfig(params.apiKey, baseUrl);
if (!isOpenAIApiBaseUrl(baseUrl)) return { provider: fallback };
const [{ buildLiveModelProviderConfig, LiveModelCatalogHttpError }, { isNonSecretApiKeyMarker }] = await Promise.all([import("openclaw/plugin-sdk/provider-catalog-live-runtime"), import("openclaw/plugin-sdk/provider-auth")]);
const rejectionScope = params.apiKey && !params.discoveryApiKey && isNonSecretApiKeyMarker(params.apiKey) ? "catalog" : void 0;
const { models, ...providerConfig } = fallback;
const catalogModels = [...models, {
id: OPENAI_CHAT_LATEST_MODEL_ID,
name: "Chat Latest",
reasoning: false,
cost: OPENAI_CHAT_LATEST_COST,
contextWindow: 4e5,
api: "openai-responses",
baseUrl,
input: ["text", "image"],
maxTokens: OPENAI_GPT_54_MAX_TOKENS
}];
try {
const provider = await buildLiveModelProviderConfig({
discoveryMode: "strict",
providerId: PROVIDER_ID,
endpoint: OPENAI_MODELS_ENDPOINT,
providerConfig,
models: catalogModels,
apiKey: params.apiKey,
discoveryApiKey: params.discoveryApiKey,
fetchGuard: params.fetchGuard,
signal: params.signal,
ttlMs: OPENAI_MODELS_CACHE_TTL_MS,
auditContext: "openai-model-discovery"
});
return {
provider: {
...provider,
models: projectOpenAIAccountModels({
listedModels: provider.models,
catalogModels,
baseUrl
})
},
outcome: {
provider: PROVIDER_ID,
status: "ready"
}
};
} catch (error) {
if (error instanceof LiveModelCatalogHttpError && (error.status === 401 || error.status === 403)) return {
provider: {
...fallback,
models: []
},
outcome: {
provider: PROVIDER_ID,
...rejectionScope ? { rejectionScope } : {},
status: "auth-rejected"
}
};
return {
provider: fallback,
outcome: {
provider: PROVIDER_ID,
status: "unavailable"
}
};
}
}
function normalizeOpenAICodexCatalogModel(model) {
const modelId = normalizeLowercaseStringOrEmpty(model.id);
if (modelId === "gpt-5.6-sol" || modelId === "gpt-5.6-terra" || modelId === "gpt-5.6-luna") {
const supportedReasoningEfforts = resolveOpenAICodexReasoningEfforts(modelId, model.compat?.supportedReasoningEfforts?.filter((effort) => effort !== "none"));
return {
...model,
contextWindow: OPENAI_CODEX_GPT_56_CONTEXT_WINDOW,
contextTokens: OPENAI_DEFAULT_RUNTIME_CONTEXT_TOKENS,
thinkingLevelMap: {
...model.thinkingLevelMap,
off: null
},
...model.compat ? { compat: {
...model.compat,
...supportedReasoningEfforts ? { supportedReasoningEfforts } : {}
} } : {}
};
}
return model;
}
function resolveCodexModelFallback(modelId) {
const fallbackModel = OPENAI_MANIFEST_PROVIDER.models.find((candidate) => normalizeLowercaseStringOrEmpty(candidate.id) === normalizeLowercaseStringOrEmpty(modelId));
return fallbackModel ? normalizeOpenAICodexCatalogModel(fallbackModel) : void 0;
}
function buildOpenAICodexModelFromLiveRow(row, readers) {
const { readLiveModelCatalogStringField, readLiveModelCatalogPositiveSafeIntegerField } = readers;
if (!shouldIncludeCodexModelRow(row, readers)) return;
const modelId = readLiveModelCatalogStringField(row, "slug") ?? readLiveModelCatalogStringField(row, "id");
if (!modelId) return;
if (isOpenAIPlatformOnlyRouteModelId(modelId)) return;
const normalizedModelId = normalizeLowercaseStringOrEmpty(modelId);
const fallback = resolveCodexModelFallback(modelId);
const reasoningLevels = readCodexReasoningLevels(row);
const observedContextTokens = readLiveModelCatalogPositiveSafeIntegerField(row, ["context_window", "contextWindow"]);
const contextTokens = matchesExactOrPrefix(normalizedModelId, ["gpt-5.6"]) ? Math.min(observedContextTokens ?? fallback?.contextTokens ?? 272e3, OPENAI_DEFAULT_RUNTIME_CONTEXT_TOKENS) : observedContextTokens;
const contextWindow = readLiveModelCatalogPositiveSafeIntegerField(row, ["max_context_window", "maxContextWindow"]) ?? fallback?.contextWindow ?? observedContextTokens ?? DEFAULT_CONTEXT_TOKENS;
const maxTokens = readLiveModelCatalogPositiveSafeIntegerField(row, [
"max_output_tokens",
"maxOutputTokens",
"max_completion_tokens",
"maxCompletionTokens"
]) ?? fallback?.maxTokens ?? OPENAI_GPT_54_MAX_TOKENS;
const compat = reasoningLevels !== void 0 ? {
...fallback?.compat,
supportsReasoningEffort: true,
supportedReasoningEfforts: [...reasoningLevels]
} : fallback?.compat;
const thinkingLevelMap = {
...reasoningLevels === void 0 ? fallback?.thinkingLevelMap : {},
...fallback?.thinkingLevelMap?.off === null || normalizedModelId.startsWith("gpt-5.6") ? { off: null } : {},
...reasoningLevels?.includes("xhigh") ? { xhigh: "xhigh" } : {},
...reasoningLevels?.includes("max") ? { max: "max" } : {}
};
return normalizeOpenAICodexCatalogModel({
id: modelId,
name: readLiveModelCatalogStringField(row, "display_name") ?? fallback?.name ?? modelId,
api: "openai-chatgpt-responses",
baseUrl: OPENAI_CODEX_RESPONSES_BASE_URL,
reasoning: (reasoningLevels?.length ?? 0) > 0 || fallback?.reasoning || false,
input: resolveCodexModelInput(row, fallback),
cost: fallback?.cost ?? OPENAI_UNKNOWN_MODEL_COST,
contextWindow,
maxTokens,
...contextTokens ?? fallback?.contextTokens ? { contextTokens: contextTokens ?? fallback?.contextTokens } : {},
...fallback?.mediaInput ? { mediaInput: fallback.mediaInput } : {},
...compat ? { compat } : {},
...Object.keys(thinkingLevelMap).length > 0 ? { thinkingLevelMap } : {}
});
}
function buildOpenAICodexStaticProviderConfig() {
return {
baseUrl: OPENAI_CODEX_RESPONSES_BASE_URL,
api: "openai-chatgpt-responses",
auth: "oauth",
models: OPENAI_MANIFEST_PROVIDER.models.flatMap((model) => {
const modelId = normalizeLowercaseStringOrEmpty(model.id);
if (isOpenAIPlatformOnlyRouteModelId(modelId)) return [];
if (OPENAI_GPT_6_MODEL_IDS.some((id) => id === modelId) || OPENAI_DAYBREAK_MODEL_IDS.some((id) => id === modelId) || modelId.startsWith("gpt-5.6") && modelId !== "gpt-5.6-sol") return [];
return [normalizeOpenAICodexCatalogModel(model)];
})
};
}
async function buildOpenAICodexLiveProviderConfig(params) {
const catalogRuntime = await import("openclaw/plugin-sdk/provider-catalog-live-runtime");
const { getCachedLiveProviderModelRows, LiveModelCatalogHttpError } = catalogRuntime;
try {
const rows = await getCachedLiveProviderModelRows({
providerId: PROVIDER_ID,
endpoint: OPENAI_CODEX_MODELS_ENDPOINT,
discoveryApiKey: params.discoveryApiKey,
fetchGuard: params.fetchGuard,
signal: params.signal,
ttlMs: OPENAI_CODEX_MODELS_CACHE_TTL_MS,
auditContext: "openai-model-discovery",
readRows: readCodexModelRows,
buildRequestHeaders: ({ discoveryApiKey }) => ({
Accept: "application/json",
...discoveryApiKey ? { Authorization: `Bearer ${discoveryApiKey}` } : {},
...params.accountId ? { "ChatGPT-Account-ID": params.accountId } : {}
}),
cacheKeyParts: [
PROVIDER_ID,
"codex-model-rows",
OPENAI_CODEX_MODELS_ENDPOINT,
params.discoveryApiKey,
params.accountId ?? ""
]
});
const models = rows.map((row) => buildOpenAICodexModelFromLiveRow(row, catalogRuntime)).filter((model) => Boolean(model));
return {
provider: {
baseUrl: OPENAI_CODEX_RESPONSES_BASE_URL,
api: "openai-chatgpt-responses",
auth: "oauth",
models
},
outcome: buildOpenAICodexReadyOutcome(rows)
};
} catch (error) {
if (error instanceof LiveModelCatalogHttpError && (error.status === 401 || error.status === 403)) return {
provider: {
...buildOpenAICodexStaticProviderConfig(),
models: []
},
outcome: {
provider: PROVIDER_ID,
status: "auth-rejected"
}
};
}
return {
provider: buildOpenAICodexStaticProviderConfig(),
outcome: {
provider: PROVIDER_ID,
status: "unavailable"
}
};
}
function isCodexCatalogAuthMode(mode) {
return mode === "oauth" || mode === "token";
}
function resolveOpenAICatalogBaseUrl(ctx) {
const configuredProvider = Object.entries(ctx.config?.models?.providers ?? {}).find(([providerId]) => normalizeProviderId(providerId) === PROVIDER_ID)?.[1];
return normalizeOptionalString(configuredProvider?.baseUrl) ?? resolveOpenAIDefaultBaseUrl(ctx.env ?? process.env);
}
function shouldUseOpenAIResponsesTransport(params) {
if (params.api !== "openai-completions") return false;
const isOwnerProvider = normalizeProviderId(params.provider) === PROVIDER_ID;
const isPlatformEndpoint = typeof params.baseUrl === "string" && classifyOpenAIBaseUrl(params.baseUrl) === "platform";
if (isOwnerProvider) {
if (resolveAuthoredOpenAICompletionsRoute(params)) return false;
return !params.baseUrl || isPlatformEndpoint;
}
return isPlatformEndpoint;
}
/** Authored Completions is a current transport contract; only catalog defaults are upgraded. */
function resolveAuthoredOpenAICompletionsRoute(params) {
const providerConfig = resolveAuthoredOpenAIProviderConfig(params);
if (!providerConfig) return;
const modelId = normalizeOpenAIModelRouteId(params.modelId);
let modelConfig;
for (const model of providerConfig.models ?? []) {
if (normalizeOpenAIModelRouteId(model.id) !== modelId) continue;
modelConfig = modelConfig ? {
...model,
...modelConfig
} : model;
}
if ((normalizeOptionalString(modelConfig?.api) ?? normalizeOptionalString(providerConfig.api)) !== "openai-completions") return;
return {
api: "openai-completions",
baseUrl: normalizeOptionalString(modelConfig?.baseUrl) ?? normalizeOptionalString(providerConfig.baseUrl) ?? resolveOpenAIDefaultBaseUrl(process.env)
};
}
function shouldUseCodexResponsesHooks(params) {
if (params?.api === "openai-chatgpt-responses") return true;
return typeof params?.baseUrl === "string" && isOpenAICodexBaseUrl(params.baseUrl);
}
function shouldResolveDynamicModelThroughCodex(ctx) {
if (shouldUseCodexResponsesHooks(ctx.providerConfig)) return true;
if (ctx.providerConfig?.api === "openai-responses" || ctx.providerConfig?.api === "openai-completions" || ctx.providerConfig?.baseUrl && !isOpenAICodexBaseUrl(ctx.providerConfig.baseUrl)) return false;
if (isOpenAIPlatformOnlyRouteModelId(ctx.modelId)) return false;
if (isOpenAISubscriptionOnlyRouteModelId(ctx.modelId)) return true;
return ctx.agentRuntimeId === "codex";
}
const OPENAI_GPT_FORWARD_COMPAT_CASES = [
{
match: OPENAI_DAYBREAK_MODEL_IDS,
templateIds: [OPENAI_GPT_56_SOL_MODEL_ID]
},
{
match: OPENAI_GPT_6_MODEL_IDS,
templateIds: [OPENAI_GPT_56_SOL_MODEL_ID, OPENAI_GPT_55_MODEL_ID]
},
{
match: [OPENAI_CHAT_LATEST_MODEL_ID],
templateIds: [OPENAI_GPT_55_MODEL_ID, OPENAI_GPT_54_MODEL_ID],
patch: {
reasoning: false,
cost: OPENAI_CHAT_LATEST_COST,
contextWindow: 4e5
}
},
{
match: [
OPENAI_GPT_56_MODEL_ID,
OPENAI_GPT_56_SOL_MODEL_ID,
OPENAI_GPT_56_TERRA_MODEL_ID,
OPENAI_GPT_56_LUNA_MODEL_ID
],
templateIds: [OPENAI_GPT_55_MODEL_ID]
},
{
match: [OPENAI_GPT_55_MODEL_ID],
templateIds: [OPENAI_GPT_55_MODEL_ID, OPENAI_GPT_54_MODEL_ID]
},
...OPENAI_CATALOG_AUGMENTATIONS.map(({ id, templateIds }) => ({
match: [id],
templateIds
}))
];
function resolveOpenAIGptForwardCompatModel(ctx) {
const trimmedModelId = ctx.modelId.trim();
const modelId = normalizeLowercaseStringOrEmpty(trimmedModelId);
if (OPENAI_PRE_GPT5_MODEL_ID_PATTERN.test(modelId)) return {
...buildOpenAIUncataloguedModel(trimmedModelId, resolveOpenAIDefaultBaseUrl()),
provider: PROVIDER_ID
};
const exactModel = ctx.modelRegistry.find(PROVIDER_ID, trimmedModelId);
if (OPENAI_DAYBREAK_MODEL_IDS.some((id) => id === modelId) || OPENAI_GPT_6_MODEL_IDS.some((id) => id === modelId) || modelId === "gpt-5.6-sol" || modelId === "gpt-5.6-terra" || modelId === "gpt-5.6-luna") {
if (exactModel) return exactModel;
}
const catalogModel = OPENAI_MANIFEST_PROVIDER.models.find(({ id }) => id === (modelId === "gpt-5.6" ? OPENAI_GPT_56_SOL_MODEL_ID : modelId));
return buildFamilyForwardCompatModel({
providerId: PROVIDER_ID,
ctx,
cases: OPENAI_GPT_FORWARD_COMPAT_CASES,
patch: {
reasoning: true,
maxTokens: OPENAI_GPT_54_MAX_TOKENS,
...catalogModel,
id: trimmedModelId,
name: trimmedModelId,
api: "openai-responses",
provider: PROVIDER_ID,
baseUrl: resolveOpenAIDefaultBaseUrl(),
input: catalogModel ? catalogModel.input.filter((item) => item === "text" || item === "image") : ["text", "image"],
compat: exactModel ? exactModel.compat : catalogModel?.compat
},
synthesize: true
});
}
function buildOpenAIProvider() {
const providerDefinition = createOpenAIProvider();
const apiKeyDefinition = providerDefinition.auth.find((method) => method.id === "api-key");
if (!apiKeyDefinition) throw new Error("OpenAI provider contract is missing API-key auth");
const chatGPTAuthRuns = buildOpenAIChatGPTAuthMethodRuns();
const apiKeyRuntime = createProviderApiKeyAuthMethod({
providerId: PROVIDER_ID,
methodId: apiKeyDefinition.id,
label: apiKeyDefinition.label,
hint: apiKeyDefinition.hint,
optionKey: "openaiApiKey",
flagName: "--openai-api-key",
envVar: "OPENAI_API_KEY",
promptMessage: "Enter OpenAI API key",
profileId: "openai:api-key",
defaultModel: OPENAI_DEFAULT_MODEL,
preserveExistingPrimary: true,
expectedProviders: [PROVIDER_ID],
applyConfig: (cfg) => applyOpenAIConfig(cfg),
wizard: apiKeyDefinition.wizard
});
for (const method of providerDefinition.auth) {
if (method.id === "siwc") {
method.starterModel = OPENAI_DEFAULT_MODEL;
method.run = async (ctx) => (await import("./token-sharing-oauth.runtime-B9lUDST7.mjs")).loginTokenSharing(ctx);
continue;
}
if (method.id === "oauth" || method.id === "device-code") {
method.starterModel = OPENAI_CODEX_DEFAULT_MODEL;
method.run = chatGPTAuthRuns[method.id];
continue;
}
if (method.id !== "api-key") throw new Error(`OpenAI provider contract has unknown auth method: ${method.id}`);
method.starterModel = apiKeyRuntime.starterModel;
method.run = apiKeyRuntime.run;
method.runNonInteractive = apiKeyRuntime.runNonInteractive;
method.validateNonInteractive = apiKeyRuntime.validateNonInteractive;
}
const codexHooks = buildOpenAICodexProviderHooks();
const nativeResponsesHooks = buildOpenAIResponsesProviderHooks();
const responsesHooks = buildOpenAIResponsesProviderHooks({ transport: "sse" });
return {
...providerDefinition,
catalog: {
order: "simple",
run: async (ctx) => {
if (ctx.providerIds && !ctx.providerIds.includes(PROVIDER_ID)) return null;
const auth = ctx.resolveProviderAuth(PROVIDER_ID);
if (isSIWCAuthFlow(auth.authFlow)) {
const { buildTokenSharingCatalog } = await import("./token-sharing-catalog-D_OMt41h.mjs");
return await buildTokenSharingCatalog({
auth,
models: buildOpenAIStaticPlatformProviderConfig(void 0, TOKEN_SHARING_RESOURCE).models,
signal: ctx.signal
});
}
if (auth.preparationFailed) return null;
const [{ resolveApiKeyForProvider, resolveProviderAuthProfileMetadata }, { isNonSecretApiKeyMarker }] = await Promise.all([import("openclaw/plugin-sdk/provider-auth-runtime"), import("openclaw/plugin-sdk/provider-auth")]);
let runtimeAuth;
if (auth.profileId || auth.mode === "none") try {
runtimeAuth = await resolveApiKeyForProvider({
provider: PROVIDER_ID,
cfg: ctx.config,
...ctx.agentDir ? { agentDir: ctx.agentDir } : {},
...ctx.workspaceDir ? { workspaceDir: ctx.workspaceDir } : {},
...auth.profileId ? {
profileId: auth.profileId,
lockedProfile: true
} : {}
});
} catch {
runtimeAuth = void 0;
}
if (runtimeAuth && isCodexCatalogAuthMode(runtimeAuth.mode) && runtimeAuth.apiKey) {
const metadata = resolveProviderAuthProfileMetadata({
provider: PROVIDER_ID,
cfg: ctx.config,
...ctx.agentDir ? { agentDir: ctx.agentDir } : {},
...runtimeAuth.profileId ?? auth.profileId ? { profileId: runtimeAuth.profileId ?? auth.profileId } : {}
});
return projectOpenAICatalog(await buildOpenAICodexLiveProviderConfig({
discoveryApiKey: runtimeAuth.apiKey,
accountId: metadata.accountId
}), runtimeAuth.profileId ?? auth.profileId);
}
if (!auth.profileId && isCodexCatalogAuthMode(auth.mode) && auth.apiKey) {
const discoveryApiKey = auth.discoveryApiKey ?? (isNonSecretApiKeyMarker(auth.apiKey) ? void 0 : auth.apiKey);
if (!discoveryApiKey) return {
providers: { [PROVIDER_ID]: buildOpenAICodexStaticProviderConfig() },
outcomes: [{
provider: PROVIDER_ID,
status: "unavailable"
}]
};
return projectOpenAICatalog(await buildOpenAICodexLiveProviderConfig({ discoveryApiKey }));
}
if (auth.profileId && isCodexCatalogAuthMode(auth.mode)) return {
providers: { [PROVIDER_ID]: buildOpenAICodexStaticProviderConfig() },
outcomes: [{
provider: PROVIDER_ID,
profileId: auth.profileId,
status: "unavailable"
}]
};
if (auth.mode === "api_key" && auth.apiKey) {
const discoveryApiKey = auth.discoveryApiKey ?? (runtimeAuth?.mode === "api-key" ? runtimeAuth.apiKey : void 0);
if (!discoveryApiKey && isNonSecretApiKeyMarker(auth.apiKey)) return {
providers: { [PROVIDER_ID]: buildOpenAIStaticPlatformProviderConfig(auth.apiKey, resolveOpenAICatalogBaseUrl(ctx)) },
outcomes: [{
provider: PROVIDER_ID,
...auth.profileId ? { profileId: auth.profileId } : {},
rejectionScope: "catalog",
status: "unavailable"
}]
};
return projectOpenAICatalog(await buildOpenAILiveProviderConfig({
apiKey: auth.apiKey,
baseUrl: resolveOpenAICatalogBaseUrl(ctx),
discoveryApiKey
}), auth.profileId);
}
return null;
}
},
staticCatalog: {
order: "simple",
run: async () => ({ providers: { [PROVIDER_ID]: OPENAI_MANIFEST_PROVIDER } })
},
resolveDynamicModel: (ctx) => shouldResolveDynamicModelThroughCodex(ctx) ? codexHooks.resolveDynamicModel?.(ctx) : resolveOpenAIGptForwardCompatModel(ctx),
preferRuntimeResolvedModel: (ctx) => codexHooks.preferRuntimeResolvedModel?.(ctx) ?? false,
normalizeResolvedModel: (ctx) => {
if (normalizeProviderId(ctx.provider ?? "") !== PROVIDER_ID) return;
const authoredCompletionsRoute = resolveAuthoredOpenAICompletionsRoute(ctx);
if (authoredCompletionsRoute) return {
...ctx.model,
...authoredCompletionsRoute
};
if (shouldUseCodexResponsesHooks(ctx.model)) return codexHooks.normalizeResolvedModel?.(ctx);
return shouldUseOpenAIResponsesTransport({
...ctx.model,
modelId: ctx.modelId,
config: ctx.config
}) ? {
...ctx.model,
api: "openai-responses"
} : ctx.model;
},
normalizeTransport: (ctx) => {
const authoredCompletionsRoute = resolveAuthoredOpenAICompletionsRoute(ctx);
if (authoredCompletionsRoute) return ctx.api === authoredCompletionsRoute.api && ctx.baseUrl === authoredCompletionsRoute.baseUrl ? void 0 : authoredCompletionsRoute;
if (shouldUseCodexResponsesHooks(ctx)) return codexHooks.normalizeTransport?.(ctx);
return shouldUseOpenAIResponsesTransport(ctx) ? {
api: "openai-responses",
baseUrl: ctx.baseUrl
} : void 0;
},
...responsesHooks,
prepareExtraParams: (ctx) => {
if (ctx.auth?.mode === "oauth" && ctx.auth.authFlow === "chatgpt-token-sharing") return {
...ctx.extraParams,
transport: "sse",
store: false,
responsesServerCompaction: false
};
const providerConfig = ctx.config?.models?.providers?.[PROVIDER_ID];
return (shouldUseCodexResponsesHooks(ctx.model) || normalizeProviderId(ctx.provider) === PROVIDER_ID && (!providerConfig?.baseUrl || isOpenAIApiBaseUrl(providerConfig.baseUrl)) && isCodexCatalogAuthMode(providerConfig?.auth ?? "") ? nativeResponsesHooks : responsesHooks).prepareExtraParams?.(ctx);
},
resolveUsageAuth: codexHooks.resolveUsageAuth,
fetchUsageSnapshot: codexHooks.fetchUsageSnapshot,
refreshOAuth: async (credential) => isSIWCAuthFlow(credential.authFlow) ? (await import("./token-sharing-oauth.runtime-B9lUDST7.mjs")).refreshTokenSharingCredential(credential) : codexHooks.refreshOAuth(credential),
buildUnknownModelHint: ({ modelId }) => buildOpenAIUnknownModelHint(modelId),
buildMissingAuthMessage: (ctx) => {
if (normalizeProviderId(ctx.provider) !== PROVIDER_ID) return;
if (ctx.listProfileIds(PROVIDER_ID).length === 0) return;
return `No API key found for provider "openai". You are authenticated with OpenAI ChatGPT/Codex OAuth. Use ${OPENAI_CODEX_DEFAULT_MODEL} with the ChatGPT/Codex OAuth profile, or set OPENAI_API_KEY for direct OpenAI API access.`;
},
matchesContextOverflowError: ({ errorMessage }) => /content_filter.*(?:prompt|input).*(?:too long|exceed)/i.test(errorMessage),
classifyFailoverReason: ({ code }) => classifyOpenAiFailoverCode(code),
resolveReasoningOutputMode: () => "native",
resolveThinkingProfile: ({ provider, modelId, agentRuntime, api, compat, thinkingLevelMap }) => normalizeProviderId(provider) === PROVIDER_ID ? resolveUnifiedOpenAIThinkingProfile(modelId, agentRuntime, compat, api, thinkingLevelMap) : null,
isModernModelRef: ({ modelId }) => matchesExactOrPrefix(modelId, OPENAI_PROVIDER_MODERN_MODEL_IDS),
augmentModelCatalog: (ctx) => {
return OPENAI_CATALOG_AUGMENTATIONS.flatMap(({ templateIds, ...model }) => {
const template = findCatalogTemplate({
entries: ctx.entries,
providerId: PROVIDER_ID,
templateIds
});
const input = ["text", "image"];
return template ? [{
...template,
...model,
name: model.id,
reasoning: true,
input
}] : [];
});
}
};
}
//#endregion
export { buildOpenAIProvider, OPENAI_DEFAULT_MODEL as t };