import "./chunk-BYW8Mqxw.js";
import { b as getEnvString, i as logger_default, t as getLogLevel, w as cliState_default } from "./logger-CATn0kWr.js";
import { r as importModule } from "./esm-tl6dENiB.js";
import "./fetch-BhuDaSn5.js";
import "./cache-C2ESUUfo.js";
import { n as getCachedResponse, r as initializeAgenticCache, t as cacheResponse } from "./cometapi-8_Ni4lN2.js";
import { createRequire } from "node:module";
import fs from "fs";
import path from "path";
import os from "os";
import dedent from "dedent";

//#region src/providers/opencode-sdk.ts
/**
* Check if promptfoo is in debug mode
*/
function isDebugMode() {
	return getLogLevel() === "debug";
}
/**
* Maximum number of sessions to keep in memory to prevent unbounded growth
*/
const MAX_SESSIONS = 100;
/**
* Resolve ESM-only package entry point by reading package.json exports
* Handles packages that only have "import" condition (no "require" condition)
*
* @param packageName - The package name (e.g., '@opencode-ai/sdk')
* @param basePath - Base path for resolution
* @returns Absolute path to the ESM entry point
*/
function resolveEsmPackage(packageName, basePath) {
	const require = createRequire(path.join(basePath, "package.json"));
	let packageJsonPath;
	try {
		packageJsonPath = require.resolve(`${packageName}/package.json`);
	} catch {
		packageJsonPath = path.join(basePath, "node_modules", ...packageName.split("/"), "package.json");
		if (!fs.existsSync(packageJsonPath)) throw new Error(`Cannot find ${packageName}/package.json`);
	}
	const packageDir = path.dirname(packageJsonPath);
	const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
	let esmEntry;
	if (packageJson.exports) {
		const mainExport = packageJson.exports["."] || packageJson.exports;
		if (typeof mainExport === "string") esmEntry = mainExport;
		else if (typeof mainExport === "object") esmEntry = mainExport.import || mainExport.default;
	}
	if (!esmEntry) esmEntry = packageJson.module || packageJson.main;
	if (!esmEntry) throw new Error(`Cannot find ESM entry point in ${packageName}/package.json`);
	return path.join(packageDir, esmEntry);
}
/**
* Helper to load the OpenCode SDK ESM module
*
* Uses a two-phase approach:
* 1. Try simple dynamic import - works when SDK is in same node_modules tree
* 2. Fall back to smart ESM resolution for edge cases (pnpm, global installs, monorepos)
*/
async function loadOpenCodeSDK() {
	try {
		logger_default.debug("Attempting simple dynamic import of @opencode-ai/sdk");
		return await import("@opencode-ai/sdk");
	} catch {
		logger_default.debug("Simple import failed, falling back to smart ESM resolution");
	}
	try {
		const modulePath = resolveEsmPackage("@opencode-ai/sdk", cliState_default.basePath && path.isAbsolute(cliState_default.basePath) ? cliState_default.basePath : process.cwd());
		logger_default.debug(`Resolved OpenCode SDK path: ${modulePath}`);
		return await importModule(modulePath);
	} catch (err) {
		logger_default.error(`Failed to load OpenCode SDK: ${err}`);
		const stack = err.stack;
		if (stack) logger_default.error(stack);
		throw new Error(dedent`The @opencode-ai/sdk package is required but not installed.

      To use the OpenCode SDK provider, install it with:
        npm install @opencode-ai/sdk

      For more information, see: https://www.promptfoo.dev/docs/providers/opencode-sdk/`);
	}
}
var OpenCodeSDKProvider = class {
	config;
	env;
	providerId = "opencode:sdk";
	opencodeModule;
	client;
	server;
	sessions = /* @__PURE__ */ new Map();
	sessionOrder = [];
	constructor(options = {}) {
		const { config, env, id } = options;
		this.config = config ?? {};
		this.env = env;
		this.providerId = id ?? this.providerId;
	}
	id() {
		return this.providerId;
	}
	/**
	* Get API key based on provider_id or common environment variables
	*/
	getApiKey() {
		if (this.config?.apiKey) return this.config.apiKey;
		const providerId = this.config?.provider_id?.toLowerCase();
		if (providerId === "anthropic") return this.env?.ANTHROPIC_API_KEY || getEnvString("ANTHROPIC_API_KEY");
		if (providerId === "openai") return this.env?.OPENAI_API_KEY || getEnvString("OPENAI_API_KEY");
		if (providerId === "google") return this.env?.GOOGLE_API_KEY || getEnvString("GOOGLE_API_KEY");
		return this.env?.ANTHROPIC_API_KEY || getEnvString("ANTHROPIC_API_KEY") || this.env?.OPENAI_API_KEY || getEnvString("OPENAI_API_KEY");
	}
	toString() {
		return "[OpenCode SDK Provider]";
	}
	async cleanup() {
		if (!this.config.persist_sessions) for (const sessionId of this.sessions.values()) try {
			await this.client?.session?.delete({ path: { id: sessionId } });
		} catch (err) {
			logger_default.debug(`Failed to delete session ${sessionId}: ${err}`);
		}
		this.sessions.clear();
		this.sessionOrder = [];
		if (this.server) {
			try {
				this.server.close();
			} catch (err) {
				logger_default.debug(`Failed to close OpenCode server: ${err}`);
			}
			this.server = void 0;
		}
	}
	/**
	* Build the tools configuration based on config and defaults
	*/
	buildToolsConfig(config) {
		if (config.tools) return config.tools;
		if (!config.working_dir) return {
			bash: false,
			edit: false,
			write: false,
			read: false,
			grep: false,
			glob: false,
			list: false,
			patch: false,
			todowrite: false,
			todoread: false,
			webfetch: false,
			question: false,
			skill: false,
			lsp: false
		};
		return {
			bash: false,
			edit: false,
			write: false,
			read: true,
			grep: true,
			glob: true,
			list: true,
			patch: false,
			todowrite: false,
			todoread: false,
			webfetch: false,
			question: false,
			skill: false,
			lsp: false
		};
	}
	/**
	* Add a session to the cache with LRU eviction
	*/
	addSession(cacheKey, sessionId) {
		while (this.sessions.size >= MAX_SESSIONS && this.sessionOrder.length > 0) {
			const oldestKey = this.sessionOrder.shift();
			if (oldestKey) {
				const oldSessionId = this.sessions.get(oldestKey);
				this.sessions.delete(oldestKey);
				if (oldSessionId) this.client?.session?.delete({ path: { id: oldSessionId } }).catch((err) => {
					logger_default.debug(`Failed to delete evicted session ${oldSessionId}: ${err}`);
				});
			}
		}
		this.sessions.set(cacheKey, sessionId);
		this.sessionOrder.push(cacheKey);
	}
	async callApi(prompt, context, callOptions) {
		const config = {
			...this.config,
			...context?.prompt?.config
		};
		let isTempDir = false;
		let workingDir;
		if (config.working_dir) {
			workingDir = path.isAbsolute(config.working_dir) ? config.working_dir : path.resolve(process.cwd(), config.working_dir);
			let stats;
			try {
				stats = fs.statSync(workingDir);
			} catch (err) {
				throw new Error(`Working directory ${config.working_dir} (resolved to ${workingDir}) does not exist or isn't accessible: ${err.message}`);
			}
			if (!stats.isDirectory()) throw new Error(`Working directory ${config.working_dir} (resolved to ${workingDir}) is not a directory`);
		} else {
			isTempDir = true;
			workingDir = fs.mkdtempSync(path.join(os.tmpdir(), "promptfoo-opencode-sdk-"));
		}
		const cacheKeyData = {
			prompt,
			provider_id: config.provider_id,
			model: config.model,
			tools: this.buildToolsConfig(config),
			permission: config.permission,
			agent: config.agent,
			custom_agent: config.custom_agent
		};
		const cacheResult = await initializeAgenticCache({
			cacheKeyPrefix: "opencode:sdk",
			workingDir: config.working_dir ? workingDir : void 0,
			bustCache: context?.bustCache
		}, cacheKeyData);
		const cachedResponse = await getCachedResponse(cacheResult, "OpenCode SDK");
		if (cachedResponse) return cachedResponse;
		if (callOptions?.abortSignal?.aborted) return { error: "OpenCode SDK call aborted before it started" };
		try {
			if (!this.opencodeModule) this.opencodeModule = await loadOpenCodeSDK();
			if (!this.client) {
				const { createOpencode, createOpencodeClient } = this.opencodeModule;
				if (config.baseUrl) this.client = createOpencodeClient({ baseUrl: config.baseUrl });
				else {
					const homeDir = os.homedir();
					const opencodeBinPath = path.join(homeDir, ".opencode", "bin");
					const serverEnv = { ...process.env };
					if (!serverEnv.PATH?.includes(opencodeBinPath)) {
						serverEnv.PATH = `${opencodeBinPath}:${serverEnv.PATH ?? ""}`;
						logger_default.debug(`Added ${opencodeBinPath} to PATH for OpenCode CLI`);
					}
					if (config.log_level === "debug" || isDebugMode()) {
						serverEnv.DEBUG = serverEnv.DEBUG || "opencode:*";
						logger_default.debug("[OpenCode SDK] Debug mode enabled, synced from promptfoo log level");
					}
					const serverOptions = {
						hostname: config.hostname ?? "127.0.0.1",
						port: config.port ?? 0,
						timeout: config.timeout ?? 3e4,
						env: serverEnv
					};
					const serverConfig = {};
					if (config.mcp && Object.keys(config.mcp).length > 0) {
						serverConfig.mcp = config.mcp;
						logger_default.debug(`[OpenCode SDK] Configuring MCP servers: ${Object.keys(config.mcp).join(", ")}`);
					}
					if (config.custom_agent) {
						serverConfig.agent = { custom: {
							description: config.custom_agent.description,
							model: config.custom_agent.model,
							temperature: config.custom_agent.temperature,
							top_p: config.custom_agent.top_p,
							tools: config.custom_agent.tools,
							permission: config.custom_agent.permission,
							prompt: config.custom_agent.prompt,
							mode: config.custom_agent.mode ?? "primary",
							maxSteps: config.custom_agent.steps ?? config.custom_agent.maxSteps,
							color: config.custom_agent.color,
							disable: config.custom_agent.disable,
							hidden: config.custom_agent.hidden
						} };
						logger_default.debug(`[OpenCode SDK] Configuring custom agent: ${config.custom_agent.description}`);
					}
					if (config.permission) {
						serverConfig.permission = config.permission;
						logger_default.debug("[OpenCode SDK] Configuring global permissions");
					}
					const toolsConfig$1 = this.buildToolsConfig(config);
					if (toolsConfig$1) serverConfig.tools = toolsConfig$1;
					if (Object.keys(serverConfig).length > 0) serverOptions.config = serverConfig;
					const opencode = await createOpencode(serverOptions);
					this.client = opencode.client;
					this.server = opencode.server;
					logger_default.debug(`OpenCode server started at ${opencode.server.url}`);
				}
			}
			let sessionId;
			const sessionCacheKey = cacheResult.cacheKey;
			if (config.session_id) sessionId = config.session_id;
			else if (config.persist_sessions && sessionCacheKey && this.sessions.has(sessionCacheKey)) sessionId = this.sessions.get(sessionCacheKey);
			else {
				const createResult = await this.client.session.create({ body: { title: `promptfoo-${Date.now()}` } });
				const extractedId = createResult?.data?.id ?? createResult?.id;
				if (!extractedId) throw new Error("Failed to get session ID from OpenCode SDK response");
				sessionId = extractedId;
				if (config.persist_sessions && sessionCacheKey) this.addSession(sessionCacheKey, sessionId);
			}
			const promptBody = { parts: [{
				type: "text",
				text: prompt
			}] };
			if (config.provider_id || config.model) promptBody.model = {
				providerID: config.provider_id ?? "",
				modelID: config.model ?? ""
			};
			const toolsConfig = this.buildToolsConfig(config);
			if (toolsConfig) promptBody.tools = toolsConfig;
			if (config.agent) promptBody.agent = config.agent;
			else if (config.custom_agent) promptBody.agent = "custom";
			if (config.custom_agent?.prompt) promptBody.system = config.custom_agent.prompt;
			if (config.permission) promptBody.permission = config.permission;
			const promptOptions = {
				path: { id: sessionId },
				body: promptBody
			};
			if (config.working_dir) promptOptions.query = { directory: workingDir };
			logger_default.debug(`OpenCode SDK prompt options:`, {
				path: promptOptions.path,
				body: promptBody,
				query: promptOptions.query
			});
			const response = await this.client.session.prompt(promptOptions);
			logger_default.debug(`OpenCode SDK response received`);
			const responseData = response?.data;
			const assistantMessage = responseData?.info;
			const parts = responseData?.parts ?? [];
			let output = "";
			for (const part of parts) if (part.type === "text" && part.text) output += (output ? "\n" : "") + part.text;
			const raw = JSON.stringify(response);
			const tokens = assistantMessage?.tokens;
			const tokenUsage = tokens ? {
				prompt: tokens.input ?? 0,
				completion: tokens.output ?? 0,
				total: (tokens.input ?? 0) + (tokens.output ?? 0)
			} : void 0;
			const cost = assistantMessage?.cost ?? 0;
			const providerResponse = {
				output,
				tokenUsage,
				cost,
				raw,
				sessionId
			};
			await cacheResponse(cacheResult, providerResponse, "OpenCode SDK");
			logger_default.debug(`OpenCode SDK response: ${output.substring(0, 100)}...`);
			return providerResponse;
		} catch (error) {
			if (error?.name === "AbortError" || callOptions?.abortSignal?.aborted) {
				logger_default.warn("OpenCode SDK call aborted");
				return { error: "OpenCode SDK call aborted" };
			}
			if (error?.code === "ENOENT" && error?.message?.includes("opencode")) {
				const cliError = dedent`The OpenCode CLI is required but not installed.

          The OpenCode SDK requires the 'opencode' CLI to be installed and available in your PATH.

          Install it with:
            curl -fsSL https://opencode.ai/install | bash

          Or see: https://opencode.ai for other installation methods.`;
				logger_default.error(cliError);
				return { error: cliError };
			}
			logger_default.error(`Error calling OpenCode SDK: ${error}`);
			return { error: `Error calling OpenCode SDK: ${error.message || error}` };
		} finally {
			if (isTempDir && workingDir) fs.rmSync(workingDir, {
				recursive: true,
				force: true
			});
			const sessionCacheKey = cacheResult.cacheKey;
			if (!config.persist_sessions && !config.session_id && sessionCacheKey) {
				this.sessions.delete(sessionCacheKey);
				const idx = this.sessionOrder.indexOf(sessionCacheKey);
				if (idx !== -1) this.sessionOrder.splice(idx, 1);
			}
		}
	}
};

//#endregion
export { OpenCodeSDKProvider };
//# sourceMappingURL=opencode-sdk-CXhSvYmT.js.map