import "./subsystem-B57zAnh3.js";
import "./paths-aMBPuCJR.js";
import "./auth-profiles-vy16iW9a.js";
import "./env-DFfOiqpV.js";
import "./exec-rVpUMUjU.js";
import "./agent-scope-C3vq_G9s.js";
import "./github-copilot-token-5G6j3BB0.js";
import "./reliability-CU83NHOD.js";
import "./sqlite-CbyDDiup.js";
import "./message-channel-Cw5DRkgR.js";
import "./image-ops-D6in9bm7.js";
import { n as loadConfig } from "./config-BWfcFg-o.js";
import "./manifest-registry-DOrMVNcS.js";
import { S as DEFAULT_AI_SNAPSHOT_MAX_CHARS } from "./chrome-CdUTjfym.js";
import { c as DEFAULT_UPLOAD_DIR, l as resolvePathsWithinRoot, o as saveMediaBuffer, r as resolveBrowserConfig, s as DEFAULT_DOWNLOAD_DIR } from "./server-context-CCjHJE9j.js";
import "./redact-BXcZFg1V.js";
import "./errors-CGLTQpIl.js";
import "./ssrf-Clw7QAxV.js";
import "./tool-images-DTeIBF5l.js";
import { t as wrapToolWithTracking } from "./tool-tracking-DIN1Kj4g.js";
import { a as jsonResult, i as imageResultFromFile, u as readStringParam } from "./common-CBZ0Olph.js";
import "./call-qL_BTieR.js";
import { n as wrapExternalContent } from "./external-content-CfbWCLcb.js";
import { t as callGatewayTool } from "./gateway-CUi0QP7h.js";
import { r as resolveNodeIdFromList, t as listNodes } from "./nodes-utils-BSKHzxrp.js";
import { i as stringEnum, r as optionalStringEnum } from "./typebox-BrLUZMNm.js";
import { _ as fetchBrowserJson, a as browserSnapshot, c as browserStop, d as browserArmDialog, f as browserArmFileChooser, g as browserWaitForDownload, h as browserScreenshotAction, i as browserProfiles, l as browserTabs, m as browserNavigate, n as browserFocusTab, o as browserStart, p as browserDownload, r as browserOpenTab, s as browserStatus, t as browserCloseTab, u as browserAct, v as buildProfileQuery, y as withBaseUrl } from "./client-B665Ltjd.js";
import { Type } from "@sinclair/typebox";
import crypto from "node:crypto";

//#region src/browser/client-actions-observe.ts
function buildQuerySuffix(params) {
	const query = new URLSearchParams();
	for (const [key, value] of params) {
		if (typeof value === "boolean") {
			query.set(key, String(value));
			continue;
		}
		if (typeof value === "string" && value.length > 0) query.set(key, value);
	}
	const encoded = query.toString();
	return encoded.length > 0 ? `?${encoded}` : "";
}
async function browserConsoleMessages(baseUrl, opts = {}) {
	return await fetchBrowserJson(withBaseUrl(baseUrl, `/console${buildQuerySuffix([
		["level", opts.level],
		["targetId", opts.targetId],
		["profile", opts.profile]
	])}`), { timeoutMs: 2e4 });
}
async function browserPdfSave(baseUrl, opts = {}) {
	return await fetchBrowserJson(withBaseUrl(baseUrl, `/pdf${buildProfileQuery(opts.profile)}`), {
		method: "POST",
		headers: { "Content-Type": "application/json" },
		body: JSON.stringify({ targetId: opts.targetId }),
		timeoutMs: 2e4
	});
}

