import { D as withGenAISpan, E as getTraceparent, U as importModule, W as resolvePackageEntryPoint, ct as getEnvString, it as logger_default, lt as cliState_default } from "./server/index.js";
import fs from "fs";
import path from "path";
import dedent from "dedent";
import crypto from "crypto";
import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";

//#region src/providers/openai/codex-sdk.ts
/**
* Helper to load the OpenAI Codex SDK ESM module
* Uses resolvePackageEntryPoint to handle ESM-only packages with restrictive exports
*/
async function loadCodexSDK() {
	const codexPath = resolvePackageEntryPoint("@openai/codex-sdk", cliState_default.basePath && path.isAbsolute(cliState_default.basePath) ? cliState_default.basePath : process.cwd());
	if (!codexPath) throw new Error(dedent`The @openai/codex-sdk package is required but not installed.

      To use the OpenAI Codex SDK provider, install it with:
        npm install @openai/codex-sdk

      Requires Node.js 18+.

      For more information, see: https://www.promptfoo.dev/docs/providers/openai-codex-sdk/`);
	try {
		return await importModule(codexPath);
	} catch (err) {
		logger_default.error(`Failed to load OpenAI Codex SDK: ${err}`);
		if (err.stack) logger_default.error(err.stack);
		throw new Error(dedent`Failed to load @openai/codex-sdk.

      The package was found but could not be loaded. This may be due to:
      - Incompatible Node.js version (requires Node.js 18+)
      - Corrupted installation

      Try reinstalling:
        npm install @openai/codex-sdk

      For more information, see: https://www.promptfoo.dev/docs/providers/openai-codex-sdk/`);
	}
}
const CODEX_MODEL_PRICING = {
	"gpt-5.2": {
		input: 2,
		output: 8
	},
	"gpt-5.1-codex": {
		input: 2,
		output: 8
	},
	"gpt-5.1-codex-max": {
		input: 3,
		output: 12
	},
	"gpt-5.1-codex-mini": {
		input: .5,
		output: 2
	},
	"gpt-5-codex": {
		input: 2,
		output: 8
	},
	"gpt-5-codex-mini": {
		input: .5,
		output: 2
	},
	"gpt-5": {
		input: 2,
		output: 8
	}
};
var OpenAICodexSDKProvider = class OpenAICodexSDKProvider {
	static OPENAI_MODELS = [
		"gpt-5.2",
		"gpt-5.1-codex",
		"gpt-5.1-codex-max",
		"gpt-5.1-codex-mini",
		"gpt-5-codex",
		"gpt-5-codex-mini",
		"gpt-5"
	];
	config;
	env;
	apiKey;
	providerId = "openai:codex-sdk";
	codexModule;
	codexInstance;
	codexInstanceEnvHash;
	threads = /* @__PURE__ */ new Map();
	deepTracingWarningShown = false;
	constructor(options = {}) {
		const { config, env, id } = options;
		this.config = config ?? {};
		this.env = env;
		this.apiKey = this.getApiKey();
		this.providerId = id ?? this.providerId;
		if (this.config.model && !OpenAICodexSDKProvider.OPENAI_MODELS.includes(this.config.model)) logger_default.warn(`Using unknown model for OpenAI Codex SDK: ${this.config.model}`);
	}
	id() {
		return this.providerId;
	}
	getApiKey() {
		return this.config?.apiKey || this.env?.OPENAI_API_KEY || this.env?.CODEX_API_KEY || getEnvString("OPENAI_API_KEY") || getEnvString("CODEX_API_KEY");
	}
	toString() {
		return "[OpenAI Codex SDK Provider]";
	}
	async cleanup() {
		this.threads.clear();
		if (this.codexInstance) {
			try {
				if (typeof this.codexInstance.destroy === "function") await this.codexInstance.destroy();
				else if (typeof this.codexInstance.cleanup === "function") await this.codexInstance.cleanup();
				else if (typeof this.codexInstance.close === "function") await this.codexInstance.close();
			} catch (error) {
				logger_default.warn("[CodexSDK] Error during cleanup", { error });
			}
			this.codexInstance = void 0;
			this.codexInstanceEnvHash = void 0;
		}
	}
	prepareEnvironment(config, traceparent) {
		const env = config.cli_env ? { ...config.cli_env } : { ...process.env };
		const sortedEnv = {};
		for (const key of Object.keys(env).sort()) if (env[key] !== void 0) sortedEnv[key] = env[key];
		if (this.apiKey) {
			sortedEnv.OPENAI_API_KEY = this.apiKey;
			sortedEnv.CODEX_API_KEY = this.apiKey;
		}
		if (this.env) for (const key of Object.keys(this.env).sort()) {
			const value = this.env[key];
			if (value !== void 0) sortedEnv[key] = value;
		}
		if (config.deep_tracing) {
			if (!sortedEnv.OTEL_EXPORTER_OTLP_ENDPOINT) sortedEnv.OTEL_EXPORTER_OTLP_ENDPOINT = "http://127.0.0.1:4318";
			if (!sortedEnv.OTEL_EXPORTER_OTLP_PROTOCOL) sortedEnv.OTEL_EXPORTER_OTLP_PROTOCOL = "http/json";
			if (!sortedEnv.OTEL_SERVICE_NAME) sortedEnv.OTEL_SERVICE_NAME = "codex-cli";
			if (!sortedEnv.OTEL_TRACES_EXPORTER) sortedEnv.OTEL_TRACES_EXPORTER = "otlp";
			if (traceparent) sortedEnv.TRACEPARENT = traceparent;
			logger_default.debug("[CodexSDK] Injecting OTEL config for deep tracing", {
				traceparent: traceparent || "(none - CLI will start own trace)",
				endpoint: sortedEnv.OTEL_EXPORTER_OTLP_ENDPOINT,
				userConfigured: {
					endpoint: !!env.OTEL_EXPORTER_OTLP_ENDPOINT,
					protocol: !!env.OTEL_EXPORTER_OTLP_PROTOCOL,
					serviceName: !!env.OTEL_SERVICE_NAME
				}
			});
		} else delete sortedEnv.TRACEPARENT;
		return sortedEnv;
	}
	validateWorkingDirectory(workingDir, skipGitCheck = false) {
		let stats;
		try {
			stats = fs.statSync(workingDir);
		} catch (err) {
			throw new Error(`Working directory ${workingDir} does not exist or isn't accessible: ${err.message}`);
		}
		if (!stats.isDirectory()) throw new Error(`Working directory ${workingDir} is not a directory`);
		if (!skipGitCheck) {
			const gitDir = path.join(workingDir, ".git");
			if (!fs.existsSync(gitDir)) throw new Error(dedent`Working directory ${workingDir} is not a Git repository.

          Codex requires a Git repository by default to prevent unrecoverable errors.

          To bypass this check, set skip_git_repo_check: true in your provider config.`);
		}
	}
	buildThreadOptions(config) {
		return {
			workingDirectory: config.working_dir,
			skipGitRepoCheck: config.skip_git_repo_check ?? false,
			...config.model ? { model: config.model } : {},
			...config.additional_directories?.length ? { additionalDirectories: config.additional_directories } : {},
			...config.sandbox_mode ? { sandboxMode: config.sandbox_mode } : {},
			...config.model_reasoning_effort ? { modelReasoningEffort: config.model_reasoning_effort } : {},
			...config.network_access_enabled !== void 0 ? { networkAccessEnabled: config.network_access_enabled } : {},
			...config.web_search_enabled !== void 0 ? { webSearchEnabled: config.web_search_enabled } : {},
			...config.approval_policy ? { approvalPolicy: config.approval_policy } : {}
		};
	}
	async getOrCreateThread(config, cacheKey, instance) {
		const threadOptions = this.buildThreadOptions(config);
		if (config.deep_tracing) return instance.startThread(threadOptions);
		if (config.thread_id) {
			const cached = this.threads.get(config.thread_id);
			if (cached) return cached;
			const thread$1 = instance.resumeThread(config.thread_id, threadOptions);
			if (config.persist_threads) this.threads.set(config.thread_id, thread$1);
			return thread$1;
		}
		if (config.persist_threads && cacheKey) {
			const cached = this.threads.get(cacheKey);
			if (cached) return cached;
			const poolSize = config.thread_pool_size ?? 1;
			if (this.threads.size >= poolSize) {
				const oldestKey = this.threads.keys().next().value;
				if (oldestKey) this.threads.delete(oldestKey);
			}
		}
		const thread = instance.startThread(threadOptions);
		if (config.persist_threads && cacheKey) this.threads.set(cacheKey, thread);
		return thread;
	}
	async runStreaming(thread, prompt, runOptions, callOptions) {
		const { events } = await thread.runStreamed(prompt, runOptions);
		const items = [];
		let usage = void 0;
		const tracer = trace.getTracer("promptfoo.codex-sdk");
		const activeSpans = /* @__PURE__ */ new Map();
		const itemStartTimes = /* @__PURE__ */ new Map();
		let lastEventTime = Date.now();
		const reasoningTexts = [];
		const conversationMessages = [];
		conversationMessages.push({
			role: "user",
			content: prompt
		});
		try {
			for await (const event of events) {
				const eventTime = Date.now();
				if (callOptions?.abortSignal?.aborted) {
					const abortError = /* @__PURE__ */ new Error("AbortError");
					abortError.name = "AbortError";
					throw abortError;
				}
				switch (event.type) {
					case "item.started": {
						const item = event.item;
						if (!item) {
							logger_default.warn("Codex item.started event missing item", { event });
							break;
						}
						if (!item.id) {
							logger_default.debug("Codex item.started without id, will create span at completion", { type: item.type });
							break;
						}
						const itemId = String(item.id);
						const spanName = this.getSpanNameForItem(item);
						const span = tracer.startSpan(spanName, {
							kind: SpanKind.INTERNAL,
							attributes: {
								"codex.item.id": itemId,
								"codex.item.type": item.type,
								...this.getAttributesForItem(item)
							}
						});
						activeSpans.set(itemId, span);
						itemStartTimes.set(itemId, eventTime);
						logger_default.debug("Codex item started", {
							itemId,
							type: item.type
						});
						break;
					}
					case "item.completed": {
						const item = event.item;
						if (!item) {
							logger_default.warn("Codex item.completed event missing item", { event });
							break;
						}
						const itemId = item.id ? String(item.id) : crypto.randomUUID();
						items.push(item);
						if (item.type === "reasoning" && typeof item.text === "string") reasoningTexts.push(item.text);
						if (item.type === "agent_message" && typeof item.text === "string") conversationMessages.push({
							role: "assistant",
							content: item.text
						});
						let span = activeSpans.get(itemId);
						const hadStartEvent = span !== void 0;
						if (!span) {
							const spanName = this.getSpanNameForItem(item);
							span = tracer.startSpan(spanName, {
								kind: SpanKind.INTERNAL,
								startTime: lastEventTime,
								attributes: {
									"codex.item.id": itemId,
									"codex.item.type": item.type,
									"codex.timing.estimated": true,
									...this.getAttributesForItem(item)
								}
							});
						}
						const completionAttrs = this.getCompletionAttributesForItem(item);
						for (const [key, value] of Object.entries(completionAttrs)) span.setAttribute(key, value);
						const durationMs = eventTime - (itemStartTimes.get(itemId) || lastEventTime);
						span.setAttribute("codex.duration_ms", durationMs);
						span.setAttribute("codex.had_start_event", hadStartEvent);
						if (item.type === "reasoning" && typeof item.text === "string") span.addEvent("reasoning", { "codex.reasoning.text": item.text });
						if (item.type === "agent_message" && typeof item.text === "string") span.addEvent("message", { "codex.message.text": item.text });
						if (item.type === "command_execution" && typeof item.aggregated_output === "string") span.addEvent("output", { "codex.command.output": item.aggregated_output });
						if (item.status === "failed" || item.type === "error" || item.error !== void 0 || item.type === "command_execution" && typeof item.exit_code === "number" && item.exit_code !== 0) span.setStatus({
							code: SpanStatusCode.ERROR,
							message: (typeof item.message === "string" ? item.message : null) || (typeof item.error?.message === "string" ? item.error.message : null) || (item.type === "command_execution" && item.exit_code !== 0 ? `Command exited with code ${item.exit_code}` : null) || "Item failed"
						});
						else span.setStatus({ code: SpanStatusCode.OK });
						span.end();
						activeSpans.delete(itemId);
						itemStartTimes.delete(itemId);
						logger_default.debug("Codex item completed", {
							itemId,
							type: item.type,
							durationMs
						});
						break;
					}
					case "turn.completed":
						usage = event.usage;
						logger_default.debug("Codex turn completed", { usage });
						break;
					default: logger_default.debug("Codex unknown event type", { type: event.type });
				}
				lastEventTime = eventTime;
			}
		} finally {
			for (const [itemId, span] of activeSpans) {
				logger_default.warn("Codex item span not properly closed", { itemId });
				span.setStatus({
					code: SpanStatusCode.ERROR,
					message: "Span not properly closed"
				});
				span.end();
			}
			activeSpans.clear();
			itemStartTimes.clear();
		}
		const agentMessages = items.filter((i) => i.type === "agent_message");
		return {
			finalResponse: agentMessages.length > 0 ? agentMessages.map((i) => i.text).join("\n") : "",
			items,
			usage,
			reasoningTexts,
			conversationMessages
		};
	}
	/**
	* Get a descriptive span name for a Codex item
	*/
	getSpanNameForItem(item) {
		switch (item.type) {
			case "command_execution": return `exec ${typeof item.command === "string" ? item.command.split(" ")[0] || "command" : "command"}`;
			case "file_change": return `file ${item.changes?.[0]?.kind || "change"}`;
			case "mcp_tool_call": return `mcp ${typeof item.server === "string" ? item.server : "unknown"}/${typeof item.tool === "string" ? item.tool : "unknown"}`;
			case "agent_message": return "agent response";
			case "reasoning": return "reasoning";
			case "web_search": return `search "${typeof item.query === "string" ? item.query.slice(0, 30) : ""}"`;
			case "todo_list": return "todo update";
			case "error": return "error";
			default: return `codex.${item.type || "unknown"}`;
		}
	}
	/**
	* Get attributes for a Codex item at start
	*/
	getAttributesForItem(item) {
		const attrs = {};
		switch (item.type) {
			case "command_execution":
				if (typeof item.command === "string") attrs["codex.command"] = item.command;
				break;
			case "mcp_tool_call":
				if (typeof item.server === "string") attrs["codex.mcp.server"] = item.server;
				if (typeof item.tool === "string") attrs["codex.mcp.tool"] = item.tool;
				break;
			case "web_search":
				if (typeof item.query === "string") attrs["codex.search.query"] = item.query;
				break;
		}
		return attrs;
	}
	/**
	* Get attributes for a Codex item at completion
	*/
	getCompletionAttributesForItem(item) {
		const attrs = {};
		switch (item.type) {
			case "command_execution":
				if (typeof item.exit_code === "number") attrs["codex.exit_code"] = item.exit_code;
				if (typeof item.status === "string") attrs["codex.status"] = item.status;
				if (typeof item.aggregated_output === "string") attrs["codex.output"] = item.aggregated_output;
				break;
			case "file_change":
				if (typeof item.status === "string") attrs["codex.status"] = item.status;
				if (Array.isArray(item.changes) && item.changes.length) {
					attrs["codex.files_changed"] = item.changes.length;
					attrs["codex.files"] = item.changes.map((c) => typeof c?.path === "string" ? c.path : "").filter(Boolean).join(", ");
				}
				break;
			case "mcp_tool_call":
				if (typeof item.status === "string") attrs["codex.status"] = item.status;
				if (typeof item.error?.message === "string") attrs["codex.error"] = item.error.message;
				break;
			case "agent_message":
				if (typeof item.text === "string") attrs["codex.message"] = item.text;
				break;
			case "reasoning":
				if (typeof item.text === "string") attrs["codex.reasoning"] = item.text;
				break;
			case "error":
				if (typeof item.message === "string") attrs["codex.error"] = item.message;
				break;
		}
		return attrs;
	}
	generateCacheKey(config, prompt) {
		const keyData = {
			working_dir: config.working_dir,
			additional_directories: config.additional_directories,
			model: config.model,
			output_schema: config.output_schema,
			sandbox_mode: config.sandbox_mode,
			model_reasoning_effort: config.model_reasoning_effort,
			network_access_enabled: config.network_access_enabled,
			web_search_enabled: config.web_search_enabled,
			approval_policy: config.approval_policy,
			prompt
		};
		return `openai:codex-sdk:${crypto.createHash("sha256").update(JSON.stringify(keyData)).digest("hex")}`;
	}
	async callApi(prompt, context$1, callOptions) {
		const config = {
			...this.config,
			...context$1?.prompt?.config
		};
		const modelName = config.model || "codex";
		const spanContext = {
			system: "openai",
			operationName: "chat",
			model: modelName,
			providerId: this.id(),
			evalId: context$1?.evaluationId || context$1?.test?.metadata?.evaluationId,
			testIndex: typeof context$1?.test?.vars?.__testIdx === "number" ? context$1.test.vars.__testIdx : void 0,
			promptLabel: context$1?.prompt?.label,
			traceparent: context$1?.traceparent,
			requestBody: prompt
		};
		const resultExtractor = (response) => {
			const result = {};
			if (response.tokenUsage) result.tokenUsage = response.tokenUsage;
			if (response.sessionId) result.responseId = response.sessionId;
			if (response.cached !== void 0) result.cacheHit = response.cached;
			result.responseModel = modelName;
			if (response.output !== void 0) try {
				result.responseBody = typeof response.output === "string" ? response.output : JSON.stringify(response.output);
			} catch {
				result.responseBody = "[unable to serialize output]";
			}
			if (response.raw) try {
				const rawData = typeof response.raw === "string" ? JSON.parse(response.raw) : response.raw;
				if (rawData.reasoningTexts?.length > 0) result.additionalAttributes = {
					"codex.reasoning.count": rawData.reasoningTexts.length,
					"codex.reasoning.summary": rawData.reasoningTexts.join("\n---\n").slice(0, 2e3)
				};
				if (rawData.conversationMessages?.length > 0) result.additionalAttributes = {
					...result.additionalAttributes,
					"codex.conversation.message_count": rawData.conversationMessages.length
				};
				if (rawData.items?.length > 0) {
					const itemCounts = {};
					for (const item of rawData.items) itemCounts[item.type] = (itemCounts[item.type] || 0) + 1;
					result.additionalAttributes = {
						...result.additionalAttributes,
						"codex.items.total": rawData.items.length,
						"codex.items.breakdown": JSON.stringify(itemCounts)
					};
				}
			} catch {}
			return result;
		};
		return withGenAISpan(spanContext, () => this.callApiInternal(prompt, context$1, callOptions, config), resultExtractor);
	}
	/**
	* Internal implementation of callApi without tracing wrapper.
	* Context is available for future use (e.g., _context?.vars for template rendering,
	* _context?.bustCache for cache control, _context?.debug for debug mode).
	*/
	async callApiInternal(prompt, _context, callOptions, config) {
		const currentTraceparent = getTraceparent();
		const env = this.prepareEnvironment(config, currentTraceparent);
		if (!this.apiKey && !env.OPENAI_API_KEY && !env.CODEX_API_KEY) throw new Error("OpenAI API key is not set. Set OPENAI_API_KEY or CODEX_API_KEY environment variable or add \"apiKey\" to provider config.");
		if (config.working_dir) this.validateWorkingDirectory(config.working_dir, config.skip_git_repo_check);
		if (callOptions?.abortSignal?.aborted) return { error: "OpenAI Codex SDK call aborted before it started" };
		if (!this.codexModule) this.codexModule = await loadCodexSDK();
		let localInstance = void 0;
		const useLocalInstance = config.deep_tracing;
		if (useLocalInstance) {
			if ((config.persist_threads || config.thread_id || (config.thread_pool_size ?? 0) > 1) && !this.deepTracingWarningShown) {
				logger_default.warn("[CodexSDK] deep_tracing is incompatible with thread persistence. Thread options (persist_threads, thread_id, thread_pool_size) are ignored when deep_tracing is enabled.");
				this.deepTracingWarningShown = true;
			}
			localInstance = new this.codexModule.Codex({
				env,
				...config.codex_path_override ? { codexPathOverride: config.codex_path_override } : {},
				...config.base_url ? { baseUrl: config.base_url } : {}
			});
		} else {
			const stableEnv = { ...env };
			delete stableEnv.TRACEPARENT;
			const envHash = crypto.createHash("sha256").update(JSON.stringify(stableEnv)).digest("hex");
			const envChanged = this.codexInstanceEnvHash !== envHash;
			if (!this.codexInstance || envChanged) {
				if (envChanged && this.codexInstance) {
					logger_default.debug("[CodexSDK] Recreating instance due to configuration change");
					try {
						if (typeof this.codexInstance.destroy === "function") await this.codexInstance.destroy();
						else if (typeof this.codexInstance.cleanup === "function") await this.codexInstance.cleanup();
						else if (typeof this.codexInstance.close === "function") await this.codexInstance.close();
					} catch (cleanupError) {
						logger_default.warn("[CodexSDK] Error cleaning up old instance", { error: cleanupError });
					}
					this.threads.clear();
				}
				this.codexInstance = new this.codexModule.Codex({
					env,
					...config.codex_path_override ? { codexPathOverride: config.codex_path_override } : {},
					...config.base_url ? { baseUrl: config.base_url } : {}
				});
				this.codexInstanceEnvHash = envHash;
			}
		}
		const activeInstance = useLocalInstance ? localInstance : this.codexInstance;
		if (!activeInstance) throw new Error("Failed to create Codex instance - SDK module may have failed to load");
		const cacheKey = this.generateCacheKey(config, prompt);
		const thread = await this.getOrCreateThread(config, cacheKey, activeInstance);
		const runOptions = {};
		if (config.output_schema) runOptions.outputSchema = config.output_schema;
		if (callOptions?.abortSignal) runOptions.signal = callOptions.abortSignal;
		try {
			const turn = config.enable_streaming ? await this.runStreaming(thread, prompt, runOptions, callOptions) : await thread.run(prompt, runOptions);
			const output = turn.finalResponse || "";
			const raw = JSON.stringify(turn);
			const tokenUsage = turn.usage ? {
				prompt: turn.usage.input_tokens + (turn.usage.cached_input_tokens || 0),
				completion: turn.usage.output_tokens,
				total: turn.usage.input_tokens + (turn.usage.cached_input_tokens || 0) + turn.usage.output_tokens
			} : void 0;
			let cost = 0;
			if (tokenUsage && config.model) {
				const pricing = CODEX_MODEL_PRICING[config.model];
				if (pricing) cost = (tokenUsage.prompt || 0) * (pricing.input / 1e6) + (tokenUsage.completion || 0) * (pricing.output / 1e6);
			}
			logger_default.debug("OpenAI Codex SDK response", {
				output,
				usage: turn.usage
			});
			return {
				output,
				tokenUsage,
				cost,
				raw,
				sessionId: thread.id || "unknown"
			};
		} catch (error) {
			if (error instanceof Error && error.name === "AbortError" || callOptions?.abortSignal?.aborted) {
				logger_default.warn("OpenAI Codex SDK call aborted");
				return { error: "OpenAI Codex SDK call aborted" };
			}
			const errorMessage = error instanceof Error ? error.message : String(error);
			logger_default.error("Error calling OpenAI Codex SDK", { error: errorMessage });
			return { error: `Error calling OpenAI Codex SDK: ${errorMessage}` };
		} finally {
			if (!config.deep_tracing && !config.persist_threads && !config.thread_id && cacheKey) this.threads.delete(cacheKey);
			if (useLocalInstance && localInstance) try {
				if (typeof localInstance.destroy === "function") await localInstance.destroy();
				else if (typeof localInstance.cleanup === "function") await localInstance.cleanup();
				else if (typeof localInstance.close === "function") await localInstance.close();
			} catch (cleanupError) {
				logger_default.debug("[CodexSDK] Error cleaning up local instance", { error: cleanupError });
			}
		}
	}
};

//#endregion
export { OpenAICodexSDKProvider };
//# sourceMappingURL=codex-sdk-Co3YTrAL.js.map