import { i as jsonResult, r as errorResult } from "../../tool-helpers-CVAcqiCy.mjs";
import { addAllowedHost, guardedFetch, isAllowedEndpoint } from "./endpoint-allowlist.mjs";
//#region extensions/crypto/src/services/sandbox-runtime.ts
/**
* Sandbox Runtime — restricted execution environment for user-defined tools.
*
* Provides a controlled execution context that:
* 1. Enforces budget limits (max USD cost per execution)
* 2. Restricts network access to allowlisted endpoints only
* 3. Limits execution time (timeout)
* 4. Caps the number of sub-tool calls for composed/custom tools
* 5. Audits all actions for the tool creator's review
*
* The sandbox does NOT use V8 isolates or Worker threads — it enforces
* limits at the application layer via budget tracking, allowlist gating,
* and call counting. This is sufficient for our threat model (the agent
* is the executor, not arbitrary user code).
*/
/**
* Execute an API connector tool in the sandbox.
*/
async function executeApiConnector(def, args, ctx) {
	if (ctx.budgetRemainingUsd <= 0) return errorResult("Budget exhausted for this tool execution.");
	if (Date.now() - ctx.startedAt > ctx.timeoutMs) return errorResult("Tool execution timed out.");
	let path = def.path;
	for (const [key, value] of Object.entries(args)) path = path.replace(`{{${key}}}`, encodeURIComponent(String(value)));
	const url = `${def.baseUrl}${path}`;
	if (!isAllowedEndpoint(url)) try {
		addAllowedHost(new URL(def.baseUrl).hostname);
	} catch {
		return errorResult(`Cannot reach "${url}" — not in allowlist and baseUrl is invalid.`);
	}
	const headers = { "Accept": "application/json" };
	if (def.headers) for (const [key, value] of Object.entries(def.headers)) if (value.startsWith("$SECRET:")) headers[key] = value;
	else headers[key] = value;
	let body;
	if (def.bodyTemplate && [
		"POST",
		"PUT",
		"PATCH"
	].includes(def.method)) {
		body = def.bodyTemplate;
		for (const [key, value] of Object.entries(args)) body = body.replace(`{{${key}}}`, JSON.stringify(value).replace(/^"|"$/g, ""));
		headers["Content-Type"] = headers["Content-Type"] ?? "application/json";
	}
	ctx.auditLog.push({
		timestamp: Date.now(),
		action: "http_request",
		detail: `${def.method} ${url}`
	});
	ctx.callCount += 1;
	try {
		const response = await guardedFetch(url, {
			method: def.method,
			headers,
			body,
			signal: AbortSignal.timeout(def.timeoutMs ?? 15e3)
		});
		if (!response.ok) {
			const text = await response.text().catch(() => "");
			return errorResult(`API returned ${response.status}: ${text.slice(0, 200)}`);
		}
		const data = await response.json();
		let result = data;
		if (def.resultPath) for (const key of def.resultPath.split(".")) result = result?.[key];
		return jsonResult(result ?? data);
	} catch (err) {
		return errorResult(`API call failed: ${err instanceof Error ? err.message : String(err)}`);
	}
}
/**
* Execute a composed tool in the sandbox.
*/
async function executeComposedTool(steps, args, ctx, dispatcher) {
	const results = [];
	for (let i = 0; i < steps.length; i++) {
		const step = steps[i];
		if (ctx.budgetRemainingUsd <= 0) return errorResult(`Budget exhausted at step ${i + 1} ("${step.label}").`);
		if (Date.now() - ctx.startedAt > ctx.timeoutMs) return errorResult(`Timeout at step ${i + 1} ("${step.label}").`);
		if (ctx.callCount >= ctx.maxCalls) return errorResult(`Max tool calls reached at step ${i + 1} ("${step.label}").`);
		const resolvedArgs = {};
		for (const [key, value] of Object.entries(step.args)) if (typeof value === "string" && value.startsWith("$step.")) {
			const match = value.match(/^\$step\.(\d+)\.(.+)$/);
			if (match) {
				const stepIdx = parseInt(match[1], 10);
				const field = match[2];
				resolvedArgs[key] = results[stepIdx]?.[field] ?? value;
			} else resolvedArgs[key] = value;
		} else if (typeof value === "string" && value.startsWith("$arg.")) resolvedArgs[key] = args[value.slice(5)] ?? value;
		else resolvedArgs[key] = value;
		ctx.auditLog.push({
			timestamp: Date.now(),
			action: "tool_call",
			detail: `Step ${i + 1}: ${step.tool}(${JSON.stringify(resolvedArgs).slice(0, 100)})`
		});
		ctx.callCount += 1;
		try {
			const result = await dispatcher.call(step.tool, resolvedArgs);
			let parsed = result;
			if (result?.content?.[0]?.text) try {
				parsed = JSON.parse(result.content[0].text);
			} catch {
				parsed = result.content[0].text;
			}
			results.push(parsed);
			if (result?.isError && step.stopOnFailure !== false) return errorResult(`Step ${i + 1} ("${step.label}") failed: ${result.content?.[0]?.text ?? "unknown error"}`);
		} catch (err) {
			if (step.stopOnFailure !== false) return errorResult(`Step ${i + 1} ("${step.label}") threw: ${err instanceof Error ? err.message : String(err)}`);
			results.push({ error: err instanceof Error ? err.message : String(err) });
		}
	}
	return jsonResult({
		stepsCompleted: results.length,
		totalSteps: steps.length,
		results,
		lastResult: results[results.length - 1]
	});
}
/**
* Create a sandbox context for tool execution.
*/
function createSandboxContext(tool, userId) {
	const maxCalls = tool.definition.type === "custom" ? tool.definition.maxCalls ?? 5 : tool.definition.type === "composed" ? tool.definition.steps?.length ?? 10 : 1;
	return {
		tool,
		userId,
		budgetRemainingUsd: tool.maxBudgetUsd,
		callCount: 0,
		maxCalls,
		startedAt: Date.now(),
		timeoutMs: 3e4,
		auditLog: []
	};
}
//#endregion
export { createSandboxContext, executeApiConnector, executeComposedTool };

//# sourceMappingURL=sandbox-runtime.mjs.map