//#endregion
//#region src/browser/proxy-files.ts
async function persistBrowserProxyFiles(files) {
	if (!files || files.length === 0) return /* @__PURE__ */ new Map();
	const mapping = /* @__PURE__ */ new Map();
	for (const file of files) {
		const buffer = Buffer.from(file.base64, "base64");
		const saved = await saveMediaBuffer(buffer, file.mimeType, "browser", buffer.byteLength);
		mapping.set(file.path, saved.path);
	}
	return mapping;
}
function applyBrowserProxyPaths(result, mapping) {
	if (!result || typeof result !== "object") return;
	const obj = result;
	if (typeof obj.path === "string" && mapping.has(obj.path)) obj.path = mapping.get(obj.path);
	if (typeof obj.imagePath === "string" && mapping.has(obj.imagePath)) obj.imagePath = mapping.get(obj.imagePath);
	const download = obj.download;
	if (download && typeof download === "object") {
		const d = download;
		if (typeof d.path === "string" && mapping.has(d.path)) d.path = mapping.get(d.path);
	}
}

//#endregion
//#region src/agents/tools/ui/browser-tool.schema.ts
const BROWSER_ACT_KINDS = [
	"click",
	"type",
	"press",
	"hover",
	"drag",
	"select",
	"fill",
	"resize",
	"wait",
	"evaluate",
	"close"
];
const BROWSER_TOOL_ACTIONS = [
	"status",
	"start",
	"stop",
	"profiles",
	"tabs",
	"open",
	"focus",
	"close",
	"snapshot",
	"screenshot",
	"navigate",
	"console",
	"pdf",
	"upload",
	"dialog",
	"act",
	"download",
	"wait_download"
];
const BROWSER_TARGETS = [
	"sandbox",
	"host",
	"node"
];
const BROWSER_SNAPSHOT_FORMATS = ["aria", "ai"];
const BROWSER_SNAPSHOT_MODES = ["efficient"];
const BROWSER_SNAPSHOT_REFS = ["role", "aria"];
const BROWSER_IMAGE_TYPES = ["png", "jpeg"];
const BrowserActSchema = Type.Object({
	kind: stringEnum(BROWSER_ACT_KINDS),
	targetId: Type.Optional(Type.String()),
	ref: Type.Optional(Type.String()),
	doubleClick: Type.Optional(Type.Boolean()),
	button: Type.Optional(Type.String()),
	modifiers: Type.Optional(Type.Array(Type.String())),
	text: Type.Optional(Type.String()),
	submit: Type.Optional(Type.Boolean()),
	slowly: Type.Optional(Type.Boolean()),
	key: Type.Optional(Type.String()),
	startRef: Type.Optional(Type.String()),
	endRef: Type.Optional(Type.String()),
	values: Type.Optional(Type.Array(Type.String())),
	fields: Type.Optional(Type.Array(Type.Object({}, { additionalProperties: true }))),
	width: Type.Optional(Type.Number()),
	height: Type.Optional(Type.Number()),
	timeMs: Type.Optional(Type.Number()),
	textGone: Type.Optional(Type.String()),
	fn: Type.Optional(Type.String())
});
const BrowserToolSchema = Type.Object({
	action: stringEnum(BROWSER_TOOL_ACTIONS),
	target: optionalStringEnum(BROWSER_TARGETS),
	node: Type.Optional(Type.String()),
	profile: Type.Optional(Type.String()),
	targetUrl: Type.Optional(Type.String()),
	targetId: Type.Optional(Type.String()),
	limit: Type.Optional(Type.Number()),
	maxChars: Type.Optional(Type.Number()),
	mode: optionalStringEnum(BROWSER_SNAPSHOT_MODES),
	snapshotFormat: optionalStringEnum(BROWSER_SNAPSHOT_FORMATS),
	refs: optionalStringEnum(BROWSER_SNAPSHOT_REFS),
	interactive: Type.Optional(Type.Boolean()),
	compact: Type.Optional(Type.Boolean()),
	depth: Type.Optional(Type.Number()),
	selector: Type.Optional(Type.String()),
	frame: Type.Optional(Type.String()),
	labels: Type.Optional(Type.Boolean()),
	fullPage: Type.Optional(Type.Boolean()),
	ref: Type.Optional(Type.String()),
	element: Type.Optional(Type.String()),
	type: optionalStringEnum(BROWSER_IMAGE_TYPES),
	level: Type.Optional(Type.String()),
	paths: Type.Optional(Type.Array(Type.String())),
	inputRef: Type.Optional(Type.String()),
	timeoutMs: Type.Optional(Type.Number()),
	accept: Type.Optional(Type.Boolean()),
	promptText: Type.Optional(Type.String()),
	request: Type.Optional(BrowserActSchema),
	url: Type.Optional(Type.String()),
	savePath: Type.Optional(Type.String())
});

