UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

1,643 lines • 69.9 kB
import { t as createSubsystemLogger } from "./subsystem-I-uqdx4f.mjs";
import { r as normalizeProviderId } from "./provider-id-D1VImdKk.mjs";
import { d as resolveAgentWorkspaceDir, g as resolveDefaultAgentId } from "./agent-scope-config-CDYpzA0r.mjs";
import { a as isCurrentPluginMetadataSnapshotRuntimeGeneration, i as getCurrentPluginMetadataSnapshot } from "./current-plugin-metadata-snapshot-BF6RZfMc.mjs";
import { s as withFileLock } from "./file-lock-CkzOvhYo.mjs";
import "./file-lock-Cl3feKST.mjs";
import { u as resolvePluginMetadataSnapshot } from "./plugin-metadata-snapshot-pYUEfhY-.mjs";
import "./agent-scope-CG17uXFn.mjs";
import { f as runPluginStreamConsumer } from "./plugin-instance-scope-DEzoxqBj.mjs";
import { n as modelTransportRoutesMatch } from "./model-compat-catalog-Dfj3P1T4.mjs";
import { n as projectConfigOntoRuntimeSourceSnapshot } from "./runtime-source-projection-rvQFgz1Z.mjs";
import { r as getRuntimeConfig } from "./io.runtime-BsUXSsD4.mjs";
import "./config-DLycWFvB.mjs";
import { r as normalizeResolvedPricing } from "./usage-cost-Va5dwVFW.mjs";
import "./src-UF9QYDzr.mjs";
import { n as OAuthProviderRegistry, t as OAuthProviderConfiguredUnavailableError } from "./provider-runtime.errors-DEkO3luo.mjs";
import { c as resolveProviderOAuthRefreshCapabilityWithPlugin, i as loginProviderOAuthWithPlugin, s as resolveProviderOAuthCredentialWithPlugin } from "./provider-runtime.runtime.js";
import { a as inspectPersistedAuthProfileStoreRaw, g as resolveAuthProfileDatabasePath, i as inspectPersistedAuthProfileStateRaw, v as runAuthProfileWriteTransaction } from "./sqlite-CW8wNN4G.mjs";
import { t as normalizeOptionalSecretInput } from "./normalize-secret-input-DBJvdk-L.mjs";
import { S as OAUTH_REFRESH_LOCK_OPTIONS, p as loadPersistedAuthProfileState, r as loadPersistedAuthProfileStore, u as AuthProfileStoreUnreadableError, x as OAUTH_REFRESH_CALL_TIMEOUT_MS } from "./persisted-BVyWmvfF.mjs";
import { l as isOAuthRefreshFence, u as isPendingOAuthRefreshFence } from "./credential-state-WXO5HScp.mjs";
import { V as normalizeOAuthRefreshCredential, W as refreshSerializedOAuthCredential } from "./runtime-snapshots-kS-hnXTd.mjs";
import { i as assertAuthProfileMigrationReady, t as AuthProfileMigrationRequiredError } from "./legacy-source-diagnostic-Cum2WXHU.mjs";
import { o as hasUsableCustomProviderApiKey } from "./model-auth-provider-config-C6nCoXKi.mjs";
import { f as saveAuthProfileStoreWithPreparedOwner, t as createAuthProfileStoreReadScope } from "./store-runtime-DZk7O5wt.mjs";
import { t as getAgentDir } from "./config-CXiuxpwt.mjs";
import { a as getBashShellEnv, i as getBashShellConfig, t as buildShellCommandInvocation } from "./shell-utils-UHoDZxJT.mjs";
import { a as sanitizeModelHeaders } from "./model.inline-provider-Dp1eODYF.mjs";
import { t as parseModelCatalogJson } from "./model-catalog-json-CxUsDrkg.mjs";
import { o as normalizeProviderMapKeys, r as mergeProviderModels, t as buildSourceModelFields } from "./models-config.merge-Blym26cW.mjs";
import { t as materializeConfiguredProviderCatalogModels } from "./models-config.providers.catalog-DNzuLtzi.mjs";
import { n as isGeneratedPluginModelCatalog } from "./plugin-model-catalog-repair-gbFum2dR.mjs";
import { a as loadPersistedPluginModelCatalogsReadOnly, i as inspectLegacyPluginModelCatalogs, r as filterGeneratedPluginModelCatalogProviders } from "./plugin-model-catalog-BkfUsFfG.mjs";
import { r as bindStreamLlmRuntime } from "./model-runtime-binding-DgnrsJj9.mjs";
import fs, { existsSync, readFileSync } from "node:fs";
import { dirname, join } from "node:path";
import { execSync, spawnSync } from "node:child_process";
import { isDeepStrictEqual } from "node:util";
import { Pointer } from "typebox/schema";
import { Type } from "typebox";
import { Compile as Compile$1 } from "typebox/compile";
import { findEnvKeys, getEnvApiKey } from "@openclaw/ai/internal/env-api-keys";
import { createApiRegistry, createLlmRuntime } from "@openclaw/ai";
import { registerBuiltInApiProviders } from "@openclaw/ai/providers";
//#region src/agents/model-discovery-context.ts
/**
* Shared context resolvers for model discovery.
* Keeps callers from reaching into runtime config or plugin metadata snapshot
* plumbing directly.
*/
/** Resolve the workspace directory model discovery should use for agent scope. */
function resolveModelWorkspaceDir(cfg, explicitWorkspaceDir, agentId) {
	if (explicitWorkspaceDir !== void 0 || !cfg) return explicitWorkspaceDir;
	return resolveAgentWorkspaceDir(cfg, agentId ?? resolveDefaultAgentId(cfg));
}
/**
* Resolve the plugin metadata snapshot for model discovery.
*
* Explicit snapshots win for tests and prepared runtimes. Otherwise we prefer
* the current process snapshot, then fall back to resolving from config/env.
*/
function resolveModelPluginMetadataSnapshot(params) {
	if (params.pluginMetadataSnapshot) return params.pluginMetadataSnapshot;
	const env = params.env ?? process.env;
	try {
		if (!params.config && params.useRuntimeConfig) {
			const current = getCurrentPluginMetadataSnapshot({
				allowWorkspaceScopedSnapshot: true,
				allowSynchronousPolicyRead: false,
				env,
				...params.workspaceDir ? { workspaceDir: params.workspaceDir } : {}
			});
			if (current && isCurrentPluginMetadataSnapshotRuntimeGeneration(current)) return current;
		}
		const config = params.config ?? (params.useRuntimeConfig ? getRuntimeConfig() : void 0);
		return getCurrentPluginMetadataSnapshot({
			allowWorkspaceScopedSnapshot: true,
			env,
			...config ? { config } : {},
			...params.workspaceDir ? { workspaceDir: params.workspaceDir } : {}
		}) ?? resolvePluginMetadataSnapshot({
			config: config ?? {},
			env,
			...params.workspaceDir ? { workspaceDir: params.workspaceDir } : {},
			...params.allowWorkspaceScopedCurrent !== void 0 ? { allowWorkspaceScopedCurrent: params.allowWorkspaceScopedCurrent } : {}
		});
	} catch {
		return;
	}
}
//#endregion
//#region src/agents/sessions/auth-storage-error.ts
var AuthStoragePersistenceError = class extends Error {
	constructor(message, cause) {
		super(message, { cause });
		this.name = "AuthStoragePersistenceError";
	}
};
//#endregion
//#region src/agents/sessions/auth-storage-oauth-registry.ts
const registries = /* @__PURE__ */ new WeakMap();
function getAuthStorageOAuthProviderRegistry(authStorage) {
	let registry = registries.get(authStorage);
	if (!registry) {
		registry = new OAuthProviderRegistry();
		registries.set(authStorage, registry);
	}
	return registry;
}
async function loginAuthStorageOAuthProvider(authStorage, providerId, callbacks) {
	const provider = getAuthStorageOAuthProviderRegistry(authStorage).get(providerId);
	if (provider) return await provider.login(callbacks);
	const resolved = await loginProviderOAuthWithPlugin({
		provider: providerId,
		context: callbacks
	});
	if (resolved.status === "unowned") throw new Error(`Unknown OAuth provider: ${providerId}`);
	if (resolved.status !== "available") throw new OAuthProviderConfiguredUnavailableError(providerId);
	return resolved.credentials;
}
async function resolveAuthStoragePluginOAuthCredential(providerId, credential, refresh) {
	const resolved = await resolveProviderOAuthCredentialWithPlugin({
		provider: providerId,
		credential: {
			...credential,
			type: "oauth",
			provider: providerId
		},
		refresh
	});
	if (resolved.status === "configured-unavailable") throw new OAuthProviderConfiguredUnavailableError(providerId);
	return resolved.status === "available" ? {
		apiKey: resolved.apiKey,
		newCredentials: resolved.credential
	} : null;
}
async function canResolveAuthStoragePluginOAuthRefresh(providerId) {
	const capability = await resolveProviderOAuthRefreshCapabilityWithPlugin({ provider: providerId });
	if (capability.status === "configured-unavailable") throw new OAuthProviderConfiguredUnavailableError(providerId);
	return capability.status === "available";
}
//#endregion
//#region src/agents/sessions/auth-storage-oauth-refresh.ts
function isAuthStorageOAuthRefreshFence(provider, credential) {
	return credential?.type === "oauth" && isOAuthRefreshFence(normalizeOAuthRefreshCredential(credential, provider));
}
async function refreshAuthStorageOAuthCredential(params) {
	const provider = getAuthStorageOAuthProviderRegistry(params.authStorage).get(params.providerId);
	const result = await refreshSerializedOAuthCredential({
		backend: params.storage,
		provider: params.providerId,
		profileId: `${params.providerId}:default`,
		label: `AuthStorage.refresh(${params.providerId})`,
		timeoutMs: OAUTH_REFRESH_CALL_TIMEOUT_MS,
		parse: params.parse,
		serialize: (data) => JSON.stringify(data, null, 2),
		readCredential: (data) => {
			const credential = data[params.providerId];
			return normalizeOAuthRefreshCredential(credential?.type === "oauth" ? credential : void 0, params.providerId);
		},
		writeCredential: (data, credential) => ({
			...data,
			[params.providerId]: credential
		}),
		canRefresh: async () => Boolean(provider) || await canResolveAuthStoragePluginOAuthRefresh(params.providerId),
		commit: (data) => {
			const expected = params.parse(JSON.stringify(data));
			const current = params.storage.withLock((raw) => {
				const authoritative = params.parse(raw);
				if (!isDeepStrictEqual(authoritative[params.providerId], expected[params.providerId])) throw new AuthStoragePersistenceError("Auth storage credentials changed before refresh publication.", void 0);
				return { result: authoritative };
			});
			params.commit(current);
		},
		refresh: async (credential, data) => {
			const oauthCredentials = Object.fromEntries(Object.entries(data).filter((entry) => {
				return entry[1].type === "oauth";
			}));
			const refreshed = provider ? await getAuthStorageOAuthProviderRegistry(params.authStorage).getApiKey(params.providerId, oauthCredentials) : await resolveAuthStoragePluginOAuthCredential(params.providerId, credential, true);
			return refreshed ? {
				apiKey: refreshed.apiKey,
				credential: {
					...credential,
					...refreshed.newCredentials,
					type: "oauth",
					provider: credential.provider
				}
			} : null;
		},
		resolve: async (credential) => {
			if (provider) return {
				apiKey: provider.getApiKey(credential),
				credential
			};
			const resolved = await resolveAuthStoragePluginOAuthCredential(params.providerId, credential, false);
			return resolved ? {
				apiKey: resolved.apiKey,
				credential: {
					...credential,
					...resolved.newCredentials,
					type: "oauth",
					provider: credential.provider
				}
			} : null;
		}
	});
	return result ? {
		apiKey: result.apiKey,
		newCredentials: result.credential
	} : null;
}
//#endregion
//#region src/agents/sessions/auth-storage-projection.ts
function materializeAuthStorageStore(store, snapshots) {
	if (snapshots.length === 0) return store;
	const profiles = Object.fromEntries(Object.entries(store.profiles).map(([profileId, credential]) => {
		return [profileId, snapshots.map((snapshot) => snapshot.profiles[profileId]).find((candidate) => credential.type === "api_key" && credential.keyRef ? candidate?.type === "api_key" && Boolean(candidate.key) && candidate.provider === credential.provider && isDeepStrictEqual(candidate.keyRef, credential.keyRef) : credential.type === "token" && credential.tokenRef ? candidate?.type === "token" && Boolean(candidate.token) && candidate.provider === credential.provider && isDeepStrictEqual(candidate.tokenRef, credential.tokenRef) : false) ?? credential];
	}));
	return {
		...store,
		profiles
	};
}
function projectAuthStorageData(store) {
	const projected = {};
	for (const [profileId, credential] of Object.entries(store?.profiles ?? {})) {
		if (profileId !== `${credential.provider}:default`) continue;
		if (credential.type === "api_key" && credential.key) projected[credential.provider] = {
			type: "api_key",
			key: credential.key
		};
		else if (credential.type === "token" && credential.token) projected[credential.provider] = {
			type: "token",
			token: credential.token,
			...credential.expires !== void 0 ? { expires: credential.expires } : {}
		};
		else if (credential.type === "oauth") projected[credential.provider] = { ...credential };
	}
	return projected;
}
function assertAuthStorageSecretRefsMaterialized(store) {
	for (const [profileId, credential] of Object.entries(store.profiles)) {
		if (profileId !== `${credential.provider}:default`) continue;
		if (credential.type === "api_key" && credential.keyRef && !credential.key || credential.type === "token" && credential.tokenRef && !credential.token) throw new AuthStoragePersistenceError("AuthStorage.forAgent requires the active secrets runtime to materialize SecretRef credentials.", void 0);
	}
}
function projectAuthoritativeAuthStorageData(store, snapshots) {
	const materialized = materializeAuthStorageStore(store, snapshots);
	assertAuthStorageSecretRefsMaterialized(materialized);
	return projectAuthStorageData(materialized);
}
function applyAuthStorageData(store, data, materializedBaseline) {
	const profiles = { ...store.profiles };
	const projectedProviders = /* @__PURE__ */ new Set([...Object.keys(materializedBaseline), ...Object.entries(store.profiles).flatMap(([profileId, credential]) => profileId === `${credential.provider}:default` ? [credential.provider] : [])]);
	for (const provider of projectedProviders) if (!data[provider]) delete profiles[`${provider}:default`];
	for (const [provider, credential] of Object.entries(data)) {
		const profileId = `${provider}:default`;
		const existing = profiles[profileId];
		if (credential.type === "api_key" && existing?.type === "api_key" && existing.keyRef && materializedBaseline[provider]?.type === "api_key" && materializedBaseline[provider].key === credential.key) {
			profiles[profileId] = existing;
			continue;
		}
		if (credential.type === "token" && existing?.type === "token" && existing.tokenRef && materializedBaseline[provider]?.type === "token" && materializedBaseline[provider].token === credential.token) {
			profiles[profileId] = existing;
			continue;
		}
		profiles[profileId] = credential.type === "api_key" ? {
			type: "api_key",
			provider,
			key: credential.key
		} : credential.type === "token" ? {
			type: "token",
			provider,
			token: credential.token,
			expires: credential.expires
		} : {
			...credential,
			provider
		};
	}
	return {
		...store,
		profiles
	};
}
//#endregion
//#region src/agents/sessions/resolve-config-value.ts
/**
* Resolve configuration values that may be shell commands, environment variables, or literals.
* Used by auth-storage.ts and model-registry.ts.
*/
const commandResultCache = /* @__PURE__ */ new Map();
/**
* Resolve a config value (API key, header value, etc.) to an actual value.
* - If starts with "!", executes the rest as a shell command and uses stdout (cached)
* - Otherwise checks environment variable first, then treats as literal (not cached)
*/
function resolveConfigValue(config) {
	if (!config.startsWith("!")) return resolveConfigValueUncached(config);
	if (!commandResultCache.has(config)) commandResultCache.set(config, executeCommandUncached(config));
	return commandResultCache.get(config);
}
function executeWithConfiguredShell(command) {
	try {
		const shellConfig = getBashShellConfig();
		const invocation = buildShellCommandInvocation(command, shellConfig);
		const [shell, ...args] = invocation.argv;
		const result = spawnSync(shell, args, {
			encoding: "utf-8",
			...invocation.input === void 0 ? {} : { input: invocation.input },
			timeout: 1e4,
			stdio: [
				invocation.stdin,
				"pipe",
				"ignore"
			],
			shell: false,
			windowsHide: true,
			env: getBashShellEnv(shellConfig.shell)
		});
		if (result.error) {
			if (result.error.code === "ENOENT") return {
				executed: false,
				value: void 0
			};
			return {
				executed: true,
				value: void 0
			};
		}
		if (result.status !== 0) return {
			executed: true,
			value: void 0
		};
		return {
			executed: true,
			value: (result.stdout ?? "").trim() || void 0
		};
	} catch {
		return {
			executed: false,
			value: void 0
		};
	}
}
function executeWithDefaultShell(command) {
	try {
		return execSync(command, {
			encoding: "utf-8",
			timeout: 1e4,
			stdio: [
				"ignore",
				"pipe",
				"ignore"
			]
		}).trim() || void 0;
	} catch {
		return;
	}
}
function executeCommandUncached(commandConfig) {
	const command = commandConfig.slice(1);
	if (process.platform === "win32") {
		const configuredResult = executeWithConfiguredShell(command);
		if (configuredResult.executed) return configuredResult.value;
	}
	return executeWithDefaultShell(command);
}
/**
* Resolve all header values using the same resolution logic as API keys.
*/
function resolveConfigValueUncached(config) {
	if (config.startsWith("!")) return executeCommandUncached(config);
	if (Object.hasOwn(process.env, config)) return process.env[config] || void 0;
	return config;
}
function resolveConfigValueOrThrow(config, description) {
	const resolvedValue = resolveConfigValueUncached(config);
	if (resolvedValue !== void 0) return resolvedValue;
	if (config.startsWith("!")) throw new Error(`Failed to resolve ${description} from shell command: ${config.slice(1)}`);
	throw new Error(`Failed to resolve ${description}`);
}
function resolveHeadersOrThrow(headers, description) {
	if (!headers) return;
	const resolved = {};
	for (const [key, value] of Object.entries(headers)) resolved[key] = resolveConfigValueOrThrow(value, `${description} header "${key}"`);
	return Object.keys(resolved).length > 0 ? resolved : void 0;
}
//#endregion
//#region src/agents/sessions/auth-storage.ts
/**
* Credential storage facade for API keys and OAuth tokens.
* Canonical persistence is the per-agent SQLite auth-profile store.
*
* The backend contract keeps the upstream session SDK shape while OpenClaw
* projects provider-default profiles into it.
*/
const AUTH_STORAGE_CREATE_DEPRECATION_CODE = "AUTH_STORAGE_CREATE_DEPRECATED";
const FILE_AUTH_STORAGE_BACKEND_DEPRECATION_CODE = "FILE_AUTH_STORAGE_BACKEND_DEPRECATED";
const emittedAuthStorageWarnings = /* @__PURE__ */ new Set();
function emitAuthStorageDeprecationWarning(params) {
	if (emittedAuthStorageWarnings.has(params.code)) return;
	emittedAuthStorageWarnings.add(params.code);
	process.emitWarning(params.message, {
		code: params.code,
		type: "DeprecationWarning"
	});
}
var AuthStorageLegacyPathMigrationRequiredError = class extends Error {
	constructor() {
		super("Deprecated AuthStorage path contains unmigrated credentials; run openclaw doctor --fix for standard agent auth.json or migrate plugin storage to AuthStorage.forAgent(agentDir).");
		this.code = "AUTH_PROFILE_MIGRATION_REQUIRED";
		this.action = "migrate to AuthStorage.forAgent(agentDir)";
		this.name = "AuthStorageLegacyPathMigrationRequiredError";
	}
};
function assertDeprecatedAuthStoragePathAbsent(authPath) {
	if (authPath && fs.existsSync(authPath)) throw new AuthStorageLegacyPathMigrationRequiredError();
}
function collectStateOnlyAuthProfileIds(store) {
	return [.../* @__PURE__ */ new Set([
		...Object.values(store.order ?? {}).flat(),
		...Object.values(store.lastGood ?? {}),
		...Object.keys(store.usageStats ?? {})
	])].filter((profileId) => !store.profiles[profileId]);
}
function loadSqliteAuthStorageStore(agentDir, database) {
	const inspection = inspectPersistedAuthProfileStoreRaw(agentDir, database);
	if (inspection.status === "missing") {
		if (inspectPersistedAuthProfileStateRaw(agentDir, database).status === "unreadable") throw new AuthProfileStoreUnreadableError(database?.path ?? resolveAuthProfileDatabasePath(agentDir));
		return {
			version: 1,
			profiles: {},
			...loadPersistedAuthProfileState(agentDir, database)
		};
	}
	const store = loadPersistedAuthProfileStore(agentDir, database ? { database } : void 0);
	if (inspection.status === "unreadable" || !store) throw new AuthProfileStoreUnreadableError(database?.path ?? resolveAuthProfileDatabasePath(agentDir));
	return store;
}
var SqliteAuthStorageBackend = class {
	constructor(scope, preparedStore) {
		this.scope = scope;
		this.preparedStore = preparedStore;
		this.credentialSources = /* @__PURE__ */ new Map();
	}
	get agentDir() {
		return this.scope.agentDir;
	}
	assertProviderReady(provider, baseUrl) {
		this.scope.assertProviderReady(provider, baseUrl);
	}
	getCredentialSource(provider) {
		return this.credentialSources.get(provider);
	}
	assertCredentialReady(source, baseUrl) {
		this.scope.assertCredentialReady(source, baseUrl);
	}
	captureCredentialSources(store, databasePath) {
		const persisted = new Set(store.runtimePersistedProfileIds ?? []);
		this.credentialSources = new Map(Object.entries(store.profiles).flatMap(([profileId, credential]) => {
			if (profileId !== `${credential.provider}:default`) return [];
			const source = databasePath ? {
				databasePath,
				provider: credential.provider
			} : store.runtimeCredentialSources?.[profileId];
			if (!source && persisted.has(profileId)) throw new AuthStoragePersistenceError("Canonical auth credential is missing its source owner.", void 0);
			return source ? [[credential.provider, source]] : [];
		}));
	}
	read() {
		const store = this.scope.read();
		const content = JSON.stringify(projectAuthoritativeAuthStorageData(store, this.resolveMaterializedRuntimeStores()));
		this.captureCredentialSources(store);
		return content;
	}
	resolveMaterializedRuntimeStores() {
		const current = this.scope.getRuntimeSnapshots();
		return current.length > 0 ? current : [this.preparedStore];
	}
	readRaw() {
		assertAuthProfileMigrationReady(this.agentDir);
		return loadSqliteAuthStorageStore(this.agentDir);
	}
	persistData(store, next, materializedData, database, owner) {
		const nextStore = applyAuthStorageData(store, JSON.parse(next), materializedData);
		saveAuthProfileStoreWithPreparedOwner(nextStore, this.agentDir, {
			filterExternalAuthProfiles: false,
			preserveStateProfileIds: collectStateOnlyAuthProfileIds(store),
			syncExternalCli: false
		}, database, owner);
		return nextStore;
	}
	withLock(fn) {
		assertAuthProfileMigrationReady(this.agentDir);
		const snapshots = this.resolveMaterializedRuntimeStores();
		assertAuthProfileMigrationReady(this.agentDir);
		const selected = runAuthProfileWriteTransaction(this.agentDir, (database, owner) => {
			const store = loadSqliteAuthStorageStore(this.agentDir, database);
			const materializedData = projectAuthoritativeAuthStorageData(store, snapshots);
			const { result, next } = fn(JSON.stringify(materializedData));
			return {
				result,
				store: next === void 0 ? store : this.persistData(store, next, materializedData, database, owner),
				databasePath: owner.databasePath
			};
		});
		this.captureCredentialSources(selected.store, selected.databasePath);
		return selected.result;
	}
	async withLockAsync(fn) {
		assertAuthProfileMigrationReady(this.agentDir);
		return await withFileLock(resolveAuthProfileDatabasePath(this.agentDir), OAUTH_REFRESH_LOCK_OPTIONS, async () => {
			const initialRaw = this.readRaw();
			const initialData = projectAuthoritativeAuthStorageData(initialRaw, this.resolveMaterializedRuntimeStores());
			const { result, next } = await fn(JSON.stringify(initialData));
			if (next === void 0) {
				this.captureCredentialSources(initialRaw, resolveAuthProfileDatabasePath(this.agentDir));
				return result;
			}
			assertAuthProfileMigrationReady(this.agentDir);
			const selected = runAuthProfileWriteTransaction(this.agentDir, (database, owner) => {
				const authoritative = loadSqliteAuthStorageStore(this.agentDir, database);
				if (!isDeepStrictEqual(authoritative.profiles, initialRaw.profiles)) throw new AuthStoragePersistenceError("Cannot update auth storage because its SQLite credentials changed concurrently.", void 0);
				return {
					store: this.persistData(authoritative, next, initialData, database, owner),
					databasePath: owner.databasePath
				};
			});
			this.captureCredentialSources(selected.store, selected.databasePath);
			return result;
		});
	}
};
function createSqliteAuthStorageBackend(agentDir, config) {
	const scope = createAuthProfileStoreReadScope(agentDir, config);
	const preparedStore = materializeAuthStorageStore(scope.store, scope.getRuntimeSnapshots());
	assertAuthStorageSecretRefsMaterialized(preparedStore);
	return new SqliteAuthStorageBackend(scope, preparedStore);
}
/**
* @deprecated Use AuthStorage.forAgent(agentDir). This compatibility adapter
* derives the owning agent directory from the old path and persists only to SQLite.
* It is eligible for removal after 2026-10-01 and a clean published-plugin sweep.
*/
var FileAuthStorageBackend = class {
	constructor(authPath) {
		emitAuthStorageDeprecationWarning({
			code: FILE_AUTH_STORAGE_BACKEND_DEPRECATION_CODE,
			message: "FileAuthStorageBackend(path) is deprecated; use AuthStorage.forAgent(agentDir). The compatibility adapter persists to SQLite and never reads or writes auth.json."
		});
		assertDeprecatedAuthStoragePathAbsent(authPath);
		this.agentDir = authPath ? dirname(authPath) : getAgentDir();
	}
	getDelegate() {
		return this.delegate ??= createSqliteAuthStorageBackend(this.agentDir, void 0);
	}
	read() {
		return this.getDelegate().read();
	}
	assertProviderReady(provider, baseUrl) {
		this.getDelegate().assertProviderReady(provider, baseUrl);
	}
	getCredentialSource(provider) {
		return this.getDelegate().getCredentialSource(provider);
	}
	assertCredentialReady(source, baseUrl) {
		this.getDelegate().assertCredentialReady(source, baseUrl);
	}
	withLock(fn) {
		return this.getDelegate().withLock(fn);
	}
	async withLockAsync(fn) {
		return await this.getDelegate().withLockAsync(fn);
	}
};
var InMemoryAuthStorageBackend = class {
	withLock(fn) {
		const { result, next } = fn(this.value);
		if (next !== void 0) this.value = next;
		return result;
	}
	async withLockAsync(fn) {
		const { result, next } = await fn(this.value);
		if (next !== void 0) this.value = next;
		return result;
	}
};
/**
* Provider-keyed credential facade backed by the canonical auth-profile store.
*/
var AuthStorage = class AuthStorage {
	constructor(storage) {
		this.data = {};
		this.credentialSources = /* @__PURE__ */ new Map();
		this.runtimeOverrides = /* @__PURE__ */ new Map();
		this.loadError = null;
		this.errors = [];
		this.storage = storage;
		this.reload();
	}
	static forAgent(agentDir = getAgentDir(), config) {
		return new AuthStorage(createSqliteAuthStorageBackend(agentDir, config));
	}
	/**
	* @deprecated Use AuthStorage.forAgent(agentDir). The path-taking compatibility
	* form is eligible for removal after 2026-10-01 and a clean published-plugin
	* reader sweep; it no longer reads or writes JSON.
	*/
	static create(authPath) {
		emitAuthStorageDeprecationWarning({
			code: AUTH_STORAGE_CREATE_DEPRECATION_CODE,
			message: "AuthStorage.create(path) is deprecated; use AuthStorage.forAgent(agentDir). The compatibility adapter persists to SQLite and never reads or writes auth.json."
		});
		assertDeprecatedAuthStoragePathAbsent(authPath);
		return AuthStorage.forAgent(authPath ? dirname(authPath) : getAgentDir(), void 0);
	}
	static fromStorage(storage) {
		return new AuthStorage(storage);
	}
	static inMemory(data = {}) {
		const storage = new InMemoryAuthStorageBackend();
		storage.withLock(() => ({
			result: void 0,
			next: JSON.stringify(data, null, 2)
		}));
		return AuthStorage.fromStorage(storage);
	}
	/**
	* Set a runtime API key override (not persisted to disk).
	* Used for CLI --api-key flag.
	*/
	setRuntimeApiKey(provider, apiKey) {
		this.runtimeOverrides.set(provider, apiKey);
	}
	removeRuntimeApiKey(provider) {
		this.runtimeOverrides.delete(provider);
	}
	/**
	* Set a fallback resolver for API keys not found in canonical storage or env vars.
	* Used for custom provider keys from models.json.
	*/
	setFallbackResolver(resolver) {
		this.fallbackResolver = resolver;
	}
	recordError(error) {
		const normalizedError = error instanceof Error ? error : new Error(String(error));
		this.errors.push(normalizedError);
	}
	getCanonicalLoadError() {
		if (!this.loadError) return null;
		return this.storage.assertProviderReady || this.loadError instanceof AuthProfileMigrationRequiredError || this.loadError instanceof AuthProfileStoreUnreadableError ? this.loadError : null;
	}
	parseStorageData(content) {
		if (!content) return {};
		return JSON.parse(content);
	}
	setData(data) {
		this.data = data;
		this.credentialSources = new Map(Object.keys(data).flatMap((provider) => {
			const source = this.storage.getCredentialSource?.(provider);
			return source ? [[provider, source]] : [];
		}));
	}
	reload() {
		try {
			const content = this.storage.read ? this.storage.read() : this.storage.withLock((current) => ({ result: current }));
			this.setData(this.parseStorageData(content));
			this.loadError = null;
		} catch (error) {
			this.loadError = error;
			this.recordError(error);
		}
	}
	persistProviderChange(provider, credential) {
		if (this.loadError) this.reload();
		if (this.loadError) {
			const error = new AuthStoragePersistenceError(`Cannot update auth storage because it could not be loaded: ${this.loadError.message}`, this.loadError);
			this.recordError(error);
			throw error;
		}
		try {
			const persistedData = this.storage.withLock((current) => {
				const merged = { ...this.parseStorageData(current) };
				if (credential) merged[provider] = credential;
				else delete merged[provider];
				return {
					result: merged,
					next: JSON.stringify(merged, null, 2)
				};
			});
			this.loadError = null;
			this.setData(persistedData);
		} catch (error) {
			const persistenceError = error instanceof AuthStoragePersistenceError ? error : new AuthStoragePersistenceError(`Failed to persist auth storage update for provider "${provider}": ${error instanceof Error ? error.message : String(error)}`, error);
			this.recordError(persistenceError);
			throw persistenceError;
		}
	}
	get(provider) {
		const credential = this.data[provider];
		return isAuthStorageOAuthRefreshFence(provider, credential) ? void 0 : credential;
	}
	set(provider, credential) {
		this.persistProviderChange(provider, credential);
	}
	remove(provider) {
		this.persistProviderChange(provider, void 0);
	}
	list() {
		return Object.keys(this.data).filter((provider) => !isAuthStorageOAuthRefreshFence(provider, this.data[provider]));
	}
	has(provider) {
		return this.get(provider) !== void 0;
	}
	/**
	* Check if any form of auth is configured for a provider.
	* Unlike getApiKey(), this doesn't refresh OAuth tokens.
	*/
	hasAuth(provider) {
		return Boolean(this.runtimeOverrides.has(provider) || this.get(provider) || getEnvApiKey(provider) || this.fallbackResolver?.(provider));
	}
	/**
	* Return auth status without exposing credential values or refreshing tokens.
	*/
	getAuthStatus(provider) {
		if (this.get(provider)) return {
			configured: true,
			source: "stored"
		};
		if (this.runtimeOverrides.has(provider)) return {
			configured: false,
			source: "runtime",
			label: "--api-key"
		};
		const envKeys = findEnvKeys(provider);
		if (envKeys?.[0]) return {
			configured: false,
			source: "environment",
			label: envKeys[0]
		};
		if (this.fallbackResolver?.(provider)) return {
			configured: false,
			source: "fallback",
			label: "custom provider config"
		};
		return { configured: false };
	}
	/**
	* Get all credentials (for passing to getOAuthApiKey).
	*/
	getAll() {
		return Object.fromEntries(Object.entries(this.data).filter(([provider, credential]) => !isAuthStorageOAuthRefreshFence(provider, credential)));
	}
	drainErrors() {
		const drained = [...this.errors];
		this.errors = [];
		return drained;
	}
	async login(providerId, callbacks) {
		const credentials = await loginAuthStorageOAuthProvider(this, providerId, callbacks);
		this.set(providerId, {
			type: "oauth",
			...credentials
		});
	}
	logout(provider) {
		this.remove(provider);
	}
	/**
	* Refresh OAuth token with backend locking to prevent race conditions.
	* Multiple agent sessions may try to refresh simultaneously when tokens expire.
	*/
	async refreshOAuthTokenWithLock(providerId) {
		let source;
		const result = await refreshAuthStorageOAuthCredential({
			authStorage: this,
			storage: this.storage,
			providerId,
			parse: (current) => this.parseStorageData(current),
			commit: (data) => {
				this.setData(data);
				source = this.credentialSources.get(providerId);
				this.loadError = null;
			}
		});
		if (!result) {
			this.reload();
			return null;
		}
		return {
			...result,
			source
		};
	}
	/**
	* Get API key for a provider.
	* Priority:
	* 1. Runtime override (CLI --api-key)
	* 2. API key from canonical storage
	* 3. OAuth token from canonical storage (auto-refreshed with locking)
	* 4. Environment variable
	* 5. Fallback resolver (models.json custom providers)
	*/
	async getApiKey(providerId, options) {
		const runtimeKey = this.runtimeOverrides.get(providerId);
		if (runtimeKey) return runtimeKey;
		this.storage.assertProviderReady?.(providerId, options?.baseUrl);
		const canonicalLoadError = this.getCanonicalLoadError();
		if (canonicalLoadError) throw canonicalLoadError;
		const { apiKey, source } = await this.resolveStoredOrFallbackApiKey(providerId, options);
		this.storage.assertProviderReady?.(providerId, options?.baseUrl);
		const reloadedError = this.getCanonicalLoadError();
		if (reloadedError) throw reloadedError;
		if (source) this.storage.assertCredentialReady?.(source, options?.baseUrl);
		return apiKey;
	}
	async resolveStoredOrFallbackApiKey(providerId, options) {
		let cred = this.data[providerId];
		if (isAuthStorageOAuthRefreshFence(providerId, cred)) {
			this.reload();
			cred = this.data[providerId];
			const normalizedFence = cred?.type === "oauth" ? normalizeOAuthRefreshCredential(cred, providerId) : void 0;
			if (isOAuthRefreshFence(normalizedFence) && !isPendingOAuthRefreshFence(normalizedFence)) cred = void 0;
		}
		let source = this.credentialSources.get(providerId);
		if (source) this.storage.assertCredentialReady?.(source, options?.baseUrl);
		const resolved = (apiKey) => ({
			apiKey,
			source
		});
		if (cred?.type === "api_key") return resolved(resolveConfigValue(cred.key));
		if (cred?.type === "token") {
			if (cred.expires === void 0 || Date.now() < cred.expires) return resolved(resolveConfigValue(cred.token));
		}
		if (cred?.type === "oauth") {
			const provider = getAuthStorageOAuthProviderRegistry(this).get(providerId);
			if (Date.now() >= cred.expires) try {
				const result = await this.refreshOAuthTokenWithLock(providerId);
				if (result) return {
					apiKey: result.apiKey,
					source: result.source
				};
			} catch (error) {
				if (error instanceof OAuthProviderConfiguredUnavailableError) throw error;
				this.recordError(error);
				this.reload();
				const canonicalStoreError = error instanceof AuthProfileMigrationRequiredError || error instanceof AuthProfileStoreUnreadableError ? error : this.getCanonicalLoadError();
				if (canonicalStoreError) throw canonicalStoreError;
				const updatedCred = this.data[providerId];
				source = this.credentialSources.get(providerId);
				if (source) this.storage.assertCredentialReady?.(source, options?.baseUrl);
				if (updatedCred?.type === "oauth" && Date.now() < updatedCred.expires) {
					if (provider) return resolved(provider.getApiKey(updatedCred));
					return resolved((await resolveAuthStoragePluginOAuthCredential(providerId, updatedCred, false))?.apiKey);
				}
				return resolved(void 0);
			}
			else {
				if (provider) return resolved(provider.getApiKey(cred));
				return resolved((await resolveAuthStoragePluginOAuthCredential(providerId, cred, false))?.apiKey);
			}
		}
		const envKey = getEnvApiKey(providerId);
		if (envKey) return resolved(envKey);
		if (options?.includeFallback !== false) return resolved(this.fallbackResolver?.(providerId) ?? void 0);
		return resolved(void 0);
	}
	getOAuthProviders() {
		return getAuthStorageOAuthProviderRegistry(this).getAll();
	}
};
//#endregion
//#region src/agents/sessions/model-registry-runtime.ts
const modelRegistryRuntimes = /* @__PURE__ */ new WeakMap();
function resetApiRegistry(runtime) {
	runtime.apiRegistry.clearApiProviders();
	registerBuiltInApiProviders(runtime.apiRegistry);
}
/** Creates the runtime facts owned by one model-registry lifecycle. */
function initializeModelRegistryRuntime(owner) {
	const apiRegistry = createApiRegistry();
	const llmRuntime = {
		...createLlmRuntime(apiRegistry),
		runStream: runPluginStreamConsumer
	};
	const runtime = {
		apiRegistry,
		llmRuntime
	};
	bindStreamLlmRuntime(llmRuntime.streamSimple, llmRuntime);
	resetApiRegistry(runtime);
	modelRegistryRuntimes.set(owner, runtime);
}
/** Returns the prepared runtime facts for one model-registry lifecycle. */
function getModelRegistryRuntime(owner) {
	const runtime = modelRegistryRuntimes.get(owner);
	if (!runtime) throw new Error("Model registry runtime is not initialized");
	return runtime;
}
function resetModelRegistryRuntime(owner) {
	resetApiRegistry(getModelRegistryRuntime(owner));
}
//#endregion
//#region src/agents/sessions/model-registry-schema.ts
/** Validation for authored and generated model registry catalogs. */
const PercentileCutoffsSchema = Type.Object({
	p50: Type.Optional(Type.Number()),
	p75: Type.Optional(Type.Number()),
	p90: Type.Optional(Type.Number()),
	p99: Type.Optional(Type.Number())
});
const OpenRouterRoutingSchema = Type.Object({
	allow_fallbacks: Type.Optional(Type.Boolean()),
	require_parameters: Type.Optional(Type.Boolean()),
	data_collection: Type.Optional(Type.Union([Type.Literal("deny"), Type.Literal("allow")])),
	zdr: Type.Optional(Type.Boolean()),
	enforce_distillable_text: Type.Optional(Type.Boolean()),
	order: Type.Optional(Type.Array(Type.String())),
	only: Type.Optional(Type.Array(Type.String())),
	ignore: Type.Optional(Type.Array(Type.String())),
	quantizations: Type.Optional(Type.Array(Type.String())),
	sort: Type.Optional(Type.Union([Type.String(), Type.Object({
		by: Type.Optional(Type.String()),
		partition: Type.Optional(Type.Union([Type.String(), Type.Null()]))
	})])),
	max_price: Type.Optional(Type.Object({
		prompt: Type.Optional(Type.Union([Type.Number(), Type.String()])),
		completion: Type.Optional(Type.Union([Type.Number(), Type.String()])),
		image: Type.Optional(Type.Union([Type.Number(), Type.String()])),
		audio: Type.Optional(Type.Union([Type.Number(), Type.String()])),
		request: Type.Optional(Type.Union([Type.Number(), Type.String()]))
	})),
	preferred_min_throughput: Type.Optional(Type.Union([Type.Number(), PercentileCutoffsSchema])),
	preferred_max_latency: Type.Optional(Type.Union([Type.Number(), PercentileCutoffsSchema]))
});
const VercelGatewayRoutingSchema = Type.Object({
	only: Type.Optional(Type.Array(Type.String())),
	order: Type.Optional(Type.Array(Type.String()))
});
const ThinkingLevelMapValueSchema = Type.Union([Type.String(), Type.Null()]);
const ThinkingLevelMapSchema = Type.Object({
	off: Type.Optional(ThinkingLevelMapValueSchema),
	minimal: Type.Optional(ThinkingLevelMapValueSchema),
	low: Type.Optional(ThinkingLevelMapValueSchema),
	medium: Type.Optional(ThinkingLevelMapValueSchema),
	high: Type.Optional(ThinkingLevelMapValueSchema),
	xhigh: Type.Optional(ThinkingLevelMapValueSchema),
	max: Type.Optional(ThinkingLevelMapValueSchema)
});
const OpenAICompletionsCompatSchema = Type.Object({
	supportsStore: Type.Optional(Type.Boolean()),
	supportsDeveloperRole: Type.Optional(Type.Boolean()),
	supportsReasoningEffort: Type.Optional(Type.Boolean()),
	supportsUsageInStreaming: Type.Optional(Type.Boolean()),
	maxTokensField: Type.Optional(Type.Union([Type.Literal("max_completion_tokens"), Type.Literal("max_tokens")])),
	requiresToolResultName: Type.Optional(Type.Boolean()),
	requiresAssistantAfterToolResult: Type.Optional(Type.Boolean()),
	requiresThinkingAsText: Type.Optional(Type.Boolean()),
	requiresReasoningContentOnAssistantMessages: Type.Optional(Type.Boolean()),
	thinkingFormat: Type.Optional(Type.Union([
		Type.Literal("openai"),
		Type.Literal("openrouter"),
		Type.Literal("together"),
		Type.Literal("deepseek"),
		Type.Literal("zai"),
		Type.Literal("qwen"),
		Type.Literal("qwen-chat-template")
	])),
	cacheControlFormat: Type.Optional(Type.Literal("anthropic")),
	openRouterRouting: Type.Optional(OpenRouterRoutingSchema),
	vercelGatewayRouting: Type.Optional(VercelGatewayRoutingSchema),
	supportsStrictMode: Type.Optional(Type.Boolean()),
	supportsJsonSchemaResponseFormat: Type.Optional(Type.Boolean()),
	supportsLongCacheRetention: Type.Optional(Type.Boolean())
});
const OpenAIResponsesCompatSchema = Type.Object({
	supportsTemperature: Type.Optional(Type.Boolean()),
	supportsInstructions: Type.Optional(Type.Boolean()),
	sendSessionIdHeader: Type.Optional(Type.Boolean()),
	supportsLongCacheRetention: Type.Optional(Type.Boolean())
});
const AnthropicMessagesCompatSchema = Type.Object({
	supportsEagerToolInputStreaming: Type.Optional(Type.Boolean()),
	supportsLongCacheRetention: Type.Optional(Type.Boolean())
});
const ProviderCompatSchema = Type.Union([
	OpenAICompletionsCompatSchema,
	OpenAIResponsesCompatSchema,
	AnthropicMessagesCompatSchema
]);
const ProviderAuthModeSchema = Type.Union([
	Type.Literal("api-key"),
	Type.Literal("aws-sdk"),
	Type.Literal("oauth"),
	Type.Literal("token")
]);
const ModelDefinitionSchema = Type.Object({
	id: Type.String({ minLength: 1 }),
	name: Type.Optional(Type.String({ minLength: 1 })),
	api: Type.Optional(Type.String({ minLength: 1 })),
	baseUrl: Type.Optional(Type.String({ minLength: 1 })),
	reasoning: Type.Optional(Type.Boolean()),
	thinkingLevelMap: Type.Optional(ThinkingLevelMapSchema),
	input: Type.Optional(Type.Array(Type.Union([
		Type.Literal("text"),
		Type.Literal("image"),
		Type.Literal("audio"),
		Type.Literal("video")
	]))),
	cost: Type.Optional(Type.Object({
		input: Type.Number(),
		output: Type.Number(),
		cacheRead: Type.Number(),
		cacheWrite: Type.Number()
	})),
	contextWindow: Type.Optional(Type.Number()),
	contextTokens: Type.Optional(Type.Number()),
	maxTokens: Type.Optional(Type.Number()),
	params: Type.Optional(Type.Record(Type.String(), Type.Unknown())),
	headers: Type.Optional(Type.Record(Type.String(), Type.String())),
	compat: Type.Optional(ProviderCompatSchema)
});
const ProviderConfigSchema = Type.Object({
	name: Type.Optional(Type.String({ minLength: 1 })),
	baseUrl: Type.Optional(Type.String({ minLength: 1 })),
	apiKey: Type.Optional(Type.String({ minLength: 1 })),
	auth: Type.Optional(ProviderAuthModeSchema),
	api: Type.Optional(Type.String({ minLength: 1 })),
	headers: Type.Optional(Type.Record(Type.String(), Type.String())),
	compat: Type.Optional(ProviderCompatSchema),
	authHeader: Type.Optional(Type.Boolean()),
	models: Type.Optional(Type.Array(ModelDefinitionSchema))
});
const ModelsConfigSchema = Type.Object({
	generatedBy: Type.Optional(Type.String()),
	providers: Type.Record(Type.String(), ProviderConfigSchema)
});
const validateModelsConfig = Compile$1(ModelsConfigSchema);
function formatValidationPath(error) {
	const path = Pointer.Indices(error.instancePath).join(".").replace(/\//g, ".");
	if (error.keyword === "required") {
		const requiredProperty = error.params.requiredProperties[0];
		if (requiredProperty) return path ? `${path}.${requiredProperty}` : requiredProperty;
	}
	return path || "root";
}
//#endregion
//#region src/agents/sessions/provider-display-names.ts
/**
* Built-in provider labels shown in session/model UI when plugin metadata does
* not supply a display name.
*/
const BUILT_IN_PROVIDER_DISPLAY_NAMES = {
	anthropic: "Anthropic",
	"amazon-bedrock": "Amazon Bedrock",
	"azure-openai-responses": "Azure OpenAI Responses",
	cerebras: "Cerebras",
	"cloudflare-ai-gateway": "Cloudflare AI Gateway",
	"cloudflare-workers-ai": "Cloudflare Workers AI",
	deepseek: "DeepSeek",
	fireworks: "Fireworks",
	google: "Google Gemini",
	"google-vertex": "Google Vertex AI",
	groq: "Groq",
	huggingface: "Hugging Face",
	"kimi-coding": "Kimi For Coding",
	meta: "Meta",
	mistral: "Mistral",
	minimax: "MiniMax",
	"minimax-cn": "MiniMax (China)",
	moonshotai: "Moonshot AI",
	"moonshotai-cn": "Moonshot AI (China)",
	opencode: "OpenCode Zen",
	"opencode-go": "OpenCode Go",
	openai: "OpenAI",
	openrouter: "OpenRouter",
	together: "Together AI",
	"vercel-ai-gateway": "Vercel AI Gateway",
	xai: "xAI",
	zai: "ZAI",
	xiaomi: "Xiaomi MiMo",
	"xiaomi-token-plan-cn": "Xiaomi MiMo Token Plan (China)",
	"xiaomi-token-plan-ams": "Xiaomi MiMo Token Plan (Amsterdam)",
	"xiaomi-token-plan-sgp": "Xiaomi MiMo Token Plan (Singapore)"
};
//#endregion
//#region src/agents/sessions/model-registry.ts
const log = createSubsystemLogger("agents/model-registry");
function captureInventoryProvider(provider, source) {
	return {
		api: provider.api,
		baseUrl: provider.baseUrl,
		compat: provider.compat,
		models: provider.models?.map(({ headers: _headers, ...model }) => ({
			...model,
			api: model.api ?? provider.api,
			baseUrl: model.baseUrl ?? provider.baseUrl,
			maxTokensSource: source === "static" ? "discovered" : model.maxTokensSource,
			compat: source === "static" ? mergeCompat(provider.compat, model.compat) : model.compat
		}))
	};
}
function emptyCustomModelsResult(error) {
	return {
		providers: {},
		error
	};
}
function mergeCompat(baseCompat, overrideCompat) {
	if (!overrideCompat) return baseCompat;
	const baseCompletions = baseCompat;
	const overrideCompletions = overrideCompat;
	return {
		...baseCompat,
		...overrideCompat,
		...baseCompletions?.openRouterRouting || overrideCompletions.openRouterRouting ? { openRouterRouting: {
			...baseCompletions?.openRouterRouting,
			...overrideCompletions.openRouterRouting
		} } : {},
		...baseCompletions?.vercelGatewayRouting || overrideCompletions.vercelGatewayRouting ? { vercelGatewayRouting: {
			...baseCompletions?.vercelGatewayRouting,
			...overrideCompletions.vercelGatewayRouting
		} } : {}
	};
}
var ModelRegistry = class ModelRegistry {
	constructor(authStorage, modelsJsonPath, options = {}, publishedModels) {
		this.models = [];
		this.providerRequestConfigs = /* @__PURE__ */ new Map();
		this.modelRequestHeaders = /* @__PURE__ */ new Map();
		this.registeredProviders = /* @__PURE__ */ new Map();
		this.loadError = void 0;
		this.includePluginCatalogs = true;
		this.authStorage = authStorage;
		this.config = options.config ?? options.sourceSnapshot?.config;
		this.includePluginCatalogs = options.includePluginCatalogs !== false;
		initializeModelRegistryRuntime(this);
		if (options.sourceSnapshot) {
			const source = options.sourceSnapshot;
			const captured = source.baseCatalogSnapshot ?? source.captureCatalogSnapshot();
			const sourceSnapshot = publishedModels ? {
				...captured,
				models: [...captured.models.filter((model) => !publishedModels.has(model.provider)), ...[...publishedModels.values()].flat()]
			} : captured;
			this.sourceSnapshot = sourceSnapshot;
			this.baseCatalogSnapshot = sourceSnapshot;
			this.restoreSourceCatalog(sourceSnapshot);
			this.registeredProviders = new Map([...source.registeredProviders].map(([provider, config]) => [provider, { ...config }]));
			getAuthStorageOAuthProviderRegistry(authStorage).reset();
			for (const oauthProvider of sourceSnapshot.oauthProviders) getAuthStorageOAuthProviderRegistry(authStorage).register(oauthProvider);
			for (const [providerName, config] of this.registeredProviders.entries()) this.applyProviderConfig(providerName, config);
			return;
		}
		this.modelsJsonPath = modelsJsonPath;
		this.modelsJsonContents = options.modelsJsonContents;
		this.pluginCatalogs = options.pluginCatalogs;
		this.staticProviderConfigs = options.staticProviderConfigs;
		this.pluginMetadataSnapshot = resolveModelPluginMetadataSnapshot({
			config: this.config,
			...options.pluginMetadataSnapshot ? { pluginMetadataSnapshot: options.pluginMetadataSnapshot } : {},
			...options.workspaceDir ? { workspaceDir: options.workspaceDir } : {},
			allowWorkspaceScopedCurrent: true,
			useRuntimeConfig: true
		});
		this.loadModels();
		this.baseCatalogSnapshot = this.captureCatalogSnapshot();
	}
	captureCatalogSnapshot() {
		return {
			models: structuredClone(this.models),
			providerRequestConfigs: new Map([...this.providerRequestConfigs].map(([provider, config]) => [provider, { ...config }])),
			modelRequestHeaders: new Map([...this.modelRequestHeaders].map(([key, headers]) => [key, { ...headers }])),
			loadError: this.loadError,
			pluginMetadataSnapshot: this.pluginMetadataSnapshot,
			oauthProviders: [...this.authStorage.getOAuthProviders()]
		};
	}
	restoreSourceCatalog(source) {
		this.models = structuredClone(source.models);
		this.providerRequestConfigs = new Map([...source.providerRequestConfigs].map(([provider, config]) => [provider, { ...config }]));
		this.modelRequestHeaders = new Map([...source.modelRequestHeaders].map(([key, headers]) => [key, { ...headers }]));
		this.loadError = source.loadError;
		this.pluginMetadataSnapshot = source.pluginMetadataSnapshot;
	}
	static create(authStorage, modelsJsonPath = join(getAgentDir(), "models.json"), options = {}) {
		return new ModelRegistry(authStorage, modelsJsonPath, options);
	}
	static inMemory(authStorage) {
		return new ModelRegistry(authStorage, void 0);
	}
	/** Creates a request-isolated registry from this lifecycle-owned catalog snapshot. */
	fork(authStorage, publishedModels) {
		return new ModelRegistry(authStorage, void 0, { sourceSnapshot: this }, publishedModels);
	}
	/**
	* Reload models from disk (models.json).
	*/
	refresh() {
		this.providerRequestConfigs.clear();
		this.modelRequestHeaders.clear();
		this.loadError = void 0;
		resetModelRegistryRuntime(this);
		getAuthStorageOAuthProviderRegistry(this.authStorage).reset();
		if (this.sourceSnapshot) {
			this.restoreSourceCatalog(this.sourceSnapshot);
			for (const oauthProvider of this.sourceSnapshot.oauthProviders) getAuthStorageOAuthProviderRegistry(this.authStorage).register(oauthProvider);
		} else {
			this.loadModels();
			this.baseCatalogSnapshot = this.captureCatalogSnapshot();
		}
		for (const [providerName, config] of this.registeredProviders.entries()) this.applyProviderConfig(providerName, config);
	}
	/** Get any root or generated plugin catalog load error. */
	getError() {
		return this.loadError;
	}
	/** Returns the exact plugin metadata generation captured with this registry. */
	getProviderMetadataOwners() {
		return this.pluginMetadataSnapshot?.owners;
	}
	loadModels() {
		const replace = this.config?.models?.mode === "replace";
		const customResult = !replace && this.modelsJsonPath && this.modelsJsonContents !== null ? this.loadCustomModels(this.modelsJsonPath, {
			...this.modelsJsonContents !== void 0 ? { contents: this.modelsJsonContents } : {},
			includePluginCatalogs: this.includePluginCatalogs && this.pluginCatalogs === void 0
		}) : emptyCustomModelsResult();
		const capturedPluginResult = !replace && this.includePluginCatalogs && this.pluginCatalogs !== void 0 ? this.loadCapturedPluginCatalogs(this.pluginCatalogs) : emptyCustomModelsResult();
		const errors = [customResult.error, capturedPluginResult.error].filter((error) => Boolean(error));
		if (!replace && this.modelsJsonPath && this.modelsJsonContents === void 0 && this.pluginCatalogs === void 0 && this.staticProviderConfigs === void 0 && this.includePluginCatalogs) {
			const inspected = inspectLegacyPluginModelCatalogs(dirname(this.modelsJsonPath));
			const diagnostics = [...inspected.warnings, ...inspected.catalogs.map(({ pathname }) => `Legacy generated provider catalog: ${pathname}`)];
			if (diagnostics.length > 0) errors.push(`${diagnostics.join("\n")}\nRun openclaw doctor --fix to verify and import legacy provider catalogs.`);
		}
		if (errors.length > 0) {
			this.loadError = errors.join("\n\n");
			log.warn(`model catalog load issue: ${this.loadError}`);
		}
		const providers = this.mergeProviderSources(replace ? {} : Object.fromEntries(Object.entries(this.staticProviderConfigs ?? {}).map(([provider, config]) => [provider, captureInventoryProvider(config, "static")])), capturedPluginResult.providers, customResult.providers);
		const sourceFields = buildSourceModelFields(materializeConfiguredProviderCatalogModels(this.config && projectConfigOntoRuntimeSourceSnapshot(this.config).models?.providers, { manifestPlugins: this.pluginMetadataSnapshot }));
		for (const [providerId, configured] of Object.entries(normalizeProviderMapKeys(materializeConfiguredProviderCatalogModels(this.config?.models?.providers, { manifestPlugins: this.pluginMetadataSnapshot })))) {
			const inherited = providers[providerId];
			const accepted = new Map(inherited?.models?.map((model) => [model.id, model]));
			const current = {
				api: configured.api,
				baseUrl: configured.baseUrl,
				models: configured.models?.map((model) => ({
					...model,
					api: model.api ?? accepted.get(model.id)?.api ?? configured.api,
					baseUrl: model.baseUrl ?? accepted.get(model.id)?.baseUrl ?? configured.baseUrl,
					maxTokensSource: "configured",
					headers: sanitizeModelHeaders(model.headers)
				}))
			};
			providers[providerId] = inherited ? mergeProviderModels(captureInventoryProvider(inherited, "composed"), current, {
				providerId,
				modelIdMatching: "exact",
				sourceModelFields: sourceFields
			}) : current;
			this.providerRequestConfigs.delete(providerId);
			this.storeProviderRequestConfig(providerId, {
				apiKey: normalizeOptionalSecretInput(configured.apiKey),
				auth: configured.auth,
				authHeader: configured.authHeader,
				headers: sanitizeModelHeaders(configured.headers)
			});
		}
		let combined = this.parseModels(providers);
		for (const oauthProvider of this.authStorage.getOAuthProviders()) {
			const cred = this.authStorage.get(oauthProvider.id);
			if (cred?.type === "oauth" && oauthProvider.modifyModels) combined = oauthProvider.modifyModels(combined, cred);
		}
		this.models = combined;
	}
	mergeProviderSources(...sources) {
		const providers = {};
		for (const source of sources) for (const [providerId, provider] of Object.entries(source)) {
			const existing = providers[providerId];
			providers[providerId] = existing ? mergeProviderModels(existing, provider, {
				providerId,
				modelIdMatching: "exact"
			}) : provider;
		}
		return providers;
	}
	loadCapturedPluginCatalogs(pluginCatalogs) {
		let providers = {};
		const errors = [];
		for (const pluginCatalog of pluginCatalogs) {
			const result = this.loadCustomModels(`sqlite:plugin-model-catalog/${pluginCatalog.pluginId}`, {
				catalogPluginId: pluginCatalog.pluginId,
				contents: pluginCatalog.contents,
				includePluginCatalogs: false,
				requireGeneratedCatalog: true
			});
			providers = this.mergeProviderSources(providers, result.providers);
			if (result.error) errors.push(result.error);
		}
		return {
			providers,
			error: errors.join("\n\n") || void 0
		};
	}
	loadCustomModels(modelsJsonPath, options = { includePluginCatalogs: true }) {
		if (options.contents === void 0 && !existsSync(modelsJsonPath)) return emptyCustomModelsResult();
		try {
			const content = options.contents ?? readFileSync(modelsJsonPath, "utf-8");
			const parsed = parseModelCatalogJson(content);
			if (options.requireGeneratedCatalog === true && !isGeneratedPluginModelCatalog(parsed)) return emptyCustomModelsResult();
			if (!validateModelsConfig.Check(parsed)) return emptyCustomModelsResult(`Invalid models.json schema:\n${validateModelsConfig.Errors(parsed).map((error) => `  - ${formatValidationPath(error)}: ${error.message}`).join("\n") || "Unknown schema error"}\n\nFile: ${modelsJsonPath}`);
			const config = parsed;
			const providers = options.requireGeneratedCatalog === true ? filterGeneratedPluginModelCatalogProviders({
				catalogPluginId: options.catalogPluginId,
				config: this.config,
				isProviderAvailable: (providerId) => this.authStorage.hasAuth(normalizeProviderId(providerId)) || hasUsableCustomProviderApiKey(this.config, providerId),
				parsedCatalog: parsed,
				pluginMetadataSnapshot: this.pluginMetadataSnapshot,
				providers: config.providers
			}) : config.providers;
			const configForUse = {
				...config,
				providers
			};
			if (options.requireGeneratedCatalog === true && Object.keys(providers).length === 0) return emptyCustomModelsResult();
			this.validateConfig(configForUse);
			const generated = options.requireGeneratedCatalog === true;
			const maxTokensSource = generated ? "discovered" : "configured";
			let sourceProviders = {};
			for (const [providerName, providerConfig] of Object.entries(configForUse.providers)) {
				if (!generated && (providerConfig.models ?? []).length > 0) this.storeProviderRequestConfig(providerName, providerConfig);
				sourceProviders[providerName] = {
					...generated ? {
						api: providerConfig.api,
						baseUrl: providerConfig.baseUrl,
						compat: providerConfig.compat
					} : providerConfig,
					models: providerConfig.models?.map((model) => {
						const { headers: _headers, ...inventory } = model;
						return Object.assign(generated ? inventory : model, {
							maxTokensSource,
							api: model.api ?? providerConfig.api,
							baseUrl: model.baseUrl ?? providerConfig.baseUrl,
							compat: mergeCompat(providerConfig.compat, model.compat)
						});
					})
				};
			}
			const pluginCatalogErrors = [];
			if (options.includePluginCatalogs !== false) {
				let pluginCatalogs = [];
				try {
					pluginCatalogs = loadPersistedPluginModelCatalogsReadOnly(dirname(modelsJsonPath));
				} catch (error) {
					pluginCatalogErrors.push(`Failed to load generated plugin model catalogs: ${error instanceof Error ? error.message : String(error)}`);
				}
				const pluginResult = this.loadCapturedPluginCatalogs(pluginCatalogs);
				sourceProviders = this.mergeProviderSources(pluginResult.providers, sourceProviders);
				if (pluginResult.error) pluginCatalogErrors.push(`${pluginResult.error}\nRun openclaw doctor --fix to repair persisted generated provider catalogs.`);
			}
			return {
				providers: sourceProviders,
				error: pluginCatalogErrors.join("\n\n") || void 0
			};
		} catch (error) {
			if (error instanceof SyntaxError) {
				if (options.requireGeneratedCatalog === true) return emptyCustomModelsResult();
				return emptyCustomModelsResult(`Failed to parse models.json: ${error.message}\n\nFile: ${modelsJsonPath}`);
			}
			return emptyCustomModelsResult(`Failed to load models.json: ${error instanceof Error ? error.message : String(error)}\n\nFile: ${modelsJsonPath}`);
		}
	}
	validateConfig(config) {
		for (const [providerName, providerConfig] of Object.entries(config.providers)) {
			const hasProviderApi = Boolean(providerConfig.api);
			const models = providerConfig.models ?? [];
			if (models.length === 0) continue;
			if (!providerConfig.baseUrl) throw new Error(`Provider ${providerName}: "baseUrl" is required when defining custom models.`);
			for (const modelDef of models) {
				if (!hasProviderApi && !modelDef.api) throw new Error(`Provider ${providerName}, model ${modelDef.id}: no "api" specified. Set at provider or model level.`);
				if (modelDef.contextWindow !== void 0 && modelDef.contextWindow <= 0) throw new Error(`Provider ${providerName}, model ${modelDef.id}: invalid contextWindow`);
				if (modelDef.maxTokens !== void 0 && modelDef.maxTokens <= 0) throw new Error(`Provider ${providerName}, model ${modelDef.id}: invalid maxTokens`);
			}
		}
	}
	parseModels(providers) {
		const models = [];
		for (const [providerName, providerConfig] of Object.entries(providers)) for (const modelDef of providerConfig.models ?? []) {
			const api = modelDef.api ?? providerConfig.api;
			if (!api) continue;
			const baseUrl = modelDef.baseUrl ?? providerConfig.baseUrl;
			if (!baseUrl) continue;
			const runtimeInput = (modelDef.input ?? ["text"]).filter((input) => input === "text" || input === "image");
			if ((modelDef.input?.length ?? 0) > 0 && runtimeInput.length === 0) continue;
			this.storeModelHeaders(providerName, modelDef.id, modelDef.headers);
			models.push({
				id: modelDef.id,
				name: modelDef.name ?? modelDef.id,
				api,
				provider: providerName,
				baseUrl,
				reasoning: modelDef.reasoning ?? false,
				thinkingLevelMap: modelDef.thinkingLevelMap,
				input: runtimeInput,
				cost: normalizeResolvedPricing(modelDef.cost ?? {}),
				contextWindow: modelDef.contextWindow ?? Math.max(modelDef.contextTokens ?? 0, 128e3),
				...modelDef.contextWindow === void 0 ? { contextWindowSource: "synthetic" } : {},
				contextTokens: modelDef.contextTokens,
				contextWindows: modelDef.contextWindows,
				contextWindowDefault: modelDef.contextWindowDefault,
				maxTokens: modelDef.maxTokens ?? 16384,
				...modelDef.maxTokens !== void 0 ? { maxTokensSource: modelDef.maxTokensSource } : {},
				params: modelDef.params,
				headers: void 0,
				compat: modelDef.compat
			});
		}
		return models;
	}
	getAll() {
		return this.models;
	}
	/**
	* Get only models that have auth configured.
	* This is a fast check that doesn't refresh OAuth tokens.
	*/
	getAvailable() {
		return this.models.filter((m) => this.hasConfiguredAuth(m));
	}
	find(provider, modelId) {
		return this.models.find((m) => m.provider === provider && m.id === modelId);
	}
	hasConfiguredAuth(model) {
		const providerConfig = this.getModelProviderRequestConfig(model);
		return this.authStorage.hasAuth(model.provider) || providerConfig?.auth === "aws-sdk" || providerConfig?.apiKey !== void 0;
	}
	getModelProviderRequestConfig(model) {
		const config = this.providerRequestConfigs.get(model.provider);
		if (config?.baseUrls && !config.baseUrls.some((baseUrl) => modelTransportRoutesMatch({ baseUrl }, { baseUrl: model.baseUrl }))) return;
		return config;
	}
	getModelRequestKey(provider, modelId) {
		return JSON.stringify([provider, modelId]);
	}
	storeProviderRequestConfig(providerName, config) {
		if (!config.apiKey && !config.auth && !config.headers && !config.authHeader) return;
		this.providerRequestConfigs.set(providerName, {
			baseUrls: config.baseUrl ? [config.baseUrl, ...(config.models ?? []).flatMap((model) => model.baseUrl ?? [])] : void 0,
			apiKey: config.apiKey,
			auth: config.auth,
			headers: config.headers,
			authHeader: config.authHeader
		});
	}
	storeModelHeaders(providerName, modelId, headers) {
		const key = this.getModelRequestKey(providerName, modelId);
		if (!headers || Object.keys(headers).length === 0) {
			this.modelRequestHeaders.delete(key);
			return;
		}
		this.modelRequestHeaders.set(key, headers);
	}
	async getApiKeyAndHeaders(model) {
		try {
			const providerConfig = this.getModelProviderRequestConfig(model);
			const usesAwsSdkAuth = providerConfig?.auth === "aws-sdk";
			const apiKey = (usesAwsSdkAuth ? void 0 : await this.authStorage.getApiKey(model.provider, {
				includeFallback: false,
				baseUrl: model.baseUrl
			})) ?? (!usesAwsSdkAuth && providerConfig?.apiKey ? resolveConfigValueOrThrow(providerConfig.apiKey, `API key for provider "${model.provider}"`) : void 0);
			const providerHeaders = resolveHeadersOrThrow(providerConfig?.headers, `provider "${model.provider}"`);
			const modelHeaders = resolveHeadersOrThrow(this.modelRequestHeaders.get(this.getModelRequestKey(model.provider, model.id)), `model "${model.provider}/${model.id}"`);
			let headers = model.headers || providerHeaders || modelHeaders ? {
				...model.headers,
				...providerHeaders,
				...modelHeaders
			} : void 0;
			if (providerConfig?.authHeader) {
				if (!apiKey) return {
					ok: false,
					error: `No API key found for "${model.provider}"`
				};
				headers = {
					...headers,
					Authorization: `Bearer ${apiKey}`
				};
			}
			return {
				ok: true,
				apiKey,
				headers: headers && Object.keys(headers).length > 0 ? headers : void 0
			};
		} catch (error) {
			return {
				ok: false,
				error: error instanceof Error ? error.message : String(error)
			};
		}
	}
	/**
	* Return auth status for a provider, including request auth configured in models.json.
	* This intentionally does not execute command-backed config values.
	*/
	getProviderAuthStatus(provider) {
		const providerRequestConfig = this.providerRequestConfigs.get(provider);
		if (providerRequestConfig?.auth === "aws-sdk") return {
			configured: true,
			source: "models_json_key",
			label: providerRequestConfig.auth
		};
		const authStatus = this.authStorage.getAuthStatus(provider);
		if (authStatus.source) return authStatus;
		const providerApiKey = providerRequestConfig?.apiKey;
		if (!providerApiKey) return authStatus;
		if (providerApiKey.startsWith("!")) return {
			configured: true,
			source: "models_json_command"
		};
		if (process.env[providerApiKey]) return {
			configured: true,
			source: "environment",
			label: providerApiKey
		};
		return {
			configured: true,
			source: "models_json_key"
		};
	}
	getProviderDisplayName(provider) {
		const registeredProvider = this.registeredProviders.get(provider);
		const oauthProvider = this.authStorage.getOAuthProviders().find((p) => p.id === provider);
		return registeredProvider?.name ?? registeredProvider?.oauth?.name ?? oauthProvider?.name ?? BUILT_IN_PROVIDER_DISPLAY_NAMES[provider] ?? provider;
	}
	async getApiKeyForProvider(provider) {
		const apiKey = await this.authStorage.getApiKey(provider, { includeFallback: false });
		if (apiKey !== void 0) return apiKey;
		const providerApiKey = this.providerRequestConfigs.get(provider)?.apiKey;
		return providerApiKey ? resolveConfigValueUncached(providerApiKey) : void 0;
	}
	/**
	* Check if a model is using OAuth credentials (subscription).
	*/
	isUsingOAuth(model) {
		return this.authStorage.get(model.provider)?.type === "oauth";
	}
	/**
	* Register a provider dynamically (from extensions).
	*
	* If provider has models: replaces all existing models for this provider.
	* Provider-level request settings are stored for already-known models but
	* never create implicit model rows.
	* If provider has oauth: registers OAuth provider for /login support.
	*/
	registerProvider(providerName, config) {
		this.validateProviderConfig(providerName, config);
		this.applyProviderConfig(providerName, config);
		this.upsertRegisteredProvider(providerName, config);
	}
	/**
	* Unregister a previously registered provider.
	*
	* Removes the provider from the registry and reloads models from disk.
	* Also resets dynamic OAuth and API stream registrations before reapplying
	* remaining dynamic providers.
	* Has no effect if the provider was never registered.
	*/
	unregisterProvider(providerName) {
		if (!this.registeredProviders.has(providerName)) return;
		this.registeredProviders.delete(providerName);
		this.refresh();
	}
	/**
	* Upsert a provider config into registeredProviders.
	* If the provider is already registered, defined values in the incoming config
	* override existing ones; undefined values are preserved from the stored config.
	* If the provider is not registered, the incoming config is stored as-is.
	*/
	upsertRegisteredProvider(providerName, config) {
		const existing = this.registeredProviders.get(providerName);
		if (!existing) {
			this.registeredProviders.set(providerName, config);
			return;
		}
		for (const k of Object.keys(config)) if (config[k] !== void 0) existing[k] = config[k];
	}
	validateProviderConfig(providerName, config) {
		if (config.streamSimple && !config.api) throw new Error(`Provider ${providerName}: "api" is required when registering streamSimple.`);
		if (!config.models || config.models.length === 0) return;
		if (!config.baseUrl) throw new Error(`Provider ${providerName}: "baseUrl" is required when defining models.`);
		for (const modelDef of config.models) if (!(modelDef.api || config.api)) throw new Error(`Provider ${providerName}, model ${modelDef.id}: no "api" specified.`);
	}
	applyProviderConfig(providerName, config) {
		if (config.oauth) {
			const oauthProvider = {
				...config.oauth,
				id: providerName
			};
			getAuthStorageOAuthProviderRegistry(this.authStorage).register(oauthProvider);
		}
		if (config.streamSimple) {
			const streamSimple = config.streamSimple;
			getModelRegistryRuntime(this).apiRegistry.registerApiProvider({
				api: config.api,
				stream: (model, context, options) => streamSimple(model, context, options),
				streamSimple
			}, `provider:${providerName}`);
		}
		this.storeProviderRequestConfig(providerName, config);
		if (config.models && config.models.length > 0) {
			this.models = this.models.filter((m) => m.provider !== providerName);
			for (const modelDef of config.models) {
				const api = modelDef.api || config.api;
				this.storeModelHeaders(providerName, modelDef.id, modelDef.headers);
				this.models.push({
					id: modelDef.id,
					name: modelDef.name,
					api,
					provider: providerName,
					baseUrl: modelDef.baseUrl ?? config.baseUrl,
					reasoning: modelDef.reasoning,
					thinkingLevelMap: modelDef.thinkingLevelMap,
					input: modelDef.input,
					cost: modelDef.cost,
					contextWindow: modelDef.contextWindow ?? Math.max(modelDef.contextTokens ?? 0, 128e3),
					...modelDef.contextWindow === void 0 ? { contextWindowSource: "synthetic" } : {},
					contextTokens: modelDef.contextTokens,
					contextWindows: modelDef.contextWindows,
					contextWindowDefault: modelDef.contextWindowDefault,
					maxTokens: modelDef.maxTokens,
					params: modelDef.params,
					headers: void 0,
					compat: modelDef.compat
				});
			}
			if (config.oauth?.modifyModels) {
				const cred = this.authStorage.get(providerName);
				if (cred?.type === "oauth") this.models = config.oauth.modifyModels(this.models, cred);
			}
		}
	}
};
//#endregion
export { FILE_AUTH_STORAGE_BACKEND_DEPRECATION_CODE as a, resolveModelPluginMetadataSnapshot as c, AuthStorage as i, resolveModelWorkspaceDir as l, getModelRegistryRuntime as n, FileAuthStorageBackend as o, AUTH_STORAGE_CREATE_DEPRECATION_CODE as r, InMemoryAuthStorageBackend as s, ModelRegistry as t };