UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

1,948 lines • 102 kB
import { c as normalizeOptionalLowercaseString, l as normalizeOptionalString } from "./string-coerce-1VuiLaaR.mjs";
import { s as asFiniteNumber } from "./number-coercion-CkhMiRw8.mjs";
import { n as isTruthyEnvValue } from "./env-Ca5Je5YP.mjs";
import { a as asOptionalRecord, c as isRecord } from "./record-coerce-TIsquoVR.mjs";
import { n as sanitizeForLog } from "./ansi-DyIsQZAA.mjs";
import { r as createInvalidConfigError } from "./io.invalid-config-BqEcsQ06.mjs";
import { a as coerceSecretRef, l as isLegacySecretRefWithoutProvider, m as parseLegacySecretRefEnvMarker, s as hasLegacySecretRefExtraFields } from "./types.secrets-DD-Q2SS_.mjs";
import { r as resolveStateDir } from "./state-dir-CYkBUd_G.mjs";
import { S as resolveIsConfigReadOnly, m as resolveConfigPath, p as resolveCanonicalConfigPath, t as CONFIG_PATH } from "./paths-BP0DYpE8.mjs";
import { t as formatCliCommand } from "./command-format-DwEdrDKL.mjs";
import { r as withArtifactPreservingStateReads } from "./artifact-preserving-state-reads-BQnQLAJR.mjs";
import { t as createSubsystemLogger } from "./subsystem-4NzR43uz.mjs";
import { c as resolveOpenClawStateSqlitePath } from "./openclaw-state-db.paths-CmKkMYdo.mjs";
import { r as hasCommandProcessCleanupError } from "./exec-result-DXhDHhYI.mjs";
import { r as createLazyRuntimeModule } from "./lazy-runtime-CJrzFzqU.mjs";
import { a as resolveUpdateRehearsalRoot } from "./update-rehearsal-paths-FgHeU_TR.mjs";
import "./session-key-DY3MOX4N.mjs";
import { t as DEFAULT_ACCOUNT_ID } from "./account-id-ifCOO7_9.mjs";
import { a as readAgentRosterProperty, l as tryResolveSoleAgentId, n as listAgentEntries } from "./agent-roster-DH6r_8f4.mjs";
import { r as normalizeProviderId } from "./provider-id-D1VImdKk.mjs";
import { d as resolveAgentWorkspaceDir } from "./agent-scope-config-BEk6-ThS.mjs";
import { h as listAgentDatabaseAdmissionRefusals, r as canIsolateAgentDatabase, u as evaluateAgentDatabaseAdmissions, v as recordAgentDatabaseAdmissions } from "./agent-database-admission-CRwW7FsI.mjs";
import { r as cloneEnvWithPlatformSemantics, y as resolveFutureConfigActionBlock } from "./config-env-vars-BzUE_dOh.mjs";
import { r as assertOpenClawStateWriteAllowedAtPath } from "./openclaw-state-ownership-C6zxQPsi.mjs";
import { a as copyConfigResolutionFactsThroughRewrite, t as cloneConfigWithResolutionFacts } from "./resolution-facts-NmRQ-VhX.mjs";
import { i as hashConfigRaw, s as parseConfigJson5, t as coerceConfig } from "./io.read-helpers-BBxWMZOW.mjs";
import { f as withOpenClawStateDatabaseReadSnapshot } from "./openclaw-state-db-readonly-ywwvuxdQ.mjs";
import { i as tryGetLegacyDefaultAgentId, r as retainLegacyDefaultAgentId, t as inheritLegacyDefaultAgentId } from "./legacy.default-agent-owner-CMzbXvh6.mjs";
import { r as UPDATE_ENVIRONMENT_FAILURE_REASONS } from "./update-outcome-DUBzR7uz.mjs";
import { f as shouldDeferConfiguredPluginInstallRepair, p as shouldSkipLegacyUpdateDoctorConfigWrite } from "./update-phase-YMT_h9sM.mjs";
import { c as readDeferredPluginMigrations, i as mergeDeferredPluginMigration, r as formatDeferredPluginMigration, t as DeferredPluginMigrationConflictError, u as recordDeferredPluginMigrations } from "./deferred-plugin-migrations-DiP5orVE.mjs";
import { C as setActiveDegradedPlugins } from "./discovery-D42cUZXk.mjs";
import { u as normalizePluginsConfig } from "./config-state-Ba1MVBAI.mjs";
import { r as getOfficialExternalPluginCatalogEntry } from "./official-external-plugin-catalog-BKfROutK.mjs";
import { i as loadInstalledPluginIndexInstallRecordsSync } from "./installed-plugin-index-record-reader-D5Zh_tO0.mjs";
import { o as getOfficialExternalPluginCatalogManifest } from "./official-external-plugin-catalog-source-8hS3NARf.mjs";
import { h as resolveNormalizedProviderModelMaxTokens } from "./validation-core-DHjeBn07.mjs";
import { c as recoverConfigFromLastKnownGoodCore, l as createConfigIoContext, n as prepareConfigRecoveryFromContext, t as createConfigIO } from "./io.factory-QmLuiLlX.mjs";
import { p as readRecentConfigAuditRecords } from "./io.audit-BD1ojDxn.mjs";
import { c as assertBaseSnapshotStillCurrent, r as withConfigSourceLocks } from "./write-lock-DKbjzOgd.mjs";
import { t as ConfigMutationConflictError } from "./mutation-conflict-Be0wSyDG.mjs";
import "./agent-scope-bJOokm5r.mjs";
import { i as passesManifestOwnerBasePolicy } from "./manifest-owner-policy-Cqbh_mxQ.mjs";
import { n as loadPluginManifestRegistryForPluginRegistry } from "./plugin-registry-contributions-vIYsvMGK.mjs";
import { a as preserveDeferredPluginMigrationConfig, o as resolveDeferredPluginMigrationConfigPaths, r as hasDeferredPluginMigrationConfig } from "./deferred-plugin-migration-config-CohVpj3v.mjs";
import { d as migrateLegacyWebFetchConfig, f as migrateLegacyWebSearchConfig, h as LEGACY_AGENT_ROSTER_RULES, n as findLegacyConfigRuleIssues, p as migrateLegacyXSearchConfig, s as normalizeLegacyTalkConfig, u as stripRetiredTuningKnobs } from "./legacy-DRcorE1g.mjs";
import { r as resolveLegacyAgentRosterOwner } from "./legacy.roster-BkkM_O4n.mjs";
import { i as isBlockedLegacyCodexModelRef } from "./codex-route-model-ref-BZxXT5lv.mjs";
import "./legacy-config-record-shared-CAk6FVG_.mjs";
import { t as classifyOtelGrpcMigrationOwnership } from "./include-migration-ownership-JOEeX0MG.mjs";
import { a as mergeModelRefMapEntries, i as isLegacyModelsAddCodexMetadataModel, n as modelEntryWithRuntimePolicy, s as rewriteModelRefs } from "./legacy-config-migrations.runtime.models-Cy_VNydm.mjs";
import { a as HeartbeatSchema } from "./zod-schema.agent-runtime-B8-yzGte.mjs";
import { n as discoverConfigSecretTargets } from "./target-registry-query-BxzYFe4n.mjs";
import { i as setPathExistingStrict } from "./path-utils-aDSU1mEq.mjs";
import "./target-registry-DA4USc5P.mjs";
import "./env-vars-BZzSFlXe.mjs";
import { t as configIncludeOwnsAgentRoster } from "./agent-roster-provenance-BpDWMBif.mjs";
import { y as recoverConfigFromJsonRootSuffix } from "./io.runtime-B134MOMz.mjs";
import "./io-BGddrCVX.mjs";
import "./plugin-registry-qMoFDG9O.mjs";
import { a as withPluginLifecycleLease } from "./plugin-lifecycle-lease-CJFG12c4.mjs";
import "./installed-plugin-index-records-3TTKz0gf.mjs";
import { a as runPluginSetupConfigMigrations } from "./setup-registry-DNr-Qxd2.mjs";
import { t as DEFAULT_GOOGLE_API_BASE_URL } from "./google-api-base-url-B5JCFm92.mjs";
import { t as readRetiredDeferredPluginMigrationWarnings } from "./deferred-plugin-migration-warnings-CpaVE9Sv.mjs";
import { t as findRetiredConfigUpgradeRequirement } from "./retired-config-formats-dWNTYZjK.mjs";
import { t as note } from "./note-Bi0VsI6R.mjs";
import { n as sanitizeDoctorNote, t as emitDoctorNotes } from "./emit-notes-CLbO37Rj.mjs";
import { a as migrateLegacyConfig, c as noteDoctorConfigPreflightIssues, d as noteMcpOriginWarning, f as noteMediaCliModelWarnings, h as noteSandboxOriginProxyWarning, l as noteDoctorHookConfigWarnings, m as noteOpencodeProviderOverrides, n as applyUnknownConfigKeyStep, o as applyLegacyDoctorMigrations, p as noteMissingDefaultAgentOwner, r as prepareDoctorConfigReferenceSource, s as applyChannelDoctorCompatibilityMigrations, t as applyLegacyCompatibilityStep, u as noteImplicitFallbackClobberWarnings } from "./config-flow-steps-BAOy-G5i.mjs";
import { t as listDoctorConfiguredChannelIds } from "./configured-channel-ids-CwITTl83.mjs";
import { r as callGateway } from "./call-D6fWfxgB.mjs";
import { t as getAgentDatabaseStartupAdmission } from "./agent-database-startup-BYk3phjn.mjs";
import { c as throwIfDoctorStateMigrationRefused, t as DoctorStateMigrationRefusalError } from "./state-migrations.messages-D99T3i-t.mjs";
import { n as resolveCronJobsStorePathFromConfig } from "./paths-Dqy-ZJ8r.mjs";
import { r as loadSetupChannelPluginFromManifestRecord } from "./read-only-DbPcZnSt.mjs";
import { a as migrateLegacyCommandOwners } from "./doctor-command-owner-6e4QI_3f.mjs";
import { t as pruneBindingsForMissingAgents } from "./legacy-config-binding-repair-CyANJAZr.mjs";
import { r as withProgress } from "./progress-CGcf72of.mjs";
import { t as createPluginCapabilityConsentPrompter } from "./plugin-capability-consent-DOqMNDMz.mjs";
import { t as resolveSingleAccountPromotion } from "./setup-promotion-helpers-C6zvBkBc.mjs";
import { t as describeConfigSnapshotInputChange } from "./snapshot-inputs-DvxsX3U7.mjs";
import { n as commitAutomaticConfigRepair, r as planAutomaticConfigRepair, t as canPlanAutomaticConfigRepair } from "./automatic-config-repair-DZlJ8gNh.mjs";
import { n as assertNoRetiredStateFiles } from "./state-migrations.retired-files-Dlsbs5FH.mjs";
import { r as listReferencedLegacyOAuthSidecarPaths } from "./doctor-auth-legacy-paths-BQcoV-Tb.mjs";
import { t as listRetiredCronStateFiles } from "./state-migrations.retired-cron-files-BfwwvNnu.mjs";
import { a as readConfigPreflightSnapshot, n as runDoctorPluginConvergence, o as measureDoctorConfigPreflightStep, r as assertPreflightConfigUnchanged } from "./doctor-config-preflight-plugin-verification-CacAlrh4.mjs";
import { t as inspectPluginMigrationAvailability } from "./plugin-migration-availability-DWT1FskW.mjs";
import { t as noteDoctorMigrationResult } from "./doctor-migration-notes-DvHZ2oiu.mjs";
import { t as assertDoctorRetiredStateAdmission } from "./doctor-retired-state-admission-5vzfZWjI.mjs";
import { t as createDoctorPluginMetadataSnapshotScope } from "./plugin-metadata-snapshot-scope-Gvinldti.mjs";
import { n as createWorkspaceAliasMigrationRepair } from "./doctor-workspace-alias-B1hHTIS8.mjs";
import { r as cronCodexRuntimePolicyTargetKey } from "./store-migration-eSXK7PQ_.mjs";
import { t as applyDoctorConfigMutation } from "./config-mutation-state-B_xx2ZMY.mjs";
import { t as migrateLegacyRuntimeModelRef } from "./legacy-runtime-model-providers-BPqY-s89.mjs";
import { t as canWriteDoctorInclude } from "./roster-include-write-cPIsc8Nj.mjs";
import fs, { existsSync } from "node:fs";
import path from "node:path";
import { homedir } from "node:os";
import { isDeepStrictEqual } from "node:util";
//#region src/commands/doctor-config-preflight-plugin-index.ts
/** Returns true during updater-managed config rewrites where plugin validation may be stale. */
function shouldSkipPluginValidationForDoctorConfigPreflight(env = process.env) {
	return isTruthyEnvValue(env.OPENCLAW_UPDATE_IN_PROGRESS);
}
/** One preflight owns completion; each read still checks the current update phase. */
function createDoctorRehearsalSnapshotPreparation(report) {
	let completed = false;
	const prepareSnapshot = async (snapshot) => {
		if (completed) return;
		const { completeUpdateCandidatePluginRehearsal } = await import("./update-candidate-plugin-repair-Bi0nlNrN.mjs");
		const result = await completeUpdateCandidatePluginRehearsal({
			config: snapshot.sourceConfig ?? snapshot.config ?? {},
			env: process.env,
			installRecords: loadInstalledPluginIndexInstallRecordsSync({ env: process.env })
		});
		completed = true;
		report({
			changes: result.copiedFiles > 0 ? [`Update rehearsal: copied ${result.copiedFiles} missing plugin dependency files.`] : [],
			warnings: result.warnings
		});
	};
	return (enabled) => enabled && resolveUpdateRehearsalRoot(process.env) && process.env.OPENCLAW_UPDATE_IN_PROGRESS === "1" ? prepareSnapshot : void 0;
}
//#endregion
//#region src/commands/doctor/shared/config-recovery.ts
const transformDoctorRecoveryCandidate = (params) => {
	const ownership = classifyOtelGrpcMigrationOwnership({
		snapshot: {
			path: params.configPath,
			includeProvenance: params.includeProvenance
		},
		authoredConfig: params.candidate.parsed,
		resolvedConfig: params.resolvedConfig
	});
	if (ownership && ownership.kind !== "direct") throw createInvalidConfigError(params.configPath, ownership.kind === "resolved-only" ? "candidate migration cannot persist an env-resolved diagnostics.otel.protocol repair" : "candidate migration requires an include-owned diagnostics.otel.protocol repair");
	const migration = applyLegacyDoctorMigrations(params.candidate.parsed, {
		sourceConfigBeforeMigrations: params.resolvedConfig,
		context: {
			authoredRaw: params.candidate.parsed,
			resolvedRaw: params.resolvedConfig
		}
	});
	return migration.next ? preserveDeferredPluginMigrationConfig({
		sourceConfig: params.candidate.parsed,
		nextConfig: migration.next,
		pending: params.deferredPluginMigrations
	}) : params.candidate.parsed;
};
function prepareDoctorConfigRecoverySnapshot(options, snapshot) {
	return prepareConfigRecoveryFromContext(createConfigIoContext(options, transformDoctorRecoveryCandidate), snapshot);
}
function recoverDoctorConfigFromLastKnownGood(params) {
	const context = createConfigIoContext({ configPath: params.snapshot.path }, transformDoctorRecoveryCandidate);
	return recoverConfigFromLastKnownGoodCore({
		...params,
		deps: context.deps,
		prepareCandidate: context.prepareRecoveryBackupCandidate
	});
}
//#endregion
//#region src/commands/doctor-config-preflight-legacy-config.ts
function createDoctorConfigRepairPlanner(params) {
	const planScopedConfigRepair = (snapshot) => params.runWithPluginMetadataSnapshot({ config: snapshot.sourceConfig ?? snapshot.config ?? {} }, () => planAutomaticConfigRepair(snapshot));
	const planAdmittedConfigRepair = (snapshot, prepared = null) => (params.options.repairPrefixedConfig === true || params.stateMigrationsRequested && params.options.migrateLegacyConfig !== false) && canPlanAutomaticConfigRepair(snapshot) && !params.skipLegacyParentConfigWrite && (params.options.repairPrefixedConfig === true || !shouldSkipPluginValidationForDoctorConfigPreflight()) && !resolveIsConfigReadOnly(process.env) && !resolveFutureConfigActionBlock({
		action: "normalize legacy config",
		snapshot
	}) ? prepared ?? planScopedConfigRepair(snapshot) : null;
	return {
		planScopedConfigRepair,
		planAdmittedConfigRepair
	};
}
async function migrateLegacyDoctorConfig(params) {
	if (!params.enabled || resolveIsConfigReadOnly(process.env)) return;
	const changes = await params.measure("legacy-config-migration", maybeMigrateLegacyConfig);
	if (changes.length > 0) note(changes.map((entry) => `- ${entry}`).join("\n"), "Doctor changes");
}
/** Repair active legacy bytes before considering an older backup. */
async function prepareDoctorConfigRecovery(params) {
	let snapshotRead = params.snapshotRead;
	let snapshot = snapshotRead.snapshot;
	const assertSupportedConfig = (config) => {
		const retired = findRetiredConfigUpgradeRequirement(config);
		if (retired) throw new Error(`${retired.message} ${retired.nextAction}`);
		assertNoRetiredStateFiles("OAuth credential sidecars", listReferencedLegacyOAuthSidecarPaths(process.env, coerceConfig(config)));
	};
	assertSupportedConfig(snapshot.sourceConfigBeforeMigrations ?? snapshot.sourceConfig);
	assertNoRetiredStateFiles("Cron state", await listRetiredCronStateFiles(resolveCronJobsStorePathFromConfig(snapshot.sourceConfigBeforeMigrations ?? snapshot.sourceConfig ?? snapshot.config)));
	let activeConfigRepair = null;
	const recoveryEnabled = params.enabled && !resolveFutureConfigActionBlock({
		action: "recover config",
		snapshot
	});
	if (recoveryEnabled && snapshot.valid) {
		const recovery = await prepareDoctorConfigRecoverySnapshot({ configPath: snapshot.path }, snapshot);
		if (recovery) {
			await recovery.apply();
			snapshotRead = await params.readSnapshot();
			snapshot = snapshotRead.snapshot;
		}
	}
	if (recoveryEnabled && snapshot.exists && !snapshot.valid) {
		activeConfigRepair = typeof snapshot.raw === "string" && parseConfigJson5(snapshot.raw).ok ? params.planRepair(snapshot) : null;
		let configRepaired = false;
		if (!activeConfigRepair && await recoverConfigFromJsonRootSuffix(snapshot, assertSupportedConfig)) {
			note("Removed non-JSON prefix from openclaw.json.", "Config");
			configRepaired = true;
		} else if (!activeConfigRepair && await recoverDoctorConfigFromLastKnownGood({
			snapshot,
			reason: "doctor-invalid-config"
		})) {
			note("Restored openclaw.json from last-known-good; original saved as .clobbered.*.", "Config");
			configRepaired = true;
		}
		if (configRepaired) {
			snapshotRead = await params.readSnapshot();
			snapshot = snapshotRead.snapshot;
		}
		if (!snapshot.valid && typeof snapshot.raw === "string" && !parseConfigJson5(snapshot.raw).ok) throw new Error(`Config at ${snapshot.path} is not parseable and cannot be repaired automatically. The file remains unchanged. Inspect the exact parse error with ${formatCliCommand("openclaw config validate")}, then hand-edit the file; or move it aside and run ${formatCliCommand("openclaw onboard")} to generate a fresh config.`);
	}
	return {
		snapshotRead,
		activeConfigRepair
	};
}
async function maybeMigrateLegacyConfig() {
	if (process.env.OPENCLAW_STATE_DIR?.trim() || process.env.OPENCLAW_HOME?.trim() || process.env.OPENCLAW_CONFIG_PATH?.trim()) return [];
	const { renameLegacyConfigFile } = await import("./state-migrations.state-dir-9h5c4mfe.mjs");
	return renameLegacyConfigFile(path.dirname(resolveCanonicalConfigPath()));
}
//#endregion
//#region src/commands/doctor-config-preflight-migrations.ts
/** Settle package repairs before state migrations select their plugin owners. */
async function prepareDoctorMigrationPlugins(params) {
	const convergence = await runDoctorPluginConvergence(params);
	setActiveDegradedPlugins(convergence.quarantinedPlugins);
	await params.onDeferredPlugins(convergence.deferredPlugins ?? [], convergence.migrationInspection);
	const refreshed = await params.readRefreshedSnapshot();
	assertPreflightConfigUnchanged(params.snapshotRead.snapshot, refreshed.snapshot);
	return {
		snapshotRead: refreshed,
		warnings: convergence.warnings ?? []
	};
}
async function assertDoctorPreflightMigrationsComplete(params) {
	const scopedRefusals = params.stepReceipts.filter((receipt) => receipt.outcome === "refused" && (receipt.refusal?.code === "agent-database-ownership-mismatch" || receipt.refusal?.code === "blocked-by-agent-database-refusal") && receipt.refusedAgentDatabasePaths?.length);
	const admissions = scopedRefusals.length > 0 ? getAgentDatabaseStartupAdmission() ? listAgentDatabaseAdmissionRefusals() : await evaluateAgentDatabaseAdmissions(params.cfg) : [];
	if (scopedRefusals.length > 0) recordAgentDatabaseAdmissions(admissions);
	const isolatedPaths = new Set(admissions.filter((refusal) => refusal.code !== "agent-database-ownership-mismatch" || canIsolateAgentDatabase(params.cfg, refusal.agentId)).flatMap((refusal) => refusal.paths.map((pathname) => path.resolve(pathname))));
	for (const receipt of scopedRefusals) if (receipt.refusedAgentDatabasePaths?.every((pathname) => isolatedPaths.has(path.resolve(pathname)))) receipt.outcome = "warning";
	try {
		throwIfDoctorStateMigrationRefused(params.stepReceipts);
	} catch (error) {
		if (error instanceof DoctorStateMigrationRefusalError) {
			const { assertConfiguredWorkspaceStateReady } = await import("./workspace-state-dirs-DjYXDBqE.mjs");
			try {
				await assertConfiguredWorkspaceStateReady({
					cfg: params.cfg,
					operation: "doctor"
				});
			} catch (workspaceError) {
				params.report({
					changes: [],
					warnings: [String(workspaceError)]
				});
			}
		}
		throw error;
	}
}
//#endregion
//#region src/commands/doctor-config-preflight-plugin-migrations.ts
/** One preflight retains unavailable owners until their migration reports completion. */
function createDoctorPluginMigrationPreparation(params) {
	const shouldDeferInstallation = () => Boolean(resolveUpdateRehearsalRoot(params.env())) || shouldDeferConfiguredPluginInstallRepair(params.env());
	const previousById = /* @__PURE__ */ new Map();
	let deferred = [];
	let expectedPending = [];
	let refreshSnapshot = false;
	let previousLoaded = false;
	const loadPrevious = async (snapshot) => {
		if (previousLoaded) return;
		const env = cloneEnvWithPlatformSemantics(params.env());
		if (!(snapshot?.exists ?? existsSync(resolveConfigPath(env)))) return;
		deferred = await withArtifactPreservingStateReads(() => withOpenClawStateDatabaseReadSnapshot(async () => readDeferredPluginMigrations({ env }), { env }));
		expectedPending = structuredClone(deferred);
		remember();
		previousLoaded = true;
	};
	let prepared = false;
	const completedIds = /* @__PURE__ */ new Set();
	const reported = /* @__PURE__ */ new Map();
	let statelessPlugins = /* @__PURE__ */ new Map();
	let runtimePluginAliases = /* @__PURE__ */ new Set();
	let unavailablePluginIds = /* @__PURE__ */ new Set();
	let discoveryBlockedPluginIds = /* @__PURE__ */ new Set();
	let replacementPluginIds = {};
	let sourceSnapshot;
	const inspectedStatelessPluginIds = /* @__PURE__ */ new Set();
	const requirePlugins = (pluginIds, requirement) => {
		for (const pluginId of pluginIds) {
			const pending = previousById.get(pluginId);
			if (pending) previousById.set(pluginId, {
				...pending,
				[requirement]: true
			});
		}
	};
	const learn = (inspection) => {
		if (!inspection) return;
		statelessPlugins = new Map(inspection.statelessPlugins.map(({ id, version }) => [id, version]));
		runtimePluginAliases = new Set(inspection.runtimePluginAliases);
		unavailablePluginIds = new Set(inspection.unavailablePluginIds);
		discoveryBlockedPluginIds = new Set(inspection.discoveryBlockedPluginIds);
		replacementPluginIds = inspection.replacementPluginIds ?? {};
		requirePlugins(inspection.requiredPluginIds, "requiresStateMigration");
		requirePlugins(inspection.inspectionRequiredPluginIds, "requiresDoctorInspection");
	};
	const retain = (pending) => mergeDeferredPluginMigration(previousById.get(pending.pluginId), pending);
	const remember = () => {
		for (const pending of deferred) previousById.set(pending.pluginId, pending);
	};
	const prepare = async (snapshot) => {
		sourceSnapshot = snapshot;
		await loadPrevious(snapshot);
		if (!snapshot.exists) return [...previousById.values()];
		if (!prepared && params.enabled) {
			const availability = await inspectPluginMigrationAvailability({
				cfg: snapshot.sourceConfig,
				env: params.env(),
				retainedPluginIds: [...previousById.keys()],
				deferInstallation: shouldDeferInstallation()
			});
			learn(availability);
			deferred = availability.pending.map(retain);
			remember();
			prepared = true;
		}
		return [...previousById.values()];
	};
	const reportPending = (plugin) => {
		const warning = formatDeferredPluginMigration(plugin, params.env());
		const previous = reported.get(plugin.pluginId);
		if (previous?.warnings[0] === warning) return;
		params.report({
			changes: [],
			warnings: [warning],
			warningDisposition: "recoverable",
			outcome: "deferred"
		});
		if (previous) {
			previous.warnings = [warning];
			return;
		}
		const receipt = {
			id: `plugin:${plugin.pluginId}`,
			phase: "final",
			source: [{
				kind: "owner",
				id: plugin.pluginId
			}],
			target: [{
				kind: "owner",
				id: plugin.pluginId
			}],
			requiredness: "conditional",
			reversibility: "checkpoint-required",
			outcome: "deferred",
			changes: [],
			warnings: [warning]
		};
		reported.set(plugin.pluginId, receipt);
		params.recordReceipt(receipt);
	};
	const persistPending = async (pending, resolvedPluginIds, settlements) => {
		try {
			const committed = await recordDeferredPluginMigrations({
				env: params.env(),
				pending,
				...resolvedPluginIds ? { resolvedPluginIds } : {},
				...settlements ? { settlements } : {},
				expectedPending
			});
			if (committed) expectedPending = structuredClone(committed);
			return true;
		} catch (error) {
			if (!(error instanceof DeferredPluginMigrationConflictError)) throw error;
			expectedPending = structuredClone(error.pending);
			previousById.clear();
			deferred = error.pending;
			completedIds.clear();
			statelessPlugins.clear();
			runtimePluginAliases.clear();
			unavailablePluginIds.clear();
			discoveryBlockedPluginIds.clear();
			replacementPluginIds = {};
			inspectedStatelessPluginIds.clear();
			refreshSnapshot = true;
			for (const plugin of deferred) {
				previousById.set(plugin.pluginId, plugin);
				reportPending(plugin);
			}
			return false;
		}
	};
	return {
		deferred: () => deferred,
		retainedPluginIds: () => [...previousById.keys()],
		prepare,
		snapshotOptions: async () => {
			await loadPrevious();
			return {
				preparePluginMigrations: !prepared && params.enabled ? prepare : void 0,
				deferredPluginMigrations: [...previousById.values()]
			};
		},
		async migrate(config) {
			const { autoMigrateLegacyPluginDoctorState } = await import("./state-migrations.plugin-doctor-DlX18koW.mjs");
			params.report(await params.measure("plugin-doctor-migrations", () => params.runWithPluginMetadataSnapshot({ config }, () => autoMigrateLegacyPluginDoctorState({
				config,
				env: params.env(),
				log: params.log,
				...params.doctorOnlyStateMigrations ? { doctorOnlyStateMigrations: true } : {}
			}))));
		},
		async converged(pending, snapshot, metadata, inspection) {
			sourceSnapshot = snapshot;
			learn(inspection);
			deferred = pending.map((plugin) => retain(Object.assign(resolveDeferredPluginMigrationConfigPaths({
				config: snapshot.sourceConfigBeforeMigrations ?? snapshot.sourceConfig,
				pluginId: plugin.pluginId,
				compatibilityMigrationPaths: metadata?.plugins.find((record) => record.id === plugin.pluginId)?.configContracts?.compatibilityMigrationPaths
			}), plugin)));
			remember();
			if (!await persistPending([...previousById.values()])) return;
			for (const plugin of deferred) reportPending(plugin);
		},
		observe(result) {
			requirePlugins(result.requiredPluginIds ?? [], "requiresStateMigration");
			for (const pluginId of result.statelessPluginIds ?? []) inspectedStatelessPluginIds.add(pluginId);
			for (const pluginId of result.completedPluginIds ?? []) completedIds.add(pluginId);
		},
		async complete() {
			if (!params.enabled) return false;
			const unavailableIds = new Set(deferred.map((plugin) => plugin.pluginId));
			const installationDeferred = shouldDeferInstallation();
			const sourceConfig = sourceSnapshot?.sourceConfigBeforeMigrations ?? sourceSnapshot?.sourceConfig;
			const settlements = [];
			const warnings = [];
			const resolvedPluginIds = [...previousById.values()].filter((plugin) => {
				if (completedIds.has(plugin.pluginId)) return true;
				if (unavailablePluginIds.has(plugin.pluginId) && sourceSnapshot?.valid && sourceConfig && !installationDeferred && !hasDeferredPluginMigrationConfig(sourceConfig, plugin)) {
					const successor = replacementPluginIds[plugin.pluginId];
					if (successor && completedIds.has(successor)) {
						settlements.push({
							pluginId: plugin.pluginId,
							status: "superseded",
							reason: `Superseded by plugin "${successor}", which completed its migration. Existing state has been kept.`
						});
						return true;
					}
					if (!(sourceConfig.plugins?.entries?.[plugin.pluginId]?.enabled === true && passesManifestOwnerBasePolicy({
						plugin: { id: plugin.pluginId },
						normalizedConfig: normalizePluginsConfig(sourceConfig.plugins)
					})) && !plugin.requiresStateMigration && !plugin.requiresDoctorInspection) {
						settlements.push({
							pluginId: plugin.pluginId,
							status: "completed",
							reason: "Not applicable: the plugin owner is unavailable and there is no protected config to migrate. Existing state has been kept."
						});
						return true;
					}
				}
				if (plugin.requiresStateMigration || unavailableIds.has(plugin.pluginId)) return false;
				if (inspectedStatelessPluginIds.has(plugin.pluginId)) return true;
				if (statelessPlugins.has(plugin.pluginId)) {
					const reason = `Plugin "${plugin.pluginId}" version "${statelessPlugins.get(plugin.pluginId) ?? "unknown"}": no plugin migration contract. No migration ran; existing data and settings have been kept.`;
					settlements.push({
						pluginId: plugin.pluginId,
						status: "completed",
						reason
					});
					warnings.push(reason);
					return true;
				}
				if (plugin.requiresDoctorInspection) return false;
				return runtimePluginAliases.has(plugin.pluginId) && !plugin.validationExcludedPaths?.length && (plugin.configPaths ?? []).every((segments) => segments.length === 2 && segments[0] === "session" && segments[1] === "store");
			}).map((plugin) => plugin.pluginId);
			const resolvedIds = new Set(resolvedPluginIds);
			const pending = [...previousById.values()].filter((plugin) => !resolvedIds.has(plugin.pluginId)).map((plugin) => unavailablePluginIds.has(plugin.pluginId) && !discoveryBlockedPluginIds.has(plugin.pluginId) && !installationDeferred ? Object.assign(plugin, { reason: `The migration owner is unavailable. Install or enable plugin "${plugin.pluginId}" to migrate its retained data and settings. For a retired plugin, ask its maintainer for a supported migration or recovery path.` }) : unavailableIds.has(plugin.pluginId) ? plugin : Object.assign(plugin, {
				reason: "The installed plugin has not confirmed that its saved data and settings are ready for this version. If Doctor cannot finish the upgrade, report this warning to the plugin maintainer.",
				command: "openclaw doctor --fix"
			}));
			if (resolvedPluginIds.length === 0 && pending.length === 0) return refreshSnapshot;
			const persist = () => persistPending(pending, resolvedPluginIds, settlements);
			let committed;
			if (settlements.length > 0 && sourceSnapshot) {
				const env = cloneEnvWithPlatformSemantics(params.env());
				const { snapshot, writeOptions } = await createConfigIO({
					env,
					observe: false,
					pluginValidation: "core-only",
					deferredPluginMigrations: expectedPending,
					shellEnvFallback: "defer"
				}).readConfigFileSnapshotForWrite();
				if (!snapshot.valid || describeConfigSnapshotInputChange(sourceSnapshot, snapshot, { compareResolvedConfig: false })) throw new ConfigMutationConflictError("Plugin migration source config changed or could not be read; rerun Doctor.");
				const includeGraph = {
					hashes: writeOptions.includeFileHashesForWrite ?? {},
					targets: writeOptions.includeFileTargetsForWrite ?? {}
				};
				const assertCurrent = () => assertBaseSnapshotStillCurrent(snapshot, resolveConfigPath(env), fs, includeGraph);
				committed = await withPluginLifecycleLease({
					env,
					assertCurrent
				}, () => withConfigSourceLocks([snapshot.path, ...Object.entries(includeGraph.targets).flat()], persist, env, assertCurrent));
			} else committed = await persist();
			if (!committed) return true;
			for (const pluginId of resolvedPluginIds) {
				previousById.delete(pluginId);
				const receipt = reported.get(pluginId);
				if (receipt) {
					receipt.outcome = "completed";
					receipt.warnings = [];
				}
			}
			deferred = deferred.filter((plugin) => !resolvedIds.has(plugin.pluginId));
			if (settlements.length > 0) params.report({
				changes: [],
				warnings,
				warningDisposition: "recoverable",
				notices: settlements.filter((entry) => !statelessPlugins.has(entry.pluginId)).map((entry) => `Plugin "${entry.pluginId}": ${entry.reason}`)
			});
			for (const plugin of pending) {
				previousById.set(plugin.pluginId, plugin);
				reportPending(plugin);
			}
			return refreshSnapshot || resolvedPluginIds.length > 0;
		}
	};
}
//#endregion
//#region src/commands/doctor-config-preflight.cron.ts
/** Preserve a retired partition selector even when other config cannot drive core migrations. */
async function migrateRetainedStore(params) {
	const cfg = retainStoreConfig(params.config);
	if (!cfg) return;
	const { repairLegacyCronStoreWithoutPrompt } = await params.measure("cron-repair-import", () => import("./legacy-repair-CG_B9Ja9.mjs"));
	params.report(await params.measure("cron-repair", () => repairLegacyCronStoreWithoutPrompt({
		cfg,
		migrateCodexModelRefs: false
	})));
	const { migrateLegacyConfigMachineState } = await import("./state-migrations.config-machine-state-CEUHNbBU.mjs");
	params.report(migrateLegacyConfigMachineState({
		config: params.config,
		env: params.env
	}));
}
/** Restores retired cron migration inputs that canonical config migration intentionally strips. */
function withLegacyConfig(config, legacyConfig) {
	const legacyCron = legacyConfig?.cron;
	if (!legacyCron || !Object.hasOwn(legacyCron, "store") && !Object.hasOwn(legacyCron, "webhook")) return config;
	return {
		...config,
		cron: {
			...config.cron,
			...Object.hasOwn(legacyCron, "store") ? { store: legacyCron.store } : {},
			...Object.hasOwn(legacyCron, "webhook") ? { webhook: legacyCron.webhook } : {}
		}
	};
}
/** Isolates the trusted partition selector from a partially valid legacy config. */
function retainStoreConfig(config) {
	const cron = config?.cron;
	if (typeof cron?.store !== "string" || !cron.store.trim()) return;
	return { cron: {
		store: cron.store,
		...Object.hasOwn(cron, "webhook") ? { webhook: cron.webhook } : {}
	} };
}
//#endregion
//#region src/commands/doctor-update-run.ts
async function noteStaleUpdateRuns(options = {}) {
	const warnings = /* @__PURE__ */ new Set();
	const reportReconciliationError = (error) => {
		if (hasCommandProcessCleanupError(error)) throw error;
		warnings.add(`Update history reconciliation could not complete: ${String(error)}`);
	};
	try {
		await inspectStaleUpdateRuns(options, reportReconciliationError);
	} catch (error) {
		reportReconciliationError(error);
	}
	for (const warning of warnings) note(warning, "Update history");
	if (warnings.size && options.migrateState !== false) try {
		await recordUpdateHistoryWarning([...warnings].join("\n"));
	} catch (error) {
		if (hasCommandProcessCleanupError(error)) throw error;
		note(`Update history warning could not be saved: ${String(error)}`, "Update history");
	}
}
async function recordUpdateHistoryWarning(detail) {
	const [{ runExistingOpenClawStateWriteTransaction }, { executeSqliteQuerySync, executeSqliteQueryTakeFirstSync, getNodeSqliteKysely }, { decodeRun }, { encodeRun }, { updateRunLedgerSchema, upsertStep }] = await Promise.all([
		import("./openclaw-state-db-existing-write-CyFac4Cu.mjs"),
		import("./kysely-sync-Cn1liaTK.mjs"),
		import("./update-run-read.kernel-D6tAP89X.mjs"),
		import("./update-run-codec-BNRjYh-w.mjs"),
		import("./update-run-write-CPEcsxlm.mjs")
	]);
	runExistingOpenClawStateWriteTransaction(({ db }) => {
		const latest = executeSqliteQueryTakeFirstSync(db, getNodeSqliteKysely(db).selectFrom("update_runs").selectAll().orderBy("created_at_ms", "desc").orderBy("run_id", "desc").limit(1));
		if (!latest) return;
		const record = decodeRun(latest);
		upsertStep(record, {
			step: "warning:update-history-reconciliation",
			status: "completed",
			detail
		});
		executeSqliteQuerySync(db, getNodeSqliteKysely(db).updateTable("update_runs").set({ steps_json: encodeRun(record, {}).steps_json }).where("run_id", "=", record.runId));
	}, {}, {
		schemaSql: updateRunLedgerSchema,
		operationLabel: "update.history.warning"
	});
}
async function inspectStaleUpdateRuns(options, reportReconciliationError) {
	const [{ staleUpdateRunGuidance }, { listUpdateRunsAsync }, { renderUpdateRunReport }, { updateRunWarningMessages }, { readInstalledUpdateCandidate, reconcileInterruptedUpdateRuns }, { isAcknowledgedAbandonedUpdateRun }, { readInterruptedUpdateCandidateAsync }] = await Promise.all([
		import("./update-run-activity-5l1Fe4_2.mjs"),
		import("./update-run-reader-CrO0ukl7.mjs"),
		import("./update-run-report-B9VKOXlR.mjs"),
		import("./update-run-step-CH4yw9xR.mjs"),
		import("./update-run-interruption-SAXnR5ey.mjs"),
		import("./update-run-record-BdaKzDzT.mjs"),
		import("./update-run-interruption-worker-BTTWZeC4.mjs")
	]);
	const discovered = await withArtifactPreservingStateReads(() => withOpenClawStateDatabaseReadSnapshot(async () => {
		let candidate;
		if (options.migrateState !== false) try {
			candidate = await readInterruptedUpdateCandidateAsync({});
		} catch (error) {
			reportReconciliationError(error);
		}
		return {
			candidate,
			active: await listUpdateRunsAsync({
				active: true,
				limit: 100
			}),
			history: await listUpdateRunsAsync({ limit: 100 })
		};
	}));
	let { active, history } = discovered;
	let reconciled = [];
	if (discovered.candidate) try {
		reconciled = await reconcileInterruptedUpdateRuns({ candidate: discovered.candidate });
		for (const run of reconciled) note(`Update ${run.runId}: recorded succeeded after verifying the installed and serving candidate build ${run.after.buildId}; its updater exited before recording completion.`, "Update history");
	} catch (error) {
		reportReconciliationError(error);
	}
	if (reconciled.length) {
		active = await listUpdateRunsAsync({
			active: true,
			limit: 100
		});
		history = await listUpdateRunsAsync({ limit: 100 });
	}
	for (const run of active) {
		const guidance = staleUpdateRunGuidance(run);
		if (guidance) note(`Update ${run.runId}: ${guidance}`, "Update history");
	}
	for (const run of history) if (run.status === "failed" && run.reason === "abandoned" && !isAcknowledgedAbandonedUpdateRun(run)) {
		const reason = readInstalledUpdateCandidate(run) ? "the recorded candidate has not been verified as installed and serving" : "the target build was not recorded, so current version equality cannot prove this update completed";
		note(`Update ${run.runId} remains abandoned: ${reason}. Run \`openclaw update repair\` to repair the installation and reconcile its history.`, "Update history");
	}
	const [latest] = history;
	if (latest) {
		if (latest.status === "failed" && latest.reason && (latest.reason === "update-activation-timeout" || UPDATE_ENVIRONMENT_FAILURE_REASONS.has(latest.reason))) note(`Update ${latest.runId}: ${renderUpdateRunReport(latest).markdown}`, "Update history");
		const retiredWarnings = await readRetiredDeferredPluginMigrationWarnings(latest.steps.map((step) => step.detail));
		const warnings = updateRunWarningMessages(latest.steps.filter((step) => !retiredWarnings.has(step.detail ?? "")));
		if (warnings.length) note(`Recorded warnings from update ${latest.runId} (a later repair may have resolved them):\n${warnings.slice(-3).join("\n")}`, "Update history");
	}
}
//#endregion
//#region src/commands/doctor/shared/legacy-config-state-migration-input.ts
function resolveStateMigrationConfigInput(params) {
	const pluginDoctorConfig = params.snapshot.sourceConfig ?? params.snapshot.config ?? params.snapshot.parsed;
	const sourceConfigBeforeMigrations = params.snapshot.sourceConfigBeforeMigrations ?? pluginDoctorConfig;
	const context = {
		sourceConfigBeforeMigrations,
		...pluginDoctorConfig ? { pluginDoctorConfig } : {}
	};
	if (params.postConvergenceConfig) return {
		cfg: params.postConvergenceConfig,
		...context
	};
	if (params.snapshot.valid) return params.snapshot.legacyIssues.length > 0 && pluginDoctorConfig !== void 0 ? {
		cfg: params.baseConfig,
		pluginDoctorConfig,
		sourceConfigBeforeMigrations
	} : {
		cfg: params.baseConfig,
		sourceConfigBeforeMigrations
	};
	const migrationSource = pluginDoctorConfig ?? params.snapshot.parsed;
	if (params.snapshot.legacyIssues.length === 0 || migrationSource === void 0) return null;
	const migrated = migrateLegacyConfig(migrationSource, { sourceConfigBeforeMigrations: params.snapshot.sourceConfigBeforeMigrations });
	if (!migrated.config || migrated.partiallyValid) return { pluginDoctorConfig: pluginDoctorConfig ?? migrationSource };
	return {
		cfg: migrated.config,
		...context
	};
}
//#endregion
//#region src/commands/doctor-config-preflight.ts
/** Config preflight for Doctor: legacy migration, recovery, and snapshot loading. */
const loadState = createLazyRuntimeModule(() => import("./state-migrations.state-dir-9h5c4mfe.mjs"));
const loadCronRepair = createLazyRuntimeModule(() => import("./legacy-repair-CG_B9Ja9.mjs"));
/** Preserve retired state inputs before the main Doctor config repair flow. */
async function runDoctorConfigPreflight(options = {}) {
	if (options.migrateState !== false && options.doctorOnlyStateMigrations === true) {
		const { withSqliteReadOnlyWorkerScope } = await import("./sqlite-readonly-worker-BGUEeu1P.mjs");
		return await withSqliteReadOnlyWorkerScope(() => runDoctorConfigPreflightOperation(options));
	}
	return await runDoctorConfigPreflightOperation(options);
}
async function runDoctorConfigPreflightOperation(options) {
	if (!options.retiredStateAdmitted) assertDoctorRetiredStateAdmission();
	const stateMigrationsRequested = options.migrateState !== false;
	const skipLegacyParentConfigWrite = shouldSkipLegacyUpdateDoctorConfigWrite(process.env);
	const measurePreflightStep = (name, run) => measureDoctorConfigPreflightStep(name, run, options.measure);
	if (stateMigrationsRequested) await measurePreflightStep("state-write-admission", () => assertOpenClawStateWriteAllowedAtPath({
		databasePath: resolveOpenClawStateSqlitePath(process.env),
		env: process.env,
		recoverOrphanedSidecars: true
	}));
	await measurePreflightStep("stale-update-runs", () => noteStaleUpdateRuns({ migrateState: stateMigrationsRequested }));
	let modelBillingRouteMigrationSource;
	const cronCodexRuntimePolicyTargets = [];
	const stateMigrationStepReceipts = [];
	let postSessionPluginMigration;
	let postSessionPluginMigrationPlanBound = false;
	let doctorMediaPersistenceAttempted = false;
	let configSnapshotRead;
	const pluginMigrations = createDoctorPluginMigrationPreparation({
		enabled: stateMigrationsRequested,
		env: () => process.env,
		report: (result) => noteDoctorStateMigrationResult(result),
		recordReceipt: (receipt) => stateMigrationStepReceipts.push(receipt),
		measure: measurePreflightStep,
		runWithPluginMetadataSnapshot: (scope, run) => pluginMetadata.run(scope, run),
		doctorOnlyStateMigrations: options.doctorOnlyStateMigrations === true
	});
	const pluginMetadata = createDoctorPluginMetadataSnapshotScope({
		getBaseSnapshot: () => configSnapshotRead?.pluginMetadataSnapshot,
		env: process.env,
		getDeferredPluginIds: () => pluginMigrations.deferred().map((pending) => pending.pluginId)
	});
	const noteDoctorStateMigrationResult = (result) => {
		pluginMigrations.observe(result);
		noteDoctorMigrationResult(result, { prefix: "- " });
	};
	const getSnapshotPreparation = createDoctorRehearsalSnapshotPreparation(noteDoctorStateMigrationResult);
	const { planScopedConfigRepair, planAdmittedConfigRepair } = createDoctorConfigRepairPlanner({
		options,
		stateMigrationsRequested,
		skipLegacyParentConfigWrite,
		runWithPluginMetadataSnapshot: pluginMetadata.run
	});
	const readConfigSnapshotForPreflight = async (allowCurrentPluginMetadata = true) => await measurePreflightStep("config-snapshot", async () => readConfigPreflightSnapshot({
		purpose: "doctor",
		allowCurrentPluginMetadata,
		includePluginMetadata: options.preparePluginMetadataSnapshot === true,
		measure: options.measure,
		observe: options.observe,
		skipPluginValidation: shouldSkipPluginValidationForDoctorConfigPreflight(),
		prepareSnapshot: getSnapshotPreparation(options.doctorOnlyStateMigrations === true),
		...await pluginMigrations.snapshotOptions()
	}));
	const stateDirMigrations = stateMigrationsRequested ? await measurePreflightStep("state-migrations-import", loadState) : void 0;
	if (stateDirMigrations) noteDoctorStateMigrationResult(await measurePreflightStep("state-dir-migrations", () => stateDirMigrations.autoMigrateLegacyStateDir({ env: process.env })));
	await migrateLegacyDoctorConfig({
		enabled: options.migrateLegacyConfig !== false,
		measure: measurePreflightStep
	});
	configSnapshotRead = await readConfigSnapshotForPreflight(!stateDirMigrations);
	const recovery = await prepareDoctorConfigRecovery({
		enabled: options.repairPrefixedConfig === true && !skipLegacyParentConfigWrite,
		snapshotRead: configSnapshotRead,
		planRepair: planScopedConfigRepair,
		readSnapshot: () => readConfigSnapshotForPreflight(false)
	});
	configSnapshotRead = recovery.snapshotRead;
	let snapshot = configSnapshotRead.snapshot;
	const rosterMigrationSource = snapshot.sourceConfigBeforeMigrations ?? snapshot.sourceConfig;
	const activeConfigRepair = recovery.activeConfigRepair;
	noteDoctorConfigPreflightIssues(snapshot, {
		invalidConfigNote: options.invalidConfigNote,
		activeRepair: activeConfigRepair !== null
	});
	let baseConfig = snapshot.sourceConfig ?? snapshot.config ?? {};
	let automaticConfigRepair = planAdmittedConfigRepair(snapshot, activeConfigRepair);
	if (options.doctorOnlyStateMigrations === true && stateDirMigrations) {
		const { prepareLegacyStateDatabaseSchema } = await import("./state-migrations.doctor-DD1CDbaH.mjs");
		const receipt = options.stateSchemaPreparation ?? await measurePreflightStep("state-schema", () => prepareLegacyStateDatabaseSchema(process.env));
		if (receipt.outcome !== "skipped") {
			stateMigrationStepReceipts.push(receipt);
			noteDoctorStateMigrationResult({
				changes: receipt.changes,
				warnings: receipt.warnings,
				notices: receipt.notices
			});
			throwIfDoctorStateMigrationRefused(stateMigrationStepReceipts);
		}
	}
	if (options.invocationPurpose === "doctor" || options.doctorOnlyStateMigrations === true) {
		const { repairLegacySessionEntryStates } = await import("./doctor-session-delivery-state-D70V5ITM.mjs");
		const repair = options.doctorOnlyStateMigrations === true && stateDirMigrations !== void 0;
		const entryState = await measurePreflightStep("session-entry-state", () => repairLegacySessionEntryStates({
			apply: repair,
			cfg: automaticConfigRepair?.config ?? baseConfig,
			env: process.env,
			deferSchemaRepair: repair
		}));
		if (!repair && entryState.found > 0) {
			const { SessionStoreMigrationRequiredError } = await import("./migration-required-BFWwCs31.mjs");
			throw new SessionStoreMigrationRequiredError(`Found ${entryState.found} session rows with legacy entry state. Run openclaw doctor --fix before further session inspection; no rows were changed.`);
		}
	}
	let postConvergenceStateConfig;
	let pluginConvergenceWarnings = [];
	if (stateDirMigrations) {
		const refreshed = await prepareDoctorMigrationPlugins({
			cfg: automaticConfigRepair?.config ?? baseConfig,
			env: process.env,
			retainedPluginIds: pluginMigrations.retainedPluginIds(),
			measure: options.measure,
			snapshotRead: {
				...configSnapshotRead,
				snapshot
			},
			readRefreshedSnapshot: () => readConfigSnapshotForPreflight(false),
			onDeferredPlugins: (pending, inspection) => pluginMigrations.converged(pending, snapshot, configSnapshotRead?.pluginMetadataSnapshot, inspection)
		});
		pluginConvergenceWarnings = refreshed.warnings;
		configSnapshotRead = refreshed.snapshotRead;
		pluginMetadata.invalidate();
		snapshot = refreshed.snapshotRead.snapshot;
		baseConfig = snapshot.sourceConfig ?? snapshot.config ?? {};
		automaticConfigRepair = planAdmittedConfigRepair(snapshot);
		postConvergenceStateConfig = automaticConfigRepair?.snapshot.config;
	}
	const stateMigrationInput = resolveStateMigrationConfigInput({
		snapshot,
		baseConfig,
		postConvergenceConfig: postConvergenceStateConfig
	});
	const rosterMigrationOwnerId = stateMigrationInput?.cfg ? tryGetLegacyDefaultAgentId(stateMigrationInput.cfg) : void 0;
	if (stateDirMigrations) {
		if (options.doctorOnlyStateMigrations === true && !stateMigrationInput?.cfg) {
			const { detectLegacyExecApprovals, migrateLegacyExecApprovals } = await import("./state-migrations.exec-approvals-B9uIHXLT.mjs");
			const stateDir = resolveStateDir(process.env);
			noteDoctorStateMigrationResult(await measurePreflightStep("exec-approvals-migration", () => migrateLegacyExecApprovals({
				detected: detectLegacyExecApprovals({
					stateDir,
					doctorOnlyStateMigrations: true
				}),
				stateDir,
				env: process.env
			})));
		}
		if (stateMigrationInput) {
			if (stateMigrationInput.cfg) {
				const { autoMigrateLegacyState } = await import("./state-migrations.doctor-DD1CDbaH.mjs");
				const migrationConfig = stateMigrationInput.cfg;
				const pluginDoctorConfig = stateMigrationInput.pluginDoctorConfig;
				const { collectCronCodexRuntimePolicyTargetsReadOnly, repairLegacyCronStoreWithoutPrompt } = await measurePreflightStep("cron-repair-import", loadCronRepair);
				noteDoctorStateMigrationResult(await measurePreflightStep("cron-repair", () => repairLegacyCronStoreWithoutPrompt({
					cfg: withLegacyConfig(migrationConfig, pluginDoctorConfig),
					migrateCodexModelRefs: false
				})));
				if (options.repairPrefixedConfig === true) {
					const cronCodexPlan = await measurePreflightStep("cron-policy-scan", () => collectCronCodexRuntimePolicyTargetsReadOnly({ cfg: migrationConfig }));
					cronCodexRuntimePolicyTargets.push(...cronCodexPlan.targets);
					noteDoctorStateMigrationResult({
						changes: [],
						warnings: cronCodexPlan.warnings
					});
				}
				const legacyStateResult = await measurePreflightStep("legacy-state-migrations", () => pluginMetadata.run({ config: pluginDoctorConfig ?? migrationConfig }, () => autoMigrateLegacyState({
					cfg: migrationConfig,
					sourceConfigBeforeMigrations: stateMigrationInput.sourceConfigBeforeMigrations,
					...pluginDoctorConfig ? { pluginDoctorConfig } : {},
					configIncludedPaths: snapshot.includedPaths ?? [],
					env: process.env,
					doctorOnlyStateMigrations: options.doctorOnlyStateMigrations,
					invocationPurpose: options.invocationPurpose,
					...options.agentDatabaseMigrationDiscovery ? { agentDatabaseMigrationDiscovery: options.agentDatabaseMigrationDiscovery } : {},
					beforeWorkspaceStateMigration: options.beforeWorkspaceStateMigration,
					onStepReceipt: (receipt) => stateMigrationStepReceipts.push(receipt)
				})));
				postSessionPluginMigration = legacyStateResult.postSessionPluginMigration;
				postSessionPluginMigrationPlanBound = options.doctorOnlyStateMigrations === true;
				doctorMediaPersistenceAttempted = options.doctorOnlyStateMigrations === true;
				noteDoctorStateMigrationResult(legacyStateResult);
				if (options.doctorOnlyStateMigrations === true) await assertDoctorPreflightMigrationsComplete({
					cfg: migrationConfig,
					stepReceipts: stateMigrationStepReceipts,
					report: noteDoctorStateMigrationResult
				});
			} else if (stateMigrationInput.pluginDoctorConfig) {
				const pluginDoctorConfig = stateMigrationInput.pluginDoctorConfig;
				await migrateRetainedStore({
					config: pluginDoctorConfig,
					env: process.env,
					measure: measurePreflightStep,
					report: noteDoctorStateMigrationResult
				});
				await pluginMigrations.migrate(pluginDoctorConfig);
			}
		}
	}
	if (stateDirMigrations && options.doctorOnlyStateMigrations === true && !doctorMediaPersistenceAttempted) {
		const { migrateLegacyMediaPersistence } = await import("./state-migrations.media-persistence-BbmCMN9e.mjs");
		noteDoctorStateMigrationResult(await measurePreflightStep("media-persistence-migration", () => migrateLegacyMediaPersistence({ env: process.env })));
		const { repairLegacySessionEntryStates } = await import("./doctor-session-delivery-state-D70V5ITM.mjs");
		await repairLegacySessionEntryStates({
			apply: true,
			cfg: automaticConfigRepair?.config ?? baseConfig,
			env: process.env
		});
	}
	if (await pluginMigrations.complete()) {
		configSnapshotRead = await readConfigSnapshotForPreflight(false);
		snapshot = configSnapshotRead.snapshot;
		baseConfig = snapshot.sourceConfig ?? snapshot.config ?? {};
		automaticConfigRepair = planAdmittedConfigRepair(snapshot);
	}
	if (stateDirMigrations && options.doctorOnlyStateMigrations === true) {
		const { repairRelativeSessionStores } = await import("./doctor-relative-session-store-DwsYi1hx.mjs");
		noteDoctorStateMigrationResult(await measurePreflightStep("relative-session-store", () => repairRelativeSessionStores({
			cfg: automaticConfigRepair?.config ?? baseConfig,
			env: process.env,
			registeredDatabases: options.agentDatabaseMigrationDiscovery?.registeredAgentDatabases
		})));
	}
	if (automaticConfigRepair && !skipLegacyParentConfigWrite) {
		modelBillingRouteMigrationSource ??= snapshot.sourceConfigBeforeMigrations ?? snapshot.sourceConfig;
		await measurePreflightStep("automatic-config-repair", () => pluginMetadata.run({ config: automaticConfigRepair.config }, () => commitAutomaticConfigRepair(automaticConfigRepair, snapshot)));
		note(`Migrated legacy config keys in the active openclaw.json:\n${automaticConfigRepair.changes.map((entry) => `- ${entry}`).join("\n")}`, "Doctor changes");
		configSnapshotRead = await readConfigSnapshotForPreflight(false);
		snapshot = configSnapshotRead.snapshot;
		baseConfig = snapshot.sourceConfig ?? snapshot.config ?? {};
	}
	const deferredPluginMigrations = pluginMigrations.deferred();
	return {
		snapshot,
		baseConfig,
		...pluginConvergenceWarnings.length > 0 ? { warnings: pluginConvergenceWarnings } : {},
		rosterMigrationSource,
		...rosterMigrationOwnerId ? { rosterMigrationOwnerId } : {},
		...deferredPluginMigrations.length > 0 ? { deferredPluginMigrations } : {},
		...modelBillingRouteMigrationSource ? { modelBillingRouteMigrationSource } : {},
		...configSnapshotRead.pluginMetadataSnapshot ? { pluginMetadataSnapshot: configSnapshotRead.pluginMetadataSnapshot } : {},
		...cronCodexRuntimePolicyTargets.length > 0 ? { cronCodexRuntimePolicyTargets } : {},
		...stateMigrationStepReceipts.length > 0 ? { stateMigrationStepReceipts } : {},
		...postSessionPluginMigration ? { postSessionPluginMigration } : {},
		...postSessionPluginMigrationPlanBound ? { postSessionPluginMigrationPlanBound: true } : {},
		...postSessionPluginMigrationPlanBound && deferredPluginMigrations.length === 0 && isDeepStrictEqual(stateMigrationInput?.cfg, baseConfig) && stateMigrationStepReceipts.every((receipt) => receipt.id === "plugin-doctor-post-session-state" || (receipt.outcome === "completed" || receipt.outcome === "skipped") && receipt.warnings.length === 0) ? { preflightLegacyStateConfig: baseConfig } : {}
	};
}
//#endregion
//#region src/commands/doctor/changes-panel-sink.ts
function createDoctorChangesPanelSink(shouldRepair) {
	const pending = [];
	return {
		emit: (changeLines, options = {}) => {
			if (changeLines.length === 0) return;
			const body = changeLines.join("\n");
			const message = options.sanitize ? sanitizeDoctorNote(body) : body;
			if (shouldRepair) {
				pending.push(message);
				return;
			}
			note(message, "Doctor changes preview");
		},
		drain: () => pending.splice(0)
	};
}
//#endregion
//#region src/commands/doctor/finalize-config-flow.ts
async function finalizeDoctorConfigFlow(params) {
	const confirmedConfigSource = {
		path: params.snapshot.path,
		hash: params.snapshot.hash ?? hashConfigRaw(params.snapshot.raw)
	};
	let cfg = params.cfg;
	let shouldWriteConfig = params.shouldRepair && params.pendingChanges;
	if (!params.shouldRepair && params.pendingChanges) {
		shouldWriteConfig = await params.confirm({
			message: "Apply recommended config repairs now?",
			initialValue: true
		});
		if (shouldWriteConfig) cfg = params.candidate;
		else if (params.fixHints.length > 0) params.note(params.fixHints.join("\n"), "Doctor");
	}
	return {
		cfg,
		shouldWriteConfig,
		confirmedConfigSource
	};
}
//#endregion
//#region src/commands/doctor/shared/config-migration-result.ts
/** Preserve migration provenance before repairs, then prepare the post-write health handoff. */
function prepareDoctorConfigMigrationResult(preflight, snapshot) {
	const sourceVersion = snapshot.sourceConfig.meta?.lastTouchedVersion;
	const sourceLastTouchedVersion = typeof sourceVersion === "string" ? sourceVersion : void 0;
	const billingRouteSource = preflight.modelBillingRouteMigrationSource ?? snapshot.sourceConfigBeforeMigrations ?? snapshot.sourceConfig;
	return async (params) => {
		let modelBillingRouteWarnings = [];
		if ((params.shouldWriteConfig || preflight.modelBillingRouteMigrationSource) && (!isDeepStrictEqual(billingRouteSource.agents, params.cfg.agents) || !isDeepStrictEqual(billingRouteSource.models, params.cfg.models))) {
			const { collectModelBillingRouteMigrationWarnings } = await import("./model-billing-route-migration-CKTUJK74.mjs");
			modelBillingRouteWarnings = params.runWithCurrentPluginMetadata(params.cfg, () => collectModelBillingRouteMigrationWarnings({
				before: billingRouteSource,
				after: params.cfg,
				metadataSnapshot: params.metadataSnapshot
			}));
		}
		let receipts = preflight.stateMigrationStepReceipts;
		let postSession = preflight.postSessionPluginMigration;
		const planBound = preflight.postSessionPluginMigrationPlanBound;
		if (planBound && params.pluginInventoryChanged && postSession) {
			const { preparePostSessionPluginMigration } = await import("./state-migrations.plugin-plan-BOk6qV7D.mjs");
			const { resolveLivePluginDoctorStateMigrationInventory } = await import("./doctor-contract-registry-Db6SLitG.mjs");
			postSession = params.runWithCurrentPluginMetadata(params.cfg, () => preparePostSessionPluginMigration({
				mode: "doctor",
				inventory: resolveLivePluginDoctorStateMigrationInventory({
					config: params.cfg,
					env: process.env
				})
			}));
			if (postSession.step.refusal) {
				const { createLegacyStateMigrationStepReceipt } = await import("./state-migrations.messages-JeVUPL7d.mjs");
				receipts = [...receipts ?? [], createLegacyStateMigrationStepReceipt(postSession.step, {
					changes: [],
					warnings: [postSession.step.refusal.message]
				})];
				postSession = void 0;
			}
		}
		return {
			...sourceLastTouchedVersion ? { sourceLastTouchedVersion } : {},
			...modelBillingRouteWarnings.length > 0 ? { modelBillingRouteWarnings } : {},
			...params.metadataSnapshot ? { pluginMetadataSnapshot: params.metadataSnapshot } : {},
			...receipts ? { stateMigrationStepReceipts: receipts } : {},
			...postSession ? { postSessionPluginMigration: postSession } : {},
			...planBound ? { postSessionPluginMigrationPlanBound: true } : {},
			...!params.shouldWriteConfig && !params.pluginInventoryChanged && isDeepStrictEqual(preflight.preflightLegacyStateConfig, params.cfg) ? { preflightLegacyStateConfig: preflight.preflightLegacyStateConfig } : {}
		};
	};
}
//#endregion
//#region src/channels/plugins/setup-promotion-discovery.ts
function resolveDiscoveredChannelSetupPromotionSurface(channelKey, cfg) {
	const registry = loadPluginManifestRegistryForPluginRegistry({
		config: cfg,
		includeDisabled: true
	});
	if (registry.plugins.some((plugin) => plugin.channels.includes(channelKey) && plugin.packageManifest?.setupFeatures?.configPromotion === "preserve-root")) return { configPromotion: "preserve-root" };
	if (getOfficialExternalPluginCatalogManifest(getOfficialExternalPluginCatalogEntry(channelKey) ?? {})?.setupFeatures?.configPromotion === "preserve-root") return { configPromotion: "preserve-root" };
	const owner = registry.plugins.find((plugin) => plugin.channels.includes(channelKey) && plugin.packageManifest?.setupFeatures?.configPromotion === true);
	if (!owner) return null;
	const { plugin } = loadSetupChannelPluginFromManifestRecord({
		record: owner,
		channelId: channelKey,
		env: process.env
	});
	const setup = plugin?.setupContract ?? plugin?.setup;
	return setup && typeof setup === "object" ? setup : null;
}
//#endregion
//#region src/commands/doctor/shared/legacy-mistral-model-cost.ts
const MISTRAL_MODEL_CACHE_READ_COST_BY_ID = /* @__PURE__ */ new Map([
	["codestral-latest", .03],
	["devstral-medium-latest", .04],
	["magistral-small", .05],
	["mistral-large-latest", .05],
	["mistral-medium-2508", .04],
	["mistral-medium-3-5", .15],
	["mistral-small-latest", .01],
	["pixtral-large-latest", .2]
]);
function normalizeLegacyMistralModelCost(params) {
	const cost = params.model.cost;
	if (!isRecord(cost) || cost.cacheRead !== 0) return params.model;
	const normalizedCacheRead = MISTRAL_MODEL_CACHE_READ_COST_BY_ID.get(params.modelId.toLowerCase());
	if (normalizedCacheRead === void 0) return params.model;
	params.changes.push(`Normalized models.providers.${sanitizeForLog(params.providerId)}.models[${params.index}].cost.cacheRead (0 → ${normalizedCacheRead}) for Mistral prompt-cache billing.`);
	return {
		...params.model,
		cost: {
			...cost,
			cacheRead: normalizedCacheRead
		}
	};
}
//#endregion
//#region src/commands/doctor/shared/legacy-config-core-normalizers.ts
const log = createSubsystemLogger("doctor");
/** Seed an empty account map without changing an existing account set or route. */
function seedMissingDefaultAccountsFromSingleAccountBase(cfg, changes) {
	const next = cloneConfigWithResolutionFacts(cfg);
	const before = changes.length;
	for (const [channelId, channel] of Object.entries(next.channels ?? {})) {
		if (!isRecord(channel) || !isRecord(channel.accounts) || Object.keys(channel.accounts).length) continue;
		const promotion = resolveSingleAccountPromotion({
			channelKey: channelId,
			channel,
			resolveBundledSurface: (key) => resolveDiscoveredChannelSetupPromotionSurface(key, cfg)
		});
		if (promotion.kind === "preserve-root") continue;
		if (promotion.shouldDeferPromotion) {
			log.debug(`Deferring channels.${channelId} single-account promotion until its plugin declares uncovered root keys.`);
			continue;
		}
		if (promotion.keysToMove.length === 0) continue;
		const defaultAccount = {};
		for (const key of promotion.keysToMove) {
			defaultAccount[key] = channel[key];
			delete channel[key];
		}
		channel.accounts = { [DEFAULT_ACCOUNT_ID]: defaultAccount };
		changes.push(`Moved channels.${channelId} single-account top-level values into channels.${channelId}.accounts.default.`);
	}
	if (changes.length > before && Array.isArray(next.channels)) next.channels = Object.fromEntries(Object.entries(next.channels));
	copyConfigResolutionFactsThroughRewrite(cfg, next);
	return inheritLegacyDefaultAgentId(cfg, next);
}
function migrateUnblockedLegacyRuntimeModelRef(modelRef, blockedModelIdentities) {
	return isBlockedLegacyCodexModelRef({
		modelRef,
		blockedModelIdentities
	}) ? null : migrateLegacyRuntimeModelRef(modelRef);
}
function mergeModelEntry(legacyEntry, currentEntry) {
	return isRecord(legacyEntry) && isRecord(currentEntry) ? mergeModelRefMapEntries(currentEntry, legacyEntry, "models").value : currentEntry ?? legacyEntry;
}
function migrateCodexCliRuntimePolicy(raw) {
	if (!isRecord(raw) || normalizeOptionalLowercaseString(raw.id) !== "codex-cli") return false;
	raw.id = "codex";
	return true;
}
function createRuntimeModelRefRewriter(blockedModelIdentities) {
	const selected = [];
	return {
		selected,
		rewrite: (ref) => {
			const migrated = typeof ref === "string" ? migrateUnblockedLegacyRuntimeModelRef(ref, blockedModelIdentities) : null;
			if (migrated) selected.push({
				ref: migrated.ref,
				runtime: migrated.runtime
			});
			return migrated?.ref ?? ref;
		}
	};
}
function migrateRuntimeSelection(owner, blockedModelIdentities) {
	const { rewrite, selected } = createRuntimeModelRefRewriter(blockedModelIdentities);
	const model = owner.model;
	if (typeof model === "string") owner.model = rewrite(model);
	else if (isRecord(model)) {
		if (typeof model.primary === "string") model.primary = rewrite(model.primary);
		if (Array.isArray(model.fallbacks)) model.fallbacks = model.fallbacks.map(rewrite);
	}
	return selected;
}
function ensureSelectedModelRuntimePolicies(agent, selected) {
	if (selected.length === 0) return false;
	const models = isRecord(agent.models) ? agent.models : {};
	let changed = false;
	for (const { ref, runtime } of selected) {
		const updated = modelEntryWithRuntimePolicy(models[ref], runtime);
		if (updated.changed) {
			models[ref] = updated.entry;
			changed = true;
		}
	}
	if (changed) agent.models = models;
	return changed;
}
function migrateRuntimeAgent(agent, path, changes, blockedModelIdentities) {
	const selected = migrateRuntimeSelection(agent, blockedModelIdentities);
	if (selected.length) changes.push(`Moved ${path}.model legacy runtime primary refs to canonical provider refs and selected ${selected[0].runtime} runtime.`);
	const policy = isRecord(agent.modelPolicy) ? agent.modelPolicy : void 0;
	const { rewrite, selected: policyRefs } = createRuntimeModelRefRewriter(blockedModelIdentities);
	const allow = Array.isArray(policy?.allow) ? policy.allow.map(rewrite) : void 0;
	if (isRecord(agent.models)) {
		const models = {};
		const legacy = [];
		for (const [ref, entry] of Object.entries(agent.models)) {
			const migrated = migrateUnblockedLegacyRuntimeModelRef(ref, blockedModelIdentities);
			if (migrated) legacy.push({
				ref: migrated.ref,
				runtime: migrated.runtime,
				entry
			});
			else models[ref] = mergeModelEntry(entry, models[ref]);
		}
		for (const { ref, runtime, entry } of legacy) models[ref] = modelEntryWithRuntimePolicy(mergeModelEntry(entry, models[ref]), runtime).entry;
		if (legacy.length) {
			agent.models = models;
			changes.push(`Moved ${path}.models legacy runtime keys to canonical provider keys.`);
		}
	}
	if (ensureSelectedModelRuntimePolicies(agent, policyRefs)) changes.push(`Preserved runtime policy for ${path}.modelPolicy.allow entries.`);
	if (ensureSelectedModelRuntimePolicies(agent, selected)) changes.push(`Selected ${selected[0].runtime} runtime for ${path}.models entries.`);
	for (const key of ["heartbeat", "subagents"]) {
		const execution = agent[key];
		if (!isRecord(execution)) continue;
		const refs = migrateRuntimeSelection(execution, blockedModelIdentities);
		if (refs.length) {
			ensureSelectedModelRuntimePolicies(agent, refs);
			changes.push(`Moved ${path}.${key}.model to canonical refs with model runtime policy.`);
		}
	}
	if (policy && policyRefs.length) {
		policy.allow = allow;
		changes.push(`Moved ${path}.modelPolicy.allow legacy runtime refs to canonical provider refs.`);
	}
	for (const [ref, entry] of Object.entries(isRecord(agent.models) ? agent.models : {})) if (isRecord(entry) && migrateCodexCliRuntimePolicy(entry.agentRuntime)) changes.push(`Moved ${path}.models.${sanitizeForLog(ref)} agentRuntime.id from codex-cli to codex.`);
}
/** Move legacy runtime-tagged refs on a private copy, preserving the caller's source. */
function normalizeLegacyRuntimeModelRefs(cfg, changes, blockedModelIdentities) {
	const next = cloneConfigWithResolutionFacts(cfg);
	for (const [providerId, provider] of modelProviders(next)) {
		if (migrateCodexCliRuntimePolicy(provider.agentRuntime)) changes.push(`Moved models.providers.${sanitizeForLog(providerId)} agentRuntime.id from codex-cli to codex.`);
		for (const [index, model] of providerModels(provider)) if (migrateCodexCliRuntimePolicy(model.agentRuntime)) {
			const modelId = normalizeOptionalString(model.id) ?? `[${index}]`;
			changes.push(`Moved models.providers.${sanitizeForLog(providerId)}.models.${sanitizeForLog(modelId)} agentRuntime.id from codex-cli to codex.`);
		}
	}
	const agents = next.agents;
	if (isRecord(agents)) {
		if (isRecord(agents.defaults)) migrateRuntimeAgent(agents.defaults, "agents.defaults", changes, blockedModelIdentities);
		for (const [key, entry] of Array.isArray(agents.list) ? agents.list.entries() : []) if (isRecord(entry)) {
			const id = normalizeOptionalString(entry.id);
			migrateRuntimeAgent(entry, id ? `agents.list.${sanitizeForLog(id)}` : `agents.list[${key}]`, changes, blockedModelIdentities);
		}
		for (const [id, entry] of Object.entries(isRecord(agents.entries) ? agents.entries : {})) if (isRecord(entry)) migrateRuntimeAgent(entry, `agents.entries.${sanitizeForLog(id)}`, changes, blockedModelIdentities);
	}
	const rewritten = rewriteModelRefs(next, "config", changes, (ref) => {
		const migrated = migrateUnblockedLegacyRuntimeModelRef(ref, blockedModelIdentities);
		return migrated && [
			"codex-cli",
			"claude-cli",
			"google-gemini-cli"
		].includes(migrated.legacyProvider) ? migrated.ref : null;
	}).value;
	copyConfigResolutionFactsThroughRewrite(cfg, rewritten);
	return inheritLegacyDefaultAgentId(cfg, rewritten);
}
function* modelProviders(cfg) {
	const providers = cfg.models?.providers;
	if (!isRecord(providers)) return;
	for (const [id, provider] of Object.entries(providers)) if (isRecord(provider)) yield [id, provider];
}
function* providerModels(provider) {
	if (Array.isArray(provider.models)) {
		for (const [index, model] of provider.models.entries()) if (isRecord(model)) yield [index, model];
	}
}
/** Mutate the Doctor-owned private candidate; plugin hooks retain their own rollback copies. */
function normalizeLegacyOpenAICodexModelsAddMetadata(cfg, changes) {
	for (const [providerId, provider] of modelProviders(cfg)) {
		if (normalizeProviderId(providerId) !== "openai-codex") continue;
		for (const [, model] of providerModels(provider)) if (!("metadataSource" in model) && isLegacyModelsAddCodexMetadataModel({
			provider: providerId,
			model
		})) {
			model.metadataSource = "models-add";
			changes.push(`Marked models.providers.${sanitizeForLog(providerId)}.models.${sanitizeForLog(normalizeOptionalString(model.id) ?? "unknown")} as /models add metadata so official OpenAI Codex metadata can override it.`);
		}
	}
}
function normalizeLegacyOpenAIModelProviderApi(cfg, changes) {
	const migrateApi = (owner, path) => {
		if (owner.api === "openai") {
			owner.api = "openai-completions";
			changes.push(`Moved ${path}.api "openai" → "openai-completions".`);
		}
	};
	for (const [providerId, provider] of modelProviders(cfg)) {
		const path = `models.providers.${sanitizeForLog(providerId)}`;
		migrateApi(provider, path);
		for (const [index, model] of providerModels(provider)) migrateApi(model, `${path}.models[${index}]`);
	}
}
/** Remove retired bundled skill config after migrating its image model and credentials. */
function normalizeLegacyNanoBananaSkill(cfg, changes) {
	const key = "nano-banana-pro";
	const model = "google/gemini-3-pro-image-preview";
	const skills = cfg.skills;
	if (!isRecord(skills)) return;
	if (Array.isArray(skills.allowBundled)) {
		const filtered = skills.allowBundled.filter((value) => typeof value !== "string" || value.trim() !== key);
		if (filtered.length !== skills.allowBundled.length) {
			if (filtered.length) {
				skills.allowBundled = filtered;
				changes.push(`Removed ${key} from skills.allowBundled.`);
			} else {
				delete skills.allowBundled;
				changes.push(`Removed skills.allowBundled entry for ${key}.`);
			}
		}
	}
	const entries = skills.entries;
	if (!isRecord(entries) || !isRecord(entries[key])) return;
	const legacy = entries[key];
	if (cfg.agents?.defaults?.mediaModels?.image === void 0) {
		cfg.agents = {
			...cfg.agents,
			defaults: {
				...cfg.agents?.defaults,
				mediaModels: {
					...cfg.agents?.defaults?.mediaModels,
					image: { primary: model }
				}
			}
		};
		changes.push(`Moved skills.entries.${key} → agents.defaults.mediaModels.image.primary (${model}).`);
	}
	const legacyEnvKey = normalizeOptionalString(isRecord(legacy.env) ? legacy.env.GEMINI_API_KEY : void 0) ?? "";
	const apiKey = legacyEnvKey || (typeof legacy.apiKey === "string" ? normalizeOptionalString(legacy.apiKey) : isRecord(legacy.apiKey) ? legacy.apiKey : void 0);
	const models = isRecord(cfg.models) ? cfg.models : {};
	const providers = isRecord(models.providers) ? models.providers : {};
	const google = isRecord(providers.google) ? providers.google : {
		baseUrl: DEFAULT_GOOGLE_API_BASE_URL,
		models: []
	};
	if (google.apiKey === void 0 && apiKey) {
		google.apiKey = apiKey;
		google.baseUrl ||= DEFAULT_GOOGLE_API_BASE_URL;
		if (!Array.isArray(google.models)) google.models = [];
		providers.google = google;
		models.providers = providers;
		cfg.models = models;
		changes.push(`Moved skills.entries.${key}.${legacyEnvKey ? "env.GEMINI_API_KEY" : "apiKey"} → models.providers.google.apiKey.`);
	}
	delete entries[key];
	if (Object.keys(entries).length === 0) delete skills.entries;
	changes.push(`Removed legacy skills.entries.${key}.`);
	if (Object.keys(skills).length === 0) delete cfg.skills;
}
function normalizeConfiguredPositiveInteger(value) {
	return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : void 0;
}
function resolveConfiguredOllamaModelNumCtxBudget(model, provider, providerNumCtxApplies) {
	if (normalizeConfiguredPositiveInteger(model.contextTokens) !== void 0) return;
	const contextWindow = normalizeConfiguredPositiveInteger(model.contextWindow);
	const providerWindow = normalizeConfiguredPositiveInteger(provider.contextWindow);
	if (contextWindow !== void 0 || providerWindow !== void 0) return contextWindow ?? (providerNumCtxApplies ? void 0 : providerWindow);
	return normalizeConfiguredPositiveInteger(model.maxTokens) ?? (providerNumCtxApplies ? void 0 : normalizeConfiguredPositiveInteger(provider.maxTokens));
}
function normalizeLegacyOllamaNativeNumCtxParams(cfg, changes) {
	for (const [providerId, provider] of modelProviders(cfg)) {
		if (!Array.isArray(provider.models)) continue;
		const models = [...providerModels(provider)];
		const nativeProvider = normalizeOptionalLowercaseString(provider.api) === "ollama";
		const providerParams = provider.params;
		if (nativeProvider && (providerParams === void 0 || isRecord(providerParams)) && !Object.hasOwn(providerParams ?? {}, "num_ctx") && !models.some(([, entry]) => normalizeConfiguredPositiveInteger(entry.contextTokens) !== void 0)) {
			const numCtx = normalizeConfiguredPositiveInteger(provider.contextWindow) ?? normalizeConfiguredPositiveInteger(provider.maxTokens);
			if (numCtx !== void 0) {
				provider.params = {
					...providerParams,
					num_ctx: numCtx
				};
				changes.push(`Set models.providers.${sanitizeForLog(providerId)}.params.num_ctx to ${numCtx} for native Ollama compatibility.`);
			}
		}
		const providerNumCtxApplies = nativeProvider && isRecord(provider.params) && Object.hasOwn(provider.params, "num_ctx");
		for (const [index, model] of models) {
			const api = normalizeOptionalLowercaseString(model.api) || normalizeOptionalLowercaseString(provider.api);
			const params = model.params;
			if (api !== "ollama" || params !== void 0 && !isRecord(params) || Object.hasOwn(params ?? {}, "num_ctx")) continue;
			const numCtx = resolveConfiguredOllamaModelNumCtxBudget(model, provider, providerNumCtxApplies);
			if (numCtx !== void 0) {
				model.params = {
					...params,
					num_ctx: numCtx
				};
				changes.push(`Set models.providers.${sanitizeForLog(providerId)}.models[${index}].params.num_ctx to ${numCtx} for native Ollama compatibility.`);
			}
		}
	}
}
function normalizeLegacyMistralModelDefaults(cfg, changes) {
	for (const [providerId, provider] of modelProviders(cfg)) {
		if (normalizeProviderId(providerId) !== "mistral") continue;
		for (const [index, model] of providerModels(provider)) {
			const modelId = normalizeOptionalString(model.id) ?? "";
			if (!modelId) continue;
			const contextWindow = asFiniteNumber(model.contextWindow) ?? null;
			const maxTokens = asFiniteNumber(model.maxTokens) ?? null;
			if (contextWindow !== null && maxTokens !== null) {
				const normalizedMaxTokens = resolveNormalizedProviderModelMaxTokens({
					providerId,
					modelId,
					contextWindow,
					rawMaxTokens: maxTokens
				});
				if (normalizedMaxTokens !== maxTokens) {
					model.maxTokens = normalizedMaxTokens;
					changes.push(`Normalized models.providers.${providerId}.models[${index}].maxTokens (${maxTokens} → ${normalizedMaxTokens}) to avoid Mistral context-window rejects.`);
				}
			}
			Object.assign(model, normalizeLegacyMistralModelCost({
				providerId,
				model,
				modelId,
				index,
				changes
			}));
		}
	}
}
//#endregion
//#region src/commands/doctor/shared/legacy-secret-inputs.ts
/** Normalize only registered credential paths before config validation and backup/write. */
function migrateLegacySecretInputs(config) {
	let next;
	const changes = [];
	for (const target of discoverConfigSecretTargets(config)) {
		const original = target.value;
		const providerless = isLegacySecretRefWithoutProvider(original);
		const ref = providerless ? coerceSecretRef(target.value, config.secrets?.defaults) : parseLegacySecretRefEnvMarker(target.value, config.secrets?.defaults?.env);
		if (!ref) continue;
		next ??= structuredClone(config);
		if (setPathExistingStrict(next, target.pathSegments, ref)) {
			changes.push(providerless ? `Added provider ${ref.provider} to ${target.path} SecretRef.` : `Moved ${target.path} ${String(target.value).trim()} marker → structured env SecretRef.`);
			if (hasLegacySecretRefExtraFields(original)) changes.push(`Canonicalized ${target.path} SecretRef to source/provider/id; Doctor preserves removed fields in the original config backup before writing the repair.`);
		}
	}
	return {
		config: next ?? config,
		changes
	};
}
//#endregion
//#region src/commands/doctor/shared/reserved-mcp-server-name-migrate.ts
const RESERVED_MCP_SERVER_NAME = "__proto__";
function resolveMcpServers(raw, nodeHost) {
	const root = asOptionalRecord(raw);
	const owner = nodeHost ? asOptionalRecord(root?.nodeHost) : root;
	return asOptionalRecord(asOptionalRecord(owner?.mcp)?.servers);
}
/** Drop reserved MCP server names before canonical config validation runs. */
function migrateReservedMcpServerNames(cfg, sourceRaw = cfg) {
	const locations = [{
		path: "mcp.servers",
		nodeHost: false
	}, {
		path: "nodeHost.mcp.servers",
		nodeHost: true
	}].filter(({ nodeHost }) => [sourceRaw, cfg].some((value) => Object.hasOwn(resolveMcpServers(value, nodeHost) ?? {}, RESERVED_MCP_SERVER_NAME)));
	if (locations.length === 0) return {
		config: cfg,
		changes: []
	};
	const next = structuredClone(cfg);
	const changes = [];
	for (const { path, nodeHost } of locations) {
		const servers = resolveMcpServers(next, nodeHost);
		if (servers) delete servers[RESERVED_MCP_SERVER_NAME];
		changes.push(`Dropped MCP server "${RESERVED_MCP_SERVER_NAME}" from ${path} because the name is reserved; re-add it under a different name.`);
	}
	return {
		config: next,
		changes
	};
}
//#endregion
//#region src/commands/doctor/shared/legacy-config-core-migrate.ts
function repairAgentRoster(cfg, repair) {
	const roster = readAgentRosterProperty(cfg);
	const values = roster?.value;
	if (!roster || !isRecord(values) && !Array.isArray(values) || Array.isArray(values) !== (roster.kind === "list")) return;
	for (const [key, agent] of Object.entries(values)) if (isRecord(agent)) repair(agent, roster.kind === "entries" ? `agents.entries.${key}` : `agents.list[${key}]`);
}
function repairInvalidHeartbeatActiveHours(cfg, changes) {
	const repairHeartbeat = (heartbeat, path) => {
		if (isRecord(heartbeat) && Object.hasOwn(heartbeat, "activeHours") && !HeartbeatSchema.safeParse({ activeHours: heartbeat.activeHours }).success) {
			delete heartbeat.activeHours;
			changes.push(`Removed invalid ${path}.activeHours; heartbeats will use unrestricted hours until it is reconfigured.`);
		}
	};
	repairHeartbeat(cfg.agents?.defaults?.heartbeat, "agents.defaults.heartbeat");
	repairAgentRoster(cfg, (agent, path) => repairHeartbeat(agent.heartbeat, `${path}.heartbeat`));
}
function repairNullAgentWorkspaces(cfg, changes) {
	let repaired = 0;
	repairAgentRoster(cfg, (agent) => {
		if (agent.workspace === null) {
			repaired += 1;
			delete agent.workspace;
		}
	});
	if (repaired) changes.push(`Removed null workspace value${repaired === 1 ? "" : "s"} from agents.${readAgentRosterProperty(cfg)?.kind} entr${repaired === 1 ? "y" : "ies"}.`);
}
/** Normalize pre-admission config through core, plugin setup, channel, and secret-ref migrations. */
function normalizeCompatibilityConfigValues(raw, options = {}) {
	if (!isRecord(raw)) throw new TypeError("Compatibility config normalization requires an object");
	const changes = [];
	const warnings = [];
	const reserved = migrateReservedMcpServerNames(raw, options.sourceRaw);
	changes.push(...reserved.changes);
	let next = seedMissingDefaultAccountsFromSingleAccountBase(reserved.config, changes);
	const setup = runPluginSetupConfigMigrations({ config: next });
	next = setup.config;
	changes.push(...setup.changes);
	warnings.push(...setup.warnings ?? []);
	for (const migrate of [
		migrateLegacyWebSearchConfig,
		migrateLegacyWebFetchConfig,
		migrateLegacyXSearchConfig
	]) {
		const migrated = migrate(next);
		if (migrated.changes.length) {
			next = migrated.config;
			changes.push(...migrated.changes);
		}
	}
	normalizeLegacyNanoBananaSkill(next, changes);
	next = normalizeLegacyTalkConfig(next, changes);
	normalizeLegacyOpenAIModelProviderApi(next, changes);
	next = normalizeLegacyRuntimeModelRefs(next, changes, options.blockedModelIdentities);
	normalizeLegacyOllamaNativeNumCtxParams(next, changes);
	normalizeLegacyMistralModelDefaults(next, changes);
	stripRetiredTuningKnobs(next, changes);
	const channels = applyChannelDoctorCompatibilityMigrations(next, { historicalWebhookListeners: true });
	warnings.push(...channels.warnings ?? []);
	if (channels.changes.length) {
		next = channels.next;
		changes.push(...channels.changes);
	}
	const secrets = migrateLegacySecretInputs(next);
	if (secrets.changes.length) {
		next = secrets.config;
		changes.push(...secrets.changes);
	}
	normalizeLegacyOpenAICodexModelsAddMetadata(next, changes);
	repairInvalidHeartbeatActiveHours(next, changes);
	repairNullAgentWorkspaces(next, changes);
	next = migrateLegacyCommandOwners(next, changes);
	next = pruneBindingsForMissingAgents(next, changes);
	copyConfigResolutionFactsThroughRewrite(raw, next);
	return {
		config: inheritLegacyDefaultAgentId(raw, next),
		changes,
		...warnings.length ? { warnings } : {}
	};
}
//#endregion
//#region src/commands/doctor-config-flow.ts
async function refreshGatewayAuthStateAfterAuthProfileRepair() {
	for (const request of [{
		method: "secrets.reload",
		params: {}
	}, {
		method: "models.authStatus",
		params: { refresh: true }
	}]) try {
		await callGateway({
			...request,
			timeoutMs: 3e3
		});
	} catch {}
}
async function loadAndMaybeMigrateDoctorConfig(params) {
	const shouldRepair = params.options.repair === true || params.options.yes === true;
	const preflight = await withProgress({
		label: "Checking OpenClaw state…",
		enabled: params.options.nonInteractive !== true && params.options.json !== true,
		delayMs: 200
	}, (progress) => runDoctorConfigPreflight({
		retiredStateAdmitted: params.retiredStateAdmitted,
		stateSchemaPreparation: params.stateSchemaPreparation,
		observe: false,
		invocationPurpose: "doctor",
		repairPrefixedConfig: shouldRepair,
		doctorOnlyStateMigrations: shouldRepair,
		preparePluginMetadataSnapshot: true,
		...params.agentDatabaseMigrationDiscovery ? { agentDatabaseMigrationDiscovery: params.agentDatabaseMigrationDiscovery } : {},
		beforeWorkspaceStateMigration: createWorkspaceAliasMigrationRepair(params.prompter, progress.done),
		measure: async (name, run) => {
			progress.setLabel(`${name.slice(name.lastIndexOf(".") + 1).replaceAll("-", " ")}…`);
			return await run();
		}
	}));
	const { snapshot, baseConfig: baseCfg } = preflight;
	const referenceSource = prepareDoctorConfigReferenceSource(snapshot);
	const pluginMetadataSnapshotState = { current: preflight.pluginMetadataSnapshot };
	const { createDoctorPluginMetadataSnapshotScope } = await import("./plugin-metadata-snapshot-scope-DF7T-x-g.mjs");
	const pluginMetadataSnapshotScope = createDoctorPluginMetadataSnapshotScope({
		getBaseSnapshot: () => pluginMetadataSnapshotState.current,
		env: process.env,
		getDeferredPluginIds: () => preflight.deferredPluginMigrations?.map((pending) => pending.pluginId) ?? []
	});
	const runWithPluginMetadataSnapshot = pluginMetadataSnapshotScope.run;
	const invalidatePluginMetadataSnapshot = () => {
		delete configResult.preflightLegacyStateConfig;
		pluginMetadataSnapshotState.current = void 0;
		pluginMetadataSnapshotScope.invalidate();
	};
	const runWithCurrentPluginMetadata = (config, run) => {
		const soleAgentId = tryResolveSoleAgentId(config);
		return runWithPluginMetadataSnapshot({
			config,
			workspaceDir: soleAgentId ? resolveAgentWorkspaceDir(config, soleAgentId) : void 0
		}, run);
	};
	let state = {
		cfg: baseCfg,
		candidate: structuredClone(baseCfg),
		pendingChanges: false,
		fixHints: []
	};
	const explicitSetPaths = [];
	let shouldRepairCronCodexModelRefsAfterConfigWrite = false;
	let openAICodexAuthProfileIdMap;
	let modelRetirementRepairRan = false;
	let retiredModelRefConfig;
	const doctorFixCommand = formatCliCommand("openclaw doctor --fix");
	const changesPanelSink = createDoctorChangesPanelSink(shouldRepair);
	const configRepairWarnings = [...preflight.warnings ?? []];
	const applyConfigMutation = (mutation, fixHint, options = {}) => {
		changesPanelSink.emit(mutation.changes, options.sanitize ? { sanitize: true } : {});
		if (options.emitWarnings !== false && mutation.warnings?.length) {
			emitDoctorNotes({
				note,
				warningNotes: mutation.warnings
			});
			configRepairWarnings.push(...mutation.warnings);
		}
		state = applyDoctorConfigMutation({
			state,
			mutation,
			shouldRepair,
			fixHint
		});
	};
	const finalizeMigrationResult = prepareDoctorConfigMigrationResult(preflight, snapshot);
	const sourceRosterConfig = snapshot.sourceConfigBeforeMigrations ?? snapshot.sourceConfig;
	const rosterMigrationNeeded = findLegacyConfigRuleIssues(sourceRosterConfig, LEGACY_AGENT_ROSTER_RULES).length > 0 || listAgentEntries(sourceRosterConfig).length === 0 || baseCfg.agents?.ownership === void 0 && listAgentEntries(baseCfg).length > 1;
	const legacyStep = runWithCurrentPluginMetadata(state.candidate, () => applyLegacyCompatibilityStep({
		snapshot,
		state,
		shouldRepair,
		doctorFixCommand
	}));
	state = legacyStep.state;
	const legacyDefaultAgentId = preflight.rosterMigrationOwnerId ?? tryGetLegacyDefaultAgentId(state.candidate) ?? resolveLegacyAgentRosterOwner(preflight.rosterMigrationSource ?? sourceRosterConfig);
	if (legacyDefaultAgentId) {
		retainLegacyDefaultAgentId(state.cfg, legacyDefaultAgentId);
		retainLegacyDefaultAgentId(state.candidate, legacyDefaultAgentId);
	}
	const includeOwnsRoster = configIncludeOwnsAgentRoster(snapshot);
	const persistCanonicalAgentRoster = snapshot.exists && rosterMigrationNeeded && !includeOwnsRoster;
	if (persistCanonicalAgentRoster) {
		applyConfigMutation({
			config: state.candidate,
			changes: ["Prepared the canonical agent roster for persistence."]
		}, `Run "${doctorFixCommand}" to persist the explicit agent roster.`, { emitWarnings: false });
		if (state.candidate.agents?.ownership === "explicit") explicitSetPaths.push(["agents", "ownership"]);
	}
	const { prepareSessionStoreOwnerRecovery } = await import("./doctor-session-store-owner-X6ujTGCi.mjs");
	const sessionStoreOwnerRecovery = await prepareSessionStoreOwnerRecovery({
		config: state.candidate,
		snapshot,
		prompter: params.prompter
	});
	applyConfigMutation(sessionStoreOwnerRecovery, `Run "${doctorFixCommand}" to review session-store ownership recovery.`, { sanitize: true });
	const { collectBlockedLegacyOpenAICodexProviderPlan } = await import("./legacy-config-migrations.runtime.models-B20TxffR.mjs");
	const blockedCodexProviderPlan = collectBlockedLegacyOpenAICodexProviderPlan(state.candidate);
	const blockedCodexModelIdentities = new Set(blockedCodexProviderPlan.blockedModelIdentities);
	if (preflight.cronCodexRuntimePolicyTargets?.length) {
		const { repairCronCodexRuntimePolicies } = await import("./runtime-policy-migration-RNSnwEln.mjs");
		const cronRuntimeRepair = repairCronCodexRuntimePolicies({
			cfg: state.candidate,
			targets: preflight.cronCodexRuntimePolicyTargets,
			blockedModelIdentities: blockedCodexModelIdentities
		});
		applyConfigMutation(cronRuntimeRepair, `Run "${doctorFixCommand}" to preserve migrated cron runtime policy.`);
		const blockedTargets = new Set(cronRuntimeRepair.blockedTargets.map(cronCodexRuntimePolicyTargetKey));
		shouldRepairCronCodexModelRefsAfterConfigWrite = preflight.cronCodexRuntimePolicyTargets.some((target) => !blockedTargets.has(cronCodexRuntimePolicyTargetKey(target)));
	}
	const pluginLegacyIssues = await (async () => {
		if (snapshot.parsed === snapshot.sourceConfig) return [];
		const { findDoctorLegacyConfigIssues } = await import("./legacy-config-issues-DiVdPnHU.mjs");
		return runWithCurrentPluginMetadata(state.candidate, () => findDoctorLegacyConfigIssues(snapshot.parsed, snapshot.parsed));
	})();
	const seenLegacyIssues = new Set(snapshot.legacyIssues.map((issue) => `${issue.path}:${issue.message}`));
	const pluginIssueLines = pluginLegacyIssues.filter((issue) => {
		const key = `${issue.path}:${issue.message}`;
		if (seenLegacyIssues.has(key)) return false;
		seenLegacyIssues.add(key);
		return true;
	}).map((issue) => `- ${issue.path}: ${issue.message}`);
	const legacyIssueLines = [...legacyStep.issueLines, ...pluginIssueLines];
	if (pluginIssueLines.length > 0 && !shouldRepair && !state.fixHints.includes(`Run "${doctorFixCommand}" to migrate legacy config keys.`)) state.fixHints.push(`Run "${doctorFixCommand}" to migrate legacy config keys.`);
	if (legacyIssueLines.length > 0) note(legacyIssueLines.join("\n"), "Legacy config keys detected");
	changesPanelSink.emit(legacyStep.changeLines);
	const { MODEL_METADATA_CORRUPTION_AUDIT_LIMIT, repairGeneratedModelMetadataCorruption } = await import("./model-metadata-corruption-repair-BKQIW4P3.mjs");
	applyConfigMutation(runWithCurrentPluginMetadata(state.candidate, () => repairGeneratedModelMetadataCorruption({
		config: state.candidate,
		authoredRoot: snapshot.parsed,
		configPath: snapshot.path,
		currentHash: hashConfigRaw(snapshot.raw),
		auditRecords: readRecentConfigAuditRecords({
			env: process.env,
			homedir,
			limit: MODEL_METADATA_CORRUPTION_AUDIT_LIMIT
		})
	})), `Run "${doctorFixCommand}" to remove audit-proven generated model metadata.`);
	noteDoctorHookConfigWarnings(state.cfg, snapshot.path);
	applyConfigMutation(runWithCurrentPluginMetadata(state.candidate, () => normalizeCompatibilityConfigValues(state.candidate, {
		blockedModelIdentities: blockedCodexModelIdentities,
		sourceRaw: snapshot.parsed
	})), `Run "${doctorFixCommand}" to apply these changes.`);
	const { recoverCommandOwnerTargetKinds } = await import("./doctor-command-owner-recovery-2KGEO_lx.mjs");
	applyConfigMutation(runWithCurrentPluginMetadata(state.candidate, () => recoverCommandOwnerTargetKinds({
		config: state.candidate,
		snapshot
	})), `Run "${doctorFixCommand}" to restore recorded command-owner target kinds.`, { sanitize: true });
	const { repairUnownedChannelAccountBindings } = await import("./legacy-config-binding-repair-7AmN49EB.mjs");
	applyConfigMutation(runWithCurrentPluginMetadata(state.candidate, () => repairUnownedChannelAccountBindings({
		config: state.candidate,
		sourceConfigBeforeMigrations: snapshot.sourceConfigBeforeMigrations
	})), `Run "${doctorFixCommand}" to preserve channel account ownership.`);
	const { collectTailscaleConfigWarnings } = await import("./doctor-tailscale-D98wNau_.mjs");
	const tailscaleWarnings = await collectTailscaleConfigWarnings({
		cfg: state.candidate,
		env: process.env
	});
	emitDoctorNotes({
		note,
		warningNotes: tailscaleWarnings
	});
	configRepairWarnings.push(...tailscaleWarnings);
	const { prepareRetiredPhoneControlCleanup } = await import("./doctor-retired-phone-control-Bbrcf67l.mjs");
	const retiredPhoneControlCleanup = await prepareRetiredPhoneControlCleanup({
		cfg: state.candidate,
		env: process.env
	});
	applyConfigMutation({
		config: retiredPhoneControlCleanup.config,
		changes: retiredPhoneControlCleanup.configChanges,
		warnings: retiredPhoneControlCleanup.warnings
	}, `Run "${doctorFixCommand}" to retire Phone Control lease configuration.`);
	if (retiredPhoneControlCleanup.cleanupPending && !shouldRepair) note(`Retired Phone Control lease state remains. Run "${doctorFixCommand}" to archive it.`, "Legacy state detected");
	const { recoverInstalledPluginConfigIds } = await import("./installed-plugin-id-recovery-ZI_z9bs_.mjs");
	const installedPluginRecovery = await recoverInstalledPluginConfigIds(state.candidate, process.env);
	applyConfigMutation({
		...installedPluginRecovery,
		warnings: installedPluginRecovery.notices
	}, `Run "${doctorFixCommand}" to apply these changes.`);
	if (referenceSource) referenceSource.installedPluginIdRecovery = installedPluginRecovery.recovery;
	const pluginActivationSourceConfig = state.candidate;
	const { collectCodexPluginActivationWarnings } = await import("./codex-plugin-activation-warning-cn_i_J1A.mjs");
	emitDoctorNotes({
		note,
		warningNotes: collectCodexPluginActivationWarnings(pluginActivationSourceConfig)
	});
	const { applyPluginAutoEnable } = await import("./plugin-auto-enable-DDt4_IPG.mjs");
	applyConfigMutation(runWithCurrentPluginMetadata(state.candidate, () => applyPluginAutoEnable({
		config: state.candidate,
		env: process.env
	})), `Run "${doctorFixCommand}" to apply these changes.`, { emitWarnings: false });
	if (!shouldRepair) {
		const { repairStaleAgentModelRefs } = await import("./stale-agent-model-ref-repair-BY0k8hNX.mjs");
		const staleAgentModelRepair = runWithCurrentPluginMetadata(state.candidate, () => repairStaleAgentModelRefs(state.candidate, { env: process.env }));
		retiredModelRefConfig = staleAgentModelRepair.retiredModelRefConfig;
		applyConfigMutation(staleAgentModelRepair, `Run "${doctorFixCommand}" to remove stale agent model references.`, { sanitize: true });
	}
	const [{ collectPluginToolAllowlistWarnings }, { collectGitHubUpgradeWarnings }, { normalizePluginsConfig }] = await Promise.all([
		import("./plugin-tool-allowlist-warnings-BbtTPFeB.mjs"),
		import("./github-preview-upgrade-BvzIANgk.mjs"),
		import("./config-state-Yib7xwUS.mjs")
	]);
	const pluginWarnings = [...runWithCurrentPluginMetadata(state.candidate, () => collectPluginToolAllowlistWarnings({
		cfg: state.candidate,
		env: process.env
	})), ...collectGitHubUpgradeWarnings(normalizePluginsConfig(state.candidate.plugins))];
	if (pluginWarnings.length > 0) note(sanitizeDoctorNote(pluginWarnings.join("\n")), "Doctor warnings");
	const hasConfiguredChannels = listDoctorConfiguredChannelIds(state.candidate, { configEntryPolicy: "raw" }).length > 0;
	let collectMutableAllowlistWarnings;
	if (hasConfiguredChannels) {
		const channelDoctor = await import("./channel-doctor-CsrlbdOa.mjs");
		collectMutableAllowlistWarnings = channelDoctor.collectChannelDoctorMutableAllowlistWarnings;
		const channelDoctorSequence = await runWithCurrentPluginMetadata(state.candidate, () => channelDoctor.runChannelDoctorConfigSequences({
			cfg: state.candidate,
			env: process.env,
			shouldRepair
		}));
		emitDoctorNotes({
			note,
			changeNotes: channelDoctorSequence.changeNotes,
			infoNotes: channelDoctorSequence.infoNotes,
			warningNotes: channelDoctorSequence.warningNotes
		});
		const staleChannelCleanups = await runWithCurrentPluginMetadata(state.candidate, () => channelDoctor.collectChannelDoctorStaleConfigMutations(state.candidate, { env: process.env }));
		for (const staleCleanup of staleChannelCleanups) applyConfigMutation(staleCleanup, `Run "${doctorFixCommand}" to remove stale channel plugin references.`, { sanitize: true });
	}
	const { repairHooksTokenReuseGatewayAuth } = await import("./hooks-token-reuse-repair-B4CSg2Xl.mjs");
	applyConfigMutation(await repairHooksTokenReuseGatewayAuth(state.candidate, process.env), `Run "${doctorFixCommand}" to rotate hooks.token away from Gateway auth.`, { emitWarnings: false });
	if (shouldRepair) {
		const { runDoctorRepairSequence } = await import("./repair-sequencing-6fiYEy2V.mjs");
		const prompter = params.prompter;
		const repairSequence = await runDoctorRepairSequence({
			state,
			installedPluginIdRecovery: installedPluginRecovery.recovery,
			doctorFixCommand,
			env: process.env,
			blockedCodexProviderPlan,
			pluginMetadataSnapshotState,
			runWithPluginMetadataSnapshot,
			...prompter ? { onCapabilityConsent: createPluginCapabilityConsentPrompter({
				note: async (message, title) => note(message, title),
				confirm: (confirmation) => prompter.confirmRuntimeRepair({
					...confirmation,
					requiresInteractiveConfirmation: true
				})
			}) } : {}
		});
		state = repairSequence.state;
		configRepairWarnings.push(...repairSequence.warningNotes);
		if (referenceSource) referenceSource.installedPluginIdRecovery = new Map([...installedPluginRecovery.recovery, ...repairSequence.installedPluginIdRecovery]);
		pluginMetadataSnapshotState.current = repairSequence.pluginMetadataSnapshot;
		openAICodexAuthProfileIdMap = repairSequence.openAICodexAuthProfileIdMap;
		retiredModelRefConfig = repairSequence.retiredModelRefConfig;
		modelRetirementRepairRan = repairSequence.modelRetirementRepairRan;
		if (repairSequence.authProfilesRepaired) await refreshGatewayAuthStateAfterAuthProfileRepair();
		emitDoctorNotes({
			note,
			changeNotes: repairSequence.changeNotes,
			warningNotes: repairSequence.warningNotes
		});
		for (const configChange of repairSequence.configChangeNotes ?? []) changesPanelSink.emit([configChange]);
	} else {
		const { collectDoctorPreviewNotes } = await import("./preview-warnings-BM5MVUbm.mjs");
		const previewNotes = await runWithCurrentPluginMetadata(state.candidate, () => collectDoctorPreviewNotes({
			cfg: state.candidate,
			activationSourceConfig: pluginActivationSourceConfig,
			doctorFixCommand,
			env: process.env,
			allowExec: params.options.allowExec === true,
			blockedCodexProviderPlan
		}));
		emitDoctorNotes({
			note,
			infoNotes: previewNotes.infoNotes,
			warningNotes: previewNotes.warningNotes
		});
	}
	const mutableAllowlistWarnings = collectMutableAllowlistWarnings ? await runWithCurrentPluginMetadata(state.candidate, () => collectMutableAllowlistWarnings({
		cfg: state.candidate,
		env: process.env
	})) : [];
	if (mutableAllowlistWarnings.length > 0) note(sanitizeDoctorNote(mutableAllowlistWarnings.join("\n")), "Doctor warnings");
	const unknownStep = applyUnknownConfigKeyStep({
		state,
		shouldRepair,
		doctorFixCommand
	});
	state = unknownStep.state;
	if (unknownStep.removed.length > 0 || unknownStep.repairs.length > 0) {
		const lines = [...unknownStep.removed.map((pathLocal) => `- ${pathLocal}`), ...unknownStep.repairs.map((change) => `- ${change}`)];
		if (shouldRepair) changesPanelSink.emit(lines);
		else note(lines.join("\n"), "Unknown config keys");
	}
	if (unknownStep.warnings.length > 0) note(unknownStep.warnings.join("\n"), "Doctor warnings");
	const finalized = await finalizeDoctorConfigFlow({
		...state,
		snapshot,
		shouldRepair,
		confirm: params.confirm,
		note
	});
	const cfg = finalized.cfg;
	const shouldWriteConfig = finalized.shouldWriteConfig && legacyStep.blocksWrite !== true;
	const includeBoundaryWrite = shouldWriteConfig && canWriteDoctorInclude(snapshot, cfg, {
		persistCanonicalAgentRoster,
		explicitSetPaths
	}, referenceSource?.installedPluginIdRecovery);
	const configuredOpencodePluginIds = [cfg.models?.providers?.opencode || cfg.models?.providers?.["opencode-zen"] ? "opencode" : void 0, cfg.models?.providers?.["opencode-go"] ? "opencode-go" : void 0].filter((pluginId) => pluginId !== void 0);
	let activeOpencodePluginIds = [];
	if (configuredOpencodePluginIds.length > 0) {
		const { resolveEnabledProviderPluginIds } = await import("./providers-Bwp2994u.mjs");
		activeOpencodePluginIds = runWithCurrentPluginMetadata(cfg, () => resolveEnabledProviderPluginIds({
			config: cfg,
			onlyPluginIds: configuredOpencodePluginIds
		}));
	}
	noteOpencodeProviderOverrides(cfg, {
		opencodePluginActive: activeOpencodePluginIds.includes("opencode"),
		opencodeGoPluginActive: activeOpencodePluginIds.includes("opencode-go")
	});
	noteImplicitFallbackClobberWarnings(cfg);
	noteSandboxOriginProxyWarning(cfg);
	noteMcpOriginWarning(cfg);
	noteMediaCliModelWarnings(cfg);
	noteMissingDefaultAgentOwner(cfg);
	const migrationResult = await finalizeMigrationResult({
		cfg,
		shouldWriteConfig,
		pluginInventoryChanged: pluginMetadataSnapshotState.inventoryChanged,
		metadataSnapshot: pluginMetadataSnapshotState.current,
		runWithCurrentPluginMetadata
	});
	const pendingChangePanels = changesPanelSink.drain();
	const configResult = {
		...finalized,
		...shouldWriteConfig && sessionStoreOwnerRecovery.changes.length > 0 ? { confirmedConfigSource: {
			path: snapshot.path,
			hash: snapshot.hash ?? hashConfigRaw(snapshot.raw)
		} } : {},
		...referenceSource ? { referenceSource } : {},
		path: snapshot.path ?? CONFIG_PATH,
		shouldWriteConfig,
		...configRepairWarnings.length ? { warnings: [...new Set(configRepairWarnings)] } : {},
		...shouldWriteConfig && pendingChangePanels.length > 0 ? { pendingChangePanels } : {},
		sourceConfigValid: snapshot.valid,
		...legacyStep.partiallyValid === true ? { skipPluginValidationOnWrite: true } : {},
		...shouldWriteConfig && explicitSetPaths.length > 0 ? { explicitSetPaths } : {},
		...shouldWriteConfig && persistCanonicalAgentRoster ? { persistCanonicalAgentRoster: true } : {},
		...includeBoundaryWrite ? { skipWizardMetadataForIncludeWrite: true } : {},
		...shouldRepairCronCodexModelRefsAfterConfigWrite ? { shouldRepairCronCodexModelRefsAfterConfigWrite: true } : {},
		...shouldRepair && retiredPhoneControlCleanup.cleanupPending && retiredPhoneControlCleanup.cleanupSafe ? { retiredPhoneControlStateCleanupPending: true } : {},
		...blockedCodexProviderPlan.blockedModelIdentities.length > 0 ? { blockedCodexModelIdentities: blockedCodexProviderPlan.blockedModelIdentities } : {},
		...openAICodexAuthProfileIdMap ? { openAICodexAuthProfileIdMap } : {},
		...retiredModelRefConfig ? { retiredModelRefConfig } : {},
		modelRetirementRepairRan: modelRetirementRepairRan && !legacyStep.blocksWrite && (shouldWriteConfig || snapshot.valid),
		...migrationResult,
		runWithPluginMetadataSnapshot,
		invalidatePluginMetadataSnapshot
	};
	return configResult;
}
//#endregion
export { loadAndMaybeMigrateDoctorConfig };