import { getCredentialVault } from "./credential-vault.mjs";
//#region extensions/crypto/src/services/agent-orchestrator.ts
function detectProvider() {
	const explicit = process.env.OPENCLAWNCH_LLM_PROVIDER;
	if (explicit === "bankr" || explicit === "openrouter" || explicit === "openai" || explicit === "anthropic") return explicit;
	if (process.env.ANTHROPIC_API_KEY) return "anthropic";
	if (process.env.OPENROUTER_API_KEY) return "openrouter";
	if (process.env.OPENAI_API_KEY) return "openai";
	if (process.env.BANKR_LLM_KEY) return "bankr";
	return "anthropic";
}
function getApiKey(provider) {
	const vault = getCredentialVault();
	switch (provider) {
		case "anthropic": return vault.getSecret("llm.anthropic.apiKey", "agent_orchestrator") ?? null;
		case "openrouter": return vault.getSecret("llm.openrouter.apiKey", "agent_orchestrator") ?? null;
		case "openai": return vault.getSecret("llm.openai.apiKey", "agent_orchestrator") ?? null;
		case "bankr": return vault.getSecret("llm.bankr.key", "agent_orchestrator") ?? null;
		default: return null;
	}
}
/** Same model map as model-command.ts — maps shortcuts to provider-specific IDs. */
const SUB_AGENT_MODELS = {
	"haiku": {
		anthropic: "claude-haiku-4-20250514",
		openrouter: "anthropic/claude-haiku-4-20250514",
		openai: "claude-haiku-4-20250514",
		bankr: "claude-haiku-4.5"
	},
	"sonnet": {
		anthropic: "claude-sonnet-4-20250514",
		openrouter: "anthropic/claude-sonnet-4-20250514",
		openai: "claude-sonnet-4-20250514",
		bankr: "claude-sonnet-4.6"
	},
	"opus": {
		anthropic: "claude-opus-4-20250514",
		openrouter: "anthropic/claude-opus-4-20250514",
		openai: "claude-opus-4-20250514",
		bankr: "claude-opus-4.6"
	},
	"gpt-mini": {
		anthropic: "claude-haiku-4-20250514",
		openrouter: "openai/gpt-5-mini",
		openai: "gpt-5-mini",
		bankr: "gpt-5-mini"
	},
	"gpt": {
		anthropic: "claude-sonnet-4-20250514",
		openrouter: "openai/gpt-5.2",
		openai: "gpt-5.2",
		bankr: "gpt-5.2"
	}
};
function resolveModel(shortcut, provider) {
	const mapping = SUB_AGENT_MODELS[shortcut];
	if (mapping) return mapping[provider];
	return shortcut;
}
async function callAnthropic(apiKey, model, system, messages, tools, maxTokens, temperature, signal) {
	const body = {
		model,
		max_tokens: maxTokens,
		temperature,
		system,
		messages
	};
	if (tools.length > 0) body.tools = tools;
	const response = await fetch("https://api.anthropic.com/v1/messages", {
		method: "POST",
		headers: {
			"x-api-key": apiKey,
			"anthropic-version": "2023-06-01",
			"content-type": "application/json"
		},
		body: JSON.stringify(body),
		signal
	});
	if (!response.ok) {
		const text = await response.text().catch(() => "");
		throw new Error(`Anthropic API ${response.status}: ${text.slice(0, 200)}`);
	}
	const data = await response.json();
	return {
		content: data.content ?? [],
		usage: data.usage ?? {
			input_tokens: 0,
			output_tokens: 0
		}
	};
}
const PROVIDER_URLS = {
	openrouter: "https://openrouter.ai/api/v1/chat/completions",
	openai: "https://api.openai.com/v1/chat/completions",
	bankr: "https://llm.bankr.bot/v1/chat/completions"
};
async function callOpenAICompat(provider, apiKey, model, system, messages, tools, maxTokens, temperature, signal) {
	const url = PROVIDER_URLS[provider];
	if (!url) throw new Error(`Unknown provider: ${provider}`);
	const body = {
		model,
		max_tokens: maxTokens,
		temperature,
		messages: [{
			role: "system",
			content: system
		}, ...messages]
	};
	if (tools.length > 0) body.tools = tools;
	const response = await fetch(url, {
		method: "POST",
		headers: {
			"Authorization": `Bearer ${apiKey}`,
			"Content-Type": "application/json"
		},
		body: JSON.stringify(body),
		signal
	});
	if (!response.ok) {
		const text = await response.text().catch(() => "");
		throw new Error(`${provider} API ${response.status}: ${text.slice(0, 200)}`);
	}
	const data = await response.json();
	return {
		message: (data.choices?.[0])?.message ?? {
			role: "assistant",
			content: ""
		},
		usage: data.usage ?? {
			prompt_tokens: 0,
			completion_tokens: 0
		}
	};
}
/**
* Build tool schemas for the sub-agent from the registered tool list.
* Only includes tools that are in the agent's allowedTools list.
*/
function buildToolSchemas(allowedTools, registeredTools) {
	if (allowedTools.length === 0) return [];
	const allowed = new Set(allowedTools);
	return registeredTools.filter((t) => allowed.has(t.name)).map((t) => ({
		name: t.name,
		description: t.description,
		input_schema: t.parameters ?? {
			type: "object",
			properties: {}
		}
	}));
}
/**
* Execute a task with a sub-agent.
*
* 1. Sends the task to the sub-agent's LLM with its system prompt
* 2. If the LLM requests tool calls, executes them via the dispatcher
* 3. Feeds tool results back and continues until the LLM produces a final response
* 4. Enforces maxToolCalls and timeout
*/
async function executeSubAgent(agent, task, dispatcher, registeredTools) {
	const startTime = Date.now();
	const provider = detectProvider();
	const apiKey = getApiKey(provider);
	if (!apiKey) return {
		response: "",
		toolCalls: [],
		tokensUsed: 0,
		durationMs: Date.now() - startTime,
		status: "error",
		error: `No API key found for provider "${provider}". Set ANTHROPIC_API_KEY, OPENROUTER_API_KEY, OPENAI_API_KEY, or BANKR_LLM_KEY.`
	};
	const model = resolveModel(agent.model, provider);
	const toolSchemas = buildToolSchemas(agent.allowedTools, registeredTools);
	const toolCallRecords = [];
	let totalTokens = 0;
	const abort = AbortSignal.timeout(agent.timeoutMs);
	try {
		if (provider === "anthropic") return await runAnthropicLoop(apiKey, model, agent, task, toolSchemas, dispatcher, toolCallRecords, startTime, abort);
		else return await runOpenAILoop(provider, apiKey, model, agent, task, toolSchemas, dispatcher, toolCallRecords, startTime, abort);
	} catch (err) {
		const isTimeout = err instanceof Error && (err.name === "TimeoutError" || err.name === "AbortError" || err.message.includes("timed out"));
		return {
			response: toolCallRecords.length > 0 ? `Sub-agent ${isTimeout ? "timed out" : "failed"} after ${toolCallRecords.length} tool call(s). Partial results may be available in the tool call log.` : "",
			toolCalls: toolCallRecords,
			tokensUsed: totalTokens,
			durationMs: Date.now() - startTime,
			status: isTimeout ? "timeout" : "error",
			error: err instanceof Error ? err.message : String(err)
		};
	}
}
async function runAnthropicLoop(apiKey, model, agent, task, toolSchemas, dispatcher, toolCallRecords, startTime, signal) {
	const messages = [{
		role: "user",
		content: [{
			type: "text",
			text: task
		}]
	}];
	let totalTokens = 0;
	let callCount = 0;
	while (callCount <= agent.maxToolCalls) {
		const result = await callAnthropic(apiKey, model, agent.systemPrompt, messages, toolSchemas, agent.maxTokens, agent.temperature, signal);
		totalTokens += result.usage.input_tokens + result.usage.output_tokens;
		const toolUses = result.content.filter((c) => c.type === "tool_use");
		if (toolUses.length === 0) return {
			response: result.content.filter((c) => c.type === "text").map((t) => t.text).join("\n"),
			toolCalls: toolCallRecords,
			tokensUsed: totalTokens,
			durationMs: Date.now() - startTime,
			status: "completed"
		};
		messages.push({
			role: "assistant",
			content: result.content
		});
		const toolResults = [];
		for (const tu of toolUses) {
			callCount++;
			if (callCount > agent.maxToolCalls) {
				toolResults.push({
					type: "tool_result",
					tool_use_id: tu.id,
					content: "Error: Maximum tool calls reached. Please provide your final response.",
					is_error: true
				});
				break;
			}
			const callStart = Date.now();
			try {
				const toolResult = await dispatcher.call(tu.name, tu.input);
				const resultText = typeof toolResult === "string" ? toolResult : toolResult?.content?.[0]?.text ?? JSON.stringify(toolResult);
				toolCallRecords.push({
					tool: tu.name,
					args: tu.input,
					result: String(resultText).slice(0, 500),
					durationMs: Date.now() - callStart
				});
				toolResults.push({
					type: "tool_result",
					tool_use_id: tu.id,
					content: String(resultText)
				});
			} catch (err) {
				const errMsg = err instanceof Error ? err.message : String(err);
				toolCallRecords.push({
					tool: tu.name,
					args: tu.input,
					result: `Error: ${errMsg}`,
					durationMs: Date.now() - callStart
				});
				toolResults.push({
					type: "tool_result",
					tool_use_id: tu.id,
					content: `Error: ${errMsg}`,
					is_error: true
				});
			}
		}
		messages.push({
			role: "user",
			content: toolResults
		});
	}
	return {
		response: `Sub-agent reached maximum tool calls (${agent.maxToolCalls}). Results from ${toolCallRecords.length} call(s) are available.`,
		toolCalls: toolCallRecords,
		tokensUsed: totalTokens,
		durationMs: Date.now() - startTime,
		status: "max_calls"
	};
}
async function runOpenAILoop(provider, apiKey, model, agent, task, toolSchemas, dispatcher, toolCallRecords, startTime, signal) {
	const oaiTools = toolSchemas.map((t) => ({
		type: "function",
		function: {
			name: t.name,
			description: t.description,
			parameters: t.input_schema
		}
	}));
	const messages = [{
		role: "user",
		content: task
	}];
	let totalTokens = 0;
	let callCount = 0;
	while (callCount <= agent.maxToolCalls) {
		const result = await callOpenAICompat(provider, apiKey, model, agent.systemPrompt, messages, oaiTools, agent.maxTokens, agent.temperature, signal);
		totalTokens += result.usage.prompt_tokens + result.usage.completion_tokens;
		const msg = result.message;
		const toolCalls = msg.tool_calls ?? [];
		if (toolCalls.length === 0) return {
			response: msg.content ?? "",
			toolCalls: toolCallRecords,
			tokensUsed: totalTokens,
			durationMs: Date.now() - startTime,
			status: "completed"
		};
		messages.push(msg);
		for (const tc of toolCalls) {
			callCount++;
			if (callCount > agent.maxToolCalls) {
				messages.push({
					role: "tool",
					tool_call_id: tc.id,
					content: "Error: Maximum tool calls reached."
				});
				break;
			}
			const callStart = Date.now();
			let parsedArgs = {};
			try {
				parsedArgs = JSON.parse(tc.function.arguments);
			} catch {
				parsedArgs = {};
			}
			try {
				const toolResult = await dispatcher.call(tc.function.name, parsedArgs);
				const resultText = typeof toolResult === "string" ? toolResult : toolResult?.content?.[0]?.text ?? JSON.stringify(toolResult);
				toolCallRecords.push({
					tool: tc.function.name,
					args: parsedArgs,
					result: String(resultText).slice(0, 500),
					durationMs: Date.now() - callStart
				});
				messages.push({
					role: "tool",
					tool_call_id: tc.id,
					content: String(resultText)
				});
			} catch (err) {
				const errMsg = err instanceof Error ? err.message : String(err);
				toolCallRecords.push({
					tool: tc.function.name,
					args: parsedArgs,
					result: `Error: ${errMsg}`,
					durationMs: Date.now() - callStart
				});
				messages.push({
					role: "tool",
					tool_call_id: tc.id,
					content: `Error: ${errMsg}`
				});
			}
		}
	}
	return {
		response: `Sub-agent reached maximum tool calls (${agent.maxToolCalls}).`,
		toolCalls: toolCallRecords,
		tokensUsed: totalTokens,
		durationMs: Date.now() - startTime,
		status: "max_calls"
	};
}
//#endregion
export { buildToolSchemas, detectProvider, executeSubAgent, getApiKey };

//# sourceMappingURL=agent-orchestrator.mjs.map