UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

1,988 lines 79.2 kB
import { a as normalizeLowercaseStringOrEmpty, c as normalizeOptionalString, f as normalizeStringifiedOptionalString } from "./string-coerce-mnp54Vah.js";
import { _ as parseStrictFiniteNumber, b as parseStrictPositiveInteger } from "./number-coercion-CJQ8TR--.js";
import "./parse-finite-number-Z7n6tXLk.js";
import { t as formatDocsLink } from "./links-CsLBrRff.js";
import { r as theme } from "./theme-vjDs9tao.js";
import { n as defaultRuntime, r as writeRuntimeJson } from "./runtime-B4lgFmsS.js";
import { i as resolveAgentModelPrimaryValue } from "./model-input-B2zxl6MM.js";
import "./agent-scope-MrLta7Pq.js";
import { a as resolveAgentDir, c as resolveDefaultAgentId } from "./agent-scope-config-CgCYpZfK.js";
import { i as isLoopbackHost } from "./net-DTe7AQiu.js";
import { i as getRuntimeConfig } from "./io-Gi7-pyU-.js";
import { t as getProviderEnvVars } from "./provider-env-vars-Clp1DREb.js";
import { r as DEFAULT_PROVIDER } from "./defaults-mDjiWzE5.js";
import { p as normalizeThinkLevel } from "./thinking-OG7pb4lf.js";
import { s as getRuntimeConfigSourceSnapshot, v as setRuntimeConfigSnapshot } from "./runtime-snapshot-D93_HOsR.js";
import "./config-C9RxTsn1.js";
import { i as GATEWAY_CLIENT_NAMES, r as GATEWAY_CLIENT_MODES } from "./client-info-CcqJJIan.js";
import { h as randomIdempotencyKey, o as callGateway } from "./call-B5-GYOlf.js";
import { t as buildGatewayConnectionDetailsWithResolvers } from "./connection-details-ClaW_IO-.js";
import { t as ADMIN_SCOPE } from "./operator-scopes-CS3xdS-V.js";
import { c as loadAuthProfileStoreForRuntime, m as updateAuthProfileStoreWithLock } from "./store-C8spD0DG.js";
import { D as registerMemoryEmbeddingProvider, T as listMemoryEmbeddingProviders } from "./registry-CYsBNH12.js";
import { n as detectMime, r as extensionForMime, u as normalizeMimeType } from "./mime-C8mVE2Bw.js";
import "./auth-profiles-84rzaGag.js";
import { n as listProfilesForProvider } from "./profile-list-DI8_o0Rw.js";
import { n as canonicalizeCaseOnlyCatalogModelRef } from "./model-selection-CA_65iXi.js";
import { n as loadModelCatalog } from "./model-catalog-0-HKr2yW.js";
import { u as saveMediaBuffer } from "./store-C9M_0A1p.js";
import { a as convertHeicToJpeg, s as getImageMetadata } from "./image-ops-PfTOpg74.js";
import { d as resolveApiKeyForProvider } from "./model-auth-DVfmdW0b.js";
import "./media-services-DE3J1qM-.js";
import { n as listRuntimeImageGenerationProviders, t as generateImage } from "./runtime-Bml72KeV.js";
import { t as buildExplicitSessionIdSessionKey } from "./session-CKACMDmr.js";
import { u as buildMediaUnderstandingRegistry } from "./defaults.constants-A49pIMjc.js";
import { a as getCapabilityWebSearchCommandSecretTargets, c as getMemoryEmbeddingCommandSecretTargetIds, l as getModelsCommandSecretTargetIds, m as getTtsCommandSecretTargetIds, r as getCapabilityWebFetchCommandSecretTargets } from "./command-secret-targets-DY8QOUoE.js";
import { r as prepareSimpleCompletionModelForAgent, t as completeWithPreparedSimpleCompletionModel } from "./simple-completion-runtime-BBvPbT7Q.js";
import { o as listSpeechProviders, r as canonicalizeSpeechProviderId } from "./directives-BaSUaNZN.js";
import { S as setTtsEnabled, T as setTtsProvider, _ as resolveTtsPrefsPath, d as listSpeechVoices, f as listTtsPersonas, g as resolveTtsConfig, k as textToSpeech, m as resolveExplicitTtsOverrides, o as getTtsPersona, s as getTtsProvider, w as setTtsPersona } from "./tts-runtime-CMRhdRNr.js";
import "./tts-Dt79-aAB.js";
import { n as listRuntimeVideoGenerationProviders, t as generateVideo } from "./runtime-CKbBHWpE.js";
import { a as runWebSearch, r as listWebSearchProviders, t as isWebSearchProviderConfigured } from "./runtime-B83FaW0j.js";
import { n as listEmbeddingProviders } from "./embedding-provider-runtime-gzo2-uRP.js";
import { t as resolveMemorySearchConfig } from "./memory-search-CTXTHmj4.js";
import { i as resolveWebFetchDefinition, r as listWebFetchProviders, t as isWebFetchProviderConfigured } from "./runtime-Co7CqClV.js";
import { n as runCommandWithRuntime } from "./cli-utils-Cuzzfy1Q.js";
import { t as removeCommandByName } from "./command-tree-CA1ToIBK.js";
import { t as collectOption } from "./helpers-gBVG4H2O.js";
import { n as describeImageFileWithModel, o as transcribeAudioFile, r as describeVideoFile, t as describeImageFile } from "./runtime-sq-ErJqP.js";
import { o as registerBuiltInMemoryEmbeddingProviders, t as createEmbeddingProvider } from "./memory-core-bundled-runtime-Cw9x8ySm.js";
import { t as resolveCommandConfigWithSecrets } from "./command-config-resolution-Ce9AmmGx.js";
import { n as parseTimeoutMsWithFallback } from "./parse-timeout-DZMZaF9B.js";
import { createWriteStream } from "node:fs";
import path from "node:path";
import fs$1 from "node:fs/promises";
import { randomUUID } from "node:crypto";
import { pipeline } from "node:stream/promises";
import { Readable } from "node:stream";
//#region src/cli/capability-cli.ts
const IMAGE_OUTPUT_FORMATS = [
	"png",
	"jpeg",
	"webp"
];
const IMAGE_BACKGROUNDS = [
	"transparent",
	"opaque",
	"auto"
];
const LOCAL_MODEL_RUN_SYSTEM_PROMPT = "You are a personal assistant running inside OpenClaw.";
const HEIC_MODEL_RUN_MIMES = new Set(["image/heic", "image/heif"]);
const CAPABILITY_METADATA = [
	{
		id: "model.run",
		description: "Run a one-shot inference turn through the selected model provider.",
		transports: ["local", "gateway"],
		flags: [
			"--prompt",
			"--file",
			"--model",
			"--local",
			"--gateway",
			"--json"
		],
		resultShape: "normalized payloads plus provider/model attribution"
	},
	{
		id: "model.list",
		description: "List known models from the model catalog.",
		transports: ["local"],
		flags: ["--json"],
		resultShape: "catalog entries"
	},
	{
		id: "model.inspect",
		description: "Inspect one model catalog entry.",
		transports: ["local"],
		flags: ["--model", "--json"],
		resultShape: "single catalog entry"
	},
	{
		id: "model.providers",
		description: "List model providers discovered from the catalog.",
		transports: ["local"],
		flags: ["--json"],
		resultShape: "provider ids with counts and defaults"
	},
	{
		id: "model.auth.login",
		description: "Run the existing provider auth login flow.",
		transports: ["local"],
		flags: ["--provider"],
		resultShape: "interactive auth result"
	},
	{
		id: "model.auth.logout",
		description: "Remove saved auth profiles for one provider.",
		transports: ["local"],
		flags: ["--provider", "--json"],
		resultShape: "removed profile ids"
	},
	{
		id: "model.auth.status",
		description: "Show configured model auth state.",
		transports: ["local"],
		flags: ["--json"],
		resultShape: "model status summary"
	},
	{
		id: "image.generate",
		description: "Generate raster images with configured image providers.",
		transports: ["local"],
		flags: [
			"--prompt",
			"--model",
			"--count",
			"--size",
			"--aspect-ratio",
			"--resolution",
			"--output",
			"--json"
		],
		resultShape: "saved image files plus attempts"
	},
	{
		id: "image.edit",
		description: "Generate edited images from one or more input files.",
		transports: ["local"],
		flags: [
			"--file",
			"--prompt",
			"--model",
			"--size",
			"--aspect-ratio",
			"--resolution",
			"--output-format",
			"--background",
			"--openai-background",
			"--timeout-ms",
			"--output",
			"--json"
		],
		resultShape: "saved image files plus attempts"
	},
	{
		id: "image.describe",
		description: "Describe one image file through media-understanding providers.",
		transports: ["local"],
		flags: [
			"--file",
			"--prompt",
			"--model",
			"--timeout-ms",
			"--json"
		],
		resultShape: "normalized text output"
	},
	{
		id: "image.describe-many",
		description: "Describe multiple image files independently.",
		transports: ["local"],
		flags: [
			"--file",
			"--prompt",
			"--model",
			"--timeout-ms",
			"--json"
		],
		resultShape: "one text output per file"
	},
	{
		id: "image.providers",
		description: "List image generation providers.",
		transports: ["local"],
		flags: ["--json"],
		resultShape: "provider ids and defaults"
	},
	{
		id: "audio.transcribe",
		description: "Transcribe one audio file.",
		transports: ["local"],
		flags: [
			"--file",
			"--model",
			"--json"
		],
		resultShape: "normalized text output"
	},
	{
		id: "audio.providers",
		description: "List audio transcription providers.",
		transports: ["local"],
		flags: ["--json"],
		resultShape: "provider ids and capabilities"
	},
	{
		id: "tts.convert",
		description: "Convert text to speech.",
		transports: ["local", "gateway"],
		flags: [
			"--text",
			"--channel",
			"--voice",
			"--model",
			"--output",
			"--local",
			"--gateway",
			"--json"
		],
		resultShape: "saved audio file plus attempts"
	},
	{
		id: "tts.voices",
		description: "List voices for a speech provider.",
		transports: ["local"],
		flags: ["--provider", "--json"],
		resultShape: "voice entries"
	},
	{
		id: "tts.providers",
		description: "List speech providers.",
		transports: ["local", "gateway"],
		flags: [
			"--local",
			"--gateway",
			"--json"
		],
		resultShape: "provider ids, configured state, models, voices"
	},
	{
		id: "tts.personas",
		description: "List TTS personas.",
		transports: ["local", "gateway"],
		flags: [
			"--local",
			"--gateway",
			"--json"
		],
		resultShape: "persona ids, labels, providers, active persona"
	},
	{
		id: "tts.status",
		description: "Show gateway-managed TTS state.",
		transports: ["gateway"],
		flags: ["--gateway", "--json"],
		resultShape: "enabled/provider state"
	},
	{
		id: "tts.enable",
		description: "Enable TTS in prefs.",
		transports: ["local", "gateway"],
		flags: [
			"--local",
			"--gateway",
			"--json"
		],
		resultShape: "enabled state"
	},
	{
		id: "tts.disable",
		description: "Disable TTS in prefs.",
		transports: ["local", "gateway"],
		flags: [
			"--local",
			"--gateway",
			"--json"
		],
		resultShape: "enabled state"
	},
	{
		id: "tts.set-provider",
		description: "Set the active TTS provider.",
		transports: ["local", "gateway"],
		flags: [
			"--provider",
			"--local",
			"--gateway",
			"--json"
		],
		resultShape: "selected provider"
	},
	{
		id: "tts.set-persona",
		description: "Set the active TTS persona.",
		transports: ["local", "gateway"],
		flags: [
			"--persona",
			"--off",
			"--local",
			"--gateway",
			"--json"
		],
		resultShape: "selected persona"
	},
	{
		id: "video.generate",
		description: "Generate video files with configured video providers.",
		transports: ["local"],
		flags: [
			"--prompt",
			"--model",
			"--size",
			"--aspect-ratio",
			"--resolution",
			"--duration",
			"--audio",
			"--watermark",
			"--timeout-ms",
			"--output",
			"--json"
		],
		resultShape: "saved video files plus attempts"
	},
	{
		id: "video.describe",
		description: "Describe one video file through media-understanding providers.",
		transports: ["local"],
		flags: [
			"--file",
			"--model",
			"--json"
		],
		resultShape: "normalized text output"
	},
	{
		id: "video.providers",
		description: "List video generation and description providers.",
		transports: ["local"],
		flags: ["--json"],
		resultShape: "provider ids and defaults"
	},
	{
		id: "web.search",
		description: "Run provider-backed web search.",
		transports: ["local"],
		flags: [
			"--query",
			"--provider",
			"--limit",
			"--json"
		],
		resultShape: "search provider result"
	},
	{
		id: "web.fetch",
		description: "Fetch URL content through configured web fetch providers.",
		transports: ["local"],
		flags: [
			"--url",
			"--provider",
			"--format",
			"--json"
		],
		resultShape: "fetch provider result"
	},
	{
		id: "web.providers",
		description: "List web search and fetch providers.",
		transports: ["local"],
		flags: ["--json"],
		resultShape: "provider ids grouped by family"
	},
	{
		id: "embedding.create",
		description: "Create embeddings through embedding providers.",
		transports: ["local"],
		flags: [
			"--text",
			"--provider",
			"--model",
			"--json"
		],
		resultShape: "vectors with provider/model attribution"
	},
	{
		id: "embedding.providers",
		description: "List embedding providers.",
		transports: ["local"],
		flags: ["--json"],
		resultShape: "provider ids and default models"
	}
];
function findCapabilityMetadata(id) {
	return CAPABILITY_METADATA.find((entry) => entry.id === id);
}
function resolveTransport(opts) {
	if (opts.local && opts.gateway) throw new Error("Pass only one of --local or --gateway.");
	if (opts.local) {
		if (!opts.supported.includes("local")) throw new Error("This command does not support --local.");
		return "local";
	}
	if (opts.gateway) {
		if (!opts.supported.includes("gateway")) throw new Error("This command does not support --gateway.");
		return "gateway";
	}
	return opts.defaultTransport;
}
function emitJsonOrText(runtime, json, value, textFormatter) {
	if (json) {
		writeRuntimeJson(runtime, value);
		return;
	}
	runtime.log(textFormatter(value));
}
function formatEnvelopeForText(value) {
	const envelope = value;
	if (!envelope.ok) return `${envelope.capability} failed: ${envelope.error ?? "unknown error"}`;
	const lines = [
		`${envelope.capability} via ${envelope.transport}`,
		...envelope.provider ? [`provider: ${envelope.provider}`] : [],
		...envelope.model ? [`model: ${envelope.model}`] : [],
		...envelope.ignoredOverrides && envelope.ignoredOverrides.length > 0 ? [`ignoredOverrides: ${JSON.stringify(envelope.ignoredOverrides)}`] : [],
		`outputs: ${String(envelope.outputs.length)}`
	];
	for (const output of envelope.outputs) {
		const pathValue = typeof output.path === "string" ? output.path : void 0;
		const textValue = typeof output.text === "string" ? output.text : void 0;
		if (pathValue) lines.push(pathValue);
		else if (textValue) lines.push(textValue);
		else lines.push(JSON.stringify(output));
	}
	return lines.join("\n");
}
function providerSummaryText(value) {
	return value.map((entry) => JSON.stringify(entry)).join("\n");
}
function hasOwnKeys(value) {
	return Boolean(value && typeof value === "object" && Object.keys(value).length > 0);
}
function resolveSelectedProviderFromModelRef(modelRef) {
	return resolveModelRefOverride(modelRef).provider;
}
function getAuthProfileIdsForProvider(cfg, providerId) {
	return listProfilesForProvider(loadAuthProfileStoreForRuntime(resolveAgentDir(cfg, resolveDefaultAgentId(cfg))), providerId);
}
function providerHasGenericConfig(params) {
	const modelsProviders = params.cfg.models?.providers ?? {};
	const pluginEntries = params.cfg.plugins?.entries ?? {};
	const ttsProviders = params.cfg.messages?.tts?.providers ?? {};
	const envConfigured = (params.envVars ?? []).some((envVar) => Boolean(process.env[envVar]?.trim()));
	return getAuthProfileIdsForProvider(params.cfg, params.providerId).length > 0 || hasOwnKeys(modelsProviders[params.providerId]) || hasOwnKeys(pluginEntries[params.providerId]?.config) || hasOwnKeys(ttsProviders[params.providerId]) || envConfigured;
}
async function writeOutputAsset(params) {
	if (!params.outputPath) {
		const saved = await saveMediaBuffer(params.buffer, params.mimeType, params.subdir, Number.MAX_SAFE_INTEGER, params.originalFilename);
		return {
			path: saved.path,
			mimeType: saved.contentType,
			size: saved.size
		};
	}
	const resolvedOutput = path.resolve(params.outputPath);
	const parsed = path.parse(resolvedOutput);
	const detectedMime = await detectMime({
		buffer: params.buffer,
		headerMime: params.mimeType
	}) ?? params.mimeType;
	const requestedMime = normalizeMimeType(await detectMime({ filePath: resolvedOutput }));
	const detectedNormalized = normalizeMimeType(detectedMime);
	const canonicalDetectedExt = extensionForMime(detectedNormalized);
	const fallbackExt = parsed.ext || path.extname(params.originalFilename ?? "") || "";
	const ext = parsed.ext && requestedMime === detectedNormalized ? parsed.ext : canonicalDetectedExt ?? fallbackExt;
	const filePath = params.outputCount <= 1 ? path.join(parsed.dir, `${parsed.name}${ext}`) : path.join(parsed.dir, `${parsed.name}-${String(params.outputIndex + 1)}${ext}`);
	await fs$1.mkdir(path.dirname(filePath), { recursive: true });
	await fs$1.writeFile(filePath, params.buffer);
	return {
		path: filePath,
		mimeType: detectedNormalized ?? params.mimeType,
		size: params.buffer.byteLength
	};
}
async function readInputFiles(files) {
	return await Promise.all(files.map(async (filePath) => ({
		path: path.resolve(filePath),
		buffer: await fs$1.readFile(path.resolve(filePath))
	})));
}
function resolveModelRefOverride(raw) {
	const trimmed = raw?.trim();
	if (!trimmed) return {};
	const slash = trimmed.indexOf("/");
	if (slash <= 0 || slash === trimmed.length - 1) return { model: trimmed };
	return {
		provider: trimmed.slice(0, slash),
		model: trimmed.slice(slash + 1)
	};
}
async function canonicalizeModelRunRef(params) {
	return await canonicalizeCaseOnlyCatalogModelRef({
		cfg: params.cfg,
		raw: params.raw,
		defaultProvider: DEFAULT_PROVIDER,
		loadCatalog: () => loadModelCatalog({
			config: params.cfg,
			readOnly: true
		}),
		preserveAuthProfile: params.preserveAuthProfile
	});
}
function requireProviderModelOverride(raw) {
	const resolved = resolveModelRefOverride(raw);
	if (!raw?.trim()) return;
	if (!resolved.provider || !resolved.model) throw new Error("Model overrides must use the form <provider/model>.");
	return {
		provider: resolved.provider,
		model: resolved.model
	};
}
function collectModelRunText(content) {
	return content.map((block) => block.type === "text" && typeof block.text === "string" ? block.text : "").join("").trim();
}
function requireModelRunPrompt(value) {
	if (typeof value !== "string" || normalizeOptionalString(value) === void 0) throw new Error("--prompt cannot be empty or whitespace-only.");
	return value;
}
async function readModelRunImageFiles(files) {
	if (!files || files.length === 0) return [];
	return await Promise.all(files.map(async (filePath) => {
		const resolvedPath = path.resolve(filePath);
		const buffer = await fs$1.readFile(resolvedPath);
		const mimeType = normalizeMimeType(await detectMime({
			buffer,
			filePath: resolvedPath
		}));
		if (!mimeType?.startsWith("image/")) throw new Error(`Unsupported --file for model run: ${resolvedPath}. Only image files are supported; use infer audio transcribe for audio files.`);
		if (HEIC_MODEL_RUN_MIMES.has(mimeType)) {
			const converted = await convertHeicToJpeg(buffer);
			return {
				path: resolvedPath,
				fileName: path.basename(resolvedPath),
				mimeType: "image/jpeg",
				data: converted.toString("base64")
			};
		}
		return {
			path: resolvedPath,
			fileName: path.basename(resolvedPath),
			mimeType,
			data: buffer.toString("base64")
		};
	}));
}
function normalizeModelRunThinking(value) {
	if (value === void 0) return;
	if (typeof value !== "string") throw new Error("--thinking must be a string.");
	const normalized = normalizeThinkLevel(value);
	if (!normalized) throw new Error("Invalid thinking level. Use one of: off, minimal, low, medium, high, adaptive, xhigh, max.");
	return normalized;
}
async function runModelRun(params) {
	const cfg = params.transport === "local" ? await resolveLocalCapabilityRuntimeConfig({
		commandName: "infer model run",
		targetIds: getModelsCommandSecretTargetIds()
	}) : getRuntimeConfig();
	const agentId = resolveDefaultAgentId(cfg);
	const modelRef = await canonicalizeModelRunRef({
		raw: params.model,
		cfg,
		preserveAuthProfile: params.transport === "local"
	});
	const explicitModelOverride = resolveModelRefOverride(params.model);
	const hasExplicitProviderModelOverride = Boolean(params.model?.trim() && explicitModelOverride.provider && explicitModelOverride.model);
	const imageFiles = await readModelRunImageFiles(params.files);
	const messageContent = imageFiles.length > 0 ? [{
		type: "text",
		text: params.prompt
	}, ...imageFiles.map((image) => ({
		type: "image",
		data: image.data,
		mimeType: image.mimeType
	}))] : params.prompt;
	if (params.transport === "local") {
		const prepared = await prepareSimpleCompletionModelForAgent({
			cfg,
			agentId,
			modelRef,
			allowMissingApiKeyModes: ["aws-sdk"],
			...hasExplicitProviderModelOverride ? { allowBundledStaticCatalogFallback: true } : {},
			skipAgentDiscovery: true
		});
		if ("error" in prepared) throw new Error(prepared.error);
		if (prepared.selection.provider === "codex") throw new Error("The codex provider is served by the Codex app-server agent runtime, not the local simple-completion transport. Use an openai/<model> ref with provider/model agentRuntime.id: \"codex\", run through the gateway, or use /codex commands.");
		const localModelRunSystemPrompt = prepared.model.api === "openai-chatgpt-responses" ? LOCAL_MODEL_RUN_SYSTEM_PROMPT : void 0;
		const result = await completeWithPreparedSimpleCompletionModel({
			model: prepared.model,
			auth: prepared.auth,
			cfg,
			context: {
				...localModelRunSystemPrompt ? { systemPrompt: localModelRunSystemPrompt } : {},
				messages: [{
					role: "user",
					content: messageContent,
					timestamp: Date.now()
				}]
			},
			options: {
				maxTokens: typeof prepared.model.maxTokens === "number" && Number.isFinite(prepared.model.maxTokens) ? prepared.model.maxTokens : void 0,
				...params.thinking ? { reasoning: params.thinking } : {}
			}
		});
		const text = collectModelRunText(result.content);
		if (!text) {
			const providerErrorMessage = result.errorMessage;
			const detail = typeof providerErrorMessage === "string" && providerErrorMessage.trim() ? `: ${providerErrorMessage.trim()}` : "";
			throw new Error(`No text output returned for provider "${prepared.selection.provider}" model "${prepared.selection.modelId}"${detail}.`);
		}
		return {
			ok: true,
			capability: "model.run",
			transport: "local",
			provider: prepared.selection.provider,
			model: prepared.selection.modelId,
			attempts: [],
			...imageFiles.length > 0 ? { inputs: imageFiles.map((image) => ({
				path: image.path,
				mimeType: image.mimeType
			})) } : {},
			outputs: [{
				text,
				mediaUrl: null
			}]
		};
	}
	const { provider, model } = resolveModelRefOverride(modelRef);
	const hasModelOverride = Boolean(provider || model);
	const sessionId = `model-run-${randomUUID()}`;
	const response = await callGateway({
		method: "agent",
		params: {
			agentId,
			sessionId,
			sessionKey: buildExplicitSessionIdSessionKey({
				agentId,
				sessionId
			}),
			message: params.prompt,
			attachments: imageFiles.length > 0 ? imageFiles.map((image) => ({
				type: "image",
				fileName: image.fileName,
				mimeType: image.mimeType,
				content: image.data
			})) : void 0,
			provider,
			model,
			...params.thinking ? { thinking: params.thinking } : {},
			modelRun: true,
			promptMode: "none",
			cleanupBundleMcpOnRunEnd: true,
			idempotencyKey: randomIdempotencyKey()
		},
		expectFinal: true,
		timeoutMs: 12e4,
		clientName: hasModelOverride ? GATEWAY_CLIENT_NAMES.GATEWAY_CLIENT : GATEWAY_CLIENT_NAMES.CLI,
		mode: hasModelOverride ? GATEWAY_CLIENT_MODES.BACKEND : GATEWAY_CLIENT_MODES.CLI,
		...hasModelOverride ? { scopes: [ADMIN_SCOPE] } : {}
	});
	return {
		ok: true,
		capability: "model.run",
		transport: "gateway",
		provider: response?.result?.meta?.agentMeta?.provider,
		model: response?.result?.meta?.agentMeta?.model,
		attempts: response?.result?.meta?.agentMeta?.fallbackAttempts ?? [],
		outputs: (response?.result?.payloads ?? []).map((payload) => ({
			text: payload.text,
			mediaUrl: payload.mediaUrl,
			mediaUrls: payload.mediaUrls
		})),
		...imageFiles.length > 0 ? { inputs: imageFiles.map((image) => ({
			path: image.path,
			mimeType: image.mimeType
		})) } : {}
	};
}
async function buildModelProviders() {
	const cfg = getRuntimeConfig();
	const catalog = await loadModelCatalog({ config: cfg });
	const selectedProvider = resolveSelectedProviderFromModelRef(resolveAgentModelPrimaryValue(cfg.agents?.defaults?.model));
	const grouped = /* @__PURE__ */ new Map();
	for (const entry of catalog) {
		const current = grouped.get(entry.provider) ?? {
			provider: entry.provider,
			count: 0,
			defaults: [],
			available: true,
			configured: providerHasGenericConfig({
				cfg,
				providerId: entry.provider
			}),
			selected: selectedProvider === entry.provider
		};
		current.count += 1;
		if (current.defaults.length < 3) current.defaults.push(entry.id);
		grouped.set(entry.provider, current);
	}
	return [...grouped.values()].toSorted((a, b) => a.provider.localeCompare(b.provider));
}
async function runModelAuthStatus() {
	const captured = [];
	const { modelsStatusCommand } = await import("./list.status-command-CGLwk2D-.js");
	await modelsStatusCommand({ json: true }, {
		log: (...args) => captured.push(args.join(" ")),
		error: (message) => {
			throw message instanceof Error ? message : new Error(String(message));
		},
		exit: (code) => {
			throw new Error(`exit ${code}`);
		}
	});
	const raw = captured.find((line) => line.trim().startsWith("{"));
	return raw ? JSON.parse(raw) : {};
}
async function runModelAuthLogout(provider, agent) {
	const cfg = getRuntimeConfig();
	const agentDir = resolveAgentDir(cfg, agent?.trim() || resolveDefaultAgentId(cfg));
	const profileIds = listProfilesForProvider(loadAuthProfileStoreForRuntime(agentDir), provider);
	if (!await updateAuthProfileStoreWithLock({
		agentDir,
		updater: (nextStore) => {
			let changed = false;
			for (const profileId of profileIds) {
				if (nextStore.profiles[profileId]) {
					delete nextStore.profiles[profileId];
					changed = true;
				}
				if (nextStore.usageStats?.[profileId]) {
					delete nextStore.usageStats[profileId];
					changed = true;
				}
			}
			if (nextStore.order?.[provider]) {
				delete nextStore.order[provider];
				changed = true;
			}
			if (nextStore.lastGood?.[provider]) {
				delete nextStore.lastGood[provider];
				changed = true;
			}
			return changed;
		}
	})) throw new Error(`Failed to remove saved auth profiles for provider ${provider}.`);
	return {
		provider,
		removedProfiles: profileIds
	};
}
async function runImageGenerate(params) {
	const cfg = await resolveLocalCapabilityRuntimeConfig({
		commandName: `infer ${params.capability}`,
		targetIds: getModelsCommandSecretTargetIds()
	});
	const agentDir = resolveAgentDir(cfg, resolveDefaultAgentId(cfg));
	const inputImages = params.file && params.file.length > 0 ? await Promise.all((await readInputFiles(params.file)).map(async (entry) => ({
		buffer: entry.buffer,
		fileName: path.basename(entry.path),
		mimeType: await detectMime({
			buffer: entry.buffer,
			filePath: entry.path
		}) ?? "image/png"
	}))) : void 0;
	const result = await generateImage({
		cfg,
		agentDir,
		prompt: params.prompt,
		modelOverride: params.model,
		count: params.count,
		size: params.size,
		aspectRatio: params.aspectRatio,
		resolution: params.resolution,
		outputFormat: params.outputFormat,
		background: params.background,
		providerOptions: params.openaiBackground ? { openai: { background: params.openaiBackground } } : void 0,
		timeoutMs: params.timeoutMs,
		inputImages
	});
	const outputs = await Promise.all(result.images.map(async (image, index) => {
		const written = await writeOutputAsset({
			buffer: image.buffer,
			mimeType: image.mimeType,
			originalFilename: image.fileName,
			outputPath: params.output,
			outputIndex: index,
			outputCount: result.images.length,
			subdir: "generated"
		});
		const metadata = await getImageMetadata(image.buffer).catch(() => void 0);
		return {
			...written,
			width: metadata?.width,
			height: metadata?.height,
			revisedPrompt: image.revisedPrompt
		};
	}));
	return {
		ok: true,
		capability: params.capability,
		transport: "local",
		provider: result.provider,
		model: result.model,
		attempts: result.attempts,
		outputs,
		ignoredOverrides: result.ignoredOverrides
	};
}
async function runImageDescribe(params) {
	const cfg = await resolveLocalCapabilityRuntimeConfig({
		commandName: `infer ${params.capability}`,
		targetIds: getModelsCommandSecretTargetIds()
	});
	const agentDir = resolveAgentDir(cfg, resolveDefaultAgentId(cfg));
	const activeModel = requireProviderModelOverride(params.model);
	const prompt = normalizeOptionalString(params.prompt);
	const outputs = await Promise.all(params.files.map(async (filePath) => {
		const resolvedPath = resolveImageDescribeInput(filePath);
		const isRemoteUrl = /^https?:\/\//i.test(resolvedPath);
		const result = activeModel ? await describeImageFileWithModel({
			filePath: resolvedPath,
			...isRemoteUrl ? { mediaUrl: resolvedPath } : {},
			cfg,
			agentDir,
			provider: activeModel.provider,
			model: activeModel.model,
			prompt: prompt ?? "Describe the image.",
			timeoutMs: params.timeoutMs
		}) : await describeImageFile({
			filePath: resolvedPath,
			...isRemoteUrl ? { mediaUrl: resolvedPath } : {},
			cfg,
			agentDir,
			prompt,
			timeoutMs: params.timeoutMs
		});
		if (!result.text) {
			if (isMissingMediaUnderstandingProvider(result)) throw new Error("No image understanding provider is configured or ready. Configure tools.media.image.models or agents.defaults.imageModel.primary, or pass --model <provider/model> after configuring that provider's auth/API key.");
			throw new Error(`No description returned for image: ${resolvedPath}`);
		}
		return {
			path: resolvedPath,
			text: result.text,
			provider: activeModel?.provider ?? ("provider" in result ? result.provider : void 0),
			model: result.model,
			kind: "image.description"
		};
	}));
	return {
		ok: true,
		capability: params.capability,
		transport: "local",
		provider: outputs[0]?.provider,
		model: outputs[0]?.model,
		attempts: [],
		outputs
	};
}
function isMissingMediaUnderstandingProvider(result) {
	const decision = result.decision;
	return decision?.outcome === "skipped" && decision.attachments.length > 0 && decision.attachments.every((attachment) => attachment.attempts.length === 0);
}
async function runAudioTranscribe(params) {
	const cfg = await resolveLocalCapabilityRuntimeConfig({
		commandName: "infer audio transcribe",
		targetIds: getModelsCommandSecretTargetIds()
	});
	const activeModel = requireProviderModelOverride(params.model);
	const result = await transcribeAudioFile({
		filePath: path.resolve(params.file),
		cfg,
		language: params.language,
		activeModel,
		prompt: params.prompt
	});
	if (!result.text) {
		if (isMissingMediaUnderstandingProvider(result)) throw new Error("No audio transcription provider is configured or ready. Configure tools.media.audio.models, or pass --model <provider/model> after configuring that provider's auth/API key.");
		throw new Error(`No transcript returned for audio: ${path.resolve(params.file)}`);
	}
	return {
		ok: true,
		capability: "audio.transcribe",
		transport: "local",
		attempts: [],
		outputs: [{
			path: path.resolve(params.file),
			text: result.text,
			kind: "audio.transcription"
		}]
	};
}
function parseOptionalFiniteNumber(raw, label) {
	if (raw === void 0 || typeof raw === "string" && raw.trim() === "") return;
	const value = parseStrictFiniteNumber(raw);
	if (value === void 0) throw new Error(`${label} must be a finite number`);
	return value;
}
function parseOptionalPositiveInteger(raw, label) {
	if (raw === void 0 || typeof raw === "string" && raw.trim() === "") return;
	const value = parseStrictPositiveInteger(raw);
	if (value === void 0) throw new Error(`${label} must be a positive integer`);
	return value;
}
function parseOptionalTimeoutMs(raw) {
	if (raw === void 0 || typeof raw === "string" && raw.trim() === "") return;
	return parseTimeoutMsWithFallback(raw, 0, { invalidType: "error" });
}
function normalizeImageOutputFormat(raw) {
	const normalized = normalizeLowercaseStringOrEmpty(raw);
	if (!normalized) return;
	if (IMAGE_OUTPUT_FORMATS.includes(normalized)) return normalized;
	throw new Error("--output-format must be one of png, jpeg, or webp");
}
function normalizeImageBackground(raw, label = "--background") {
	const normalized = normalizeLowercaseStringOrEmpty(raw);
	if (!normalized) return;
	if (IMAGE_BACKGROUNDS.includes(normalized)) return normalized;
	throw new Error(`${label} must be one of transparent, opaque, or auto`);
}
function normalizeVideoResolution(raw) {
	const normalized = raw?.trim().toUpperCase();
	if (!normalized) return;
	if (normalized === "360P" || normalized === "480P" || normalized === "540P" || normalized === "720P" || normalized === "768P" || normalized === "1080P") return normalized;
	throw new Error("video resolution must be one of 360P, 480P, 540P, 720P, 768P, or 1080P");
}
async function runVideoGenerate(params) {
	const cfg = await resolveLocalCapabilityRuntimeConfig({
		commandName: "infer video.generate",
		targetIds: getModelsCommandSecretTargetIds()
	});
	const result = await generateVideo({
		cfg,
		agentDir: resolveAgentDir(cfg, resolveDefaultAgentId(cfg)),
		prompt: params.prompt,
		modelOverride: params.model,
		size: params.size,
		aspectRatio: params.aspectRatio,
		resolution: params.resolution,
		durationSeconds: params.durationSeconds,
		audio: params.audio,
		watermark: params.watermark,
		timeoutMs: params.timeoutMs
	});
	const outputs = await Promise.all(result.videos.map(async (video, index) => {
		if (!video.buffer && !video.url) throw new Error(`Video asset at index ${index} has neither buffer nor url`);
		let videoBuffer = video.buffer;
		if (!videoBuffer && video.url) {
			const response = await fetch(video.url, { signal: AbortSignal.timeout(12e4) });
			if (!response.ok) throw new Error(`Failed to download video from ${video.url}: ${response.status}`);
			if (params.output && response.body) {
				const ext = extensionForMime(normalizeMimeType(video.mimeType)) || path.extname(video.fileName ?? "") || path.extname(params.output ?? "");
				const resolvedOutput = path.resolve(params.output);
				const parsed = path.parse(resolvedOutput);
				const filePath = result.videos.length <= 1 ? path.join(parsed.dir, `${parsed.name}${ext}`) : path.join(parsed.dir, `${parsed.name}-${String(index + 1)}${ext}`);
				await fs$1.mkdir(path.dirname(filePath), { recursive: true });
				await pipeline(Readable.fromWeb(response.body), createWriteStream(filePath));
				const stat = await fs$1.stat(filePath);
				return {
					path: filePath,
					mimeType: video.mimeType,
					size: stat.size
				};
			}
			videoBuffer = Buffer.from(await response.arrayBuffer());
		}
		return { ...await writeOutputAsset({
			buffer: videoBuffer,
			mimeType: video.mimeType,
			originalFilename: video.fileName,
			outputPath: params.output,
			outputIndex: index,
			outputCount: result.videos.length,
			subdir: "generated"
		}) };
	}));
	return {
		ok: true,
		capability: "video.generate",
		transport: "local",
		provider: result.provider,
		model: result.model,
		attempts: result.attempts,
		outputs
	};
}
async function runVideoDescribe(params) {
	const cfg = await resolveLocalCapabilityRuntimeConfig({
		commandName: "infer video.describe",
		targetIds: getModelsCommandSecretTargetIds()
	});
	const activeModel = requireProviderModelOverride(params.model);
	const result = await describeVideoFile({
		filePath: path.resolve(params.file),
		cfg,
		activeModel
	});
	if (!result.text) throw new Error(`No description returned for video: ${path.resolve(params.file)}`);
	return {
		ok: true,
		capability: "video.describe",
		transport: "local",
		provider: result.provider,
		model: result.model,
		attempts: [],
		outputs: [{
			path: path.resolve(params.file),
			text: result.text,
			kind: "video.description"
		}]
	};
}
async function runTtsConvert(params) {
	if (params.transport === "gateway") {
		const gatewayConnection = buildGatewayConnectionDetailsWithResolvers({ config: getRuntimeConfig() });
		const result = await callGateway({
			method: "tts.convert",
			params: {
				text: params.text,
				channel: params.channel,
				provider: normalizeOptionalString(params.provider),
				modelId: params.modelId,
				voiceId: params.voiceId
			},
			timeoutMs: 12e4
		});
		let outputPath = result.audioPath;
		if (params.output && result.audioPath) {
			const gatewayHost = new URL(gatewayConnection.url).hostname;
			if (!isLoopbackHost(gatewayHost)) throw new Error(`--output is not supported for remote gateway TTS yet (gateway target: ${gatewayConnection.url}).`);
			const target = path.resolve(params.output);
			await fs$1.mkdir(path.dirname(target), { recursive: true });
			await fs$1.copyFile(result.audioPath, target);
			outputPath = target;
		}
		return {
			ok: true,
			capability: "tts.convert",
			transport: "gateway",
			provider: result.provider,
			attempts: [],
			outputs: [{
				path: outputPath,
				format: result.outputFormat,
				voiceCompatible: result.voiceCompatible
			}]
		};
	}
	const cfg = await resolveLocalCapabilityRuntimeConfig({
		commandName: "infer tts convert",
		targetIds: getTtsCommandSecretTargetIds()
	});
	const effectiveCfg = await injectTtsAuthProfileApiKey({
		cfg,
		provider: resolveTtsProviderForAuthHydration({
			cfg,
			provider: params.provider,
			modelId: params.modelId,
			channelId: params.channel
		}),
		channelId: params.channel
	});
	if (effectiveCfg !== cfg) pinRuntimeConfigSnapshot(effectiveCfg);
	const overrides = resolveExplicitTtsOverrides({
		cfg: effectiveCfg,
		provider: params.provider,
		modelId: params.modelId,
		voiceId: params.voiceId,
		channelId: params.channel
	});
	const hasExplicitSelection = Boolean(overrides.provider || normalizeOptionalString(params.modelId) || normalizeOptionalString(params.voiceId));
	const result = await textToSpeech({
		text: params.text,
		cfg: effectiveCfg,
		channel: params.channel,
		overrides,
		disableFallback: hasExplicitSelection
	});
	if (!result.success || !result.audioPath) throw new Error(result.error ?? "TTS conversion failed");
	let outputPath = result.audioPath;
	if (params.output) {
		const target = path.resolve(params.output);
		await fs$1.mkdir(path.dirname(target), { recursive: true });
		await fs$1.copyFile(result.audioPath, target);
		outputPath = target;
	}
	return {
		ok: true,
		capability: "tts.convert",
		transport: "local",
		provider: result.provider,
		attempts: result.attempts ?? [],
		outputs: [{
			path: outputPath,
			format: result.outputFormat,
			voiceCompatible: result.voiceCompatible
		}]
	};
}
function resolveTtsProviderForAuthHydration(params) {
	const explicitProvider = params.provider ?? resolveSelectedProviderFromModelRef(normalizeOptionalString(params.modelId));
	if (explicitProvider) return explicitProvider;
	const ttsConfig = resolveTtsConfig(params.cfg, { channelId: params.channelId });
	return getTtsProvider(ttsConfig, resolveTtsPrefsPath(ttsConfig));
}
async function injectTtsAuthProfileApiKey(params) {
	if (!params.provider) return params.cfg;
	const providerId = canonicalizeSpeechProviderId(params.provider, params.cfg) ?? normalizeLowercaseStringOrEmpty(params.provider);
	if (!providerId) return params.cfg;
	if (resolvedTtsConfigHasProviderApiKey(resolveTtsConfig(params.cfg, { channelId: params.channelId }), providerId)) return params.cfg;
	const existingProviderConfig = resolveExistingTtsProviderConfig({
		cfg: params.cfg,
		providerId,
		channelId: params.channelId
	});
	if (ttsProviderConfigHasApiKey(existingProviderConfig?.value)) return params.cfg;
	const auth = await resolveApiKeyForProvider({
		provider: providerId,
		cfg: params.cfg,
		credentialPrecedence: "profile-first"
	}).catch(() => void 0);
	if (!auth?.apiKey || auth.mode !== "api-key") return params.cfg;
	if (existingProviderConfig?.scope === "channel") {
		const channels = { ...params.cfg.channels };
		const channel = channels[existingProviderConfig.channelKey];
		if (!isObjectRecord(channel)) return params.cfg;
		const nextChannel = {
			...channel,
			tts: buildTtsConfigWithHydratedProvider({
				tts: channel.tts,
				existingProviderConfig,
				providerId,
				apiKey: auth.apiKey
			})
		};
		return {
			...params.cfg,
			channels: {
				...channels,
				[existingProviderConfig.channelKey]: nextChannel
			}
		};
	}
	const messages = { ...params.cfg.messages };
	const nextTts = buildTtsConfigWithHydratedProvider({
		tts: messages.tts,
		existingProviderConfig,
		providerId,
		apiKey: auth.apiKey
	});
	return {
		...params.cfg,
		messages: {
			...messages,
			tts: nextTts
		}
	};
}
function resolveExistingTtsProviderConfig(params) {
	const channelTts = resolveChannelTtsConfigForAuthHydration(params);
	if (channelTts) {
		const channelProviderConfig = resolveExistingTtsProviderConfigInTts({
			cfg: params.cfg,
			tts: channelTts.tts,
			providerId: params.providerId
		});
		if (channelProviderConfig) return {
			...channelProviderConfig,
			scope: "channel",
			channelKey: channelTts.channelKey
		};
	}
	const rootProviderConfig = resolveExistingTtsProviderConfigInTts({
		cfg: params.cfg,
		tts: params.cfg.messages?.tts,
		providerId: params.providerId
	});
	return rootProviderConfig ? {
		...rootProviderConfig,
		scope: "root"
	} : void 0;
}
function resolveExistingTtsProviderConfigInTts(params) {
	if (!isObjectRecord(params.tts)) return;
	const providers = isObjectRecord(params.tts.providers) ? params.tts.providers : void 0;
	if (!providers) return resolveDirectTtsProviderConfig(params);
	const exact = providers[params.providerId];
	if (exact !== void 0) return {
		container: "providers",
		key: params.providerId,
		value: exact
	};
	for (const [key, value] of Object.entries(providers)) if (normalizeLowercaseStringOrEmpty(canonicalizeSpeechProviderId(key, params.cfg) ?? key) === params.providerId) return {
		container: "providers",
		key,
		value
	};
	return resolveDirectTtsProviderConfig(params);
}
const TTS_CONFIG_RESERVED_KEYS = new Set([
	"auto",
	"enabled",
	"maxTextLength",
	"mode",
	"modelOverrides",
	"persona",
	"personas",
	"prefsPath",
	"provider",
	"providers",
	"summaryModel",
	"timeoutMs"
]);
function resolveDirectTtsProviderConfig(params) {
	if (!isObjectRecord(params.tts)) return;
	for (const [key, value] of Object.entries(params.tts)) {
		if (TTS_CONFIG_RESERVED_KEYS.has(key)) continue;
		if (normalizeLowercaseStringOrEmpty(canonicalizeSpeechProviderId(key, params.cfg) ?? key) === params.providerId) return {
			container: "direct",
			key,
			value
		};
	}
}
function resolveChannelTtsConfigForAuthHydration(params) {
	const channels = params.cfg.channels;
	const normalizedChannelId = normalizeOptionalString(params.channelId);
	if (!isObjectRecord(channels) || !normalizedChannelId) return;
	const channelKey = Object.hasOwn(channels, normalizedChannelId) ? normalizedChannelId : Object.keys(channels).find((candidate) => normalizeLowercaseStringOrEmpty(candidate) === normalizeLowercaseStringOrEmpty(normalizedChannelId));
	const channel = channelKey ? channels[channelKey] : void 0;
	if (!channelKey || !isObjectRecord(channel)) return;
	return {
		channelKey,
		tts: channel.tts
	};
}
function buildTtsConfigWithHydratedProvider(params) {
	const tts = isObjectRecord(params.tts) ? { ...params.tts } : {};
	const providers = isObjectRecord(tts.providers) ? { ...tts.providers } : {};
	const providerConfigKey = params.existingProviderConfig?.key ?? params.providerId;
	const nextProviderConfig = {
		...isObjectRecord(params.existingProviderConfig?.value) ? params.existingProviderConfig.value : {},
		apiKey: params.apiKey
	};
	if (params.existingProviderConfig?.container === "direct") tts[providerConfigKey] = nextProviderConfig;
	else {
		providers[providerConfigKey] = nextProviderConfig;
		tts.providers = providers;
	}
	return tts;
}
function isObjectRecord(value) {
	return Boolean(value && typeof value === "object" && !Array.isArray(value));
}
function ttsProviderConfigHasApiKey(value) {
	return isObjectRecord(value) && "apiKey" in value;
}
function resolvedTtsConfigHasProviderApiKey(config, providerId) {
	if (!isObjectRecord(config) || !isObjectRecord(config.providerConfigs)) return false;
	return ttsProviderConfigHasApiKey(config.providerConfigs[providerId]);
}
async function runTtsProviders(transport) {
	const cfg = getRuntimeConfig();
	if (transport === "gateway") {
		const payload = await callGateway({
			method: "tts.providers",
			timeoutMs: 3e4
		});
		return {
			...payload,
			providers: (payload.providers ?? []).map((provider) => {
				const id = typeof provider.id === "string" ? provider.id : "";
				return Object.assign({
					available: true,
					configured: typeof provider.configured === `boolean` ? provider.configured : providerHasGenericConfig({
						cfg,
						providerId: id
					}),
					selected: Boolean(id && payload.active === id)
				}, provider);
			})
		};
	}
	const config = resolveTtsConfig(cfg);
	const active = getTtsProvider(config, resolveTtsPrefsPath(config));
	return {
		providers: listSpeechProviders(cfg).map((provider) => ({
			available: true,
			configured: active === provider.id || providerHasGenericConfig({
				cfg,
				providerId: provider.id
			}),
			selected: active === provider.id,
			id: provider.id,
			name: provider.label,
			models: [...provider.models ?? []],
			voices: [...provider.voices ?? []]
		})),
		active
	};
}
function resolveImageDescribeInput(filePath) {
	const trimmed = filePath.trim();
	return /^https?:\/\//i.test(trimmed) ? trimmed : path.resolve(filePath);
}
async function runTtsPersonas(transport) {
	if (transport === "gateway") return await callGateway({
		method: "tts.personas",
		timeoutMs: 3e4
	});
	const config = resolveTtsConfig(getRuntimeConfig());
	return {
		active: getTtsPersona(config, resolveTtsPrefsPath(config))?.id ?? null,
		personas: listTtsPersonas(config).map((persona) => ({
			id: persona.id,
			label: persona.label,
			description: persona.description,
			provider: persona.provider,
			fallbackPolicy: persona.fallbackPolicy,
			providers: Object.keys(persona.providers ?? {})
		}))
	};
}
async function runTtsVoices(providerRaw) {
	const cfg = await resolveLocalCapabilityRuntimeConfig({
		commandName: "infer tts voices",
		targetIds: getTtsCommandSecretTargetIds()
	});
	const config = resolveTtsConfig(cfg);
	const prefsPath = resolveTtsPrefsPath(config);
	return await listSpeechVoices({
		provider: normalizeOptionalString(providerRaw) || getTtsProvider(config, prefsPath),
		cfg,
		config
	});
}
async function runTtsStateMutation(params) {
	if (params.transport === "gateway") return await callGateway({
		method: params.capability === "tts.enable" ? "tts.enable" : params.capability === "tts.disable" ? "tts.disable" : params.capability === "tts.set-provider" ? "tts.setProvider" : "tts.setPersona",
		params: params.capability === "tts.set-provider" ? { provider: params.provider } : params.capability === "tts.set-persona" ? { persona: params.persona ?? "off" } : void 0,
		timeoutMs: 3e4
	});
	const cfg = getRuntimeConfig();
	const config = resolveTtsConfig(cfg);
	const prefsPath = resolveTtsPrefsPath(config);
	if (params.capability === "tts.enable") {
		setTtsEnabled(prefsPath, true);
		return { enabled: true };
	}
	if (params.capability === "tts.disable") {
		setTtsEnabled(prefsPath, false);
		return { enabled: false };
	}
	if (params.capability === "tts.set-persona") {
		if (!params.persona) {
			setTtsPersona(prefsPath, null);
			return { persona: null };
		}
		const persona = listTtsPersonas(config).find((entry) => entry.id === normalizeLowercaseStringOrEmpty(params.persona ?? ""));
		if (!persona) throw new Error(`Unknown TTS persona: ${params.persona}`);
		setTtsPersona(prefsPath, persona.id);
		return { persona: persona.id };
	}
	if (!params.provider) throw new Error("--provider is required");
	const provider = canonicalizeSpeechProviderId(params.provider, cfg);
	if (!provider) throw new Error(`Unknown speech provider: ${params.provider}`);
	setTtsProvider(prefsPath, provider);
	return { provider };
}
async function resolveLocalCapabilityRuntimeConfig(params) {
	const { effectiveConfig } = await resolveCommandConfigWithSecrets({
		config: params.config ?? getRuntimeConfig(),
		commandName: params.commandName,
		targetIds: params.targetIds,
		...params.allowedPaths ? { allowedPaths: params.allowedPaths } : {},
		...params.forcedActivePaths ? { forcedActivePaths: params.forcedActivePaths } : {},
		...params.optionalActivePaths ? { optionalActivePaths: params.optionalActivePaths } : {},
		runtime: defaultRuntime,
		autoEnable: true
	});
	pinRuntimeConfigSnapshot(effectiveConfig);
	return effectiveConfig;
}
function pinRuntimeConfigSnapshot(config) {
	const sourceConfig = getRuntimeConfigSourceSnapshot();
	if (sourceConfig) setRuntimeConfigSnapshot(config, sourceConfig);
	else setRuntimeConfigSnapshot(config);
}
async function runWebSearchCommand(params) {
	const rawConfig = getRuntimeConfig();
	const scopedTargets = getCapabilityWebSearchCommandSecretTargets(rawConfig, { providerId: params.provider });
	const result = await runWebSearch({
		config: await resolveLocalCapabilityRuntimeConfig({
			commandName: "infer web search",
			targetIds: scopedTargets.targetIds,
			...scopedTargets.allowedPaths ? { allowedPaths: scopedTargets.allowedPaths } : {},
			...scopedTargets.forcedActivePaths ? { forcedActivePaths: scopedTargets.forcedActivePaths } : {},
			...scopedTargets.optionalActivePaths ? { optionalActivePaths: scopedTargets.optionalActivePaths } : {},
			config: rawConfig
		}),
		providerId: params.provider,
		args: {
			query: params.query,
			count: params.limit,
			limit: params.limit
		}
	});
	return {
		ok: true,
		capability: "web.search",
		transport: "local",
		provider: result.provider,
		attempts: [],
		outputs: [{ result: result.result }]
	};
}
async function runWebFetchCommand(params) {
	const rawConfig = getRuntimeConfig();
	const scopedTargets = getCapabilityWebFetchCommandSecretTargets(rawConfig, { providerId: params.provider });
	const resolved = resolveWebFetchDefinition({
		config: await resolveLocalCapabilityRuntimeConfig({
			commandName: "infer web fetch",
			targetIds: scopedTargets.targetIds,
			...scopedTargets.allowedPaths ? { allowedPaths: scopedTargets.allowedPaths } : {},
			...scopedTargets.forcedActivePaths ? { forcedActivePaths: scopedTargets.forcedActivePaths } : {},
			...scopedTargets.optionalActivePaths ? { optionalActivePaths: scopedTargets.optionalActivePaths } : {},
			config: rawConfig
		}),
		providerId: params.provider
	});
	if (!resolved) throw new Error("web.fetch is disabled or no provider is available.");
	const result = await resolved.definition.execute({
		url: params.url,
		format: params.format
	});
	return {
		ok: true,
		capability: "web.fetch",
		transport: "local",
		provider: resolved.provider.id,
		attempts: [],
		outputs: [{ result }]
	};
}
async function runMemoryEmbeddingCreate(params) {
	ensureMemoryEmbeddingProvidersRegistered();
	const cfg = await resolveLocalCapabilityRuntimeConfig({
		commandName: "infer embedding create",
		targetIds: getMemoryEmbeddingCommandSecretTargetIds()
	});
	const modelRef = resolveModelRefOverride(params.model);
	const requestedProvider = normalizeOptionalString(params.provider) || modelRef.provider || "auto";
	const result = await createEmbeddingProvider({
		config: cfg,
		agentDir: resolveAgentDir(cfg, resolveDefaultAgentId(cfg)),
		provider: requestedProvider,
		fallback: "none",
		model: modelRef.model ?? ""
	});
	if (!result.provider) throw new Error(result.providerUnavailableReason ?? "No embedding provider available.");
	const embeddings = await result.provider.embedBatch(params.texts);
	return {
		ok: true,
		capability: "embedding.create",
		transport: "local",
		provider: result.provider.id,
		model: result.provider.model,
		attempts: result.fallbackFrom ? [{
			provider: result.fallbackFrom,
			outcome: "failed",
			error: result.fallbackReason
		}] : [],
		outputs: embeddings.map((embedding, index) => ({
			text: params.texts[index],
			embedding,
			dimensions: embedding.length
		}))
	};
}
function ensureMemoryEmbeddingProvidersRegistered() {
	if (listMemoryEmbeddingProviders().length > 0) return;
	registerBuiltInMemoryEmbeddingProviders({ registerMemoryEmbeddingProvider });
}
function registerCapabilityListAndInspect(capability) {
	capability.command("list").description("List canonical capability ids and supported transports").option("--json", "Output JSON", false).action(async (opts) => {
		await runCommandWithRuntime(defaultRuntime, async () => {
			const result = CAPABILITY_METADATA.map((entry) => ({
				id: entry.id,
				transports: entry.transports,
				description: entry.description
			}));
			emitJsonOrText(defaultRuntime, Boolean(opts.json), result, providerSummaryText);
		});
	});
	capability.command("inspect").description("Inspect one canonical capability id").requiredOption("--name <capability>", "Capability id").option("--json", "Output JSON", false).action(async (opts) => {
		await runCommandWithRuntime(defaultRuntime, async () => {
			const entry = findCapabilityMetadata(String(opts.name));
			if (!entry) throw new Error(`Unknown capability: ${String(opts.name)}`);
			emitJsonOrText(defaultRuntime, Boolean(opts.json), entry, (value) => JSON.stringify(value, null, 2));
		});
	});
}
function registerCapabilityCli(program) {
	removeCommandByName(program, "infer");
	removeCommandByName(program, "capability");
	const capability = program.command("infer").alias("capability").description("Run provider-backed inference commands through a stable CLI surface").addHelpText("after", () => `\n${theme.muted("Docs:")} ${formatDocsLink("/cli/infer", "docs.openclaw.ai/cli/infer")}\n`);
	registerCapabilityListAndInspect(capability);
	const model = capability.command("model").description("Text inference and model catalog commands");
	model.command("run").description("Run a one-shot model turn").requiredOption("--prompt <text>", "Prompt text").option("--file <path>", "Image file", collectOption, []).option("--model <provider/model>", "Model override").option("--thinking <level>", "Thinking level override").option("--local", "Force local execution", false).option("--gateway", "Force gateway execution", false).option("--json", "Output JSON", false).action(async (opts) => {
		await runCommandWithRuntime(defaultRuntime, async () => {
			const prompt = requireModelRunPrompt(opts.prompt);
			const thinking = normalizeModelRunThinking(opts.thinking);
			const transport = resolveTransport({
				local: Boolean(opts.local),
				gateway: Boolean(opts.gateway),
				supported: ["local", "gateway"],
				defaultTransport: "local"
			});
			const result = await runModelRun({
				prompt,
				files: opts.file,
				model: opts.model,
				thinking,
				transport
			});
			emitJsonOrText(defaultRuntime, Boolean(opts.json), result, formatEnvelopeForText);
		});
	});
	model.command("list").description("List known models").option("--json", "Output JSON", false).action(async (opts) => {
		await runCommandWithRuntime(defaultRuntime, async () => {
			const result = await loadModelCatalog({ config: getRuntimeConfig() });
			emitJsonOrText(defaultRuntime, Boolean(opts.json), result, providerSummaryText);
		});
	});
	model.command("inspect").description("Inspect one model catalog entry").requiredOption("--model <provider/model>", "Model id").option("--json", "Output JSON", false).action(async (opts) => {
		await runCommandWithRuntime(defaultRuntime, async () => {
			const target = normalizeStringifiedOptionalString(opts.model) ?? "";
			const catalog = await loadModelCatalog({ config: getRuntimeConfig() });
			const entry = catalog.find((candidate) => `${candidate.provider}/${candidate.id}` === target) ?? catalog.find((candidate) => candidate.id === target);
			if (!entry) throw new Error(`Model not found: ${target}`);
			emitJsonOrText(defaultRuntime, Boolean(opts.json), entry, (value) => JSON.stringify(value, null, 2));
		});
	});
	model.command("providers").description("List model providers from the catalog").option("--json", "Output JSON", false).action(async (opts) => {
		await runCommandWithRuntime(defaultRuntime, async () => {
			const result = await buildModelProviders();
			emitJsonOrText(defaultRuntime, Boolean(opts.json), result, providerSummaryText);
		});
	});
	const modelAuth = model.command("auth").description("Provider auth helpers");
	modelAuth.command("login").description("Run provider auth login").requiredOption("--provider <id>", "Provider id").option("--method <id>", "Provider auth method id").action(async (opts) => {
		await runCommandWithRuntime(defaultRuntime, async () => {
			const { modelsAuthLoginCommand } = await import("./auth-CZEKK1PO.js");
			await modelsAuthLoginCommand({
				provider: String(opts.provider),
				method: opts.method ? String(opts.method) : void 0
			}, defaultRuntime);
		});
	});
	modelAuth.command("logout").description("Remove saved auth profiles for one provider").requiredOption("--provider <id>", "Provider id").option("--agent <id>", "Agent id (default: configured default agent)").option("--json", "Output JSON", false).action(async (opts) => {
		await runCommandWithRuntime(defaultRuntime, async () => {
			const result = await runModelAuthLogout(String(opts.provider), typeof opts.agent === "string" ? opts.agent : void 0);
			emitJsonOrText(defaultRuntime, Boolean(opts.json), result, (value) => JSON.stringify(value, null, 2));
		});
	});
	modelAuth.command("status").description("Show configured auth state").option("--json", "Output JSON", false).action(async (opts) => {
		await runCommandWithRuntime(defaultRuntime, async () => {
			const result = await runModelAuthStatus();
			emitJsonOrText(defaultRuntime, Boolean(opts.json), result, (value) => JSON.stringify(value, null, 2));
		});
	});
	const image = capability.command("image").description("Image generation and description");
	image.command("generate").description("Generate images").requiredOption("--prompt <text>", "Prompt text").option("--model <provider/model>", "Model override").option("--count <n>", "Number of images").option("--size <size>", "Size hint like 1024x1024").option("--aspect-ratio <ratio>", "Aspect ratio hint like 16:9").option("--resolution <value>", "Resolution hint: 1K, 2K, or 4K").option("--output-format <format>", "Output format hint: png, jpeg, or webp").option("--background <value>", "Background hint: transparent, opaque, or auto").option("--openai-background <value>", "OpenAI background hint: transparent, opaque, or auto").option("--timeout-ms <ms>", "Provider request timeout in milliseconds").option("--output <path>", "Output path").option("--json", "Output JSON", false).action(async (opts) => {
		await runCommandWithRuntime(defaultRuntime, async () => {
			const result = await runImageGenerate({
				capability: "image.generate",
				prompt: String(opts.prompt),
				model: opts.model,
				count: parseOptionalPositiveInteger(opts.count, "--count"),
				size: opts.size,
				aspectRatio: opts.aspectRatio,
				resolution: opts.resolution,
				outputFormat: normalizeImageOutputFormat(opts.outputFormat),
				background: normalizeImageBackground(opts.background),
				openaiBackground: normalizeImageBackground(opts.openaiBackground, "--openai-background"),
				timeoutMs: parseOptionalTimeoutMs(opts.timeoutMs),
				output: opts.output
			});
			emitJsonOrText(defaultRuntime, Boolean(opts.json), result, formatEnvelopeForText);
		});
	});
	image.command("edit").description("Edit images with one or more input files").requiredOption("--file <path>", "Input file", collectOption, []).requiredOption("--prompt <text>", "Prompt text").option("--model <provider/model>", "Model override").option("--size <size>", "Size hint like 1024x1024").option("--aspect-ratio <ratio>", "Aspect ratio hint like 16:9").option("--resolution <value>", "Resolution hint: 1K, 2K, or 4K").option("--output-format <format>", "Output format hint: png, jpeg, or webp").option("--background <value>", "Background hint: transparent, opaque, or auto").option("--openai-background <value>", "OpenAI background hint: transparent, opaque, or auto").option("--timeout-ms <ms>", "Provider request timeout in milliseconds").option("--output <path>", "Output path").option("--json", "Output JSON", false).action(async (opts) => {
		await runCommandWithRuntime(defaultRuntime, async () => {
			const files = Array.isArray(opts.file) ? opts.file : [String(opts.file)];
			const result = await runImageGenerate({
				capability: "image.edit",
				prompt: String(opts.prompt),
				model: opts.model,
				size: opts.size,
				aspectRatio: opts.aspectRatio,
				resolution: opts.resolution,
				file: files,
				outputFormat: normalizeImageOutputFormat(opts.outputFormat),
				background: normalizeImageBackground(opts.background),
				openaiBackground: normalizeImageBackground(opts.openaiBackground, "--openai-background"),
				timeoutMs: parseOptionalTimeoutMs(opts.timeoutMs),
				output: opts.output
			});
			emitJsonOrText(defaultRuntime, Boolean(opts.json), result, formatEnvelopeForText);
		});
	});
	image.command("describe").description("Describe one image file").requiredOption("--file <path>", "Image file").option("--prompt <text>", "Prompt hint").option("--model <provider/model>", "Model override").option("--timeout-ms <ms>", "Provider request timeout in milliseconds").option("--json", "Output JSON", false).action(async (opts) => {
		await runCommandWithRuntime(defaultRuntime, async () => {
			const result = await runImageDescribe({
				capability: "image.describe",
				files: [String(opts.file)],
				model: opts.model,
				prompt: opts.prompt,
				timeoutMs: parseOptionalTimeoutMs(opts.timeoutMs)
			});
			emitJsonOrText(defaultRuntime, Boolean(opts.json), result, formatEnvelopeForText);
		});
	});
	image.command("describe-many").description("Describe multiple image files").requiredOption("--file <path>", "Image file", collectOption, []).option("--prompt <text>", "Prompt hint").option("--model <provider/model>", "Model override").option("--timeout-ms <ms>", "Provider request timeout in milliseconds").option("--json", "Output JSON", false).action(async (opts) => {
		await runCommandWithRuntime(defaultRuntime, async () => {
			const result = await runImageDescribe({
				capability: "image.describe-many",
				files: opts.file,
				model: opts.model,
				prompt: opts.prompt,
				timeoutMs: parseOptionalTimeoutMs(opts.timeoutMs)
			});
			emitJsonOrText(defaultRuntime, Boolean(opts.json), result, formatEnvelopeForText);
		});
	});
	image.command("providers").description("List image generation providers").option("--json", "Output JSON", false).action(async (opts) => {
		await runCommandWithRuntime(defaultRuntime, async () => {
			const cfg = getRuntimeConfig();
			const selectedProvider = resolveSelectedProviderFromModelRef(resolveAgentModelPrimaryValue(cfg.agents?.defaults?.imageGenerationModel));
			const result = listRuntimeImageGenerationProviders({ config: cfg }).map((provider) => ({
				available: true,
				configured: selectedProvider === provider.id || providerHasGenericConfig({
					cfg,
					providerId: provider.id
				}),
				selected: selectedProvider === provider.id,
				id: provider.id,
				label: provider.label,
				defaultModel: provider.defaultModel,
				models: provider.models ?? [],
				capabilities: provider.capabilities
			}));
			emitJsonOrText(defaultRuntime, Boolean(opts.json), result, providerSummaryText);
		});
	});
	const audio = capability.command("audio").description("Audio transcription");
	audio.command("transcribe").description("Transcribe one audio file").requiredOption("--file <path>", "Audio file").option("--language <code>", "Language hint").option("--prompt <text>", "Prompt hint").option("--model <provider/model>", "Model override").option("--json", "Output JSON", false).action(async (opts) => {
		await runCommandWithRuntime(defaultRuntime, async () => {
			const result = await runAudioTranscribe({
				file: String(opts.file),
				language: opts.language,
				model: opts.model,
				prompt: opts.prompt
			});
			emitJsonOrText(defaultRuntime, Boolean(opts.json), result, formatEnvelopeForText);
		});
	});
	audio.command("providers").description("List audio transcription providers").option("--json", "Output JSON", false).action(async (opts) => {
		await runCommandWithRuntime(defaultRuntime, async () => {
			const cfg = getRuntimeConfig();
			const providers = [...buildMediaUnderstandingRegistry(void 0, cfg).values()].filter((provider) => provider.capabilities?.includes("audio")).map((provider) => ({
				available: true,
				configured: providerHasGenericConfig({
					cfg,
					providerId: provider.id,
					envVars: getProviderEnvVars(provider.id, {
						config: cfg,
						includeUntrustedWorkspacePlugins: false
					})
				}),
				selected: false,
				id: provider.id,
				capabilities: provider.capabilities,
				defaultModels: provider.defaultModels
			}));
			emitJsonOrText(defaultRuntime, Boolean(opts.json), providers, providerSummaryText);
		});
	});
	const tts = capability.command("tts").description("Text to speech");
	tts.command("convert").description("Convert text to speech").requiredOption("--text <text>", "Input text").option("--channel <id>", "Channel hint").option("--voice <id>", "Voice hint").option("--model <provider/model>", "Model override").option("--output <path>", "Output path").option("--local", "Force local execution", false).option("--gateway", "Force gateway execution", false).option("--json", "Output JSON", false).action(async (opts) => {
		await runCommandWithRuntime(defaultRuntime, async () => {
			const transport = resolveTransport({
				local: Boolean(opts.local),
				gateway: Boolean(opts.gateway),
				supported: ["local", "gateway"],
				defaultTransport: "local"
			});
			const modelRef = resolveModelRefOverride(opts.model);
			if (opts.model && !modelRef.provider) throw new Error("TTS model overrides must use the form <provider/model>.");
			const result = await runTtsConvert({
				text: String(opts.text),
				channel: opts.channel,
				provider: modelRef.provider,
				modelId: modelRef.provider ? modelRef.model : void 0,
				voiceId: opts.voice,
				output: opts.output,
				transport
			});
			emitJsonOrText(defaultRuntime, Boolean(opts.json), result, formatEnvelopeForText);
		});
	});
	tts.command("voices").description("List voices for a TTS provider").option("--provider <id>", "Speech provider id").option("--json", "Output JSON", false).action(async (opts) => {
		await runCommandWithRuntime(defaultRuntime, async () => {
			const voices = await runTtsVoices(opts.provider);
			emitJsonOrText(defaultRuntime, Boolean(opts.json), voices, providerSummaryText);
		});
	});
	tts.command("providers").description("List speech providers").option("--local", "Force local execution", false).option("--gateway", "Force gateway execution", false).option("--json", "Output JSON", false).action(async (opts) => {
		await runCommandWithRuntime(defaultRuntime, async () => {
			const result = await runTtsProviders(resolveTransport({
				local: Boolean(opts.local),
				gateway: Boolean(opts.gateway),
				supported: ["local", "gateway"],
				defaultTransport: "local"
			}));
			emitJsonOrText(defaultRuntime, Boolean(opts.json), result, (value) => JSON.stringify(value, null, 2));
		});
	});
	tts.command("personas").description("List TTS personas").option("--local", "Force local execution", false).option("--gateway", "Force gateway execution", false).option("--json", "Output JSON", false).action(async (opts) => {
		await runCommandWithRuntime(defaultRuntime, async () => {
			const result = await runTtsPersonas(resolveTransport({
				local: Boolean(opts.local),
				gateway: Boolean(opts.gateway),
				supported: ["local", "gateway"],
				defaultTransport: "local"
			}));
			emitJsonOrText(defaultRuntime, Boolean(opts.json), result, (value) => JSON.stringify(value, null, 2));
		});
	});
	tts.command("status").description("Show TTS status").option("--gateway", "Force gateway execution", false).option("--json", "Output JSON", false).action(async (opts) => {
		await runCommandWithRuntime(defaultRuntime, async () => {
			const transport = resolveTransport({
				gateway: Boolean(opts.gateway),
				supported: ["gateway"],
				defaultTransport: "gateway"
			});
			const result = await callGateway({
				method: "tts.status",
				timeoutMs: 3e4
			});
			emitJsonOrText(defaultRuntime, Boolean(opts.json), {
				transport,
				...result
			}, (value) => JSON.stringify(value, null, 2));
		});
	});
	for (const [commandName, capabilityId] of [["enable", "tts.enable"], ["disable", "tts.disable"]]) tts.command(commandName).description(`${commandName === "enable" ? "Enable" : "Disable"} TTS`).option("--local", "Force local execution", false).option("--gateway", "Force gateway execution", false).option("--json", "Output JSON", false).action(async (opts) => {
		await runCommandWithRuntime(defaultRuntime, async () => {
			const result = await runTtsStateMutation({
				capability: capabilityId,
				transport: resolveTransport({
					local: Boolean(opts.local),
					gateway: Boolean(opts.gateway),
					supported: ["local", "gateway"],
					defaultTransport: "gateway"
				})
			});
			emitJsonOrText(defaultRuntime, Boolean(opts.json), result, (value) => JSON.stringify(value, null, 2));
		});
	});
	tts.command("set-provider").description("Set the active TTS provider").requiredOption("--provider <id>", "Speech provider id").option("--local", "Force local execution", false).option("--gateway", "Force gateway execution", false).option("--json", "Output JSON", false).action(async (opts) => {
		await runCommandWithRuntime(defaultRuntime, async () => {
			const transport = resolveTransport({
				local: Boolean(opts.local),
				gateway: Boolean(opts.gateway),
				supported: ["local", "gateway"],
				defaultTransport: "gateway"
			});
			const result = await runTtsStateMutation({
				capability: "tts.set-provider",
				provider: String(opts.provider),
				transport
			});
			emitJsonOrText(defaultRuntime, Boolean(opts.json), result, (value) => JSON.stringify(value, null, 2));
		});
	});
	tts.command("set-persona").description("Set the active TTS persona").option("--persona <id>", "TTS persona id").option("--off", "Disable the active TTS persona", false).option("--local", "Force local execution", false).option("--gateway", "Force gateway execution", false).option("--json", "Output JSON", false).action(async (opts) => {
		await runCommandWithRuntime(defaultRuntime, async () => {
			const transport = resolveTransport({
				local: Boolean(opts.local),
				gateway: Boolean(opts.gateway),
				supported: ["local", "gateway"],
				defaultTransport: "gateway"
			});
			if (!opts.off && !opts.persona) throw new Error("--persona is required unless --off is set");
			const result = await runTtsStateMutation({
				capability: "tts.set-persona",
				persona: opts.off ? null : String(opts.persona),
				transport
			});
			emitJsonOrText(defaultRuntime, Boolean(opts.json), result, (value) => JSON.stringify(value, null, 2));
		});
	});
	const video = capability.command("video").description("Video generation and description");
	video.command("generate").description("Generate video").requiredOption("--prompt <text>", "Prompt text").option("--model <provider/model>", "Model override").option("--size <size>", "Size hint like 1280x720").option("--aspect-ratio <ratio>", "Aspect ratio hint like 16:9").option("--resolution <value>", "Resolution hint: 360P, 480P, 540P, 720P, 768P, or 1080P").option("--duration <seconds>", "Target duration in seconds").option("--audio", "Enable generated audio when supported").option("--watermark", "Request provider watermark when supported").option("--timeout-ms <ms>", "Provider request timeout in milliseconds").option("--output <path>", "Output path").option("--json", "Output JSON", false).action(async (opts) => {
		await runCommandWithRuntime(defaultRuntime, async () => {
			const result = await runVideoGenerate({
				prompt: String(opts.prompt),
				model: opts.model,
				output: opts.output,
				size: opts.size,
				aspectRatio: opts.aspectRatio,
				resolution: normalizeVideoResolution(opts.resolution),
				durationSeconds: parseOptionalFiniteNumber(opts.duration, "--duration"),
				audio: opts.audio === true ? true : void 0,
				watermark: opts.watermark === true ? true : void 0,
				timeoutMs: parseOptionalTimeoutMs(opts.timeoutMs)
			});
			emitJsonOrText(defaultRuntime, Boolean(opts.json), result, formatEnvelopeForText);
		});
	});
	video.command("describe").description("Describe one video file").requiredOption("--file <path>", "Video file").option("--model <provider/model>", "Model override").option("--json", "Output JSON", false).action(async (opts) => {
		await runCommandWithRuntime(defaultRuntime, async () => {
			const result = await runVideoDescribe({
				file: String(opts.file),
				model: opts.model
			});
			emitJsonOrText(defaultRuntime, Boolean(opts.json), result, formatEnvelopeForText);
		});
	});
	video.command("providers").description("List video generation and description providers").option("--json", "Output JSON", false).action(async (opts) => {
		await runCommandWithRuntime(defaultRuntime, async () => {
			const cfg = getRuntimeConfig();
			const selectedGenerationProvider = resolveSelectedProviderFromModelRef(resolveAgentModelPrimaryValue(cfg.agents?.defaults?.videoGenerationModel));
			const result = {
				generation: listRuntimeVideoGenerationProviders({ config: cfg }).map((provider) => ({
					available: true,
					configured: selectedGenerationProvider === provider.id || providerHasGenericConfig({
						cfg,
						providerId: provider.id
					}),
					selected: selectedGenerationProvider === provider.id,
					id: provider.id,
					label: provider.label,
					defaultModel: provider.defaultModel,
					models: provider.models ?? [],
					capabilities: provider.capabilities
				})),
				description: [...buildMediaUnderstandingRegistry(void 0, cfg).values()].filter((provider) => provider.capabilities?.includes("video")).map((provider) => ({
					available: true,
					configured: providerHasGenericConfig({
						cfg,
						providerId: provider.id
					}),
					selected: false,
					id: provider.id,
					capabilities: provider.capabilities,
					defaultModels: provider.defaultModels
				}))
			};
			emitJsonOrText(defaultRuntime, Boolean(opts.json), result, (value) => JSON.stringify(value, null, 2));
		});
	});
	const web = capability.command("web").description("Web capabilities");
	web.command("search").description("Run web search").requiredOption("--query <text>", "Search query").option("--provider <id>", "Provider id").option("--limit <n>", "Result limit").option("--json", "Output JSON", false).action(async (opts) => {
		await runCommandWithRuntime(defaultRuntime, async () => {
			const result = await runWebSearchCommand({
				query: String(opts.query),
				provider: opts.provider,
				limit: parseOptionalPositiveInteger(opts.limit, "--limit")
			});
			emitJsonOrText(defaultRuntime, Boolean(opts.json), result, formatEnvelopeForText);
		});
	});
	web.command("fetch").description("Fetch one URL").requiredOption("--url <url>", "URL").option("--provider <id>", "Provider id").option("--format <format>", "Format hint").option("--json", "Output JSON", false).action(async (opts) => {
		await runCommandWithRuntime(defaultRuntime, async () => {
			const result = await runWebFetchCommand({
				url: String(opts.url),
				provider: opts.provider,
				format: opts.format
			});
			emitJsonOrText(defaultRuntime, Boolean(opts.json), result, formatEnvelopeForText);
		});
	});
	web.command("providers").description("List web providers").option("--json", "Output JSON", false).action(async (opts) => {
		await runCommandWithRuntime(defaultRuntime, async () => {
			const cfg = getRuntimeConfig();
			const selectedSearchProvider = typeof cfg.tools?.web?.search?.provider === "string" ? normalizeLowercaseStringOrEmpty(cfg.tools.web.search.provider) : "";
			const selectedFetchProvider = typeof cfg.tools?.web?.fetch?.provider === "string" ? normalizeLowercaseStringOrEmpty(cfg.tools.web.fetch.provider) : "";
			const result = {
				search: listWebSearchProviders({ config: cfg }).map((provider) => ({
					available: true,
					configured: isWebSearchProviderConfigured({
						provider,
						config: cfg
					}),
					selected: provider.id === selectedSearchProvider,
					id: provider.id,
					envVars: provider.envVars
				})),
				fetch: listWebFetchProviders({ config: cfg }).map((provider) => ({
					available: true,
					configured: isWebFetchProviderConfigured({
						provider,
						config: cfg
					}),
					selected: provider.id === selectedFetchProvider,
					id: provider.id,
					envVars: provider.envVars
				}))
			};
			emitJsonOrText(defaultRuntime, Boolean(opts.json), result, (value) => JSON.stringify(value, null, 2));
		});
	});
	const embedding = capability.command("embedding").description("Embedding providers");
	embedding.command("create").description("Create embeddings").requiredOption("--text <text>", "Input text", collectOption, []).option("--provider <id>", "Provider id").option("--model <provider/model>", "Model override").option("--json", "Output JSON", false).action(async (opts) => {
		await runCommandWithRuntime(defaultRuntime, async () => {
			const result = await runMemoryEmbeddingCreate({
				texts: opts.text,
				provider: opts.provider,
				model: opts.model
			});
			emitJsonOrText(defaultRuntime, Boolean(opts.json), result, formatEnvelopeForText);
		});
	});
	embedding.command("providers").description("List embedding providers").option("--json", "Output JSON", false).action(async (opts) => {
		await runCommandWithRuntime(defaultRuntime, async () => {
			ensureMemoryEmbeddingProvidersRegistered();
			const cfg = getRuntimeConfig();
			const resolvedMemory = resolveMemorySearchConfig(cfg, resolveDefaultAgentId(cfg));
			const selectedProvider = resolvedMemory?.provider;
			const providers = new Map(listMemoryEmbeddingProviders().map((provider) => [provider.id, {
				id: provider.id,
				defaultModel: provider.defaultModel,
				transport: provider.transport,
				autoSelectPriority: provider.autoSelectPriority
			}]));
			for (const provider of listEmbeddingProviders(cfg)) {
				if (providers.has(provider.id)) continue;
				providers.set(provider.id, {
					id: provider.id,
					defaultModel: provider.defaultModel,
					transport: provider.transport,
					autoSelectPriority: void 0
				});
			}
			if (selectedProvider && !providers.has(selectedProvider)) providers.set(selectedProvider, {
				id: selectedProvider,
				defaultModel: resolvedMemory?.model || void 0,
				transport: providerHasGenericConfig({
					cfg,
					providerId: selectedProvider
				}) ? "remote" : void 0,
				autoSelectPriority: void 0
			});
			const result = Array.from(providers.values()).map((provider) => ({
				available: true,
				configured: provider.id === selectedProvider || providerHasGenericConfig({
					cfg,
					providerId: provider.id
				}),
				selected: provider.id === selectedProvider,
				id: provider.id,
				defaultModel: provider.defaultModel,
				transport: provider.transport,
				autoSelectPriority: provider.autoSelectPriority
			}));
			emitJsonOrText(defaultRuntime, Boolean(opts.json), result, providerSummaryText);
		});
	});
}
//#endregion
export { registerCapabilityCli };