import { d as isInstalledPluginEnabled, f as loadInstalledPluginIndex, u as getInstalledPluginRecord } from "./installed-plugin-index-store-CEcgxYPz.js";
import { t as AGENT_MODEL_CONFIG_KEYS } from "./model-refs-DK8Y1vYS.js";
import { T as evaluateStoredCredentialEligibility, n as ensureAuthProfileStore } from "./store-B25JueFs.js";
import { t as loadSessionStore } from "./store-load-BvV_smwU.js";
import { r as resolveAllAgentSessionStoreTargetsSync } from "./targets-863D3bPE.js";
import { o as updateSessionStore } from "./store-BSfK8O0O.js";
import "./auth-profiles-5sBNswrQ.js";
import { n as resolveAuthProfileOrder } from "./order-hjUPLIG0.js";
import { o as resolveProfileUnusableUntilForDisplay } from "./usage-B4yhn9ny.js";
import fs from "node:fs";
//#region src/commands/doctor/shared/codex-route-warnings.ts
function normalizeString(value) {
	return typeof value === "string" && value.trim() ? value.trim().toLowerCase() : void 0;
}
function asMutableRecord(value) {
	return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
}
function isOpenAICodexModelRef(model) {
	return normalizeString(model)?.startsWith("openai-codex/") === true;
}
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 normalizeString(params.env?.OPENCLAW_AGENT_RUNTIME) ?? normalizeString(params.agentRuntime?.id) ?? normalizeString(params.defaultsRuntime?.id) ?? "pi";
}
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 } : {},
		...params.setsRuntime ? { setsRuntime: true } : {}
	});
	return canonicalModel;
}
function collectStringModelSlot(params) {
	if (typeof params.value !== "string") return false;
	const model = params.value.trim();
	if (!model || !isOpenAICodexModelRef(model)) return false;
	return !!recordCodexModelHit({
		hits: params.hits,
		path: params.path,
		model,
		runtime: params.runtime,
		setsRuntime: params.setsRuntime
	});
}
function collectModelConfigSlot(params) {
	if (typeof params.value === "string") return collectStringModelSlot({
		hits: params.hits,
		path: params.path,
		value: params.value,
		runtime: params.runtime,
		setsRuntime: params.setsRuntimeOnPrimary
	});
	const record = asMutableRecord(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,
		setsRuntime: params.setsRuntimeOnPrimary
	});
	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 collectModelsMapRefs(params) {
	const record = asMutableRecord(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 = asMutableRecord(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,
		setsRuntimeOnPrimary: key === "model"
	});
	collectStringModelSlot({
		hits: params.hits,
		path: `${params.path}.heartbeat.model`,
		value: asMutableRecord(agent.heartbeat)?.model
	});
	collectModelConfigSlot({
		hits: params.hits,
		path: `${params.path}.subagents.model`,
		value: asMutableRecord(agent.subagents)?.model
	});
	const compaction = asMutableRecord(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: asMutableRecord(compaction?.memoryFlush)?.model
	});
	if (params.collectModelsMap) collectModelsMapRefs({
		hits: params.hits,
		path: `${params.path}.models`,
		models: agent.models
	});
}
function collectConfigModelRefs(cfg, env) {
	const hits = [];
	const defaults = cfg.agents?.defaults;
	const defaultsRuntime = defaults?.agentRuntime;
	collectAgentModelRefs({
		hits,
		agent: defaults,
		path: "agents.defaults",
		runtime: resolveRuntime({
			env,
			defaultsRuntime
		}),
		collectModelsMap: true
	});
	for (const [index, agent] of (cfg.agents?.list ?? []).entries()) collectAgentModelRefs({
		hits,
		agent,
		path: `agents.list.${typeof agent.id === "string" && agent.id.trim() ? agent.id.trim() : String(index)}`,
		runtime: resolveRuntime({
			env,
			agentRuntime: agent.agentRuntime,
			defaultsRuntime
		})
	});
	const channelsModelByChannel = asMutableRecord(cfg.channels?.modelByChannel);
	if (channelsModelByChannel) for (const [channelId, channelMap] of Object.entries(channelsModelByChannel)) {
		const targets = asMutableRecord(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
	});
	collectModelConfigSlot({
		hits,
		path: "tools.subagents.model",
		value: cfg.tools?.subagents?.model
	});
	collectStringModelSlot({
		hits,
		path: "messages.tts.summaryModel",
		value: cfg.messages?.tts?.summaryModel
	});
	collectStringModelSlot({
		hits,
		path: "channels.discord.voice.model",
		value: asMutableRecord(asMutableRecord(cfg.channels?.discord)?.voice)?.model
	});
	return hits;
}
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,
		setsRuntime: params.setsRuntime
	});
	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,
		setsRuntime: params.setsRuntimeOnPrimary
	});
	const record = asMutableRecord(value);
	if (!record) return false;
	const rewrotePrimary = rewriteStringModelSlot({
		hits: params.hits,
		container: record,
		key: "primary",
		path: `${params.path}.primary`,
		runtime: params.runtime,
		setsRuntime: params.setsRuntimeOnPrimary
	});
	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
		});
		params.models[canonicalModel] ??= params.models[legacyRef] ?? {};
		delete params.models[legacyRef];
	}
}
function rewriteAgentModelRefs(params) {
	if (!params.agent) return;
	for (const key of AGENT_MODEL_CONFIG_KEYS) {
		const rewrotePrimary = rewriteModelConfigSlot({
			hits: params.hits,
			container: params.agent,
			key,
			path: `${params.path}.${key}`,
			runtime: key === "model" ? params.currentRuntime : void 0,
			setsRuntimeOnPrimary: key === "model"
		});
		if (key === "model" && rewrotePrimary) {
			const agentRuntime = asMutableRecord(params.agent.agentRuntime) ?? {};
			agentRuntime.id = params.runtime;
			params.agent.agentRuntime = agentRuntime;
		}
	}
	rewriteStringModelSlot({
		hits: params.hits,
		container: asMutableRecord(params.agent.heartbeat),
		key: "model",
		path: `${params.path}.heartbeat.model`
	});
	rewriteModelConfigSlot({
		hits: params.hits,
		container: asMutableRecord(params.agent.subagents),
		key: "model",
		path: `${params.path}.subagents.model`
	});
	const compaction = asMutableRecord(params.agent.compaction);
	rewriteStringModelSlot({
		hits: params.hits,
		container: compaction,
		key: "model",
		path: `${params.path}.compaction.model`
	});
	rewriteStringModelSlot({
		hits: params.hits,
		container: asMutableRecord(compaction?.memoryFlush),
		key: "model",
		path: `${params.path}.compaction.memoryFlush.model`
	});
	if (params.rewriteModelsMap) rewriteModelsMap({
		hits: params.hits,
		models: asMutableRecord(params.agent.models),
		path: `${params.path}.models`
	});
}
function rewriteConfigModelRefs(params) {
	const nextConfig = structuredClone(params.cfg);
	const hits = [];
	const defaultsRuntime = nextConfig.agents?.defaults?.agentRuntime;
	rewriteAgentModelRefs({
		hits,
		agent: asMutableRecord(nextConfig.agents?.defaults),
		path: "agents.defaults",
		runtime: params.runtime,
		currentRuntime: resolveRuntime({
			env: params.env,
			defaultsRuntime
		}),
		rewriteModelsMap: true
	});
	for (const [index, agent] of (nextConfig.agents?.list ?? []).entries()) rewriteAgentModelRefs({
		hits,
		agent,
		path: `agents.list.${typeof agent.id === "string" && agent.id.trim() ? agent.id.trim() : String(index)}`,
		runtime: params.runtime,
		currentRuntime: resolveRuntime({
			env: params.env,
			agentRuntime: agent.agentRuntime,
			defaultsRuntime
		})
	});
	const channelsModelByChannel = asMutableRecord(nextConfig.channels?.modelByChannel);
	if (channelsModelByChannel) for (const [channelId, channelMap] of Object.entries(channelsModelByChannel)) {
		const targets = asMutableRecord(channelMap);
		if (!targets) continue;
		for (const targetId of Object.keys(targets)) rewriteStringModelSlot({
			hits,
			container: targets,
			key: targetId,
			path: `channels.modelByChannel.${channelId}.${targetId}`
		});
	}
	for (const [index, mapping] of (nextConfig.hooks?.mappings ?? []).entries()) rewriteStringModelSlot({
		hits,
		container: mapping,
		key: "model",
		path: `hooks.mappings.${index}.model`
	});
	rewriteStringModelSlot({
		hits,
		container: asMutableRecord(nextConfig.hooks?.gmail),
		key: "model",
		path: "hooks.gmail.model"
	});
	rewriteModelConfigSlot({
		hits,
		container: asMutableRecord(nextConfig.tools?.subagents),
		key: "model",
		path: "tools.subagents.model"
	});
	rewriteStringModelSlot({
		hits,
		container: asMutableRecord(nextConfig.messages?.tts),
		key: "summaryModel",
		path: "messages.tts.summaryModel"
	});
	rewriteStringModelSlot({
		hits,
		container: asMutableRecord(asMutableRecord(nextConfig.channels?.discord)?.voice),
		key: "model",
		path: "channels.discord.voice.model"
	});
	return {
		cfg: hits.length > 0 ? nextConfig : params.cfg,
		changes: hits
	};
}
function hasUsableCodexOAuthProfile(cfg) {
	try {
		const store = ensureAuthProfileStore(void 0, {
			allowKeychainPrompt: false,
			config: cfg
		});
		const now = Date.now();
		return resolveAuthProfileOrder({
			cfg,
			store,
			provider: "openai-codex"
		}).some((profileId) => {
			const credential = store.profiles[profileId];
			if (!credential || credential.type !== "oauth") return false;
			const unusableUntil = resolveProfileUnusableUntilForDisplay(store, profileId);
			if (unusableUntil && now < unusableUntil) return false;
			return evaluateStoredCredentialEligibility({
				credential,
				now
			}).eligible;
		});
	} catch {
		return false;
	}
}
function isCodexPluginInstalledAndEnabled(cfg, env) {
	const index = loadInstalledPluginIndex({
		config: cfg,
		env
	});
	const record = getInstalledPluginRecord(index, "codex");
	if (!record || !record.startup.agentHarnesses.includes("codex")) return false;
	return isInstalledPluginEnabled(index, "codex", cfg);
}
function resolveCodexRepairRuntime(params) {
	if (params.codexRuntimeReady !== void 0) return params.codexRuntimeReady ? "codex" : "pi";
	return isCodexPluginInstalledAndEnabled(params.cfg, params.env) && hasUsableCodexOAuthProfile(params.cfg) ? "codex" : "pi";
}
function formatCodexRouteChange(hit, runtime) {
	const suffix = hit.setsRuntime ? `; set agentRuntime.id to "${runtime}"` : "";
	return `${hit.path}: ${hit.model} -> ${hit.canonicalModel}${suffix}.`;
}
function collectCodexRouteWarnings(params) {
	const hits = collectConfigModelRefs(params.cfg, params.env);
	if (hits.length === 0) return [];
	return [[
		"- 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; primary routes select `agentRuntime.id: \"codex\"` only when Codex is installed, enabled, and has usable OAuth, otherwise they select OpenClaw PI."
	].join("\n")];
}
function maybeRepairCodexRoutes(params) {
	if (collectConfigModelRefs(params.cfg, params.env).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 runtime = resolveCodexRepairRuntime({
		cfg: params.cfg,
		env: params.env,
		codexRuntimeReady: params.codexRuntimeReady
	});
	const repaired = rewriteConfigModelRefs({
		cfg: params.cfg,
		env: params.env,
		runtime
	});
	return {
		cfg: repaired.cfg,
		warnings: [],
		changes: [`Repaired Codex model routes:\n${repaired.changes.map((hit) => `- ${formatCodexRouteChange(hit, runtime)}`).join("\n")}`]
	};
}
function rewriteSessionModelPair(params) {
	let changed = false;
	const provider = normalizeString(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";
		changed = true;
		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 clearStaleCodexAuthOverride(entry, runtime) {
	if (runtime === "codex" || !entry.authProfileOverride?.startsWith("openai-codex:")) return false;
	delete entry.authProfileOverride;
	delete entry.authProfileOverrideSource;
	delete entry.authProfileOverrideCompactionCount;
	return true;
}
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 changedModelRoute = changedRuntimeModelRoute || changedOverrideModelRoute;
		const changedFallbackNotice = clearStaleCodexFallbackNotice(entry);
		const changedAuthOverride = clearStaleCodexAuthOverride(entry, params.runtime);
		const shouldRepinCodexHarness = entry.agentHarnessId === "codex" && params.runtime !== "codex";
		if (!changedModelRoute && !changedFallbackNotice && !changedAuthOverride && !shouldRepinCodexHarness) continue;
		if (changedModelRoute || shouldRepinCodexHarness) {
			entry.agentHarnessId = params.runtime;
			entry.agentRuntimeOverride = params.runtime;
		}
		entry.updatedAt = now;
		sessionKeys.push(sessionKey);
	}
	return {
		changed: sessionKeys.length > 0,
		sessionKeys
	};
}
function scanCodexSessionStoreRoutes(store, runtime) {
	return Object.entries(store).flatMap(([sessionKey, entry]) => {
		if (!entry) return [];
		return normalizeString(entry.modelProvider) === "openai-codex" || normalizeString(entry.providerOverride) === "openai-codex" || isOpenAICodexModelRef(entry.model) || isOpenAICodexModelRef(entry.modelOverride) || isOpenAICodexModelRef(entry.fallbackNoticeSelectedModel) || isOpenAICodexModelRef(entry.fallbackNoticeActiveModel) || runtime !== "codex" && entry.authProfileOverride?.startsWith("openai-codex:") === true || runtime !== "codex" && entry.agentHarnessId === "codex" ? [sessionKey] : [];
	});
}
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 runtime = resolveCodexRepairRuntime({
			cfg: params.cfg,
			env: params.env,
			codexRuntimeReady: params.codexRuntimeReady
		});
		const stale = targets.flatMap((target) => {
			return scanCodexSessionStoreRoutes(loadSessionStore(target.storePath), runtime).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: []
		};
	}
	const runtime = resolveCodexRepairRuntime({
		cfg: params.cfg,
		env: params.env,
		codexRuntimeReady: params.codexRuntimeReady
	});
	let repairedStores = 0;
	let repairedSessions = 0;
	for (const target of targets) {
		if (scanCodexSessionStoreRoutes(loadSessionStore(target.storePath), runtime).length === 0) continue;
		const result = await updateSessionStore(target.storePath, (store) => repairCodexSessionStoreRoutes({
			store,
			runtime
		}), { 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/* with agentRuntime "${runtime}".`] : []
	};
}
//#endregion
export { repairCodexSessionStoreRoutes as i, maybeRepairCodexRoutes as n, maybeRepairCodexSessionRoutes as r, collectCodexRouteWarnings as t };
