openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
2,149 lines • 92.8 kB
JavaScript
import { a as asOptionalRecord, c as isRecord } from "./record-coerce-DItp3I4t.js";
import { a as normalizeFastMode, c as normalizeOptionalLowercaseString } from "./string-coerce-CIXf7egm.js";
import { n as normalizeAgentId } from "./agent-id-CeT3w4ap.js";
import { u as resolveAgentDir } from "./agent-scope-config-DcbEhP0R.js";
import { r as normalizeProviderId } from "./provider-id-DMd-TDFp.js";
import "./session-key-BnWWjqNc.js";
import { t as AGENT_MODEL_CONFIG_KEYS } from "./configured-model-refs-DEXTV_K3.js";
import { i as rewriteKnownModelRefs, n as collectBlockedLegacyOpenAICodexProviderPlan } from "./legacy-config-migrations.runtime.models-Ak4QjGte.js";
import { h as migrateLegacyRuntimeModelRef } from "./legacy-D15RYgFr.js";
import { S as isLegacyCodexProviderId, _ as resolveImplicitDefaultAgentModelRef, a as isBlockedLegacyCodexModelRef, b as toCanonicalOpenAIModelRef, c as isProviderlessModelRef, d as normalizeDefaultProviderModelRef, f as normalizeRuntimeString, g as readModelConfigPrimaryRef, h as readLegacyDefaultsRuntime, i as isBlockedLegacyCodexModelPair, m as readAgentPrimaryModelRef, n as asAgentRuntimePolicyConfig, o as isOpenAICodexAuthProfileRef, p as parseCodexRouteModelRef, r as canonicalOpenAIModelUsesCodexRuntime, s as isOpenAICodexModelRef, t as agentUsesCodexRuntimeForCompaction, u as modelRefUsesCodexRuntime, v as resolveRuntime, x as toOpenAIModelId, y as resolveRuntimeModelRef } from "./codex-route-model-ref-Br4ltBF-.js";
import "./agent-scope-DbtJyKUL.js";
import { t as resolveModelRuntimePolicy } from "./model-runtime-policy-BAKiaBCi.js";
import { m as resolveModelExtraParamSources, p as isAgentRuntimeModelParam } from "./openai-routing-Ex_3E9TF.js";
import { t as listMutableCodexRouteAgentEntries } from "./codex-route-agent-entries-BI7NtrYf.js";
import { S as hasMatchingOAuthIdentity, a as loadPersistedAuthProfileStore, l as parseLegacyCredentialEntry, s as loadPersistedSharedAuthProfileStore, x as areOAuthCredentialsEquivalent } from "./persisted-B_qhhBlh.js";
import { t as loadJsonFileThroughSymlink } from "./json-file-CHTTn1IF.js";
import { Gt as applySessionEntryReplacements, lt as iterateDoctorSessionKeyBatches, mt as scanDoctorSessionEntriesTolerant, pt as scanDoctorSessionEntriesStrict } from "./session-accessor-YsytfDtG.js";
import { o as resolveAllAgentSessionStoreTargetsSync } from "./targets-Cknmo7YZ.js";
import { c as isValidAgentHarnessSessionStoreEntry } from "./agent-harness-session-key-BOz3yx0-.js";
import { n as detectWindowsSpawnCommandInlineArgs } from "./windows-spawn-oyE-2b7Z.js";
import { i as updateLegacySessionStore, t as loadLegacySessionStore } from "./state-migrations.legacy-session-store-CQJX6yZy.js";
import { n as resolveLegacyAuthProfilesPath } from "./doctor-auth-legacy-paths-CCOCSqnd.js";
import fs from "node:fs";
//#region src/commands/doctor/shared/codex-route-compaction-scan.ts
const COMPACTION_OVERRIDE_KEYS = ["model", "provider"];
const LOSSLESS_CONTEXT_ENGINE_ID = "lossless-claw";
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,
env: params.env
})) return [];
const providerValue = compaction?.provider ?? inheritedCompaction?.provider;
if (normalizeOptionalLowercaseString(providerValue) === "lossless-claw") 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,
env: params.env
})) return [];
const localProvider = compaction?.provider;
const hasLocalProvider = typeof localProvider === "string" && localProvider.trim();
const providerValue = hasLocalProvider ? localProvider : inheritedCompaction?.provider;
if (normalizeOptionalLowercaseString(providerValue) !== "lossless-claw") 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 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 }),
env: params.env
});
for (const { agent: agentRecord, agentId: id, path } of listMutableCodexRouteAgentEntries(params.cfg)) hits.push(...collectLegacyLosslessCompactionForAgent({
cfg: params.cfg,
agent: agentRecord,
path,
agentId: id,
currentRuntime: resolveRuntime({
agentRuntime: params.ignoreLegacyAgentRuntimePins ? void 0 : asAgentRuntimePolicyConfig(agentRecord.agentRuntime),
defaultsRuntime
}),
inheritedModelRef: defaultModelRef,
inheritedCompaction: defaultCompaction,
inheritedCompactionPath: "agents.defaults.compaction",
env: params.env
}));
return dedupeLegacyLosslessCompactionConfigs(hits);
}
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 }),
env: params.env
});
for (const { agent: agentRecord, agentId: id, path } of listMutableCodexRouteAgentEntries(params.cfg)) hits.push(...collectUnsupportedCodexCompactionOverridesForAgent({
cfg: params.cfg,
agent: agentRecord,
path,
agentId: id,
currentRuntime: resolveRuntime({
agentRuntime: params.ignoreLegacyAgentRuntimePins ? void 0 : asAgentRuntimePolicyConfig(agentRecord.agentRuntime),
defaultsRuntime
}),
inheritedModelRef: defaultModelRef,
inheritedCompaction: defaultCompaction,
inheritedCompactionPath: "agents.defaults.compaction",
env: params.env
}));
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 }),
env: params.env
})) {
consumers.model ||= Boolean(hasDefaultModel);
consumers.provider ||= Boolean(hasDefaultProvider);
if ((!hasDefaultModel || consumers.model) && (!hasDefaultProvider || consumers.provider)) return consumers;
}
for (const { agent: agentRecord, agentId: id } of listMutableCodexRouteAgentEntries(params.cfg)) {
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;
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,
env: params.env
})) {
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 }),
env: params.env
})) return true;
const inheritedModelRef = readAgentPrimaryModelRef(defaults);
for (const { agent: agentRecord, agentId: id } of listMutableCodexRouteAgentEntries(params.cfg)) {
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;
if (!agentUsesCodexRuntimeForCompaction({
cfg: params.cfg,
agent: agentRecord,
agentId: id,
env: params.env,
currentRuntime: resolveRuntime({
agentRuntime: params.ignoreLegacyAgentRuntimePins ? void 0 : asAgentRuntimePolicyConfig(agentRecord.agentRuntime),
defaultsRuntime
}),
inheritedModelRef
})) return true;
}
return false;
}
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 canAutoMigrateLegacyLosslessCompaction(params) {
if (params.contextEngine && params.contextEngine !== "lossless-claw") 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 entries = asOptionalRecord(plugins?.entries);
const entry = asOptionalRecord(entries?.[LOSSLESS_CONTEXT_ENGINE_ID]);
const config = asOptionalRecord(entry?.config);
return typeof config?.summaryModel === "string" && config.summaryModel.trim() ? config.summaryModel.trim() : void 0;
}
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 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;
});
}
//#endregion
//#region src/commands/doctor/shared/codex-route-model-slots.ts
function recordCodexModelHit(params) {
if (isBlockedLegacyCodexModelRef({
modelRef: params.model,
blockedModelIdentities: params.blockedModelIdentities
})) return;
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,
blockedModelIdentities: params.blockedModelIdentities
}));
}
function collectModelConfigSlot(params) {
if (typeof params.value === "string") return collectStringModelSlot(params);
const record = asOptionalRecord(params.value);
if (!record) return false;
const rewrotePrimary = collectStringModelSlot({
hits: params.hits,
path: `${params.path}.primary`,
value: record.primary,
runtime: params.runtime,
blockedModelIdentities: params.blockedModelIdentities
});
if (Array.isArray(record.fallbacks)) for (const [index, entry] of record.fallbacks.entries()) collectStringModelSlot({
hits: params.hits,
path: `${params.path}.fallbacks.${index}`,
value: entry,
blockedModelIdentities: params.blockedModelIdentities
});
return rewrotePrimary;
}
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 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,
blockedModelIdentities: params.blockedModelIdentities
});
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(params);
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,
blockedModelIdentities: params.blockedModelIdentities
});
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,
blockedModelIdentities: params.blockedModelIdentities
}) ?? 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;
if (!recordCodexModelHit({
hits: params.hits,
path: `${params.path}.${legacyRef}`,
model: legacyRef,
blockedModelIdentities: params.blockedModelIdentities
})) continue;
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 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 runtimePolicyHasExplicitNonDefaultPin(value) {
const id = normalizeOptionalLowercaseString(asOptionalRecord(value)?.id);
return Boolean(id && id !== "auto" && id !== "default");
}
//#endregion
//#region src/commands/doctor/shared/codex-route-runtime-policy.ts
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 resolveCurrentRuntimeIdForCanonicalModel(params) {
const parsed = parseCodexRouteModelRef(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 canonicalOpenAIModelUsesCodexRuntime({
cfg: params.cfg,
modelRef: params.modelRef,
agentId: params.agentId,
env: params.env
}) ? "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 { agent, agentId, path } of listMutableCodexRouteAgentEntries(params.cfg)) {
if (!agentExplicitlyReferencesCanonicalModel(agent, params.modelRef)) continue;
const runtimeId = resolveCurrentRuntimeIdForCanonicalModel({
cfg: params.cfg,
modelRef: params.modelRef,
agentId,
env: params.env
});
if (runtimeId === params.targetRuntimeId) continue;
setModelRuntimePolicy({
agent,
agentPath: path,
modelRef: params.modelRef,
runtimeId,
changes: params.changes,
reason: "so default runtime repair does not change explicit agent routing"
});
}
}
function legacyEntryExplicitNonDefaultRuntimeId(models, canonicalModelRef) {
for (const ref of Object.keys(models)) {
if (ref === canonicalModelRef || toCanonicalOpenAIModelRef(ref) !== canonicalModelRef) continue;
const legacyEntry = asOptionalRecord(models[ref]);
const id = normalizeOptionalLowercaseString(asOptionalRecord(legacyEntry?.agentRuntime)?.id);
if (id && id !== "auto" && id !== "default") return id;
}
}
function agentIdFromAgentPath(agentPath) {
for (const prefix of ["agents.entries.", "agents.list."]) if (agentPath.startsWith(prefix)) return agentPath.slice(prefix.length);
}
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 = parseCodexRouteModelRef(params.legacyModelRef);
if (!parsed) return;
const agentId = normalizeAgentId(params.agentId);
const agent = agentId ? listMutableCodexRouteAgentEntries(params.cfg).find((entry) => entry.agentId === agentId)?.agent : void 0;
const modelMaps = [asOptionalRecord(agent?.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 = parseCodexRouteModelRef(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 entry = asOptionalRecord(models?.[params.modelRef]);
const priorRuntime = asOptionalRecord(entry?.agentRuntime);
const runtimeId = normalizeOptionalLowercaseString(priorRuntime?.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,
env: params.env
});
if (pinnedRuntimeId || 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 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,
env: params.env
})) return;
rewriteStringModelSlot({
hits: params.hits,
container: params.container,
key: params.key,
path: params.path,
blockedModelIdentities: params.blockedModelIdentities
});
}
function rewriteModelConfigSlotIfCanonicalCodexRuntime(params) {
const value = params.container?.[params.key];
if (typeof value === "string") {
rewriteStringModelSlotIfCanonicalCodexRuntime(params);
return;
}
const record = asOptionalRecord(value);
if (!record) return;
rewriteStringModelSlotIfCanonicalCodexRuntime({
...params,
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 || isBlockedLegacyCodexModelRef({
modelRef: entry,
blockedModelIdentities: params.blockedModelIdentities
}) || !canonicalOpenAIModelUsesCodexRuntime({
cfg: params.cfg,
modelRef: canonicalModel,
agentId: params.agentId,
env: params.env
})) continue;
fallbacks[index] = canonicalModel;
params.hits.push({
path: `${params.path}.fallbacks.${index}`,
model: entry.trim(),
canonicalModel
});
}
}
function clearConfigLegacyAgentRuntimePolicies(cfg) {
const changes = [];
clearLegacyAgentRuntimePolicy(asOptionalRecord(cfg.agents?.defaults), "agents.defaults", changes);
for (const { agent, path } of listMutableCodexRouteAgentEntries(cfg)) clearLegacyAgentRuntimePolicy(agent, path, changes);
return changes;
}
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.`);
}
}
//#endregion
//#region src/commands/doctor/shared/codex-route-compaction-repair.ts
function rewriteAgentCompactionRefs(params) {
const compaction = asOptionalRecord(params.agent.compaction);
const inheritedCompaction = asOptionalRecord(params.inheritedCompaction);
if (!agentUsesCodexRuntimeForCompaction({
cfg: params.cfg,
agent: params.agent,
agentId: params.agentId,
currentRuntime: params.currentRuntime,
inheritedModelRef: params.inheritedModelRef,
env: params.env
})) {
rewriteStringModelSlotIfCanonicalCodexRuntime({
cfg: params.cfg,
agentId: params.agentId,
hits: params.hits,
container: compaction,
key: "model",
path: `${params.path}.compaction.model`,
blockedModelIdentities: params.blockedModelIdentities,
env: params.env
});
rewriteCompactionMemoryFlushModel(params, compaction);
return;
}
const effectiveProvider = compaction?.provider ?? inheritedCompaction?.provider;
if (normalizeOptionalLowercaseString(effectiveProvider) === "lossless-claw") rewriteLosslessCompactionModel(params, compaction, inheritedCompaction);
else {
removeUnsupportedCodexCompactionOverrides({
agent: params.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`,
blockedModelIdentities: params.blockedModelIdentities
});
}
rewriteCompactionMemoryFlushModel(params, compaction);
}
function rewriteLosslessCompactionModel(params, compaction, inheritedCompaction) {
const start = params.hits.length;
rewriteStringModelSlot({
hits: params.hits,
container: compaction,
key: "model",
path: `${params.path}.compaction.model`,
blockedModelIdentities: params.blockedModelIdentities
});
preserveCodexRuntimePolicyForHits(params, 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)) return;
const inheritedStart = params.hits.length;
rewriteStringModelSlot({
hits: params.hits,
container: inheritedCompaction,
key: "model",
path: inheritedModelPath,
blockedModelIdentities: params.blockedModelIdentities
});
const inheritedHit = params.hits[inheritedStart];
const inheritedCanonicalModel = inheritedHit?.canonicalModel ?? params.rewrittenInheritedCompactionModels?.get(inheritedModelPath);
if (inheritedHit) {
params.rewrittenInheritedCompactionModels?.set(inheritedModelPath, inheritedHit.canonicalModel);
preserveCodexRuntimePolicyForHits(params, inheritedStart);
} else if (inheritedCanonicalModel) ensureCodexRuntimePolicy({
cfg: params.cfg,
agent: params.agent,
agentPath: params.path,
agentId: params.agentId,
modelRef: inheritedCanonicalModel,
isDefaults: params.path === "agents.defaults",
preRepairCfg: params.preRepairCfg,
changes: params.runtimePolicyChanges,
env: params.env
});
}
function preserveCodexRuntimePolicyForHits(params, fromIndex) {
for (const hit of params.hits.slice(fromIndex)) ensureCodexRuntimePolicy({
cfg: params.cfg,
agent: params.agent,
agentPath: params.path,
agentId: params.agentId,
modelRef: hit.canonicalModel,
legacyModelRef: hit.model,
isDefaults: params.path === "agents.defaults",
preRepairCfg: params.preRepairCfg,
changes: params.runtimePolicyChanges,
env: params.env
});
}
function rewriteCompactionMemoryFlushModel(params, compaction) {
rewriteStringModelSlotIfCanonicalCodexRuntime({
cfg: params.cfg,
agentId: params.agentId,
hits: params.hits,
container: asOptionalRecord(compaction?.memoryFlush),
key: "model",
path: `${params.path}.compaction.memoryFlush.model`,
blockedModelIdentities: params.blockedModelIdentities,
env: params.env
});
}
function removeUnsupportedCodexCompactionOverrides(params) {
if (!params.compaction) return;
if (normalizeOptionalLowercaseString(params.compaction.provider) === "lossless-claw") return;
for (const key of COMPACTION_OVERRIDE_KEYS) {
const path = `${params.path}.compaction.${key}`;
if (params.preservePaths?.has(path) || 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 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 entries = ensureMutablePath(plugins, ["entries"]);
const entry = asOptionalRecord(entries["lossless-claw"]) ?? {};
if (entries["lossless-claw"] !== entry) entries[LOSSLESS_CONTEXT_ENGINE_ID] = entry;
const config = ensureMutablePath(entry, ["config"]);
const changes = [];
if (slots.contextEngine !== "lossless-claw") {
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,
env: params.env
});
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 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,
env: params.env
});
}
}
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 removeMigratedLosslessCompactionKey(params) {
const owner = readCompactionOwnerForPath(params.cfg, readCompactionOwnerPathForKeyPath(params.path));
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 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 : [];
return asOptionalRecord(agents.find((agent) => agent.id === label)) ?? asOptionalRecord(Number.isInteger(Number(label)) ? agents[Number(label)] : void 0);
}
function readMutablePath(root, pathLabel) {
let cursor = root;
for (const part of pathLabel.split(".")) {
const record = asOptionalRecord(cursor);
if (!record) return;
cursor = record[part];
}
return asOptionalRecord(cursor);
}
function readCompactionOwnerPathForKeyPath(path) {
return path.replace(/\.(model|provider)$/, "").replace(/\.compaction$/, "");
}
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;
}
//#endregion
//#region src/commands/doctor/shared/codex-route-config-repair.ts
function rewriteModelPolicyAllowRefs(params) {
const modelPolicy = asOptionalRecord(params.agent.modelPolicy);
if (!Array.isArray(modelPolicy?.allow)) return;
modelPolicy.allow = modelPolicy.allow.map((entry, index) => {
if (typeof entry !== "string") return entry;
return recordCodexModelHit({
hits: params.hits,
path: `${params.path}.modelPolicy.allow.${index}`,
model: entry.trim(),
blockedModelIdentities: params.blockedModelIdentities
}) ?? entry;
});
}
function rewriteAgentModelRefs(params) {
if (!params.agent) return;
const preserveCodexRuntimePolicyForNewHits = (fromIndex) => {
for (const hit of params.hits.slice(fromIndex)) ensureCodexRuntimePolicy({
cfg: params.cfg,
agent: params.agent,
agentPath: params.path,
agentId: params.agentId,
modelRef: hit.canonicalModel,
legacyModelRef: hit.model,
isDefaults: params.path === "agents.defaults",
preRepairCfg: params.preRepairCfg,
changes: params.runtimePolicyChanges,
env: params.env
});
};
for (const key of AGENT_MODEL_CONFIG_KEYS) {
const start = params.hits.length;
if (key === "model") {
rewriteModelConfigSlot({
hits: params.hits,
container: params.agent,
key,
path: `${params.path}.${key}`,
runtime: params.currentRuntime,
blockedModelIdentities: params.blockedModelIdentities
});
preserveCodexRuntimePolicyForNewHits(start);
} else rewriteModelConfigSlotIfCanonicalCodexRuntime({
cfg: params.cfg,
agentId: params.agentId,
hits: params.hits,
container: params.agent,
key,
path: `${params.path}.${key}`,
blockedModelIdentities: params.blockedModelIdentities,
env: params.env
});
}
rewriteStringModelSlotIfCanonicalCodexRuntime({
cfg: params.cfg,
agentId: params.agentId,
hits: params.hits,
container: asOptionalRecord(params.agent.heartbeat),
key: "model",
path: `${params.path}.heartbeat.model`,
blockedModelIdentities: params.blockedModelIdentities,
env: params.env
});
rewriteModelConfigSlotIfCanonicalCodexRuntime({
cfg: params.cfg,
agentId: params.agentId,
hits: params.hits,
container: asOptionalRecord(params.agent.subagents),
key: "model",
path: `${params.path}.subagents.model`,
blockedModelIdentities: params.blockedModelIdentities,
env: params.env
});
rewriteAgentCompactionRefs({
cfg: params.cfg,
preRepairCfg: params.preRepairCfg,
hits: params.hits,
agent: params.agent,
path: params.path,
agentId: params.agentId,
currentRuntime: params.currentRuntime,
inheritedModelRef: params.inheritedModelRef,
inheritedCompaction: params.inheritedCompaction,
inheritedCompactionPath: params.inheritedCompactionPath,
preserveUnsupportedCompactionOverrides: params.preserveUnsupportedCompactionOverrides,
preserveUnsupportedCompactionPaths: params.preserveUnsupportedCompactionPaths,
rewrittenInheritedCompactionModels: params.rewrittenInheritedCompactionModels,
runtimePolicyChanges: params.runtimePolicyChanges,
unsupportedCompactionChanges: params.unsupportedCompactionChanges,
blockedModelIdentities: params.blockedModelIdentities,
env: params.env
});
const mediaModels = asOptionalRecord(params.agent.mediaModels);
for (const key of ["image", "video"]) rewriteModelConfigSlot({
hits: params.hits,
container: mediaModels ?? {},
key,
path: `${params.path}.mediaModels.${key}`,
blockedModelIdentities: params.blockedModelIdentities
});
const modelPolicyStart = params.hits.length;
rewriteModelPolicyAllowRefs({
hits: params.hits,
agent: params.agent,
path: params.path,
blockedModelIdentities: params.blockedModelIdentities
});
preserveCodexRuntimePolicyForNewHits(modelPolicyStart);
if (params.rewriteModelsMap) {
const start = params.hits.length;
rewriteModelsMap({
hits: params.hits,
models: asOptionalRecord(params.agent.models),
path: `${params.path}.models`,
blockedModelIdentities: params.blockedModelIdentities
});
preserveCodexRuntimePolicyForNewHits(start);
}
}
function rewriteConfigModelRefsWithCompactionPolicy(params) {
const nextConfig = structuredClone(params.cfg);
const hits = [];
const runtimePolicyChanges = [];
const unsupportedCompactionChanges = [];
const ignoreLegacyAgentRuntimePins = params.ignoreLegacyAgentRuntimePins ?? configRepairWouldClearLegacyRuntimePins({
cfg: nextConfig,
blockedModelIdentities: params.blockedModelIdentities,
env: params.env
});
unsupportedCompactionChanges.push(...maybeMigrateLegacyLosslessCompactionConfig({
cfg: nextConfig,
ignoreLegacyAgentRuntimePins,
env: params.env
}));
const preservedLegacyLosslessCompactionPaths = new Set(collectLegacyLosslessCompactionConfigs({
cfg: nextConfig,
ignoreLegacyAgentRuntimePins,
env: params.env
}).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: params.cfg,
hits,
agent: asOptionalRecord(nextConfig.agents?.defaults),
path: "agents.defaults",
currentRuntime: resolveRuntime({ defaultsRuntime }),
rewriteModelsMap: true,
preserveUnsupportedCompactionOverrides: params.preserveSharedDefaultCompactionOverrides,
preserveUnsupportedCompactionPaths: preservedLegacyLosslessCompactionPaths,
rewrittenInheritedCompactionModels,
runtimePolicyChanges,
unsupportedCompactionChanges,
blockedModelIdentities: params.blockedModelIdentities,
env: params.env
});
const inheritedModelRef = readAgentPrimaryModelRef(nextConfig.agents?.defaults);
const agents = listMutableCodexRouteAgentEntries(nextConfig);
for (const { agent: agentRecord, agentId, path } of agents) rewriteAgentModelRefs({
cfg: nextConfig,
preRepairCfg: params.cfg,
hits,
agent: agentRecord,
path,
agentId,
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,
blockedModelIdentities: params.blockedModelIdentities,
env: params.env
});
rewriteNonAgentModelRefs({
cfg: nextConfig,
hits,
blockedModelIdentities: params.blockedModelIdentities,
env: params.env
});
const runtimePinChanges = !params.blockedModelIdentities?.size && 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 rewriteNonAgentModelRefs(params) {
const channelsModelByChannel = asOptionalRecord(params.cfg.channels?.modelByChannel);
for (const [channelId, channelMap] of Object.entries(channelsModelByChannel ?? {})) {
const targets = asOptionalRecord(channelMap);
if (!targets) continue;
for (const targetId of Object.keys(targets)) rewriteStringModelSlotIfCanonicalCodexRuntime({
cfg: params.cfg,
hits: params.hits,
container: targets,
key: targetId,
path: `channels.modelByChannel.${channelId}.${targetId}`,
blockedModelIdentities: params.blockedModelIdentities,
env: params.env
});
}
for (const [index, mapping] of (params.cfg.hooks?.mappings ?? []).entries()) rewriteStringModelSlotIfCanonicalCodexRuntime({
cfg: params.cfg,
hits: params.hits,
container: mapping,
key: "model",
path: `hooks.mappings.${index}.model`,
blockedModelIdentities: params.blockedModelIdentities,
env: params.env
});
rewriteStringModelSlotIfCanonicalCodexRuntime({
cfg: params.cfg,
hits: params.hits,
container: asOptionalRecord(params.cfg.hooks?.gmail),
key: "model",
path: "hooks.gmail.model",
blockedModelIdentities: params.blockedModelIdentities,
env: params.env
});
rewriteStringModelSlotIfCanonicalCodexRuntime({
cfg: params.cfg,
hits: params.hits,
container: asOptionalRecord(params.cfg.tts),
key: "summaryModel",
path: "tts.summaryModel",
blockedModelIdentities: params.blockedModelIdentities,
env: params.env
});
rewriteStringModelSlotIfCanonicalCodexRuntime({
cfg: params.cfg,
hits: params.hits,
container: asOptionalRecord(asOptionalRecord(params.cfg.channels?.discord)?.voice),
key: "model",
path: "channels.discord.voice.model",
blockedModelIdentities: params.blockedModelIdentities,
env: params.env
});
}
function configRepairWouldClearLegacyRuntimePins(params) {
return rewriteConfigModelRefsWithCompactionPolicy({
cfg: params.cfg,
preserveSharedDefaultCompactionOverrides: {
model: true,
provider: true
},
ignoreLegacyAgentRuntimePins: false,
blockedModelIdentities: params.blockedModelIdentities,
env: params.env
}).runtimePinChanges.length > 0;
}
function rewriteConfigModelRefs(params) {
const preserveSharedDefaultCompactionOverrides = getSharedDefaultCompactionOverrideConsumers({
cfg: params.cfg,
ignoreLegacyAgentRuntimePins: configRepairWouldClearLegacyRuntimePins(params),
env: params.env
});
return rewriteConfigModelRefsWithCompactionPolicy({
cfg: params.cfg,
preserveSharedDefaultCompactionOverrides,
blockedModelIdentities: params.blockedModelIdentities,
env: params.env
});
}
function isCompactionOnlyRouteHit(hit) {
return hit.path.startsWith("agents.") && (hit.path.endsWith(".compaction.model") || hit.path.endsWith(".compaction.memoryFlush.model"));
}
//#endregion
//#region src/commands/doctor/shared/codex-route-config-scan.ts
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,
blockedModelIdentities: params.blockedModelIdentities
});
}
}
function collectModelPolicyAllowRefs(params) {
const allow = asOptionalRecord(params.modelPolicy)?.allow;
if (!Array.isArray(allow)) return;
for (const [index, modelRef] of allow.entries()) collectStringModelSlot({
hits: params.hits,
path: `${params.path}.allow.${index}`,
value: modelRef,
blockedModelIdentities: params.blockedModelIdentities
});
}
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,
blockedModelIdentities: params.blockedModelIdentities
});
const mediaModels = asOptionalRecord(agent.mediaModels);
for (const key of ["image", "video"]) collectModelConfigSlot({
hits: params.hits,
path: `${params.path}.mediaModels.${key}`,
value: mediaModels?.[key],
blockedModelIdentities: params.blockedModelIdentities
});
collectStringModelSlot({
hits: params.hits,
path: `${params.path}.heartbeat.model`,
value: asOptionalRecord(agent.heartbeat)?.model,
blockedModelIdentities: params.blockedModelIdentities
});
collectModelConfigSlot({
hits: params.hits,
path: `${params.path}.subagents.model`,
value: asOptionalRecord(agent.subagents)?.model,
blockedModelIdentities: params.blockedModelIdentities
});
const compaction = asOptionalRecord(agent.compaction);
collectStringModelSlot({
hits: params.hits,
path: `${params.path}.compaction.model`,
value: compaction?.model,
blockedModelIdentities: params.blockedModelIdentities
});
collectStringModelSlot({
hits: params.hits,
path: `${params.path}.compaction.memoryFlush.model`,
value: asOptionalRecord(compaction?.memoryFlush)?.model,
blockedModelIdentities: params.blockedModelIdentities
});
collectModelsMapRefs({
hits: params.hits,
path: `${params.path}.models`,
models: agent.models,
blockedModelIdentities: params.blockedModelIdentities
});
collectModelPolicyAllowRefs({
hits: params.hits,
path: `${params.path}.modelPolicy`,
modelPolicy: agent.modelPolicy,
blockedModelIdentities: params.blockedModelIdentities
});
}
function collectConfigModelRefs(cfg, blockedModelIdentities) {
const hits = [];
const defaults = cfg.agents?.defaults;
const defaultsRuntime = readLegacyDefaultsRuntime(defaults);
collectAgentModelRefs({
hits,
agent: defaults,
path: "agents.defaults",
runtime: resolveRuntime({ defaultsRuntime }),
blockedModelIdentities
});
const agents = listMutableCodexRouteAgentEntries(cfg);
for (const { agent: agentRecord, path } of agents) collectAgentModelRefs({
hits,
agent: agentRecord,
path,
runtime: resolveRuntime({
agentRuntime: asAgentRuntimePolicyConfig(agentRecord.agentRuntime),
defaultsRuntime
}),
blockedModelIdentities
});
const channelsModelByChannel = asOptionalRecord(cfg.channels?.modelByChannel);
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,
blockedModelIdentities
});
}
for (const [index, mapping] of (cfg.hooks?.mappings ?? []).entries()) collectStringModelSlot({
hits,
path: `hooks.mappings.${index}.model`,
value: mapping.model,
blockedModelIdentities
});
collectStringModelSlot({
hits,
path: "hooks.gmail.model",
value: cfg.hooks?.gmail?.model,
blockedModelIdentities
});
collectStringModelSlot({
hits,
path: "tts.summaryModel",
value: cfg.tts?.summaryModel,
blockedModelIdentities
});
collectStringModelSlot({
hits,
path: "channels.discord.voice.model",
value: asOptionalRecord(asOptionalRecord(cfg.channels?.discord)?.voice)?.model,
blockedModelIdentities
});
return hits;
}
function collectDisabledCodexPluginRouteHits(cfg, env) {
if (!isCodexPluginUnavailableByConfig(cfg)) return [];
return collectCodexRuntimeRouteHits(cfg, env);
}
/** Find effective configured model routes that select the Codex runtime. */
function collectCodexRuntimeRouteHits(cfg, env) {
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 = listMutableCodexRouteAgentEntries(cfg);
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 channelRefs = collectChannelAgentRuntimeModelRefs(cfg);
const candidateRefs = agents.length === 0 ? [...defaultRefs, ...channelRefs] : [];
for (const { agent: agentRecord, agentId, path } of agents) {
for (const ref of channelRefs) candidateRefs.push({
path: ref.path,
modelRef: ref.modelRef,
agentId
});
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,
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,
env
})) 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,
...ref.agentId ? { agentId: ref.agentId } : {}
});
}
return hits;
}
/** Find Codex-routed model refs that require the Codex plugin while it is disabled. */
function collectDisabledCodexPluginRouteIssues(cfg, env) {
const repairBlocked = codexPluginRepairIsBlocked(cfg);
return collectDisabledCodexPluginRouteHits(cfg, env).map((hit) => ({
path: hit.path,
modelRef: hit.modelRef,
canonicalModel: hit.canonicalModel,
repairBlocked
}));
}
function enableCodexPluginForRequiredRoutes(params) {
if (params.routeHits.length === 0 || codexPluginRepairIsBlocked(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 codexPluginIsBlockedOutsideEntry(cfg) {
return cfg.plugins?.enabled === false || pluginIdListIncludes(cfg.plugins?.deny, "codex");
}
function codexPluginRepairIsBlocked(cfg) {
return codexPluginIsBlockedOutsideEntry(cfg) || asOptionalRecord(asOptionalRecord(cfg.plugins?.entries)?.codex)?.enabled === false;
}
function isCodexPluginUnavailableByConfig(cfg) {
if (codexPluginRepairIsBlocked(cfg)) return true;
const allow = cfg.plugins?.allow;
return Array.isArray(allow) && allow.length > 0 && !pluginIdListIncludes(allow, "codex");
}
function pluginIdListIncludes(value, pluginId) {
return Array.isArray(value) && value.some((entry) => normalizeOptionalLowercaseString(entry) === pluginId);
}
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;
}
//#endregion
//#region src/commands/doctor/shared/codex-route-session-repair.ts
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;
const legacyProviderModelRef = sessionProviderAllowsScopedModelRef(provider) && isOpenAICodexModelRef(model) ? model : void 0;
if (isBlockedLegacyCodexModelPair({
providerId: provider,
modelId: model,
blockedModelIdentities: params.blockedModelIdentities
}) || (legacyProviderModelRef ? isBlockedLegacyCodexModelRef({
modelRef: legacyProviderModelRef,
blockedModelIdentities: params.blockedModelIdentities
}) : false)) return false;
if (isLegacyCodexProviderId(provider)) {
params.entry[params.providerKey] = "openai";
if (model) {
const modelId = toOpenAIModelId(model);
if (modelId) params.entry[params.modelKey] = modelId;
}
return true;
}
if (legacyProviderModelRef) {
const canonicalModel = provider === "openai" ? toOpenAIModelId(legacyProviderModelRef) : toCanonicalOpenAIModelRef(legacyProviderModelRef);
if (canonicalModel) {
params.entry[params.modelKey] = canonicalModel;
changed = true;
}
}
return changed;
}
function sessionProviderAllowsScopedModelRef(provider) {
return !provider || isLegacyCodexProviderId(provider);
}
function sessionModelPairHasLegacyRoute(provider, model) {
const normalizedProvider = normalizeOptionalLowercaseString(provider);
return isLegacyCodexProviderId(normalizedProvider) || sessionProviderAllowsScopedModelRef(normalizedProvider) && typeof model === "string" && isOpenAICodexModelRef(model);
}
function clearStaleCodexFallbackNotice(entry, blockedModelIdentities) {
const endpoints = [entry.fallbackNotice?.selectedModel, entry.fallbackNotice?.activeModel];
if (endpoints.some((modelRef) => isOpenAICodexModelRef(modelRef) && isBlockedLegacyCodexModelRef({
modelRef,
blockedModelIdentities
})) || !endpoints.some(isOpenAICodexModelRef)) return false;
delete entry.fallbackNotice;
return true;
}
function preserveRepairedSessionRuntimeIntent(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 (overrideRuntime !== "openclaw" && entry.agentRuntimeOverride !== "codex") {
entry.agentRuntimeOverride = "codex";
changed = true;
}
return changed;
}
function repairProviderlessCodexSessionOverride(entry, blockedModelIdentities) {
if (!isProviderlessModelRef(entry.modelOverride) || !isOpenAICodexAuthProfileRef(entry.authProfileOverride) || entry.authProfileOverrideSource !== "auto" || entry.modelOverrideSource !== "auto" || normalizeOptionalLowercaseString(entry.providerOverride)) return false;
const authProvider = normalizeOptionalLowercaseString(entry.authProfileOverride)?.split(":", 1)[0];
if (isBlockedLegacyCodexModelPair({
providerId: authProvider,
modelId: entry.modelOverride,
blockedModelIdentities
})) return false;
entry.providerOverride = "openai";
entry.modelOverrideRouteResolution = "resolved";
if (entry.model !== void 0 || entry.modelProvider !== void 0) {
delete entry.model;
delete entry.modelProvider;
}
if (entry.contextTokens !== void 0 || entry.contextTokensSource !== void 0) {
delete entry.contextTokens;
delete entry.contextTokensSource;
}
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 || isValidAgentHarnessSessionStoreEntry(sessionKey, entry)) continue;
const changedRuntimeModelRoute = rewriteSessionModelPair({
entry,
providerKey: "modelProvider",
modelKey: "model",
blockedModelIdentities: params.blockedModelIdentities
});
const changedOverrideModelRoute = rewriteSessionModelPair({
entry,
providerKey: "providerOverride",
modelKey: "modelOverride",
blockedModelIdentities: params.blockedModelIdentities
});
if (changedOverrideModelRoute) entry.modelOverrideRouteResolution = "resolved";
const changedProviderlessOverride = repairProviderlessCodexSessionOverride(entry, params.blockedModelIdentities);
const changedModelRoute = changedRuntimeModelRoute || changedOverrideModelRoute || changedProviderlessOverride;
const changedFallbackNotice = clearStaleCodexFallbackNotice(entry, params.blockedModelIdentities);
const changedRuntimePins = changedModelRoute ? preserveRepairedSessionRuntimeIntent(entry) : false;
const mappedAuthProfileId = typeof entry.authProfileOverride === "string" ? params.authProfileIdMap?.get(entry.authProfileOverride) : void 0;
const changedAuthProfile = mappedAuthProfileId !== void 0 && mappedAuthProfileId !== entry.authProfileOverride;
if (changedAuthProfile) entry.authProfileOverride = mappedAuthProfileId;
if (!changedModelRoute && !changedFallbackNotice && !changedRuntimePins && !changedAuthProfile) continue;
entry.updatedAt = now;
sessionKeys.push(sessionKey);
}
return {
changed: sessionKeys.length > 0,
sessionKeys
};
}
if (process.env.VITEST || false) globalThis[Symbol.for("openclaw.codexRouteSessionRepairTestApi")] = { repairCodexSessionStoreRoutes };
function scanCodexSessionStoreRoutes(store, blockedModelIdentities, authProfileIdMap) {
return Object.entries(store).flatMap(([sessionKey, entry]) => entry && sessionEntryHasLegacyCodexRoute(sessionKey, entry, blockedModelIdentities, authProfileIdMap) ? [sessionKey] : []);
}
function sessionEntryHasLegacyCodexRoute(sessionKey, entry, blockedModelIdentities, authProfileIdMap) {
if (isValidAgentHarnessSessionStoreEntry(sessionKey, entry)) return false;
const isBlockedPair = (provider, model) => {
const legacyProviderModelRef = sessionProviderAllowsScopedModelRef(normalizeOptionalLowercaseString(provider)) && typeof model === "string" && isOpenAICodexModelRef(model) ? model : void 0;
return isBlockedLegacyCodexModelPair({
providerId: provider,
modelId: model,
blockedModelIdentities
}) || legacyProviderModelRef && isBlockedLegacyCodexModelRef({
modelRef: legacyProviderModelRef,
blockedModelIdentities
});
};
const fallbackNoticeEndpoints = [entry.fallbackNotice?.selectedModel, entry.fallbackNotice?.activeModel];
const hasRewritableFallbackNotice = !fallbackNoticeEndpoints.some((modelRef) => isOpenAICodexModelRef(modelRef) && isBlockedLegacyCodexModelRef({
modelRef,
blockedModelIdentities
})) && fallbackNoticeEndpoints.some(isOpenAICodexModelRef);
return sessionModelPairHasLegacyRoute(entry.modelProvider, entry.model) && !isBlockedPair(entry.modelProvider, entry.model) || sessionModelPairHasLegacyRoute(entry.providerOverride, entry.modelOverride) && !isBlockedPair(entry.providerOverride, entry.modelOverride) || isProviderlessModelRef(entry.modelOverride) && isOpenAICodexAuthProfileRef(entry.authProfileOverride) && entry.authProfileOverrideSource === "auto" && entry.modelOverrideSource === "auto" && !normalizeOptionalLowercaseString(entry.providerOverride) && !isBlockedPair(normalizeOptionalLowercaseString(entry.authProfileOverride)?.split(":", 1)[0], entry.modelOverride) || typeof entry.authProfileOverride === "string" && authProfileIdMap?.has(entry.authProfileOverride) || hasRewritableFallbackNotice;
}
function resolveVerifiedSessionAuthProfileIdMap(params) {
if (!params.authProfileIdMap || params.authProfileIdMap.size === 0) return params.authProfileIdMap;
const agentDir = resolveAgentDir(params.cfg, params.agentId, params.env);
const localProfiles = loadPersistedAuthProfileStore(agentDir)?.profiles ?? {};
const mainProfiles = loadPersistedSharedAuthProfileStore(params.env)?.profiles ?? {};
const localLegacyAuthPath = resolveLegacyAuthProfilesPath(agentDir);
const localLegacySourceExists = fs.existsSync(localLegacyAuthPath);
const localLegacySource = localLegacySourceExists ? loadJsonFileThroughSymlink(localLegacyAuthPath) : null;
const localLegacyProfiles = isRecord(localLegacySource) && isRecord(localLegacySource.profiles) ? localLegacySource.profiles : void 0;
return new Map([...params.authProfileIdMap].filter(([legacyProfileId, canonicalProfileId]) => {
const localCredential = localProfiles[canonicalProfileId];
if (localCredential) return normalizeOptionalLowercaseString(localCredential.provider) === "openai";
const inheritedCredential = mainProfiles[canonicalProfileId];
if (localLegacySourceExists) {
if (!localLegacyProfiles) return false;
const legacyCredential = localLegacyProfiles[legacyProfileId];
if (legacyCredential !== void 0) {
if (!isRecord(legacyCredential)) return false;
const canonicalLegacyCredential = parseLegacyCredentialEntry({
...legacyCredential,
provider: "openai"
}, "openai");
return canonicalLegacyCredential?.type === "oauth" && inheritedCredential?.type === "oauth" && inheritedCredential.provider === "openai" && (hasMatchingOAuthIdentity(canonicalLegacyCredential, inheritedCredential) || areOAuthCredentialsEquivalent(canonicalLegacyCredential, inheritedCredential));
}
}
return normalizeOptionalLowercaseString(inheritedCredential?.provider) === "openai";
}));
}
/** Scan or repair all configured agent session stores that still contain legacy Codex routes. */
async function maybeRepairCodexSessionRoutes(params) {
const env = params.env ?? process.env;
const targets = resolveAllAgentSessionStoreTargetsSync(params.cfg, { env }).flatMap((target) => {
const sessionScope = {
storePath: target.storePath,
agentId: target.agentId,
env
};
const authProfileIdMap = resolveVerifiedSessionAuthProfileIdMap({
agentId: target.agentId,
cfg: params.cfg,
env,
authProfileIdMap: params.authProfileIdMap
});
const staleSqliteSessionKeys = [];
const scanEntry = ({ entry, sessionKey }) => {
if (sessionEntryHasLegacyCodexRoute(sessionKey, entry, params.blockedModelIdentities, authProfileIdMap)) staleSqliteSessionKeys.push(sessionKey);
};
const sqliteEntryCount = params.shouldRepair ? scanDoctorSessionEntriesStrict(sessionScope, scanEntry) : scanDoctorSessionEntriesTolerant(sessionScope, scanEntry);
const hasLegacyStore = !target.storePath.endsWith(".sqlite") && fs.existsSync(target.storePath);
return sqliteEntryCount > 0 || hasLegacyStore ? [{
...target,
staleSqliteSessionKeys,
hasLegacyStore,
authProfileIdMap
}] : [];
});
if (targets.length === 0) return emptyRepairSummary();
if (!params.shouldRepair) {
const stale = targets.flatMap((target) => {
const sessionKeys = new Set(target.staleSqliteSessionKeys);
if (target.hasLegacyStore) for (const sessionKey of scanCodexSessionStoreRoutes(loadLegacySessionStore(target.storePath), params.blockedModelIdentities, target.authProfileIdMap)) sessionKeys.add(sessionKey);
return Array.from(sessionKeys, (sessionKey) => `${target.agentId}:${sessionKey}`);
});
return {
scannedStores: targets.length,
repairedStores: 0,
repairedSessions: 0,
warnings: stale.length > 0 ? [[
"- Legacy `codex/*` or `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) {
const repairedSessionKeys = /* @__PURE__ */ new Set();
const { staleSqliteSessionKeys } = target;
for (const sessionKeys of iterateDoctorSessionKeyBatches(staleSqliteSessionKeys)) {
const result = await applySessionEntryReplacements({
agentId: target.agentId,
storePath: target.storePath,
sessionKeys,
skipMaintenance: true,
update: (entries) => {
const store = Object.fromEntries(entries.map(({ sessionKey, entry }) => [sessionKey, entry]));
const repair = repairCodexSessionStoreRoutes({
store,
blockedModelIdentities: params.blockedModelIdentities,
authProfileIdMap: target.authProfileIdMap
});
return {
result: repair,
replacements: repair.sessionKeys.map((sessionKey) => ({
sessionKey,
entry: store[sessionKey]
}))
};
}
});
for (const sessionKey of result.sessionKeys) repairedSessionKeys.add(sessionKey);
}
if (target.hasLegacyStore) {
if (scanCodexSessionStoreRoutes(loadLegacySessionStore(target.storePath), params.blockedModelIdentities, target.authProfileIdMap).length > 0) {
const result = await updateLegacySessionStore(target.storePath, (store) => repairCodexSessionStoreRoutes({
store,
blockedModelIdentities: params.blockedModelIdentities,
authProfileIdMap: target.authProfileIdMap
}), { skipMaintenance: true });
for (const sessionKey of result.sessionKeys) repairedSessionKeys.add(sessionKey);
}
}
if (repairedSessionKeys.size > 0) {
repairedStores += 1;
repairedSessions += repairedSessionKeys.size;
}
}
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.`] : []
};
}
function emptyRepairSummary() {
return {
scannedStores: 0,
repairedStores: 0,
repairedSessions: 0,
warnings: [],
changes: []
};
}
//#endregion
//#region src/commands/doctor/shared/codex-route-warnings.ts
function resolveKnownModelRefMigrationTarget(cfg, ref) {
const blockedModelIdentities = new Set(collectBlockedLegacyOpenAICodexProviderPlan(cfg).blockedModelIdentities);
if (isBlockedLegacyCodexModelRef({
modelRef: ref,
blockedModelIdentities
})) return;
const providerRef = migrateLegacyRuntimeModelRef(ref)?.ref ?? toCanonicalOpenAIModelRef(ref) ?? ref;
const migrated = rewriteKnownModelRefs(providerRef, "model", []).value;
return typeof migrated === "string" && migrated !== ref ? migrated : void 0;
}
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.repairBlocked ? "- Enable plugins.entries.codex and 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 plugins = asOptionalRecord(cfg.plugins);
const entries = asOptionalRecord(plugins?.entries);
const codex = asOptionalRecord(entries?.codex);
const config = asOptionalRecord(codex?.config);
const appServer = asOptionalRecord(config?.appServer);
if (typeof appServer?.command !== "string" || !appServer.command.trim()) return [];
const command = appServer.command.trim();
const inlineArgs = detectWindowsSpawnCommandInlineArgs(command);
return [...inlineArgs ? [[
"- 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")] : [], [
"- Custom Codex app-server command bypasses OpenClaw's managed exact-version binary.",
"- plugins.entries.codex.config.appServer.command: Doctor did not execute, inspect, or rewrite this command.",
"- Remove the override to use managed Codex startup, or verify the custom binary matches the Codex version bundled with this OpenClaw release."
].join("\n")];
}
const FAST_MODE_PARAM_KEYS = ["fastMode", "fast_mode"];
const SERVICE_TIER_PARAM_KEYS = ["serviceTier", "service_tier"];
function ownValues(record, keys) {
return keys.filter((key) => Object.hasOwn(record, key)).map((key) => record[key]);
}
function modelUsesCodexForEveryAgent(cfg, modelRef) {
const parsed = parseCodexRouteModelRef(modelRef);
if (!parsed || parsed.modelId === "*") return false;
return listMutableCodexRouteAgentEntries(cfg).every(({ agentId }) => normalizeOptionalLowercaseString(resolveModelRuntimePolicy({
config: cfg,
provider: parsed.provider,
modelId: parsed.modelId,
agentId
}).policy?.id) === "codex");
}
function collectCodexModelParamHits(cfg, env) {
const hits = [];
const seen = /* @__PURE__ */ new Set();
const agentPaths = new Map(listMutableCodexRouteAgentEntries(cfg).map(({ agentId, path }) => [agentId, path]));
for (const route of collectCodexRuntimeRouteHits(cfg, env)) {
const parsed = parseCodexRouteModelRef(route.canonicalModel);
if (!parsed || parsed.provider !== "openai") continue;
const sources = resolveModelExtraParamSources({
config: cfg,
provider: parsed.provider,
modelId: parsed.modelId,
agentId: route.agentId
});
const modelParams = sources.modelParams;
const fastModes = ownValues(modelParams ?? {}, FAST_MODE_PARAM_KEYS);
const serviceTiers = ownValues(modelParams ?? {}, SERVICE_TIER_PARAM_KEYS);
const canRemoveServiceTier = fastModes.length > 0 && fastModes.every((configured) => normalizeFastMode(configured) === true) && serviceTiers.length > 0 && serviceTiers.every((configured) => normalizeOptionalLowercaseString(configured) === "priority") && modelUsesCodexForEveryAgent(cfg, route.canonicalModel);
const paramSources = [
{
params: sources.defaultParams,
path: "agents.defaults.params",
modelScoped: false
},
{
params: modelParams,
path: `agents.defaults.models.${route.canonicalModel}.params`,
modelScoped: true
},
...route.agentId ? [{
params: sources.agentParams,
path: `${agentPaths.get(route.agentId) ?? `agents.entries.${route.agentId}`}.params`,
modelScoped: false
}] : []
];
for (const source of paramSources) for (const [key, paramValue] of Object.entries(source.params ?? {})) {
if (source.modelScoped && isAgentRuntimeModelParam(key, paramValue)) continue;
const path = `${source.path}.${key}`;
if (seen.has(path)) continue;
seen.add(path);
hits.push({
key,
path,
modelRef: route.canonicalModel,
removable: source.modelScoped && canRemoveServiceTier && SERVICE_TIER_PARAM_KEYS.some((alias) => alias === key)
});
}
}
return hits;
}
function formatCodexModelParamWarning(hits) {
const fixHint = hits.some((hit) => hit.removable) ? "- Run `openclaw doctor --fix` to remove only redundant priority service-tier params; remove any remaining params or set the affected route's agentRuntime.id to \"openclaw\"." : "- Remove these params or set the affected route's agentRuntime.id to \"openclaw\"; Doctor cannot migrate them without changing behavior.";
return [
"- Explicit native Codex model routes cannot reproduce authored request transport parameters.",
...hits.map((hit) => `- ${hit.path}: ${hit.removable ? "redundant because this model's fastMode already selects native priority service tier" : `authored ${hit.key} cannot be migrated automatically`}.`),
fixHint
].join("\n");
}
function repairRedundantCodexServiceTiers(cfg, env) {
const removable = collectCodexModelParamHits(cfg, env).filter((hit) => hit.removable);
if (removable.length === 0) return {
config: cfg,
changes: []
};
const config = structuredClone(cfg);
const models = asOptionalRecord(config.agents?.defaults?.models);
const changes = [];
for (const hit of removable) {
const params = asOptionalRecord(asOptionalRecord(models?.[hit.modelRef])?.params);
if (params) {
delete params[hit.key];
changes.push(`Removed redundant agents.defaults.models.${hit.modelRef}.params.${hit.key}; fastMode already selects native priority.`);
}
}
return {
config,
changes
};
}
/** Collect non-executing Codex runtime compatibility diagnostics for Doctor and lint. */
function collectCodexRuntimeCompatibilityWarnings(cfg, env) {
const warnings = collectCodexAppServerCommandWarnings(cfg);
const modelParamHits = collectCodexModelParamHits(cfg, env);
if (modelParamHits.length > 0) warnings.push(formatCodexModelParamWarning(modelParamHits));
return warnings;
}
function collectCodexComputerUseWarnings(cfg) {
const plugins = asOptionalRecord(cfg.plugins);
const entries = asOptionalRecord(plugins?.entries);
const codex = asOptionalRecord(entries?.codex);
const config = asOptionalRecord(codex?.config);
const computerUse = asOptionalRecord(config?.computerUse);
if (!computerUse) return [];
if (!(computerUse.enabled === true || computerUse.autoInstall === true || typeof computerUse.marketplaceSource === "string" || typeof computerUse.marketplacePath === "string" || typeof computerUse.marketplaceName === "string")) return [];
const cadence = computerUse.healthCheckIntervalMinutes === 30 || computerUse.healthCheckIntervalMinutes === 60 || computerUse.healthCheckIntervalMinutes === 120 || computerUse.healthCheckIntervalMinutes === 240 ? computerUse.healthCheckIntervalMinutes : 60;
return [[
"- Codex Computer Use is enabled.",
"- Doctor config review found Computer Use enabled; run `/codex computer-use status` to inspect installation, exposure, and the live `list_apps` probe.",
computerUse.healthCheckEnabled === true ? `- Periodic Computer Use health checks are enabled with a ${cadence}-minute cadence.` : "- Periodic Computer Use health checks are disabled by default; set `computerUse.healthCheckEnabled` to true to enable them.",
computerUse.autoRepair === true ? "- Stale Computer Use MCP child repair is enabled and limited to SkyComputerUseClient children." : "- Stale Computer Use MCP child repair is disabled by default; set `computerUse.autoRepair` to true to repair before retrying a failed probe."
].join("\n")];
}
/** Collect doctor warnings for legacy Codex model refs, runtime pins, and compaction overrides. */
function collectCodexRouteWarnings(params) {
const env = params.env ?? process.env;
const blockedProviderPlan = params.blockedProviderPlan ?? collectBlockedLegacyOpenAICodexProviderPlan(params.cfg);
const blockedModelIdentities = new Set(blockedProviderPlan.blockedModelIdentities);
const hits = collectConfigModelRefs(params.cfg, blockedModelIdentities);
const disabledCodexPluginHits = collectDisabledCodexPluginRouteHits(params.cfg, env);
const ignoreLegacyAgentRuntimePins = configRepairWouldClearLegacyRuntimePins({
cfg: params.cfg,
blockedModelIdentities,
env
});
const legacyLosslessCompactionConfigs = collectLegacyLosslessCompactionConfigs({
cfg: params.cfg,
ignoreLegacyAgentRuntimePins,
env
});
const legacyLosslessCompactionPaths = new Set(legacyLosslessCompactionConfigs.flatMap((hit) => hit.modelPath ? [hit.providerPath, hit.modelPath] : [hit.providerPath]));
const unsupportedCompactionOverrides = collectUnsupportedCodexCompactionOverrides({
cfg: params.cfg,
ignoreLegacyAgentRuntimePins,
env
}).filter((hit) => !legacyLosslessCompactionPaths.has(hit.path));
const sharedDefaultCompactionConsumers = getSharedDefaultCompactionOverrideConsumers({
cfg: params.cfg,
ignoreLegacyAgentRuntimePins,
env
});
const sharedLosslessDefaultHasNonCodexConsumer = sharedDefaultLosslessCompactionHasNonCodexConsumer({
cfg: params.cfg,
ignoreLegacyAgentRuntimePins,
env
});
const warnings = [
...blockedProviderPlan.warning ? [blockedProviderPlan.warning] : [],
...collectCodexRuntimeCompatibilityWarnings(params.cfg, env),
...collectCodexComputerUseWarnings(params.cfg)
];
if (hits.length > 0) warnings.push([
"- Legacy `codex/*` and `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,
repairBlocked: codexPluginRepairIsBlocked(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 env = params.env ?? process.env;
const blockedProviderPlan = params.blockedProviderPlan ?? collectBlockedLegacyOpenAICodexProviderPlan(params.cfg);
const blockedModelIdentities = new Set(blockedProviderPlan.blockedModelIdentities);
const hits = collectConfigModelRefs(params.cfg, blockedModelIdentities);
const disabledCodexPluginHits = collectDisabledCodexPluginRouteHits(params.cfg, env);
const ignoreLegacyAgentRuntimePins = configRepairWouldClearLegacyRuntimePins({
cfg: params.cfg,
blockedModelIdentities,
env
});
const unsupportedCompactionOverrides = collectUnsupportedCodexCompactionOverrides({
cfg: params.cfg,
ignoreLegacyAgentRuntimePins,
env
});
const legacyLosslessCompactionConfigs = collectLegacyLosslessCompactionConfigs({
cfg: params.cfg,
ignoreLegacyAgentRuntimePins,
env
});
const hasRemovableServiceTier = collectCodexModelParamHits(params.cfg, env).some((hit) => hit.removable);
if (hits.length === 0 && disabledCodexPluginHits.length === 0 && unsupportedCompactionOverrides.length === 0 && legacyLosslessCompactionConfigs.length === 0 && !hasRemovableServiceTier && !blockedProviderPlan.warning) return {
cfg: params.cfg,
warnings: collectCodexRouteWarnings({
cfg: params.cfg,
env,
blockedProviderPlan
}),
changes: []
};
if (!params.shouldRepair) return {
cfg: params.cfg,
warnings: collectCodexRouteWarnings({
cfg: params.cfg,
env,
blockedProviderPlan
}),
changes: []
};
const repaired = rewriteConfigModelRefs({
cfg: params.cfg,
env,
blockedModelIdentities
});
const serviceTierRepair = repairRedundantCodexServiceTiers(repaired.cfg, env);
const codexPluginRepair = enableCodexPluginForRequiredRoutes({
cfg: serviceTierRepair.config,
routeHits: collectDisabledCodexPluginRouteHits(serviceTierRepair.config, env)
});
const warnings = collectCodexRouteWarnings({
cfg: codexPluginRepair.cfg,
env,
blockedProviderPlan
});
const routeChanges = repaired.changes.length > 0 ? [`Repaired Codex model routes:\n${repaired.changes.map((hit) => `- ${formatCodexRouteChange(hit)}`).join("\n")}`] : [];
return {
cfg: codexPluginRepair.cfg,
warnings,
changes: [
...routeChanges,
...repaired.runtimePolicyChanges,
...repaired.runtimePinChanges,
...repaired.unsupportedCompactionChanges,
...codexPluginRepair.changes,
...serviceTierRepair.changes
]
};
}
//#endregion
export { maybeRepairCodexSessionRoutes as a, resolveKnownModelRefMigrationTarget as i, collectCodexRuntimeCompatibilityWarnings as n, collectDisabledCodexPluginRouteIssues as o, maybeRepairCodexRoutes as r, collectCodexRouteWarnings as t };