openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
1,880 lines • 79.2 kB
JavaScript
import { s as normalizeOptionalLowercaseString } from "./string-coerce-mnp54Vah.js";
import { i as asOptionalRecord } from "./record-coerce-DHZ4bFlT.js";
import { i as normalizeProviderId } from "./provider-id-Dq06Bcx6.js";
import { u as normalizeAgentId } from "./session-key-B_NoIfpX.js";
import { c as AGENT_MODEL_CONFIG_KEYS } from "./channel-env-var-names-B6Cczraa.js";
import { a as normalizeOptionalAgentRuntimeId } from "./agent-runtime-id-DiL2DId7.js";
import { t as resolveModelRuntimePolicy } from "./model-runtime-policy-CTIBeOo2.js";
import { o as openAIProviderUsesCodexRuntimeByDefault } from "./openai-routing-DXJmS9CT.js";
import { t as splitTrailingAuthProfile } from "./model-ref-profile-BIKs-96s.js";
import { n as DEFAULT_MODEL, r as DEFAULT_PROVIDER } from "./defaults-mDjiWzE5.js";
import { t as loadSessionStore } from "./store-load-C4uq6Lrq.js";
import { d as updateSessionStore } from "./store-Qsgtu-0y.js";
import { r as resolveAllAgentSessionStoreTargetsSync } from "./targets-BrMDn2yj.js";
import { r as normalizeConfiguredProviderCatalogModelId } from "./model-ref-shared-CqcindZs.js";
import { t as resolveConfiguredProviderFallback } from "./configured-provider-fallback-Crd282ov.js";
import { n as detectWindowsSpawnCommandInlineArgs } from "./windows-spawn-C3eNecff.js";
import fs from "node:fs";
//#region src/commands/doctor/shared/codex-route-warnings.ts
const COMPACTION_OVERRIDE_KEYS = ["model", "provider"];
const AGENT_MEDIA_MODEL_CONFIG_KEYS = ["imageGenerationModel", "videoGenerationModel"];
const LOSSLESS_CONTEXT_ENGINE_ID = "lossless-claw";
function normalizeRuntimeString(value) {
return normalizeOptionalAgentRuntimeId(value);
}
function asAgentRuntimePolicyConfig(value) {
const record = asOptionalRecord(value);
return record ? { id: typeof record.id === "string" ? record.id : void 0 } : void 0;
}
function readLegacyDefaultsRuntime(defaults) {
return asAgentRuntimePolicyConfig(asOptionalRecord(defaults)?.agentRuntime);
}
function isOpenAICodexModelRef(model) {
return normalizeOptionalLowercaseString(model)?.startsWith("openai-codex/") === true;
}
function isOpenAICodexAuthProfileRef(profile) {
return normalizeOptionalLowercaseString(profile)?.startsWith("openai-codex:") === true;
}
function isProviderlessModelRef(model) {
const normalized = normalizeOptionalLowercaseString(model);
return Boolean(normalized && !normalized.includes("/"));
}
function toCanonicalOpenAIModelRef(model) {
if (!isOpenAICodexModelRef(model)) return;
const modelId = model.slice(13).trim();
return modelId ? `openai/${modelId}` : void 0;
}
function toOpenAIModelId(model) {
if (!isOpenAICodexModelRef(model)) return;
return model.slice(13).trim() || void 0;
}
function resolveRuntime(params) {
return normalizeRuntimeString(params.agentRuntime?.id) ?? normalizeRuntimeString(params.defaultsRuntime?.id);
}
function recordCodexModelHit(params) {
const canonicalModel = toCanonicalOpenAIModelRef(params.model);
if (!canonicalModel) return;
params.hits.push({
path: params.path,
model: params.model,
canonicalModel,
...params.runtime ? { runtime: params.runtime } : {}
});
return canonicalModel;
}
function collectStringModelSlot(params) {
if (typeof params.value !== "string") return false;
const model = params.value.trim();
if (!model || !isOpenAICodexModelRef(model)) return false;
return Boolean(recordCodexModelHit({
hits: params.hits,
path: params.path,
model,
runtime: params.runtime
}));
}
function collectModelConfigSlot(params) {
if (typeof params.value === "string") return collectStringModelSlot({
hits: params.hits,
path: params.path,
value: params.value,
runtime: params.runtime
});
const record = asOptionalRecord(params.value);
if (!record) return false;
let rewrotePrimary = false;
if (typeof record.primary === "string") rewrotePrimary = collectStringModelSlot({
hits: params.hits,
path: `${params.path}.primary`,
value: record.primary,
runtime: params.runtime
});
if (Array.isArray(record.fallbacks)) for (const [index, entry] of record.fallbacks.entries()) collectStringModelSlot({
hits: params.hits,
path: `${params.path}.fallbacks.${index}`,
value: entry
});
return rewrotePrimary;
}
function readModelConfigPrimaryRef(value) {
if (typeof value === "string") return value.trim() || void 0;
const record = asOptionalRecord(value);
if (typeof record?.primary === "string") return record.primary.trim() || void 0;
}
function readAgentPrimaryModelRef(agent, fallback) {
const record = asOptionalRecord(agent);
if (!record) return fallback;
return readModelConfigPrimaryRef(record.model) ?? fallback;
}
function concreteRuntimeId(runtime) {
return runtime && runtime !== "auto" && runtime !== "default" ? runtime : void 0;
}
function modelRefUsesCodexRuntime(params) {
const effectiveModelRef = params.modelRef?.trim() || `openai/gpt-5.5`;
if (isOpenAICodexModelRef(effectiveModelRef)) return true;
return canonicalOpenAIModelUsesCodexRuntime({
cfg: params.cfg,
modelRef: resolveRuntimeModelRef({
cfg: params.cfg,
modelRef: effectiveModelRef,
agentId: params.agentId
}),
agentId: params.agentId
});
}
function resolveRuntimeModelRef(params) {
const effectiveModelRef = normalizeProviderModelRefAuthProfile(params.modelRef) ?? `openai/gpt-5.5`;
const legacyCodexModel = toCanonicalOpenAIModelRef(effectiveModelRef);
if (legacyCodexModel) return legacyCodexModel;
return resolveKnownCompatModelAliasRef(effectiveModelRef) ?? resolveConfiguredModelAliasRef({
cfg: params.cfg,
modelRef: effectiveModelRef,
agentId: params.agentId
}) ?? resolveConfiguredBareModelRef({
cfg: params.cfg,
modelRef: effectiveModelRef,
agentId: params.agentId
}) ?? normalizeDefaultProviderModelRef(effectiveModelRef);
}
function normalizeProviderModelRefAuthProfile(modelRef) {
const trimmed = modelRef.trim();
if (!trimmed) return;
return splitTrailingAuthProfile(trimmed).model || trimmed;
}
function resolveKnownCompatModelAliasRef(modelRef) {
const normalized = normalizeOptionalLowercaseString(modelRef);
if (!normalized?.startsWith("openrouter:")) return;
const modelId = normalized.slice(11).trim();
return modelId ? `openrouter/openrouter/${modelId}` : void 0;
}
function resolveConfiguredModelAliasRef(params) {
const aliasKey = normalizeOptionalLowercaseString(params.modelRef);
if (!aliasKey) return;
const defaultProvider = resolveDefaultProviderForAliasContext({
cfg: params.cfg,
agentId: params.agentId
});
return resolveAliasFromModelsMap(asOptionalRecord(params.cfg.agents?.defaults?.models), aliasKey, defaultProvider);
}
function resolveDefaultProviderForAliasContext(params) {
const primaryModelRef = readModelConfigPrimaryRef(findAgentById(params.cfg, params.agentId)?.model) ?? readModelConfigPrimaryRef(params.cfg.agents?.defaults?.model);
if (primaryModelRef) {
const effectivePrimaryModelRef = normalizeProviderModelRefAuthProfile(primaryModelRef) ?? primaryModelRef;
const legacyCodexModel = toCanonicalOpenAIModelRef(effectivePrimaryModelRef);
const compatModelRef = resolveKnownCompatModelAliasRef(effectivePrimaryModelRef);
return normalizeProviderId((parseModelRef(resolveAliasFromModelsMap(asOptionalRecord(params.cfg.agents?.defaults?.models), normalizeOptionalLowercaseString(effectivePrimaryModelRef) ?? "", "openai") ?? compatModelRef ?? legacyCodexModel ?? effectivePrimaryModelRef) ?? parseModelRef(resolveConfiguredBareModelRef({
cfg: params.cfg,
modelRef: effectivePrimaryModelRef,
agentId: params.agentId
}) ?? ""))?.provider ?? "openai") || "openai";
}
return normalizeProviderId(parseModelRef(resolveImplicitDefaultAgentModelRef(params.cfg))?.provider ?? "openai") || "openai";
}
function findAgentById(cfg, agentId) {
if (!agentId) return;
const normalizedAgentId = normalizeAgentId(agentId);
return (Array.isArray(cfg.agents?.list) ? cfg.agents.list : []).map((agent) => asOptionalRecord(agent)).find((agent) => normalizeAgentId(typeof agent?.id === "string" ? agent.id : void 0) === normalizedAgentId);
}
function resolveAliasFromModelsMap(models, aliasKey, defaultProvider) {
for (const [modelRef, entry] of Object.entries(models ?? {})) {
if (normalizeOptionalLowercaseString(asOptionalRecord(entry)?.alias) !== aliasKey) continue;
const compatRef = resolveKnownCompatModelAliasRef(modelRef);
if (compatRef) return compatRef;
return modelRef.includes("/") ? normalizeDefaultProviderModelRef(modelRef) : `${defaultProvider}/${modelRef}`;
}
}
function resolveConfiguredBareModelRef(params) {
const modelId = params.modelRef.trim();
if (!modelId || modelId.includes("/")) return;
const matches = /* @__PURE__ */ new Set();
const pushModelMapMatches = (models) => {
for (const key of Object.keys(models ?? {})) {
const parsed = parseModelRef(key);
if (parsed?.modelId === modelId) matches.add(`${parsed.provider}/${parsed.modelId}`);
}
};
pushModelMapMatches(asOptionalRecord(params.cfg.agents?.defaults?.models));
for (const [provider, providerConfig] of Object.entries(params.cfg.models?.providers ?? {})) for (const model of providerConfig?.models ?? []) if (providerCatalogModelMatches(provider, model?.id, modelId)) matches.add(`${normalizeProviderId(provider)}/${modelId}`);
return matches.size === 1 ? [...matches][0] : void 0;
}
function providerCatalogModelMatches(provider, catalogModelId, modelId) {
const rawId = catalogModelId?.trim();
if (!rawId) return false;
const normalizedId = normalizeConfiguredProviderCatalogModelId(provider, rawId);
if (normalizedId === modelId) return true;
return normalizeOptionalLowercaseString(normalizedId) === normalizeOptionalLowercaseString(modelId);
}
function normalizeDefaultProviderModelRef(modelRef) {
return modelRef.includes("/") ? modelRef : `${DEFAULT_PROVIDER}/${modelRef}`;
}
function normalizeProviderModelRef(provider, modelId) {
const normalizedProvider = normalizeProviderId(provider);
const normalizedModelId = normalizeConfiguredProviderCatalogModelId(normalizedProvider, modelId);
const slash = normalizedModelId.indexOf("/");
if (slash > 0 && normalizeProviderId(normalizedModelId.slice(0, slash)) === normalizedProvider && slash < normalizedModelId.length - 1) return `${normalizedProvider}/${normalizedModelId.slice(slash + 1)}`;
return `${normalizedProvider}/${normalizedModelId}`;
}
function resolveImplicitDefaultAgentModelRef(cfg) {
const fallbackProvider = resolveConfiguredProviderFallback({
cfg,
defaultProvider: DEFAULT_PROVIDER
});
return fallbackProvider ? normalizeProviderModelRef(fallbackProvider.provider, fallbackProvider.model) : `${DEFAULT_PROVIDER}/${DEFAULT_MODEL}`;
}
function agentUsesCodexRuntimeForCompaction(params) {
const runtime = concreteRuntimeId(normalizeOptionalLowercaseString(params.currentRuntime));
if (runtime) return runtime === "codex";
return modelRefUsesCodexRuntime({
cfg: params.cfg,
modelRef: readAgentPrimaryModelRef(params.agent, params.inheritedModelRef),
agentId: params.agentId
});
}
function collectUnsupportedCodexCompactionOverridesForAgent(params) {
const agent = asOptionalRecord(params.agent);
const compaction = asOptionalRecord(agent?.compaction);
const inheritedCompaction = asOptionalRecord(params.inheritedCompaction);
if (!agentUsesCodexRuntimeForCompaction({
cfg: params.cfg,
agent,
agentId: params.agentId,
currentRuntime: params.currentRuntime,
inheritedModelRef: params.inheritedModelRef
})) return [];
if (normalizeOptionalLowercaseString(compaction?.provider ?? inheritedCompaction?.provider) === LOSSLESS_CONTEXT_ENGINE_ID) return [];
return COMPACTION_OVERRIDE_KEYS.map((key) => {
const localValue = compaction?.[key];
const hasLocalValue = typeof localValue === "string" && localValue.trim();
return {
key,
value: hasLocalValue ? localValue : inheritedCompaction?.[key],
path: hasLocalValue ? `${params.path}.compaction.${key}` : params.inheritedCompactionPath ? `${params.inheritedCompactionPath}.${key}` : `${params.path}.compaction.${key}`
};
}).flatMap(({ key, path, value }) => typeof value === "string" && value.trim() ? [{
path,
key,
value: value.trim()
}] : []);
}
function collectLegacyLosslessCompactionForAgent(params) {
const agent = asOptionalRecord(params.agent);
const compaction = asOptionalRecord(agent?.compaction);
const inheritedCompaction = asOptionalRecord(params.inheritedCompaction);
if (!agentUsesCodexRuntimeForCompaction({
cfg: params.cfg,
agent,
agentId: params.agentId,
currentRuntime: params.currentRuntime,
inheritedModelRef: params.inheritedModelRef
})) return [];
const localProvider = compaction?.provider;
const hasLocalProvider = typeof localProvider === "string" && localProvider.trim();
const providerValue = hasLocalProvider ? localProvider : inheritedCompaction?.provider;
if (normalizeOptionalLowercaseString(providerValue) !== LOSSLESS_CONTEXT_ENGINE_ID) return [];
const compactionPath = hasLocalProvider ? `${params.path}.compaction` : params.inheritedCompactionPath ?? `${params.path}.compaction`;
const localModel = compaction?.model;
const hasLocalModel = typeof localModel === "string" && localModel.trim();
const inheritedModel = inheritedCompaction?.model;
const modelValue = hasLocalModel ? localModel : inheritedModel;
const modelCompactionPath = hasLocalModel ? `${params.path}.compaction` : params.inheritedCompactionPath ?? compactionPath;
return [{
path: params.path,
compactionPath,
providerPath: `${compactionPath}.provider`,
providerValue: String(providerValue).trim(),
...typeof modelValue === "string" && modelValue.trim() ? {
modelPath: `${modelCompactionPath}.model`,
modelValue: modelValue.trim()
} : {}
}];
}
function dedupeLegacyLosslessCompactionConfigs(hits) {
const seen = /* @__PURE__ */ new Set();
return hits.filter((hit) => {
const key = `${hit.compactionPath}\0${hit.providerValue}\0${hit.modelPath ?? ""}\0${hit.modelValue ?? ""}`;
if (seen.has(key)) return false;
seen.add(key);
return true;
});
}
function collectLegacyLosslessCompactionConfigs(params) {
const defaults = params.cfg.agents?.defaults;
const defaultsRuntime = params.ignoreLegacyAgentRuntimePins ? void 0 : readLegacyDefaultsRuntime(defaults);
const defaultModelRef = readAgentPrimaryModelRef(defaults);
const defaultCompaction = asOptionalRecord(defaults?.compaction);
const hits = collectLegacyLosslessCompactionForAgent({
cfg: params.cfg,
agent: defaults,
path: "agents.defaults",
currentRuntime: resolveRuntime({ defaultsRuntime })
});
const agents = Array.isArray(params.cfg.agents?.list) ? params.cfg.agents.list : [];
for (const [index, agent] of agents.entries()) {
const agentRecord = asOptionalRecord(agent);
if (!agentRecord) continue;
const id = typeof agentRecord.id === "string" && agentRecord.id.trim() ? agentRecord.id.trim() : String(index);
hits.push(...collectLegacyLosslessCompactionForAgent({
cfg: params.cfg,
agent: agentRecord,
path: `agents.list.${id}`,
agentId: id,
currentRuntime: resolveRuntime({
agentRuntime: params.ignoreLegacyAgentRuntimePins ? void 0 : asAgentRuntimePolicyConfig(agentRecord.agentRuntime),
defaultsRuntime
}),
inheritedModelRef: defaultModelRef,
inheritedCompaction: defaultCompaction,
inheritedCompactionPath: "agents.defaults.compaction"
}));
}
return dedupeLegacyLosslessCompactionConfigs(hits);
}
function dedupeUnsupportedCompactionOverrides(hits) {
const seen = /* @__PURE__ */ new Set();
return hits.filter((hit) => {
const key = `${hit.path}\0${hit.key}\0${hit.value}`;
if (seen.has(key)) return false;
seen.add(key);
return true;
});
}
function collectUnsupportedCodexCompactionOverrides(params) {
const defaults = params.cfg.agents?.defaults;
const defaultsRuntime = params.ignoreLegacyAgentRuntimePins ? void 0 : readLegacyDefaultsRuntime(defaults);
const defaultModelRef = readAgentPrimaryModelRef(defaults);
const defaultCompaction = asOptionalRecord(defaults?.compaction);
const hits = collectUnsupportedCodexCompactionOverridesForAgent({
cfg: params.cfg,
agent: defaults,
path: "agents.defaults",
currentRuntime: resolveRuntime({ defaultsRuntime })
});
const agents = Array.isArray(params.cfg.agents?.list) ? params.cfg.agents.list : [];
for (const [index, agent] of agents.entries()) {
const agentRecord = asOptionalRecord(agent);
if (!agentRecord) continue;
const id = typeof agentRecord.id === "string" && agentRecord.id.trim() ? agentRecord.id.trim() : String(index);
hits.push(...collectUnsupportedCodexCompactionOverridesForAgent({
cfg: params.cfg,
agent: agentRecord,
path: `agents.list.${id}`,
agentId: id,
currentRuntime: resolveRuntime({
agentRuntime: params.ignoreLegacyAgentRuntimePins ? void 0 : asAgentRuntimePolicyConfig(agentRecord.agentRuntime),
defaultsRuntime
}),
inheritedModelRef: defaultModelRef,
inheritedCompaction: defaultCompaction,
inheritedCompactionPath: "agents.defaults.compaction"
}));
}
return dedupeUnsupportedCompactionOverrides(hits);
}
function getSharedDefaultCompactionOverrideConsumers(params) {
const consumers = {
model: false,
provider: false
};
const defaults = params.cfg.agents?.defaults;
const defaultCompaction = asOptionalRecord(defaults?.compaction);
if (!defaultCompaction) return consumers;
const hasDefaultModel = typeof defaultCompaction.model === "string" && defaultCompaction.model.trim();
const hasDefaultProvider = typeof defaultCompaction.provider === "string" && defaultCompaction.provider.trim();
if (!hasDefaultModel && !hasDefaultProvider) return consumers;
const defaultsRuntime = readLegacyDefaultsRuntime(defaults);
const inheritedModelRef = readAgentPrimaryModelRef(defaults);
if (!agentUsesCodexRuntimeForCompaction({
cfg: params.cfg,
agent: defaults,
currentRuntime: resolveRuntime({ defaultsRuntime: params.ignoreLegacyAgentRuntimePins ? void 0 : defaultsRuntime })
})) {
consumers.model ||= Boolean(hasDefaultModel);
consumers.provider ||= Boolean(hasDefaultProvider);
if ((!hasDefaultModel || consumers.model) && (!hasDefaultProvider || consumers.provider)) return consumers;
}
const agents = Array.isArray(params.cfg.agents?.list) ? params.cfg.agents.list : [];
if (agents.length === 0) return consumers;
for (const [index, agent] of agents.entries()) {
const agentRecord = asOptionalRecord(agent);
if (!agentRecord) continue;
const compaction = asOptionalRecord(agentRecord.compaction);
const inheritsDefaultModel = Boolean(hasDefaultModel) && !(typeof compaction?.model === "string" && compaction.model.trim());
const inheritsDefaultProvider = Boolean(hasDefaultProvider) && !(typeof compaction?.provider === "string" && compaction.provider.trim());
if (!inheritsDefaultModel && !inheritsDefaultProvider) continue;
const id = typeof agentRecord.id === "string" && agentRecord.id.trim() ? agentRecord.id.trim() : String(index);
if (!agentUsesCodexRuntimeForCompaction({
cfg: params.cfg,
agent: agentRecord,
agentId: id,
currentRuntime: resolveRuntime({
agentRuntime: params.ignoreLegacyAgentRuntimePins ? void 0 : asAgentRuntimePolicyConfig(agentRecord.agentRuntime),
defaultsRuntime: params.ignoreLegacyAgentRuntimePins ? void 0 : defaultsRuntime
}),
inheritedModelRef
})) {
consumers.model ||= inheritsDefaultModel;
consumers.provider ||= inheritsDefaultProvider;
if ((!hasDefaultModel || consumers.model) && (!hasDefaultProvider || consumers.provider)) break;
}
}
return consumers;
}
function sharedDefaultLosslessCompactionHasNonCodexConsumer(params) {
const defaults = params.cfg.agents?.defaults;
const defaultCompaction = asOptionalRecord(defaults?.compaction);
const hasDefaultLosslessProvider = normalizeOptionalLowercaseString(defaultCompaction?.provider) === LOSSLESS_CONTEXT_ENGINE_ID;
const hasDefaultModel = typeof defaultCompaction?.model === "string" && defaultCompaction.model.trim();
if (!hasDefaultLosslessProvider && !hasDefaultModel) return false;
const defaultsRuntime = params.ignoreLegacyAgentRuntimePins ? void 0 : readLegacyDefaultsRuntime(defaults);
if (!agentUsesCodexRuntimeForCompaction({
cfg: params.cfg,
agent: defaults,
currentRuntime: resolveRuntime({ defaultsRuntime })
})) return true;
const inheritedModelRef = readAgentPrimaryModelRef(defaults);
const agents = Array.isArray(params.cfg.agents?.list) ? params.cfg.agents.list : [];
for (const [index, agent] of agents.entries()) {
const agentRecord = asOptionalRecord(agent);
if (!agentRecord) continue;
const compaction = asOptionalRecord(agentRecord.compaction);
const inheritsDefaultProvider = hasDefaultLosslessProvider && !(typeof compaction?.provider === "string" && compaction.provider.trim());
const inheritsDefaultModel = Boolean(hasDefaultModel) && !(typeof compaction?.model === "string" && compaction.model.trim());
if (!inheritsDefaultProvider && !inheritsDefaultModel) continue;
const id = typeof agentRecord.id === "string" && agentRecord.id.trim() ? agentRecord.id.trim() : String(index);
if (!agentUsesCodexRuntimeForCompaction({
cfg: params.cfg,
agent: agentRecord,
agentId: id,
currentRuntime: resolveRuntime({
agentRuntime: params.ignoreLegacyAgentRuntimePins ? void 0 : asAgentRuntimePolicyConfig(agentRecord.agentRuntime),
defaultsRuntime
}),
inheritedModelRef
})) return true;
}
return false;
}
function collectModelsMapRefs(params) {
const record = asOptionalRecord(params.models);
if (!record) return;
for (const modelRef of Object.keys(record)) {
if (!isOpenAICodexModelRef(modelRef)) continue;
recordCodexModelHit({
hits: params.hits,
path: `${params.path}.${modelRef}`,
model: modelRef
});
}
}
function collectAgentModelRefs(params) {
const agent = asOptionalRecord(params.agent);
if (!agent) return;
for (const key of AGENT_MODEL_CONFIG_KEYS) collectModelConfigSlot({
hits: params.hits,
path: `${params.path}.${key}`,
value: agent[key],
runtime: key === "model" ? params.runtime : void 0
});
for (const key of AGENT_MEDIA_MODEL_CONFIG_KEYS) collectModelConfigSlot({
hits: params.hits,
path: `${params.path}.${key}`,
value: agent[key]
});
collectStringModelSlot({
hits: params.hits,
path: `${params.path}.heartbeat.model`,
value: asOptionalRecord(agent.heartbeat)?.model
});
collectModelConfigSlot({
hits: params.hits,
path: `${params.path}.subagents.model`,
value: asOptionalRecord(agent.subagents)?.model
});
const compaction = asOptionalRecord(agent.compaction);
collectStringModelSlot({
hits: params.hits,
path: `${params.path}.compaction.model`,
value: compaction?.model
});
collectStringModelSlot({
hits: params.hits,
path: `${params.path}.compaction.memoryFlush.model`,
value: asOptionalRecord(compaction?.memoryFlush)?.model
});
if (params.collectModelsMap) collectModelsMapRefs({
hits: params.hits,
path: `${params.path}.models`,
models: agent.models
});
}
function collectConfigModelRefs(cfg) {
const hits = [];
const defaults = cfg.agents?.defaults;
const defaultsRuntime = readLegacyDefaultsRuntime(defaults);
collectAgentModelRefs({
hits,
agent: defaults,
path: "agents.defaults",
runtime: resolveRuntime({ defaultsRuntime }),
collectModelsMap: true
});
const agents = Array.isArray(cfg.agents?.list) ? cfg.agents.list : [];
for (const [index, agent] of agents.entries()) {
const agentRecord = asOptionalRecord(agent);
if (!agentRecord) continue;
collectAgentModelRefs({
hits,
agent: agentRecord,
path: `agents.list.${typeof agentRecord.id === "string" && agentRecord.id.trim() ? agentRecord.id.trim() : String(index)}`,
runtime: resolveRuntime({
agentRuntime: asAgentRuntimePolicyConfig(agentRecord.agentRuntime),
defaultsRuntime
})
});
}
const channelsModelByChannel = asOptionalRecord(cfg.channels?.modelByChannel);
if (channelsModelByChannel) for (const [channelId, channelMap] of Object.entries(channelsModelByChannel)) {
const targets = asOptionalRecord(channelMap);
if (!targets) continue;
for (const [targetId, model] of Object.entries(targets)) collectStringModelSlot({
hits,
path: `channels.modelByChannel.${channelId}.${targetId}`,
value: model
});
}
for (const [index, mapping] of (cfg.hooks?.mappings ?? []).entries()) collectStringModelSlot({
hits,
path: `hooks.mappings.${index}.model`,
value: mapping.model
});
collectStringModelSlot({
hits,
path: "hooks.gmail.model",
value: cfg.hooks?.gmail?.model
});
collectStringModelSlot({
hits,
path: "messages.tts.summaryModel",
value: cfg.messages?.tts?.summaryModel
});
collectStringModelSlot({
hits,
path: "channels.discord.voice.model",
value: asOptionalRecord(asOptionalRecord(cfg.channels?.discord)?.voice)?.model
});
return hits;
}
function pluginIdListIncludes(value, pluginId) {
return Array.isArray(value) && value.some((entry) => normalizeOptionalLowercaseString(entry) === pluginId);
}
function codexPluginAllowlistIsRestrictive(cfg) {
const allow = cfg.plugins?.allow;
return Array.isArray(allow) && allow.length > 0 && !pluginIdListIncludes(allow, "codex");
}
function isCodexPluginUnavailableByConfig(cfg) {
if (codexPluginIsBlockedOutsideEntry(cfg)) return true;
if (asOptionalRecord(asOptionalRecord(cfg.plugins?.entries)?.codex)?.enabled === false) return true;
return codexPluginAllowlistIsRestrictive(cfg);
}
function codexPluginIsBlockedOutsideEntry(cfg) {
if (cfg.plugins?.enabled === false) return true;
return pluginIdListIncludes(cfg.plugins?.deny, "codex");
}
function collectAgentRuntimeModelRefs(params) {
const refs = [];
const agent = asOptionalRecord(params.agent);
if (agent && Object.hasOwn(agent, "model")) collectModelConfigRefs({
refs,
path: `${params.path}.model`,
value: agent.model
});
if (!hasAgentPrimaryModelConfig(agent) && params.fallbackModelRefs) refs.push(...params.fallbackModelRefs);
collectStringModelConfigRef({
refs,
path: `${params.path}.heartbeat.model`,
value: asOptionalRecord(agent?.heartbeat)?.model
});
collectModelConfigRefs({
refs,
path: `${params.path}.subagents.model`,
value: asOptionalRecord(agent?.subagents)?.model
});
if (params.inheritedModelRefs) refs.push(...params.inheritedModelRefs);
collectCodexRuntimeModelPolicyRefs({
refs,
path: `${params.path}.models`,
models: agent?.models
});
return refs;
}
function hasAgentPrimaryModelConfig(agent) {
const record = asOptionalRecord(agent);
return Boolean(record && readModelConfigPrimaryRef(record.model));
}
function collectChannelAgentRuntimeModelRefs(cfg) {
const refs = [];
const channelsModelByChannel = asOptionalRecord(cfg.channels?.modelByChannel);
for (const [channelId, channelMapValue] of Object.entries(channelsModelByChannel ?? {})) {
const channelMap = asOptionalRecord(channelMapValue);
if (!channelMap) continue;
for (const [targetId, modelRef] of Object.entries(channelMap)) collectStringModelConfigRef({
refs,
path: `channels.modelByChannel.${channelId}.${targetId}`,
value: modelRef
});
}
return refs;
}
function collectDisabledCodexPluginRouteHits(cfg) {
if (!isCodexPluginUnavailableByConfig(cfg)) return [];
const defaults = cfg.agents?.defaults;
const defaultRefs = collectAgentRuntimeModelRefs({
agent: defaults,
path: "agents.defaults"
});
if (cfg.agents && !hasAgentPrimaryModelConfig(defaults) && !defaultRefs.some((ref) => resolveRuntimeModelRef({
cfg,
modelRef: ref.modelRef
}) === resolveImplicitDefaultAgentModelRef(cfg))) defaultRefs.push({
path: "agents.defaults.model",
modelRef: resolveImplicitDefaultAgentModelRef(cfg)
});
const agents = Array.isArray(cfg.agents?.list) ? cfg.agents.list : [];
const inheritedDefaultAuxRefs = defaultRefs.filter((ref) => ref.path === "agents.defaults.heartbeat.model" || ref.path.startsWith("agents.defaults.subagents.model"));
const inheritedDefaultModelPolicyRefs = defaultRefs.filter((ref) => ref.path.startsWith("agents.defaults.models."));
const inheritedDefaultModelRefs = defaultRefs.filter((ref) => !inheritedDefaultAuxRefs.includes(ref) && !inheritedDefaultModelPolicyRefs.includes(ref));
const candidateRefs = agents.length === 0 ? [...defaultRefs] : [];
candidateRefs.push(...collectChannelAgentRuntimeModelRefs(cfg));
for (const [index, agent] of agents.entries()) {
const agentRecord = asOptionalRecord(agent);
if (!agentRecord) continue;
const pathId = typeof agentRecord.id === "string" && agentRecord.id.trim() ? agentRecord.id.trim() : String(index);
const agentId = normalizeAgentId(typeof agentRecord.id === "string" ? agentRecord.id : void 0);
const inheritedModelRefs = inheritedDefaultAuxRefs.filter((ref) => {
if (ref.path === "agents.defaults.heartbeat.model") return !normalizeOptionalLowercaseString(asOptionalRecord(agentRecord.heartbeat)?.model);
if (ref.path.startsWith("agents.defaults.subagents.model")) return !readModelConfigPrimaryRef(asOptionalRecord(agentRecord.subagents)?.model);
return true;
});
inheritedModelRefs.push(...inheritedDefaultModelPolicyRefs);
for (const ref of collectAgentRuntimeModelRefs({
agent: agentRecord,
path: `agents.list.${pathId}`,
fallbackModelRefs: inheritedDefaultModelRefs,
inheritedModelRefs
})) candidateRefs.push({
...ref,
agentId
});
}
const hits = [];
const seen = /* @__PURE__ */ new Set();
for (const ref of candidateRefs) {
const canonicalModel = resolveRuntimeModelRef({
cfg,
modelRef: ref.modelRef,
agentId: ref.agentId
});
if (!modelRefUsesCodexRuntime({
cfg,
modelRef: ref.modelRef,
agentId: ref.agentId
})) continue;
const key = `${ref.agentId ?? ""}\0${ref.path}\0${canonicalModel}`;
if (seen.has(key)) continue;
seen.add(key);
hits.push({
path: ref.path,
modelRef: ref.modelRef,
canonicalModel
});
}
return hits;
}
/** Find Codex-routed model refs that require the Codex plugin while it is disabled. */
function collectDisabledCodexPluginRouteIssues(cfg) {
const blockedOutsideEntry = codexPluginIsBlockedOutsideEntry(cfg);
return collectDisabledCodexPluginRouteHits(cfg).map((hit) => ({
path: hit.path,
modelRef: hit.modelRef,
canonicalModel: hit.canonicalModel,
blockedOutsideEntry
}));
}
function enableCodexPluginForRequiredRoutes(params) {
if (params.routeHits.length === 0 || codexPluginIsBlockedOutsideEntry(params.cfg)) return {
cfg: params.cfg,
changes: []
};
const cfg = structuredClone(params.cfg);
const plugins = asOptionalRecord(cfg.plugins) ?? {};
if (cfg.plugins !== plugins) cfg.plugins = plugins;
const entries = asOptionalRecord(plugins.entries) ?? {};
if (plugins.entries !== entries) plugins.entries = entries;
const codexEntry = asOptionalRecord(entries.codex) ?? {};
const changes = [];
if (codexEntry.enabled !== true) {
entries.codex = {
...codexEntry,
enabled: true
};
changes.push("Enabled plugins.entries.codex because configured agent routes use Codex runtime.");
} else if (entries.codex !== codexEntry) entries.codex = codexEntry;
if (Array.isArray(plugins.allow) && plugins.allow.length > 0 && !plugins.allow.some((id) => normalizeOptionalLowercaseString(id) === "codex")) {
plugins.allow = [...plugins.allow, "codex"];
changes.push("Added codex to plugins.allow because configured agent routes use Codex runtime.");
}
return {
cfg,
changes
};
}
function rewriteStringModelSlot(params) {
if (!params.container) return false;
const value = params.container[params.key];
const model = typeof value === "string" ? value.trim() : "";
if (!model || !isOpenAICodexModelRef(model)) return false;
const canonicalModel = recordCodexModelHit({
hits: params.hits,
path: params.path,
model,
runtime: params.runtime
});
if (!canonicalModel) return false;
params.container[params.key] = canonicalModel;
return true;
}
function rewriteModelConfigSlot(params) {
if (!params.container) return false;
const value = params.container[params.key];
if (typeof value === "string") return rewriteStringModelSlot({
hits: params.hits,
container: params.container,
key: params.key,
path: params.path,
runtime: params.runtime
});
const record = asOptionalRecord(value);
if (!record) return false;
const rewrotePrimary = rewriteStringModelSlot({
hits: params.hits,
container: record,
key: "primary",
path: `${params.path}.primary`,
runtime: params.runtime
});
if (Array.isArray(record.fallbacks)) record.fallbacks = record.fallbacks.map((entry, index) => {
if (typeof entry !== "string") return entry;
const model = entry.trim();
return recordCodexModelHit({
hits: params.hits,
path: `${params.path}.fallbacks.${index}`,
model
}) ?? entry;
});
return rewrotePrimary;
}
function rewriteModelsMap(params) {
if (!params.models) return;
for (const legacyRef of Object.keys(params.models)) {
const canonicalModel = toCanonicalOpenAIModelRef(legacyRef);
if (!canonicalModel) continue;
recordCodexModelHit({
hits: params.hits,
path: `${params.path}.${legacyRef}`,
model: legacyRef
});
const legacyEntry = params.models[legacyRef] ?? {};
const canonicalEntry = params.models[canonicalModel];
const legacyRecord = asOptionalRecord(legacyEntry);
const canonicalRecord = asOptionalRecord(canonicalEntry);
params.models[canonicalModel] = legacyRecord && canonicalRecord ? mergeCanonicalModelMapRecord({
legacyRecord,
canonicalRecord
}) : canonicalEntry ?? legacyEntry;
delete params.models[legacyRef];
}
}
function runtimePolicyHasExplicitNonDefaultPin(value) {
const id = normalizeOptionalLowercaseString(asOptionalRecord(value)?.id);
return Boolean(id && id !== "auto" && id !== "default");
}
function mergeCanonicalModelMapRecord(params) {
const merged = {
...params.legacyRecord,
...params.canonicalRecord
};
const legacyRuntime = asOptionalRecord(params.legacyRecord.agentRuntime);
const canonicalRuntime = asOptionalRecord(params.canonicalRecord.agentRuntime);
if (legacyRuntime && runtimePolicyHasExplicitNonDefaultPin(legacyRuntime) && !runtimePolicyHasExplicitNonDefaultPin(canonicalRuntime)) merged.agentRuntime = {
...legacyRuntime,
...canonicalRuntime,
id: legacyRuntime.id
};
return merged;
}
function modelConfigContainsRef(value, modelRef) {
if (typeof value === "string") return value.trim() === modelRef;
const record = asOptionalRecord(value);
if (!record) return false;
if (typeof record.primary === "string" && record.primary.trim() === modelRef) return true;
return Array.isArray(record.fallbacks) && record.fallbacks.some((entry) => typeof entry === "string" && entry.trim() === modelRef);
}
function collectModelConfigRefs(params) {
if (typeof params.value === "string") {
collectStringModelConfigRef(params);
return;
}
const record = asOptionalRecord(params.value);
if (!record) return;
if (typeof record.primary === "string" && record.primary.trim()) params.refs.push({
path: `${params.path}.primary`,
modelRef: record.primary.trim()
});
if (Array.isArray(record.fallbacks)) {
for (const [index, entry] of record.fallbacks.entries()) if (typeof entry === "string" && entry.trim()) params.refs.push({
path: `${params.path}.fallbacks.${index}`,
modelRef: entry.trim()
});
}
}
function collectStringModelConfigRef(params) {
if (typeof params.value !== "string") return;
const modelRef = params.value.trim();
if (modelRef) params.refs.push({
path: params.path,
modelRef
});
}
function collectCodexRuntimeModelPolicyRefs(params) {
const record = asOptionalRecord(params.models);
if (!record) return;
for (const [modelRef, entry] of Object.entries(record)) {
const trimmed = modelRef.trim();
if (!trimmed) continue;
if (normalizeRuntimeString(asOptionalRecord(asOptionalRecord(entry)?.agentRuntime)?.id) === "codex") params.refs.push({
path: `${params.path}.${trimmed}`,
modelRef: trimmed
});
}
}
function agentExplicitlyReferencesCanonicalModel(agent, modelRef) {
const record = asOptionalRecord(agent);
if (!record) return false;
for (const key of AGENT_MODEL_CONFIG_KEYS) if (modelConfigContainsRef(record[key], modelRef)) return true;
if (modelConfigContainsRef(asOptionalRecord(record.heartbeat)?.model, modelRef)) return true;
if (modelConfigContainsRef(asOptionalRecord(record.subagents)?.model, modelRef)) return true;
const compaction = asOptionalRecord(record.compaction);
return modelConfigContainsRef(compaction?.model, modelRef) || modelConfigContainsRef(asOptionalRecord(compaction?.memoryFlush)?.model, modelRef) || asOptionalRecord(record.models)?.[modelRef] !== void 0;
}
function parseModelRef(modelRef) {
const slash = modelRef.indexOf("/");
if (slash <= 0 || slash >= modelRef.length - 1) return;
return {
provider: modelRef.slice(0, slash),
modelId: modelRef.slice(slash + 1)
};
}
function resolveCurrentRuntimeIdForCanonicalModel(params) {
const parsed = parseModelRef(params.modelRef);
if (!parsed) return "auto";
const configured = normalizeRuntimeString(resolveModelRuntimePolicy({
config: params.cfg,
provider: parsed.provider,
modelId: parsed.modelId,
agentId: params.agentId
}).policy?.id);
if (configured) return configured;
return openAIProviderUsesCodexRuntimeByDefault({
provider: parsed.provider,
config: params.cfg
}) ? "codex" : "auto";
}
function setModelRuntimePolicy(params) {
const models = asOptionalRecord(params.agent.models) ?? {};
if (params.agent.models !== models) params.agent.models = models;
const entry = asOptionalRecord(models[params.modelRef]) ?? {};
if (models[params.modelRef] !== entry) models[params.modelRef] = entry;
const priorRuntime = asOptionalRecord(entry.agentRuntime);
if (normalizeOptionalLowercaseString(priorRuntime?.id) === params.runtimeId) return;
entry.agentRuntime = {
...priorRuntime,
id: params.runtimeId
};
params.changes.push(`Set ${params.agentPath}.models.${params.modelRef}.agentRuntime.id to "${params.runtimeId}" ${params.reason}.`);
}
function shieldExplicitListedAgentRefsFromDefaultPolicy(params) {
for (const [index, agent] of (params.cfg.agents?.list ?? []).entries()) {
if (!agentExplicitlyReferencesCanonicalModel(agent, params.modelRef)) continue;
const id = typeof agent.id === "string" && agent.id.trim() ? agent.id.trim() : String(index);
const runtimeId = resolveCurrentRuntimeIdForCanonicalModel({
cfg: params.cfg,
modelRef: params.modelRef,
agentId: id
});
if (runtimeId === params.targetRuntimeId) continue;
setModelRuntimePolicy({
agent,
agentPath: `agents.list.${id}`,
modelRef: params.modelRef,
runtimeId,
changes: params.changes,
reason: "so default runtime repair does not change explicit agent routing"
});
}
}
function rewriteAgentModelRefs(params) {
if (!params.agent) return;
const agent = params.agent;
const preserveCodexRuntimePolicyForNewHits = (fromIndex) => {
for (const hit of params.hits.slice(fromIndex)) ensureCodexRuntimePolicy({
cfg: params.cfg,
agent,
agentPath: params.path,
agentId: params.agentId,
modelRef: hit.canonicalModel,
legacyModelRef: hit.model,
isDefaults: params.path === "agents.defaults",
preRepairCfg: params.preRepairCfg,
changes: params.runtimePolicyChanges
});
};
for (const key of AGENT_MODEL_CONFIG_KEYS) {
const start = params.hits.length;
if (key === "model") {
rewriteModelConfigSlot({
hits: params.hits,
container: agent,
key,
path: `${params.path}.${key}`,
runtime: params.currentRuntime
});
preserveCodexRuntimePolicyForNewHits(start);
} else rewriteModelConfigSlotIfCanonicalCodexRuntime({
cfg: params.cfg,
agentId: params.agentId,
hits: params.hits,
container: agent,
key,
path: `${params.path}.${key}`
});
}
rewriteStringModelSlotIfCanonicalCodexRuntime({
cfg: params.cfg,
agentId: params.agentId,
hits: params.hits,
container: asOptionalRecord(agent.heartbeat),
key: "model",
path: `${params.path}.heartbeat.model`
});
rewriteModelConfigSlotIfCanonicalCodexRuntime({
cfg: params.cfg,
agentId: params.agentId,
hits: params.hits,
container: asOptionalRecord(agent.subagents),
key: "model",
path: `${params.path}.subagents.model`
});
const compaction = asOptionalRecord(agent.compaction);
const inheritedCompaction = asOptionalRecord(params.inheritedCompaction);
if (agentUsesCodexRuntimeForCompaction({
cfg: params.cfg,
agent,
agentId: params.agentId,
currentRuntime: params.currentRuntime,
inheritedModelRef: params.inheritedModelRef
})) if (normalizeOptionalLowercaseString(compaction?.provider ?? inheritedCompaction?.provider) === LOSSLESS_CONTEXT_ENGINE_ID) {
const start = params.hits.length;
rewriteStringModelSlot({
hits: params.hits,
container: compaction,
key: "model",
path: `${params.path}.compaction.model`
});
preserveCodexRuntimePolicyForNewHits(start);
const localModel = typeof compaction?.model === "string" ? compaction.model.trim() : "";
const inheritedModelPath = params.inheritedCompactionPath ? `${params.inheritedCompactionPath}.model` : void 0;
if (!localModel && inheritedModelPath && params.preserveUnsupportedCompactionPaths?.has(inheritedModelPath)) {
const inheritedStart = params.hits.length;
rewriteStringModelSlot({
hits: params.hits,
container: inheritedCompaction,
key: "model",
path: inheritedModelPath
});
const inheritedHit = params.hits[inheritedStart];
const inheritedCanonicalModel = inheritedHit?.canonicalModel ?? params.rewrittenInheritedCompactionModels?.get(inheritedModelPath);
if (inheritedHit) {
params.rewrittenInheritedCompactionModels?.set(inheritedModelPath, inheritedHit.canonicalModel);
preserveCodexRuntimePolicyForNewHits(inheritedStart);
} else if (inheritedCanonicalModel) ensureCodexRuntimePolicy({
cfg: params.cfg,
agent,
agentPath: params.path,
agentId: params.agentId,
modelRef: inheritedCanonicalModel,
isDefaults: params.path === "agents.defaults",
preRepairCfg: params.preRepairCfg,
changes: params.runtimePolicyChanges
});
}
} else {
removeUnsupportedCodexCompactionOverrides({
agent,
compaction,
path: params.path,
preserve: params.preserveUnsupportedCompactionOverrides,
preservePaths: params.preserveUnsupportedCompactionPaths,
changes: params.unsupportedCompactionChanges
});
if (params.preserveUnsupportedCompactionOverrides?.model) rewriteStringModelSlot({
hits: params.hits,
container: compaction,
key: "model",
path: `${params.path}.compaction.model`
});
}
else rewriteStringModelSlotIfCanonicalCodexRuntime({
cfg: params.cfg,
agentId: params.agentId,
hits: params.hits,
container: compaction,
key: "model",
path: `${params.path}.compaction.model`
});
rewriteStringModelSlotIfCanonicalCodexRuntime({
cfg: params.cfg,
agentId: params.agentId,
hits: params.hits,
container: asOptionalRecord(compaction?.memoryFlush),
key: "model",
path: `${params.path}.compaction.memoryFlush.model`
});
for (const key of AGENT_MEDIA_MODEL_CONFIG_KEYS) rewriteModelConfigSlot({
hits: params.hits,
container: agent,
key,
path: `${params.path}.${key}`
});
if (params.rewriteModelsMap) {
const start = params.hits.length;
rewriteModelsMap({
hits: params.hits,
models: asOptionalRecord(agent.models),
path: `${params.path}.models`
});
preserveCodexRuntimePolicyForNewHits(start);
}
}
function removeUnsupportedCodexCompactionOverrides(params) {
if (!params.compaction) return;
if (normalizeOptionalLowercaseString(params.compaction.provider) === LOSSLESS_CONTEXT_ENGINE_ID) return;
for (const key of COMPACTION_OVERRIDE_KEYS) {
const path = `${params.path}.compaction.${key}`;
if (params.preservePaths?.has(path)) continue;
if (params.preserve?.[key]) continue;
const value = params.compaction[key];
if (typeof value !== "string" || !value.trim()) continue;
delete params.compaction[key];
params.changes.push(`Removed ${path}; Codex runtime uses native server-side compaction.`);
}
if (Object.keys(params.compaction).length === 0) delete params.agent.compaction;
}
function readMutablePath(root, pathLabel) {
const parts = pathLabel.split(".");
let cursor = root;
for (const part of parts) {
const record = asOptionalRecord(cursor);
if (!record) return;
cursor = record[part];
}
return asOptionalRecord(cursor);
}
function readCompactionOwnerForPath(cfg, ownerPath) {
if (ownerPath === "agents.defaults") return asOptionalRecord(cfg.agents?.defaults);
if (!ownerPath.startsWith("agents.list.")) return readMutablePath(cfg, ownerPath);
const label = ownerPath.slice(12);
const agents = Array.isArray(cfg.agents?.list) ? cfg.agents.list : [];
const agentWithId = agents.find((agent) => agent.id === label);
if (agentWithId) return asOptionalRecord(agentWithId);
const index = Number(label);
const candidate = Number.isInteger(index) ? agents[index] : void 0;
if (candidate) return asOptionalRecord(candidate);
}
function removeMigratedLosslessCompactionKey(params) {
const ownerPath = readCompactionOwnerPathForKeyPath(params.path);
const owner = readCompactionOwnerForPath(params.cfg, ownerPath);
const compaction = asOptionalRecord(owner?.compaction);
if (!owner || !compaction) return;
const value = compaction[params.key];
if (typeof value !== "string" || !value.trim()) return;
delete compaction[params.key];
params.changes.push(params.changeMessage);
if (Object.keys(compaction).length === 0) delete owner.compaction;
}
function readCompactionOwnerPathForKeyPath(path) {
return path.replace(/\.(model|provider)$/, "").replace(/\.compaction$/, "");
}
function legacyLosslessSummaryModels(hits) {
const models = /* @__PURE__ */ new Set();
for (const hit of hits) {
if (!hit.modelValue) continue;
models.add(toCanonicalOpenAIModelRef(hit.modelValue) ?? normalizeDefaultProviderModelRef(hit.modelValue));
}
return [...models];
}
function preserveMigratedLosslessCodexRuntimePolicy(params) {
if (!params.summaryModel) return;
const preservedOwners = /* @__PURE__ */ new Set();
for (const hit of params.hits) {
if (!hit.modelValue || !isOpenAICodexModelRef(hit.modelValue)) continue;
if (toCanonicalOpenAIModelRef(hit.modelValue) !== params.summaryModel) continue;
const ownerPath = readCompactionOwnerPathForKeyPath(hit.modelPath ?? hit.providerPath);
if (preservedOwners.has(ownerPath)) continue;
const owner = readCompactionOwnerForPath(params.cfg, ownerPath);
if (!owner) continue;
preservedOwners.add(ownerPath);
ensureCodexRuntimePolicy({
cfg: params.cfg,
agent: owner,
agentPath: ownerPath,
agentId: agentIdFromAgentPath(ownerPath),
modelRef: params.summaryModel,
isDefaults: ownerPath === "agents.defaults",
changes: params.changes
});
}
}
function canAutoMigrateLegacyLosslessCompaction(params) {
if (params.contextEngine && params.contextEngine !== LOSSLESS_CONTEXT_ENGINE_ID) return false;
const models = legacyLosslessSummaryModels(params.hits);
if (params.hits.some((hit) => !hit.modelValue) && (models.length > 0 || params.summaryModel)) return false;
if (models.length === 0) return true;
if (params.summaryModel) return models.every((model) => model === params.summaryModel);
return models.length === 1;
}
function readLosslessSummaryModel(plugins) {
const config = asOptionalRecord(asOptionalRecord(asOptionalRecord(plugins?.entries)?.[LOSSLESS_CONTEXT_ENGINE_ID])?.config);
return typeof config?.summaryModel === "string" && config.summaryModel.trim() ? config.summaryModel.trim() : void 0;
}
function ensureMutablePath(root, path) {
let cursor = root;
for (const part of path) {
const next = asOptionalRecord(cursor[part]) ?? {};
if (cursor[part] !== next) cursor[part] = next;
cursor = next;
}
return cursor;
}
function ensureLosslessLlmPolicy(params) {
if (!params.summaryModel) return;
const llm = ensureMutablePath(params.entry, ["llm"]);
if (llm.allowModelOverride !== true) {
llm.allowModelOverride = true;
params.changes.push(`Set plugins.entries.${LOSSLESS_CONTEXT_ENGINE_ID}.llm.allowModelOverride to true for Lossless summary model overrides.`);
}
const allowedModels = Array.isArray(llm.allowedModels) ? [...llm.allowedModels] : [];
if (!allowedModels.includes(params.summaryModel)) {
allowedModels.push(params.summaryModel);
llm.allowedModels = allowedModels;
params.changes.push(`Added ${params.summaryModel} to plugins.entries.${LOSSLESS_CONTEXT_ENGINE_ID}.llm.allowedModels.`);
}
}
function maybeMigrateLegacyLosslessCompactionConfig(params) {
const root = params.cfg;
const hits = collectLegacyLosslessCompactionConfigs(params);
if (hits.length === 0) return [];
const existingPlugins = asOptionalRecord(root.plugins);
const existingSlots = asOptionalRecord(existingPlugins?.slots);
const configuredContextEngine = typeof existingSlots?.contextEngine === "string" && existingSlots.contextEngine.trim() ? existingSlots.contextEngine.trim() : void 0;
const existingSummaryModel = readLosslessSummaryModel(existingPlugins);
const contextEngine = normalizeOptionalLowercaseString(configuredContextEngine);
if (sharedDefaultLosslessCompactionHasNonCodexConsumer(params) || !canAutoMigrateLegacyLosslessCompaction({
hits,
contextEngine,
summaryModel: existingSummaryModel
})) return [];
const plugins = ensureMutablePath(root, ["plugins"]);
const slots = ensureMutablePath(plugins, ["slots"]);
const changes = [];
const entries = ensureMutablePath(plugins, ["entries"]);
const entry = asOptionalRecord(entries[LOSSLESS_CONTEXT_ENGINE_ID]) ?? {};
if (entries[LOSSLESS_CONTEXT_ENGINE_ID] !== entry) entries[LOSSLESS_CONTEXT_ENGINE_ID] = entry;
const config = ensureMutablePath(entry, ["config"]);
if (slots.contextEngine !== LOSSLESS_CONTEXT_ENGINE_ID) {
slots.contextEngine = LOSSLESS_CONTEXT_ENGINE_ID;
changes.push(`Set plugins.slots.contextEngine to "${LOSSLESS_CONTEXT_ENGINE_ID}" for legacy Lossless compaction config.`);
}
if (entry.enabled !== true) {
entry.enabled = true;
changes.push(`Enabled plugins.entries.${LOSSLESS_CONTEXT_ENGINE_ID}.`);
}
let summaryModel = existingSummaryModel;
const firstModel = legacyLosslessSummaryModels(hits)[0];
if (!summaryModel && firstModel) {
summaryModel = firstModel;
config.summaryModel = summaryModel;
changes.push(`Moved ${hits.find((hit) => hit.modelValue)?.modelPath ?? "legacy compaction model"} to plugins.entries.${LOSSLESS_CONTEXT_ENGINE_ID}.config.summaryModel.`);
}
ensureLosslessLlmPolicy({
entry,
summaryModel,
changes
});
preserveMigratedLosslessCodexRuntimePolicy({
cfg: params.cfg,
hits,
summaryModel,
changes
});
for (const hit of hits) {
removeMigratedLosslessCompactionKey({
cfg: params.cfg,
path: hit.providerPath,
key: "provider",
changes,
changeMessage: `Removed ${hit.providerPath}; Lossless now runs through plugins.slots.contextEngine.`
});
if (hit.modelPath) removeMigratedLosslessCompactionKey({
cfg: params.cfg,
path: hit.modelPath,
key: "model",
changes,
changeMessage: `Removed ${hit.modelPath} after migrating the Lossless summary model.`
});
}
return changes;
}
function legacyEntryExplicitNonDefaultRuntimeId(models, canonicalModelRef) {
for (const ref of Object.keys(models)) {
if (ref === canonicalModelRef) continue;
if (toCanonicalOpenAIModelRef(ref) !== canonicalModelRef) continue;
const id = normalizeOptionalLowercaseString(asOptionalRecord(asOptionalRecord(models[ref])?.agentRuntime)?.id);
if (id && id !== "auto" && id !== "default") return id;
}
}
function agentIdFromAgentPath(agentPath) {
return agentPath.startsWith("agents.list.") ? agentPath.slice(12) : void 0;
}
function modelIdMatchesProviderModelEntry(params) {
if (typeof params.entryId !== "string") return false;
const entryId = params.entryId.trim();
if (entryId === params.modelId) return true;
const slash = entryId.indexOf("/");
if (slash <= 0) return false;
return normalizeProviderId(entryId.slice(0, slash)) === normalizeProviderId(params.provider) && entryId.slice(slash + 1).trim() === params.modelId;
}
function providerModelExplicitNonDefaultRuntimeId(params) {
const providers = asOptionalRecord(asOptionalRecord(params.cfg.models)?.providers);
for (const [providerId, providerConfig] of Object.entries(providers ?? {})) {
if (normalizeProviderId(providerId) !== normalizeProviderId(params.provider)) continue;
const models = asOptionalRecord(providerConfig)?.models;
if (!Array.isArray(models)) continue;
for (const model of models) {
const record = asOptionalRecord(model);
if (!modelIdMatchesProviderModelEntry({
entryId: record?.id,
provider: params.provider,
modelId: params.modelId
})) continue;
const runtimeId = normalizeRuntimeString(asOptionalRecord(record?.agentRuntime)?.id);
if (runtimeId && runtimeId !== "auto" && runtimeId !== "default" && runtimeId !== "codex") return runtimeId;
}
}
}
function agentModelMapExactRuntimeIdForLegacyRef(params) {
const parsed = parseModelRef(params.legacyModelRef);
if (!parsed) return;
const agentId = normalizeAgentId(params.agentId);
const modelMaps = [asOptionalRecord((agentId ? (params.cfg.agents?.list ?? []).find((entry) => normalizeAgentId(entry.id) === agentId) : void 0)?.models), asOptionalRecord(params.cfg.agents?.defaults?.models)];
for (const models of modelMaps) for (const [key, entry] of Object.entries(models ?? {})) {
if (!modelIdMatchesProviderModelEntry({
entryId: key,
provider: parsed.provider,
modelId: parsed.modelId
})) continue;
const runtimeId = normalizeRuntimeString(asOptionalRecord(asOptionalRecord(entry)?.agentRuntime)?.id);
if (runtimeId && runtimeId !== "auto" && runtimeId !== "default") return runtimeId;
}
}
function preRepairLegacyModelPolicyExplicitNonDefaultRuntimePin(params) {
if (!params.legacyModelRef || !isOpenAICodexModelRef(params.legacyModelRef)) return;
const parsed = parseModelRef(params.legacyModelRef);
if (!parsed) return;
const resolved = resolveModelRuntimePolicy({
config: params.cfg,
provider: parsed.provider,
modelId: parsed.modelId,
agentId: params.agentId
});
const runtimeId = normalizeRuntimeString(resolved.policy?.id);
if (!runtimeId || runtimeId === "auto" || runtimeId === "default" || runtimeId === "codex") return;
if (resolved.source === "model") {
const providerModelRuntimeId = providerModelExplicitNonDefaultRuntimeId({
cfg: params.cfg,
provider: parsed.provider,
modelId: parsed.modelId
});
const agentModelRuntimeId = agentModelMapExactRuntimeIdForLegacyRef({
cfg: params.cfg,
legacyModelRef: params.legacyModelRef,
agentId: params.agentId
});
if (providerModelRuntimeId === runtimeId && !agentModelRuntimeId) return {
runtimeId,
source: "provider-model"
};
}
return {
runtimeId,
source: resolved.source ?? "model"
};
}
function ensureCodexRuntimePolicy(params) {
const models = asOptionalRecord(params.agent.models);
const runtimeId = normalizeOptionalLowercaseString(asOptionalRecord(asOptionalRecord(models?.[params.modelRef])?.agentRuntime)?.id);
const pinnedRuntimeId = runtimeId && runtimeId !== "auto" && runtimeId !== "default" ? runtimeId : void 0;
const legacyModelRuntimeId = models ? legacyEntryExplicitNonDefaultRuntimeId(models, params.modelRef) : void 0;
const preRepairRuntimePin = preRepairLegacyModelPolicyExplicitNonDefaultRuntimePin({
cfg: params.preRepairCfg ?? params.cfg,
legacyModelRef: params.legacyModelRef,
agentId: params.agentId
});
const targetRuntimeId = pinnedRuntimeId ?? legacyModelRuntimeId ?? (preRepairRuntimePin?.source === "provider" || preRepairRuntimePin?.source === "provider-model" ? preRepairRuntimePin.runtimeId : void 0) ?? "codex";
if (params.isDefaults) shieldExplicitListedAgentRefsFromDefaultPolicy({
cfg: params.cfg,
modelRef: params.modelRef,
targetRuntimeId,
changes: params.changes
});
if (pinnedRuntimeId) return;
if (legacyModelRuntimeId) return;
if (preRepairRuntimePin) {
if (preRepairRuntimePin.source === "provider" || preRepairRuntimePin.source === "provider-model") setModelRuntimePolicy({
agent: params.agent,
agentPath: params.agentPath,
modelRef: params.modelRef,
runtimeId: preRepairRuntimePin.runtimeId,
changes: params.changes,
reason: "so legacy provider runtime pins survive Codex route repair"
});
return;
}
setModelRuntimePolicy({
agent: params.agent,
agentPath: params.agentPath,
modelRef: params.modelRef,
runtimeId: "codex",
changes: params.changes,
reason: "so repaired OpenAI refs keep Codex auth routing"
});
}
function canonicalOpenAIModelUsesCodexRuntime(params) {
const slash = params.modelRef.indexOf("/");
if (slash <= 0 || slash >= params.modelRef.length - 1) return false;
const parsed = parseModelRef(params.modelRef);
if (!parsed) return false;
const configured = normalizeRuntimeString(resolveModelRuntimePolicy({
config: params.cfg,
provider: parsed.provider,
modelId: parsed.modelId,
agentId: params.agentId
}).policy?.id);
if (configured && configured !== "auto" && configured !== "default") return configured === "codex";
return openAIProviderUsesCodexRuntimeByDefault({
provider: parsed.provider,
config: params.cfg
});
}
function rewriteStringModelSlotIfCanonicalCodexRuntime(params) {
const value = params.container?.[params.key];
if (typeof value !== "string") return;
const canonicalModel = toCanonicalOpenAIModelRef(value.trim());
if (!canonicalModel || !canonicalOpenAIModelUsesCodexRuntime({
cfg: params.cfg,
modelRef: canonicalModel,
agentId: params.agentId
})) return;
rewriteStringModelSlot({
hits: params.hits,
container: params.container,
key: params.key,
path: params.path
});
}
function rewriteModelConfigSlotIfCanonicalCodexRuntime(params) {
const value = params.container?.[params.key];
if (typeof value === "string") {
rewriteStringModelSlotIfCanonicalCodexRuntime(params);
return;
}
const record = asOptionalRecord(value);
if (!record) return;
rewriteStringModelSlotIfCanonicalCodexRuntime({
cfg: params.cfg,
agentId: params.agentId,
hits: params.hits,
container: record,
key: "primary",
path: `${params.path}.primary`
});
const fallbacks = Array.isArray(record.fallbacks) ? record.fallbacks : void 0;
if (!fallbacks) return;
for (const [index, entry] of fallbacks.entries()) {
if (typeof entry !== "string") continue;
const canonicalModel = toCanonicalOpenAIModelRef(entry.trim());
if (!canonicalModel || !canonicalOpenAIModelUsesCodexRuntime({
cfg: params.cfg,
modelRef: canonicalModel,
agentId: params.agentId
})) continue;
fallbacks[index] = canonicalModel;
params.hits.push({
path: `${params.path}.fallbacks.${index}`,
model: entry.trim(),
canonicalModel
});
}
}
function clearLegacyAgentRuntimePolicy(container, pathLabel, changes) {
if (!container) return;
if (asOptionalRecord(container.embeddedHarness)) {
delete container.embeddedHarness;
changes.push(`Removed ${pathLabel}.embeddedHarness; runtime is now provider/model scoped.`);
}
if (asOptionalRecord(container.agentRuntime)) {
delete container.agentRuntime;
changes.push(`Removed ${pathLabel}.agentRuntime; runtime is now provider/model scoped.`);
}
}
function clearConfigLegacyAgentRuntimePolicies(cfg) {
const changes = [];
clearLegacyAgentRuntimePolicy(asOptionalRecord(cfg.agents?.defaults), "agents.defaults", changes);
const agents = Array.isArray(cfg.agents?.list) ? cfg.agents.list : [];
for (const [index, agent] of agents.entries()) {
const agentRecord = asOptionalRecord(agent);
if (!agentRecord) continue;
clearLegacyAgentRuntimePolicy(agentRecord, `agents.list.${typeof agentRecord.id === "string" && agentRecord.id.trim() ? agentRecord.id.trim() : String(index)}`, changes);
}
return changes;
}
function isCompactionOnlyRouteHit(hit) {
return hit.path.startsWith("agents.") && (hit.path.endsWith(".compaction.model") || hit.path.endsWith(".compaction.memoryFlush.model"));
}
function rewriteConfigModelRefsWithCompactionPolicy(params) {
const nextConfig = structuredClone(params.cfg);
const preRepairCfg = params.cfg;
const hits = [];
const runtimePolicyChanges = [];
const unsupportedCompactionChanges = [];
const ignoreLegacyAgentRuntimePins = params.ignoreLegacyAgentRuntimePins ?? configRepairWouldClearLegacyRuntimePins({ cfg: nextConfig });
unsupportedCompactionChanges.push(...maybeMigrateLegacyLosslessCompactionConfig({
cfg: nextConfig,
ignoreLegacyAgentRuntimePins
}));
const preservedLegacyLosslessCompactionPaths = new Set(collectLegacyLosslessCompactionConfigs({
cfg: nextConfig,
ignoreLegacyAgentRuntimePins
}).flatMap((hit) => hit.modelPath ? [hit.providerPath, hit.modelPath] : [hit.providerPath]));
const defaultsRuntime = ignoreLegacyAgentRuntimePins ? void 0 : readLegacyDefaultsRuntime(nextConfig.agents?.defaults);
const rewrittenInheritedCompactionModels = /* @__PURE__ */ new Map();
rewriteAgentModelRefs({
cfg: nextConfig,
preRepairCfg,
hits,
agent: asOptionalRecord(nextConfig.agents?.defaults),
path: "agents.defaults",
currentRuntime: resolveRuntime({ defaultsRuntime }),
rewriteModelsMap: true,
preserveUnsupportedCompactionOverrides: params.preserveSharedDefaultCompactionOverrides,
preserveUnsupportedCompactionPaths: preservedLegacyLosslessCompactionPaths,
rewrittenInheritedCompactionModels,
runtimePolicyChanges,
unsupportedCompactionChanges
});
const inheritedModelRef = readAgentPrimaryModelRef(nextConfig.agents?.defaults);
const agents = Array.isArray(nextConfig.agents?.list) ? nextConfig.agents.list : [];
for (const [index, agent] of agents.entries()) {
const agentRecord = asOptionalRecord(agent);
if (!agentRecord) continue;
const id = typeof agentRecord.id === "string" && agentRecord.id.trim() ? agentRecord.id.trim() : String(index);
rewriteAgentModelRefs({
cfg: nextConfig,
preRepairCfg,
hits,
agent: agentRecord,
path: `agents.list.${id}`,
agentId: id,
currentRuntime: resolveRuntime({
agentRuntime: ignoreLegacyAgentRuntimePins ? void 0 : asAgentRuntimePolicyConfig(agentRecord.agentRuntime),
defaultsRuntime
}),
inheritedModelRef,
inheritedCompaction: nextConfig.agents?.defaults?.compaction,
inheritedCompactionPath: "agents.defaults.compaction",
rewriteModelsMap: true,
preserveUnsupportedCompactionPaths: preservedLegacyLosslessCompactionPaths,
rewrittenInheritedCompactionModels,
runtimePolicyChanges,
unsupportedCompactionChanges
});
}
const channelsModelByChannel = asOptionalRecord(nextConfig.channels?.modelByChannel);
if (channelsModelByChannel) for (const [channelId, channelMap] of Object.entries(channelsModelByChannel)) {
const targets = asOptionalRecord(channelMap);
if (!targets) continue;
for (const targetId of Object.keys(targets)) rewriteStringModelSlotIfCanonicalCodexRuntime({
cfg: nextConfig,
hits,
container: targets,
key: targetId,
path: `channels.modelByChannel.${channelId}.${targetId}`
});
}
for (const [index, mapping] of (nextConfig.hooks?.mappings ?? []).entries()) rewriteStringModelSlotIfCanonicalCodexRuntime({
cfg: nextConfig,
hits,
container: mapping,
key: "model",
path: `hooks.mappings.${index}.model`
});
rewriteStringModelSlotIfCanonicalCodexRuntime({
cfg: nextConfig,
hits,
container: asOptionalRecord(nextConfig.hooks?.gmail),
key: "model",
path: "hooks.gmail.model"
});
rewriteStringModelSlotIfCanonicalCodexRuntime({
cfg: nextConfig,
hits,
container: asOptionalRecord(nextConfig.messages?.tts),
key: "summaryModel",
path: "messages.tts.summaryModel"
});
rewriteStringModelSlotIfCanonicalCodexRuntime({
cfg: nextConfig,
hits,
container: asOptionalRecord(asOptionalRecord(nextConfig.channels?.discord)?.voice),
key: "model",
path: "channels.discord.voice.model"
});
const runtimePinChanges = hits.some((hit) => !isCompactionOnlyRouteHit(hit)) ? clearConfigLegacyAgentRuntimePolicies(nextConfig) : [];
return {
cfg: hits.length > 0 || runtimePolicyChanges.length > 0 || runtimePinChanges.length > 0 || unsupportedCompactionChanges.length > 0 ? nextConfig : params.cfg,
changes: hits,
runtimePinChanges,
runtimePolicyChanges,
unsupportedCompactionChanges
};
}
function configRepairWouldClearLegacyRuntimePins(params) {
return rewriteConfigModelRefsWithCompactionPolicy({
cfg: params.cfg,
preserveSharedDefaultCompactionOverrides: {
model: true,
provider: true
},
ignoreLegacyAgentRuntimePins: false
}).changes.some((hit) => !isCompactionOnlyRouteHit(hit));
}
function rewriteConfigModelRefs(params) {
const preserveSharedDefaultCompactionOverrides = getSharedDefaultCompactionOverrideConsumers({
cfg: params.cfg,
ignoreLegacyAgentRuntimePins: configRepairWouldClearLegacyRuntimePins(params)
});
return rewriteConfigModelRefsWithCompactionPolicy({
cfg: params.cfg,
preserveSharedDefaultCompactionOverrides
});
}
function formatCodexRouteChange(hit) {
return `${hit.path}: ${hit.model} -> ${hit.canonicalModel}.`;
}
function formatUnsupportedCompactionWarning(params) {
return [
"- Codex runtime uses native server-side compaction and ignores OpenClaw compaction summarizer overrides.",
...params.hits.map((hit) => `- ${hit.path}: ${hit.value} is ignored while this agent uses Codex runtime.`),
params.fixHint
].join("\n");
}
function formatLegacyLosslessCompactionWarning(params) {
const configLines = [];
const providerPaths = /* @__PURE__ */ new Set();
for (const hit of params.hits) {
if (!providerPaths.has(hit.providerPath)) {
providerPaths.add(hit.providerPath);
configLines.push(`- ${hit.providerPath}: ${hit.providerValue} should become plugins.slots.contextEngine: ${LOSSLESS_CONTEXT_ENGINE_ID}.`);
}
if (hit.modelPath && hit.modelValue) configLines.push(`- ${hit.modelPath}: ${hit.modelValue} should become plugins.entries.${LOSSLESS_CONTEXT_ENGINE_ID}.config.summaryModel.`);
}
return [
"- Legacy Lossless compaction config should use the Lossless context-engine slot for Codex.",
...configLines,
params.canAutoFix ? "- Run `openclaw doctor --fix`: it migrates legacy Lossless compaction config to the Lossless context-engine slot." : "- Move the Lossless config manually; doctor will not overwrite an existing non-Lossless context-engine slot or collapse conflicting per-agent summary models."
].join("\n");
}
function formatDisabledCodexPluginWarning(params) {
const fixHint = params.blockedOutsideEntry ? "- Enable plugin loading and remove `codex` from plugins.deny, or set the affected OpenAI models to an OpenClaw runtime policy." : "- Run `openclaw doctor --fix`: it enables plugins.entries.codex, or set the affected OpenAI models to an OpenClaw runtime policy.";
return [
"- Codex runtime is selected, but the Codex plugin is disabled.",
...params.hits.map((hit) => `- ${hit.path}: ${hit.modelRef} resolves to ${hit.canonicalModel} with Codex runtime while the Codex plugin is disabled by config.`),
fixHint
].join("\n");
}
function collectCodexAppServerCommandWarnings(cfg) {
const appServer = asOptionalRecord(asOptionalRecord(asOptionalRecord(asOptionalRecord(asOptionalRecord(cfg.plugins)?.entries)?.codex)?.config)?.appServer);
const command = typeof appServer?.command === "string" ? appServer.command.trim() : "";
if (!command) return [];
const inlineArgs = detectWindowsSpawnCommandInlineArgs(command);
if (!inlineArgs) return [];
return [[
"- Codex app-server command override includes inline arguments.",
`- plugins.entries.codex.config.appServer.command: "${command}" starts with "${inlineArgs.executable}" and embeds "${inlineArgs.arguments}". The command field must be only the executable path.`,
"- Remove the override to use managed Codex startup, or move script/options to plugins.entries.codex.config.appServer.args."
].join("\n")];
}
/** Collect doctor warnings for legacy Codex model refs, runtime pins, and compaction overrides. */
function collectCodexRouteWarnings(params) {
const hits = collectConfigModelRefs(params.cfg);
const disabledCodexPluginHits = collectDisabledCodexPluginRouteHits(params.cfg);
const ignoreLegacyAgentRuntimePins = configRepairWouldClearLegacyRuntimePins(params);
const legacyLosslessCompactionConfigs = collectLegacyLosslessCompactionConfigs({
cfg: params.cfg,
ignoreLegacyAgentRuntimePins
});
const legacyLosslessCompactionPaths = new Set(legacyLosslessCompactionConfigs.flatMap((hit) => hit.modelPath ? [hit.providerPath, hit.modelPath] : [hit.providerPath]));
const unsupportedCompactionOverrides = collectUnsupportedCodexCompactionOverrides({
cfg: params.cfg,
ignoreLegacyAgentRuntimePins
}).filter((hit) => !legacyLosslessCompactionPaths.has(hit.path));
const sharedDefaultCompactionConsumers = getSharedDefaultCompactionOverrideConsumers({
cfg: params.cfg,
ignoreLegacyAgentRuntimePins: configRepairWouldClearLegacyRuntimePins(params)
});
const sharedLosslessDefaultHasNonCodexConsumer = sharedDefaultLosslessCompactionHasNonCodexConsumer({
cfg: params.cfg,
ignoreLegacyAgentRuntimePins
});
const warnings = [];
warnings.push(...collectCodexAppServerCommandWarnings(params.cfg));
if (hits.length > 0) warnings.push([
"- Legacy `openai-codex/*` model refs should be rewritten to `openai/*`.",
...hits.map((hit) => `- ${hit.path}: ${hit.model} should become ${hit.canonicalModel}${hit.runtime ? `; current runtime is "${hit.runtime}"` : ""}.`),
"- Run `openclaw doctor --fix`: it rewrites configured model refs and stale sessions to `openai/*`, moves Codex intent to provider/model runtime policy, and clears old whole-agent runtime pins."
].join("\n"));
if (legacyLosslessCompactionConfigs.length > 0) {
const plugins = asOptionalRecord(params.cfg.plugins);
const contextEngine = normalizeOptionalLowercaseString(asOptionalRecord(plugins?.slots)?.contextEngine);
warnings.push(formatLegacyLosslessCompactionWarning({
hits: legacyLosslessCompactionConfigs,
canAutoFix: !sharedLosslessDefaultHasNonCodexConsumer && canAutoMigrateLegacyLosslessCompaction({
hits: legacyLosslessCompactionConfigs,
contextEngine,
summaryModel: readLosslessSummaryModel(plugins)
})
}));
}
if (disabledCodexPluginHits.length > 0) warnings.push(formatDisabledCodexPluginWarning({
hits: disabledCodexPluginHits,
blockedOutsideEntry: codexPluginIsBlockedOutsideEntry(params.cfg)
}));
const preservedSharedDefaultHits = unsupportedCompactionOverrides.filter((hit) => hit.path.startsWith("agents.defaults.compaction.") && sharedDefaultCompactionConsumers[hit.key]);
const fixableHits = unsupportedCompactionOverrides.filter((hit) => !hit.path.startsWith("agents.defaults.compaction.") || !sharedDefaultCompactionConsumers[hit.key]);
if (preservedSharedDefaultHits.length > 0) warnings.push(formatUnsupportedCompactionWarning({
hits: preservedSharedDefaultHits,
fixHint: "- Move or remove shared `agents.defaults.compaction.model/provider` settings manually; doctor keeps shared defaults while non-Codex agents can inherit them."
}));
if (fixableHits.length > 0) warnings.push(formatUnsupportedCompactionWarning({
hits: fixableHits,
fixHint: "- Run `openclaw doctor --fix`: it removes unsupported Codex compaction overrides."
}));
return warnings;
}
/** Rewrite legacy Codex config routes to OpenAI refs and explicit runtime policy when allowed. */
function maybeRepairCodexRoutes(params) {
const hits = collectConfigModelRefs(params.cfg);
const disabledCodexPluginHits = collectDisabledCodexPluginRouteHits(params.cfg);
const ignoreLegacyAgentRuntimePins = configRepairWouldClearLegacyRuntimePins(params);
const unsupportedCompactionOverrides = collectUnsupportedCodexCompactionOverrides({
cfg: params.cfg,
ignoreLegacyAgentRuntimePins
});
const legacyLosslessCompactionConfigs = collectLegacyLosslessCompactionConfigs({
cfg: params.cfg,
ignoreLegacyAgentRuntimePins
});
if (hits.length === 0 && disabledCodexPluginHits.length === 0 && unsupportedCompactionOverrides.length === 0 && legacyLosslessCompactionConfigs.length === 0) return {
cfg: params.cfg,
warnings: [],
changes: []
};
if (!params.shouldRepair) return {
cfg: params.cfg,
warnings: collectCodexRouteWarnings({
cfg: params.cfg,
env: params.env
}),
changes: []
};
const repaired = rewriteConfigModelRefs({ cfg: params.cfg });
const codexPluginRepair = enableCodexPluginForRequiredRoutes({
cfg: repaired.cfg,
routeHits: collectDisabledCodexPluginRouteHits(repaired.cfg)
});
const warnings = collectCodexRouteWarnings({
cfg: codexPluginRepair.cfg,
env: params.env
});
const changes = repaired.changes.length > 0 ? [`Repaired Codex model routes:\n${repaired.changes.map((hit) => `- ${formatCodexRouteChange(hit)}`).join("\n")}`] : [];
return {
cfg: codexPluginRepair.cfg,
warnings,
changes: [
...changes,
...repaired.runtimePolicyChanges,
...repaired.runtimePinChanges,
...repaired.unsupportedCompactionChanges,
...codexPluginRepair.changes
]
};
}
function rewriteSessionModelPair(params) {
let changed = false;
const provider = normalizeOptionalLowercaseString(params.entry[params.providerKey]);
const model = typeof params.entry[params.modelKey] === "string" ? params.entry[params.modelKey] : void 0;
if (provider === "openai-codex") {
params.entry[params.providerKey] = "openai";
if (model) {
const modelId = toOpenAIModelId(model);
if (modelId) params.entry[params.modelKey] = modelId;
}
return true;
}
if (model && isOpenAICodexModelRef(model)) {
const canonicalModel = toCanonicalOpenAIModelRef(model);
if (canonicalModel) {
params.entry[params.modelKey] = canonicalModel;
changed = true;
}
}
return changed;
}
function clearStaleCodexFallbackNotice(entry) {
if (!isOpenAICodexModelRef(entry.fallbackNoticeSelectedModel) && !isOpenAICodexModelRef(entry.fallbackNoticeActiveModel)) return false;
delete entry.fallbackNoticeSelectedModel;
delete entry.fallbackNoticeActiveModel;
delete entry.fallbackNoticeReason;
return true;
}
function clearStaleSessionRuntimePins(entry) {
const harnessRuntime = normalizeRuntimeString(entry.agentHarnessId);
const overrideRuntime = normalizeRuntimeString(entry.agentRuntimeOverride);
let changed = false;
if (entry.agentHarnessId !== void 0 && harnessRuntime !== "openclaw") {
delete entry.agentHarnessId;
changed = true;
}
if (entry.agentRuntimeOverride !== void 0 && overrideRuntime !== "openclaw") {
delete entry.agentRuntimeOverride;
changed = true;
}
return changed;
}
function repairProviderlessCodexSessionOverride(entry) {
if (!isProviderlessModelRef(entry.modelOverride) || !isOpenAICodexAuthProfileRef(entry.authProfileOverride) || entry.authProfileOverrideSource !== "auto" || entry.modelOverrideSource !== "auto" || normalizeOptionalLowercaseString(entry.providerOverride)) return false;
entry.providerOverride = "openai";
if (entry.model !== void 0 || entry.modelProvider !== void 0) {
delete entry.model;
delete entry.modelProvider;
}
if (entry.contextTokens !== void 0) delete entry.contextTokens;
if (entry.contextBudgetStatus !== void 0) delete entry.contextBudgetStatus;
return true;
}
/** Rewrite stale Codex model/provider/session runtime fields inside one session store object. */
function repairCodexSessionStoreRoutes(params) {
const now = params.now ?? Date.now();
const sessionKeys = [];
for (const [sessionKey, entry] of Object.entries(params.store)) {
if (!entry) continue;
const changedRuntimeModelRoute = rewriteSessionModelPair({
entry,
providerKey: "modelProvider",
modelKey: "model"
});
const changedOverrideModelRoute = rewriteSessionModelPair({
entry,
providerKey: "providerOverride",
modelKey: "modelOverride"
});
const changedProviderlessOverride = repairProviderlessCodexSessionOverride(entry);
const changedModelRoute = changedRuntimeModelRoute || changedOverrideModelRoute || changedProviderlessOverride;
const changedFallbackNotice = clearStaleCodexFallbackNotice(entry);
const changedRuntimePins = changedModelRoute || changedFallbackNotice ? clearStaleSessionRuntimePins(entry) : false;
if (!changedModelRoute && !changedFallbackNotice && !changedRuntimePins) continue;
entry.updatedAt = now;
sessionKeys.push(sessionKey);
}
return {
changed: sessionKeys.length > 0,
sessionKeys
};
}
function scanCodexSessionStoreRoutes(store) {
return Object.entries(store).flatMap(([sessionKey, entry]) => {
if (!entry) return [];
return normalizeOptionalLowercaseString(entry.modelProvider) === "openai-codex" || normalizeOptionalLowercaseString(entry.providerOverride) === "openai-codex" || isOpenAICodexModelRef(entry.model) || isOpenAICodexModelRef(entry.modelOverride) || isProviderlessModelRef(entry.modelOverride) && isOpenAICodexAuthProfileRef(entry.authProfileOverride) && entry.authProfileOverrideSource === "auto" && entry.modelOverrideSource === "auto" && !normalizeOptionalLowercaseString(entry.providerOverride) || isOpenAICodexModelRef(entry.fallbackNoticeSelectedModel) || isOpenAICodexModelRef(entry.fallbackNoticeActiveModel) ? [sessionKey] : [];
});
}
/** Scan or repair all configured agent session stores that still contain legacy Codex routes. */
async function maybeRepairCodexSessionRoutes(params) {
const targets = resolveAllAgentSessionStoreTargetsSync(params.cfg, { env: params.env ?? process.env }).filter((target) => fs.existsSync(target.storePath));
if (targets.length === 0) return {
scannedStores: 0,
repairedStores: 0,
repairedSessions: 0,
warnings: [],
changes: []
};
if (!params.shouldRepair) {
const stale = targets.flatMap((target) => {
return scanCodexSessionStoreRoutes(loadSessionStore(target.storePath, {
skipCache: true,
clone: false
})).map((sessionKey) => `${target.agentId}:${sessionKey}`);
});
return {
scannedStores: targets.length,
repairedStores: 0,
repairedSessions: 0,
warnings: stale.length > 0 ? [[
"- Legacy `openai-codex/*` session route state detected.",
`- Affected sessions: ${stale.length}.`,
"- Run `openclaw doctor --fix` to rewrite stale session model/provider pins across all agent session stores."
].join("\n")] : [],
changes: []
};
}
let repairedStores = 0;
let repairedSessions = 0;
for (const target of targets) {
if (scanCodexSessionStoreRoutes(loadSessionStore(target.storePath, {
skipCache: true,
clone: false
})).length === 0) continue;
const result = await updateSessionStore(target.storePath, (store) => repairCodexSessionStoreRoutes({ store }), { skipMaintenance: true });
if (!result.changed) continue;
repairedStores += 1;
repairedSessions += result.sessionKeys.length;
}
return {
scannedStores: targets.length,
repairedStores,
repairedSessions,
warnings: [],
changes: repairedSessions > 0 ? [`Repaired Codex session routes: moved ${repairedSessions} session${repairedSessions === 1 ? "" : "s"} across ${repairedStores} store${repairedStores === 1 ? "" : "s"} to openai/* while preserving auth-profile pins.`] : []
};
}
//#endregion
export { repairCodexSessionStoreRoutes as a, maybeRepairCodexSessionRoutes as i, collectDisabledCodexPluginRouteIssues as n, maybeRepairCodexRoutes as r, collectCodexRouteWarnings as t };