//#endregion
//#region src/agents/tools/ui/browser-tool.ts
function wrapBrowserExternalJson(params) {
	return {
		wrappedText: wrapExternalContent(JSON.stringify(params.payload, null, 2), {
			source: "browser",
			includeWarning: params.includeWarning ?? true
		}),
		safeDetails: {
			ok: true,
			externalContent: {
				untrusted: true,
				source: "browser",
				kind: params.kind,
				wrapped: true
			}
		}
	};
}
const DEFAULT_BROWSER_PROXY_TIMEOUT_MS = 2e4;
function isBrowserNode(node) {
	const caps = Array.isArray(node.caps) ? node.caps : [];
	const commands = Array.isArray(node.commands) ? node.commands : [];
	return caps.includes("browser") || commands.includes("browser.proxy");
}
async function resolveBrowserNodeTarget(params) {
	const policy = loadConfig().gateway?.nodes?.browser;
	const mode = policy?.mode ?? "auto";
	if (mode === "off") {
		if (params.target === "node" || params.requestedNode) throw new Error("Node browser proxy is disabled (gateway.nodes.browser.mode=off).");
		return null;
	}
	if (params.sandboxBridgeUrl?.trim() && params.target !== "node" && !params.requestedNode) return null;
	if (params.target && params.target !== "node") return null;
	if (mode === "manual" && params.target !== "node" && !params.requestedNode) return null;
	const browserNodes = (await listNodes({})).filter((node) => node.connected && isBrowserNode(node));
	if (browserNodes.length === 0) {
		if (params.target === "node" || params.requestedNode) throw new Error("No connected browser-capable nodes.");
		return null;
	}
	const requested = params.requestedNode?.trim() || policy?.node?.trim();
	if (requested) {
		const nodeId = resolveNodeIdFromList(browserNodes, requested, false);
		const node = browserNodes.find((entry) => entry.nodeId === nodeId);
		return {
			nodeId,
			label: node?.displayName ?? node?.remoteIp ?? nodeId
		};
	}
	if (params.target === "node") {
		if (browserNodes.length === 1) {
			const node = browserNodes[0];
			return {
				nodeId: node.nodeId,
				label: node.displayName ?? node.remoteIp ?? node.nodeId
			};
		}
		throw new Error(`Multiple browser-capable nodes connected (${browserNodes.length}). Set gateway.nodes.browser.node or pass node=<id>.`);
	}
	if (mode === "manual") return null;
	if (browserNodes.length === 1) {
		const node = browserNodes[0];
		return {
			nodeId: node.nodeId,
			label: node.displayName ?? node.remoteIp ?? node.nodeId
		};
	}
	return null;
}
async function callBrowserProxy(params) {
	const payload = await callGatewayTool("node.invoke", { timeoutMs: typeof params.timeoutMs === "number" && Number.isFinite(params.timeoutMs) ? Math.max(1, Math.floor(params.timeoutMs)) : DEFAULT_BROWSER_PROXY_TIMEOUT_MS }, {
		nodeId: params.nodeId,
		command: "browser.proxy",
		params: {
			method: params.method,
			path: params.path,
			query: params.query,
			body: params.body,
			timeoutMs: params.timeoutMs,
			profile: params.profile
		},
		idempotencyKey: crypto.randomUUID()
	});
	const parsed = payload?.payload ?? (typeof payload?.payloadJSON === "string" && payload.payloadJSON ? JSON.parse(payload.payloadJSON) : null);
	if (!parsed || typeof parsed !== "object" || !("result" in parsed)) throw new Error("browser proxy failed");
	return parsed;
}
async function persistProxyFiles(files) {
	return await persistBrowserProxyFiles(files);
}
function applyProxyPaths(result, mapping) {
	applyBrowserProxyPaths(result, mapping);
}
function resolveBrowserBaseUrl(params) {
	const cfg = loadConfig();
	const resolved = resolveBrowserConfig(cfg.browser, cfg);
	const normalizedSandbox = params.sandboxBridgeUrl?.trim() ?? "";
	if ((params.target ?? (normalizedSandbox ? "sandbox" : "host")) === "sandbox") {
		if (!normalizedSandbox) throw new Error("Sandbox browser is unavailable. Enable agents.defaults.sandbox.browser.enabled or use target=\"host\" if allowed.");
		return normalizedSandbox.replace(/\/$/, "");
	}
	if (params.allowHostControl === false) throw new Error("Host browser control is disabled by sandbox policy.");
	if (!resolved.enabled) throw new Error("Browser control is disabled. Set browser.enabled=true in ~/.minion/minion.json.");
}
function createBrowserTool(opts) {
	const targetDefault = opts?.sandboxBridgeUrl ? "sandbox" : "host";
	const hostHint = opts?.allowHostControl === false ? "Host target blocked by policy." : "Host target allowed.";
	return wrapToolWithTracking({
		label: "Browser",
		name: "browser",
		description: [
			"Control the browser via Minion's browser control server (status/start/stop/profiles/tabs/open/snapshot/screenshot/navigate/act/download/wait_download).",
			"Profiles: use profile=\"chrome\" for Chrome extension relay takeover (your existing Chrome tabs). Use profile=\"minion\" for the isolated minion-managed browser.",
			"If the user mentions the Chrome extension / Browser Relay / toolbar button / “attach tab”, ALWAYS use profile=\"chrome\" (do not ask which profile).",
			"When a node-hosted browser proxy is available, the tool may auto-route to it. Pin a node with node=<id|name> or target=\"node\".",
			"Chrome extension relay needs an attached tab: user must click the Minion Browser Relay toolbar icon on the tab (badge ON). If no tab is connected, ask them to attach it.",
			"When using refs from snapshot (e.g. e12), keep the same tab: prefer passing targetId from the snapshot response into subsequent actions (act/click/type/etc).",
			"For stable, self-resolving refs across calls, use snapshot with refs=\"aria\" (Playwright aria-ref ids). Default refs=\"role\" are role+name-based.",
			"Use snapshot+act for UI automation. Avoid act:wait by default; use only in exceptional cases when no reliable UI state exists.",
			`target selects browser location (sandbox|host|node). Default: ${targetDefault}.`,
			hostHint
		].join(" "),
		parameters: BrowserToolSchema,
		execute: async (_toolCallId, args) => {
			const params = args;
			const action = readStringParam(params, "action", { required: true });
			const profile = readStringParam(params, "profile");
			const requestedNode = readStringParam(params, "node");
			let target = readStringParam(params, "target");
			if (requestedNode && target && target !== "node") throw new Error("node is only supported with target=\"node\".");
			if (!target && !requestedNode && profile === "chrome") target = "host";
			const nodeTarget = await resolveBrowserNodeTarget({
				requestedNode: requestedNode ?? void 0,
				target,
				sandboxBridgeUrl: opts?.sandboxBridgeUrl
			});
			const baseUrl = nodeTarget ? void 0 : resolveBrowserBaseUrl({
				target: target === "node" ? void 0 : target,
				sandboxBridgeUrl: opts?.sandboxBridgeUrl,
				allowHostControl: opts?.allowHostControl
			});
			const proxyRequest = nodeTarget ? async (opts) => {
				const proxy = await callBrowserProxy({
					nodeId: nodeTarget.nodeId,
					method: opts.method,
					path: opts.path,
					query: opts.query,
					body: opts.body,
					timeoutMs: opts.timeoutMs,
					profile: opts.profile
				});
				const mapping = await persistProxyFiles(proxy.files);
				applyProxyPaths(proxy.result, mapping);
				return proxy.result;
			} : null;
			switch (action) {
				case "status":
					if (proxyRequest) return jsonResult(await proxyRequest({
						method: "GET",
						path: "/",
						profile
					}));
					return jsonResult(await browserStatus(baseUrl, { profile }));
				case "start":
					if (proxyRequest) {
						await proxyRequest({
							method: "POST",
							path: "/start",
							profile
						});
						return jsonResult(await proxyRequest({
							method: "GET",
							path: "/",
							profile
						}));
					}
					await browserStart(baseUrl, { profile });
					return jsonResult(await browserStatus(baseUrl, { profile }));
				case "stop":
					if (proxyRequest) {
						await proxyRequest({
							method: "POST",
							path: "/stop",
							profile
						});
						return jsonResult(await proxyRequest({
							method: "GET",
							path: "/",
							profile
						}));
					}
					await browserStop(baseUrl, { profile });
					return jsonResult(await browserStatus(baseUrl, { profile }));
				case "profiles":
					if (proxyRequest) return jsonResult(await proxyRequest({
						method: "GET",
						path: "/profiles"
					}));
					return jsonResult({ profiles: await browserProfiles(baseUrl) });
				case "tabs":
					if (proxyRequest) {
						const tabs = (await proxyRequest({
							method: "GET",
							path: "/tabs",
							profile
						})).tabs ?? [];
						const wrapped = wrapBrowserExternalJson({
							kind: "tabs",
							payload: { tabs },
							includeWarning: false
						});
						return {
							content: [{
								type: "text",
								text: wrapped.wrappedText
							}],
							details: {
								...wrapped.safeDetails,
								tabCount: tabs.length
							}
						};
					}
					{
						const tabs = await browserTabs(baseUrl, { profile });
						const wrapped = wrapBrowserExternalJson({
							kind: "tabs",
							payload: { tabs },
							includeWarning: false
						});
						return {
							content: [{
								type: "text",
								text: wrapped.wrappedText
							}],
							details: {
								...wrapped.safeDetails,
								tabCount: tabs.length
							}
						};
					}
				case "open": {
					const targetUrl = readStringParam(params, "targetUrl", { required: true });
					if (proxyRequest) return jsonResult(await proxyRequest({
						method: "POST",
						path: "/tabs/open",
						profile,
						body: { url: targetUrl }
					}));
					return jsonResult(await browserOpenTab(baseUrl, targetUrl, { profile }));
				}
				case "focus": {
					const targetId = readStringParam(params, "targetId", { required: true });
					if (proxyRequest) return jsonResult(await proxyRequest({
						method: "POST",
						path: "/tabs/focus",
						profile,
						body: { targetId }
					}));
					await browserFocusTab(baseUrl, targetId, { profile });
					return jsonResult({ ok: true });
				}
				case "close": {
					const targetId = readStringParam(params, "targetId");
					if (proxyRequest) return jsonResult(targetId ? await proxyRequest({
						method: "DELETE",
						path: `/tabs/${encodeURIComponent(targetId)}`,
						profile
					}) : await proxyRequest({
						method: "POST",
						path: "/act",
						profile,
						body: { kind: "close" }
					}));
					if (targetId) await browserCloseTab(baseUrl, targetId, { profile });
					else await browserAct(baseUrl, { kind: "close" }, { profile });
					return jsonResult({ ok: true });
				}
				case "snapshot": {
					const snapshotDefaults = loadConfig().browser?.snapshotDefaults;
					const format = params.snapshotFormat === "ai" || params.snapshotFormat === "aria" ? params.snapshotFormat : "ai";
					const mode = params.mode === "efficient" ? "efficient" : format === "ai" && snapshotDefaults?.mode === "efficient" ? "efficient" : void 0;
					const labels = typeof params.labels === "boolean" ? params.labels : void 0;
					const refs = params.refs === "aria" || params.refs === "role" ? params.refs : void 0;
					const hasMaxChars = Object.hasOwn(params, "maxChars");
					const targetId = typeof params.targetId === "string" ? params.targetId.trim() : void 0;
					const limit = typeof params.limit === "number" && Number.isFinite(params.limit) ? params.limit : void 0;
					const maxChars = typeof params.maxChars === "number" && Number.isFinite(params.maxChars) && params.maxChars > 0 ? Math.floor(params.maxChars) : void 0;
					const resolvedMaxChars = format === "ai" ? hasMaxChars ? maxChars : mode === "efficient" ? void 0 : DEFAULT_AI_SNAPSHOT_MAX_CHARS : void 0;
					const interactive = typeof params.interactive === "boolean" ? params.interactive : void 0;
					const compact = typeof params.compact === "boolean" ? params.compact : void 0;
					const depth = typeof params.depth === "number" && Number.isFinite(params.depth) ? params.depth : void 0;
					const selector = typeof params.selector === "string" ? params.selector.trim() : void 0;
					const frame = typeof params.frame === "string" ? params.frame.trim() : void 0;
					const snapshot = proxyRequest ? await proxyRequest({
						method: "GET",
						path: "/snapshot",
						profile,
						query: {
							format,
							targetId,
							limit,
							...typeof resolvedMaxChars === "number" ? { maxChars: resolvedMaxChars } : {},
							refs,
							interactive,
							compact,
							depth,
							selector,
							frame,
							labels,
							mode
						}
					}) : await browserSnapshot(baseUrl, {
						format,
						targetId,
						limit,
						...typeof resolvedMaxChars === "number" ? { maxChars: resolvedMaxChars } : {},
						refs,
						interactive,
						compact,
						depth,
						selector,
						frame,
						labels,
						mode,
						profile
					});
					if (snapshot.format === "ai") {
						const wrappedSnapshot = wrapExternalContent(snapshot.snapshot ?? "", {
							source: "browser",
							includeWarning: true
						});
						const safeDetails = {
							ok: true,
							format: snapshot.format,
							targetId: snapshot.targetId,
							url: snapshot.url,
							truncated: snapshot.truncated,
							stats: snapshot.stats,
							refs: snapshot.refs ? Object.keys(snapshot.refs).length : void 0,
							labels: snapshot.labels,
							labelsCount: snapshot.labelsCount,
							labelsSkipped: snapshot.labelsSkipped,
							imagePath: snapshot.imagePath,
							imageType: snapshot.imageType,
							externalContent: {
								untrusted: true,
								source: "browser",
								kind: "snapshot",
								format: "ai",
								wrapped: true
							}
						};
						if (labels && snapshot.imagePath) return await imageResultFromFile({
							label: "browser:snapshot",
							path: snapshot.imagePath,
							extraText: wrappedSnapshot,
							details: safeDetails
						});
						return {
							content: [{
								type: "text",
								text: wrappedSnapshot
							}],
							details: safeDetails
						};
					}
					{
						const wrapped = wrapBrowserExternalJson({
							kind: "snapshot",
							payload: snapshot
						});
						return {
							content: [{
								type: "text",
								text: wrapped.wrappedText
							}],
							details: {
								...wrapped.safeDetails,
								format: "aria",
								targetId: snapshot.targetId,
								url: snapshot.url,
								nodeCount: snapshot.nodes.length,
								externalContent: {
									untrusted: true,
									source: "browser",
									kind: "snapshot",
									format: "aria",
									wrapped: true
								}
							}
						};
					}
				}
				case "screenshot": {
					const targetId = readStringParam(params, "targetId");
					const fullPage = Boolean(params.fullPage);
					const ref = readStringParam(params, "ref");
					const element = readStringParam(params, "element");
					const type = params.type === "jpeg" ? "jpeg" : "png";
					const result = proxyRequest ? await proxyRequest({
						method: "POST",
						path: "/screenshot",
						profile,
						body: {
							targetId,
							fullPage,
							ref,
							element,
							type
						}
					}) : await browserScreenshotAction(baseUrl, {
						targetId,
						fullPage,
						ref,
						element,
						type,
						profile
					});
					return await imageResultFromFile({
						label: "browser:screenshot",
						path: result.path,
						details: result
					});
				}
				case "navigate": {
					const targetUrl = readStringParam(params, "targetUrl", { required: true });
					const targetId = readStringParam(params, "targetId");
					if (proxyRequest) return jsonResult(await proxyRequest({
						method: "POST",
						path: "/navigate",
						profile,
						body: {
							url: targetUrl,
							targetId
						}
					}));
					return jsonResult(await browserNavigate(baseUrl, {
						url: targetUrl,
						targetId,
						profile
					}));
				}
				case "console": {
					const level = typeof params.level === "string" ? params.level.trim() : void 0;
					const targetId = typeof params.targetId === "string" ? params.targetId.trim() : void 0;
					if (proxyRequest) {
						const result = await proxyRequest({
							method: "GET",
							path: "/console",
							profile,
							query: {
								level,
								targetId
							}
						});
						const wrapped = wrapBrowserExternalJson({
							kind: "console",
							payload: result,
							includeWarning: false
						});
						return {
							content: [{
								type: "text",
								text: wrapped.wrappedText
							}],
							details: {
								...wrapped.safeDetails,
								targetId: typeof result.targetId === "string" ? result.targetId : void 0,
								messageCount: Array.isArray(result.messages) ? result.messages.length : void 0
							}
						};
					}
					{
						const result = await browserConsoleMessages(baseUrl, {
							level,
							targetId,
							profile
						});
						const wrapped = wrapBrowserExternalJson({
							kind: "console",
							payload: result,
							includeWarning: false
						});
						return {
							content: [{
								type: "text",
								text: wrapped.wrappedText
							}],
							details: {
								...wrapped.safeDetails,
								targetId: result.targetId,
								messageCount: result.messages.length
							}
						};
					}
				}
				case "pdf": {
					const targetId = typeof params.targetId === "string" ? params.targetId.trim() : void 0;
					const result = proxyRequest ? await proxyRequest({
						method: "POST",
						path: "/pdf",
						profile,
						body: { targetId }
					}) : await browserPdfSave(baseUrl, {
						targetId,
						profile
					});
					return {
						content: [{
							type: "text",
							text: `FILE:${result.path}`
						}],
						details: result
					};
				}
				case "upload": {
					const paths = Array.isArray(params.paths) ? params.paths.map((p) => String(p)) : [];
					if (paths.length === 0) throw new Error("paths required");
					const uploadPathsResult = resolvePathsWithinRoot({
						rootDir: DEFAULT_UPLOAD_DIR,
						requestedPaths: paths,
						scopeLabel: `uploads directory (${DEFAULT_UPLOAD_DIR})`
					});
					if (!uploadPathsResult.ok) throw new Error(uploadPathsResult.error);
					const normalizedPaths = uploadPathsResult.paths;
					const ref = readStringParam(params, "ref");
					const inputRef = readStringParam(params, "inputRef");
					const element = readStringParam(params, "element");
					const targetId = typeof params.targetId === "string" ? params.targetId.trim() : void 0;
					const timeoutMs = typeof params.timeoutMs === "number" && Number.isFinite(params.timeoutMs) ? params.timeoutMs : void 0;
					if (proxyRequest) return jsonResult(await proxyRequest({
						method: "POST",
						path: "/hooks/file-chooser",
						profile,
						body: {
							paths: normalizedPaths,
							ref,
							inputRef,
							element,
							targetId,
							timeoutMs
						}
					}));
					return jsonResult(await browserArmFileChooser(baseUrl, {
						paths: normalizedPaths,
						ref,
						inputRef,
						element,
						targetId,
						timeoutMs,
						profile
					}));
				}
				case "dialog": {
					const accept = Boolean(params.accept);
					const promptText = typeof params.promptText === "string" ? params.promptText : void 0;
					const targetId = typeof params.targetId === "string" ? params.targetId.trim() : void 0;
					const timeoutMs = typeof params.timeoutMs === "number" && Number.isFinite(params.timeoutMs) ? params.timeoutMs : void 0;
					if (proxyRequest) return jsonResult(await proxyRequest({
						method: "POST",
						path: "/hooks/dialog",
						profile,
						body: {
							accept,
							promptText,
							targetId,
							timeoutMs
						}
					}));
					return jsonResult(await browserArmDialog(baseUrl, {
						accept,
						promptText,
						targetId,
						timeoutMs,
						profile
					}));
				}
				case "act": {
					const request = params.request;
					if (!request || typeof request !== "object") throw new Error("request required");
					try {
						return jsonResult(proxyRequest ? await proxyRequest({
							method: "POST",
							path: "/act",
							profile,
							body: request
						}) : await browserAct(baseUrl, request, { profile }));
					} catch (err) {
						const msg = String(err);
						if (msg.includes("404:") && msg.includes("tab not found") && profile === "chrome") {
							if (!(proxyRequest ? (await proxyRequest({
								method: "GET",
								path: "/tabs",
								profile
							})).tabs ?? [] : await browserTabs(baseUrl, { profile }).catch(() => [])).length) throw new Error("No Chrome tabs are attached via the Minion Browser Relay extension. Click the toolbar icon on the tab you want to control (badge ON), then retry.", { cause: err });
							throw new Error(`Chrome tab not found (stale targetId?). Run action=tabs profile="chrome" and use one of the returned targetIds.`, { cause: err });
						}
						throw err;
					}
				}
				case "download": {
					const ref = readStringParam(params, "ref", { required: true });
					const savePath = readStringParam(params, "savePath");
					const targetId = typeof params.targetId === "string" ? params.targetId.trim() : void 0;
					const timeoutMs = typeof params.timeoutMs === "number" && Number.isFinite(params.timeoutMs) ? params.timeoutMs : void 0;
					return jsonResult(proxyRequest ? await proxyRequest({
						method: "POST",
						path: "/download",
						profile,
						body: {
							ref,
							path: savePath,
							targetId,
							timeoutMs
						}
					}) : await browserDownload(baseUrl, {
						ref,
						path: savePath ?? DEFAULT_DOWNLOAD_DIR,
						targetId,
						timeoutMs,
						profile
					}));
				}
				case "wait_download": {
					const savePath = readStringParam(params, "savePath");
					const targetId = typeof params.targetId === "string" ? params.targetId.trim() : void 0;
					const timeoutMs = typeof params.timeoutMs === "number" && Number.isFinite(params.timeoutMs) ? params.timeoutMs : void 0;
					return jsonResult(proxyRequest ? await proxyRequest({
						method: "POST",
						path: "/wait/download",
						profile,
						body: {
							path: savePath,
							targetId,
							timeoutMs
						}
					}) : await browserWaitForDownload(baseUrl, {
						path: savePath,
						targetId,
						timeoutMs,
						profile
					}));
				}
				default: throw new Error(`Unknown action: ${action}`);
			}
		}
	}, "builtin:browser");
}

//#endregion
export { createBrowserTool };