#!/usr/bin/env node
import { L as state, M as getEnvString, _ as sanitizeObject, c as logger, d as REDACTED, m as normalizeFieldName } from "./logger-4ntIRWmf.js";
import { f as HttpRateLimitError, h as formatRateLimitErrorMessage } from "./fetch-gOBR1GF3.js";
import { i as resolvePackageEntryPoint, r as importModule, t as getDirectory } from "./esm-BOYGx5J1.js";
import { t as providerRegistry } from "./providerRegistry-R6Au81Gn.js";
import { $ as closeTurnSpan, it as openTurnSpan, ot as withGenAISpan, q as renderVarsInObject, rt as getTraceparent } from "./util-DAS7mIn_.js";
import { o as resolveAgenticWorkingDir } from "./agentic-utils-vF_Yu2eT.js";
import { r as calculateOpenAIUsageCostFromTokenUsage } from "./billing-D-k_rN9I.js";
import { a as applyApiKeyToCliEnv, i as getCodexSkillRootPrefixes, n as extractCodexSkillPathCandidates, o as shouldInjectApiKey, r as getCodexSkillMetadataFields, s as usesCustomModelProvider, t as buildCodexSkillMetadata } from "./codexSkillMetadata-C41eJaPn.js";
import { z } from "zod";
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
const MINIMAL_CLI_ENV_KEYS = [
	"PATH",
	"Path",
	"HOME",
	"USER",
	"USERNAME",
	"USERPROFILE",
	"TMPDIR",
	"TMP",
	"TEMP",
	"SHELL",
	"COMSPEC",
	"SystemRoot",
	"PATHEXT",
	"LANG",
	"LC_ALL",
	"TERM"
];
const COMMON_OPTIONAL_PROCESS_ENV_KEYS = [
	"CODEX_HOME",
	"HTTP_PROXY",
	"HTTPS_PROXY",
	"ALL_PROXY",
	"NO_PROXY",
	"SSL_CERT_FILE",
	"SSL_CERT_DIR",
	"REQUESTS_CA_BUNDLE",
	"NODE_EXTRA_CA_CERTS",
	"SSH_AUTH_SOCK",
	"GIT_SSH_COMMAND"
];
const CodexCliEnvValueSchema = z.union([
	z.string(),
	z.number(),
	z.boolean()
]).transform(String);
const OpenAICodexSDKConfigShape = {
	basePath: z.string().optional(),
	prefix: z.string().optional(),
	suffix: z.string().optional(),
	provider: z.unknown().optional(),
	linkedTargetId: z.string().optional(),
	apiKey: z.string().min(1).optional(),
	base_url: z.string().min(1).optional(),
	maxRetries: z.number().int().nonnegative().optional(),
	working_dir: z.string().min(1).optional(),
	additional_directories: z.array(z.string().min(1)).optional(),
	skip_git_repo_check: z.boolean().optional(),
	codex_path_override: z.string().min(1).optional(),
	model: z.string().min(1).optional(),
	model_provider: z.string().min(1).optional(),
	sandbox_mode: z.enum([
		"read-only",
		"workspace-write",
		"danger-full-access"
	]).optional(),
	model_reasoning_effort: z.enum([
		"minimal",
		"low",
		"medium",
		"high",
		"xhigh"
	]).optional(),
	network_access_enabled: z.boolean().optional(),
	web_search_enabled: z.boolean().optional(),
	web_search_mode: z.enum([
		"disabled",
		"cached",
		"live"
	]).optional(),
	collaboration_mode: z.enum(["coding", "plan"]).optional(),
	approval_policy: z.enum([
		"never",
		"on-request",
		"on-failure",
		"untrusted"
	]).optional(),
	thread_id: z.string().min(1).optional(),
	persist_threads: z.boolean().optional(),
	thread_pool_size: z.number().int().positive().optional(),
	output_schema: z.record(z.string(), z.unknown()).optional(),
	cli_env: z.record(z.string(), CodexCliEnvValueSchema).optional(),
	inherit_process_env: z.boolean().optional(),
	enable_streaming: z.boolean().optional(),
	deep_tracing: z.boolean().optional(),
	cli_config: z.record(z.string(), z.unknown()).optional()
};
const OpenAICodexSDKConfigSchema = z.object(OpenAICodexSDKConfigShape).strict();
const OpenAICodexSDKMergedPromptConfigSchema = z.object(OpenAICodexSDKConfigShape).strip();
function parseCodexConfig(config, options = {}) {
	const schema = options.stripUnknownKeys ? OpenAICodexSDKMergedPromptConfigSchema : OpenAICodexSDKConfigSchema;
	try {
		return schema.parse(config ?? {});
	} catch (error) {
		if (error instanceof z.ZodError) {
			const issues = error.issues.map((issue) => {
				return `${issue.path.length > 0 ? issue.path.join(".") : "(root)"}: ${issue.message}`;
			}).join("; ");
			throw new Error(`Invalid OpenAI Codex SDK config: ${issues}`);
		}
		throw error;
	}
}
function getMinimalProcessEnv() {
	const env = {};
	for (const key of MINIMAL_CLI_ENV_KEYS) {
		const value = process.env[key];
		if (typeof value === "string" && value.length > 0) env[key] = value;
	}
	return env;
}
const CODEX_RATE_LIMIT_CODES = [
	"rate_limit_exceeded",
	"insufficient_quota",
	"billing_hard_limit_reached",
	"billing_not_active",
	"access_terminated",
	"quota_exceeded"
];
const CODEX_DEFAULT_RATE_LIMIT_WAIT_MS = 6e4;
const CODEX_RATE_LIMIT_PATTERNS = [
	/\b429\b/,
	/\brate[\s_-]*limit(?:ed|ing| reached| exceeded)?\b/i,
	/\btoo many requests\b/i,
	/\btokens per (?:minute|min)\b/i,
	/\brequests per (?:minute|min)\b/i,
	/\bexceeded your current quota\b/i
];
function extractCodexRateLimitCode(message) {
	const lowerMessage = message.toLowerCase();
	const explicitCode = CODEX_RATE_LIMIT_CODES.find((code) => lowerMessage.includes(code));
	if (explicitCode) return explicitCode;
	if (/\bexceeded your current quota\b/i.test(message) || /\bcheck your plan and billing details\b/i.test(message)) return "insufficient_quota";
}
function extractCodexRetryAfterMs(message) {
	const match = message.match(/\b(?:please\s+)?(?:try\s+again\s+in|retry(?:\s+again)?\s+(?:after|in))\s+(\d+(?:\.\d+)?)\s*(milliseconds?|ms|seconds?|secs?|s|minutes?|mins?|m)\b/i);
	if (!match) return;
	const amount = Number(match[1]);
	if (!Number.isFinite(amount) || amount < 0) return;
	const unit = match[2].toLowerCase();
	const multiplier = unit.startsWith("m") && unit !== "ms" && !unit.startsWith("milli") ? 6e4 : unit.startsWith("s") ? 1e3 : 1;
	return Math.ceil(amount * multiplier);
}
function buildCodexRateLimitResponse(error, message) {
	const errorRecord = typeof error === "object" && error !== null ? error : void 0;
	const status = typeof errorRecord?.status === "number" ? errorRecord.status : typeof errorRecord?.statusCode === "number" ? errorRecord.statusCode : void 0;
	const rawCode = typeof errorRecord?.code === "string" ? errorRecord.code.toLowerCase() : void 0;
	const code = rawCode && CODEX_RATE_LIMIT_CODES.some((knownCode) => knownCode === rawCode) ? rawCode : extractCodexRateLimitCode(message);
	if (status !== 429 && code === void 0 && !CODEX_RATE_LIMIT_PATTERNS.some((pattern) => pattern.test(message))) return;
	const retryAfterMs = extractCodexRetryAfterMs(message);
	const rateLimitError = new HttpRateLimitError({
		status: status ?? 429,
		retryAfterMs,
		code
	});
	const schedulerRetryAfterMs = rateLimitError.kind === "rate_limit" ? retryAfterMs ?? CODEX_DEFAULT_RATE_LIMIT_WAIT_MS : void 0;
	const headers = schedulerRetryAfterMs === void 0 ? {} : { "retry-after-ms": String(schedulerRetryAfterMs) };
	return {
		error: formatRateLimitErrorMessage(rateLimitError, message),
		metadata: {
			rateLimitKind: rateLimitError.kind,
			http: {
				status: rateLimitError.status,
				statusText: rateLimitError.statusText,
				headers
			}
		}
	};
}
/**
* Helper to load the OpenAI Codex SDK ESM module
* Uses resolvePackageEntryPoint to handle ESM-only packages with restrictive exports
*/
async function loadCodexSDK() {
	const basePaths = [
		state.basePath ? path.resolve(state.basePath) : void 0,
		process.cwd(),
		path.resolve(getDirectory(), ".."),
		path.resolve(getDirectory(), "../..")
	].filter((candidate) => Boolean(candidate));
	let codexPath = null;
	for (const basePath of new Set(basePaths)) {
		codexPath = resolvePackageEntryPoint("@openai/codex-sdk", basePath);
		if (codexPath) break;
	}
	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 ^20.20.0 or >=22.22.0.

      For more information, see: https://www.promptfoo.dev/docs/providers/openai-codex-sdk/`);
	try {
		return await importModule(codexPath);
	} catch (err) {
		logger.error(`Failed to load OpenAI Codex SDK: ${err}`);
		if (err.stack) logger.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 ^20.20.0 or >=22.22.0)
      - Corrupted installation

      Try reinstalling:
        npm install @openai/codex-sdk

      For more information, see: https://www.promptfoo.dev/docs/providers/openai-codex-sdk/`);
	}
}
var OpenAICodexSDKProvider = class OpenAICodexSDKProvider {
	static OPENAI_MODELS = [
		"gpt-5.5",
		"gpt-5.5-pro",
		"gpt-5.4",
		"gpt-5.4-pro",
		"gpt-5.3-codex",
		"gpt-5.3-codex-spark",
		"gpt-5.2",
		"gpt-5.2-codex",
		"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;
	codexInstances = /* @__PURE__ */ new Map();
	threads = /* @__PURE__ */ new Map();
	threadRunQueues = /* @__PURE__ */ new Map();
	deepTracingWarningShown = false;
	ignoredProviderEnvWarningShown = false;
	omittedProcessEnvWarningShown = false;
	constructor(options = {}) {
		const { config, env, id } = options;
		this.config = parseCodexConfig(config);
		this.env = env;
		this.apiKey = this.getApiKey();
		this.providerId = id ?? this.providerId;
		providerRegistry.register(this);
		if (this.config.model && !usesCustomModelProvider(this.config) && !OpenAICodexSDKProvider.OPENAI_MODELS.includes(this.config.model)) logger.warn(`Using unknown model for OpenAI Codex SDK: ${this.config.model}`);
	}
	id() {
		return this.providerId;
	}
	getApiKey(config = this.config) {
		return config?.apiKey || this.env?.OPENAI_API_KEY || this.env?.CODEX_API_KEY || getEnvString("OPENAI_API_KEY") || getEnvString("CODEX_API_KEY");
	}
	requiresApiKey() {
		return false;
	}
	toString() {
		return "[OpenAI Codex SDK Provider]";
	}
	/**
	* Safely tear down a Codex instance by calling its cleanup method
	* (destroy, cleanup, or close -- whichever is available).
	*/
	async destroyInstance(instance) {
		if (typeof instance.destroy === "function") await instance.destroy();
		else if (typeof instance.cleanup === "function") await instance.cleanup();
		else if (typeof instance.close === "function") await instance.close();
	}
	async cleanup() {
		this.threads.clear();
		this.threadRunQueues.clear();
		for (const instance of this.codexInstances.values()) try {
			await this.destroyInstance(instance);
		} catch (error) {
			logger.warn("[CodexSDK] Error during cleanup", { error });
		}
		this.codexInstances.clear();
	}
	async shutdown() {
		try {
			await this.cleanup();
		} finally {
			providerRegistry.unregister(this);
		}
	}
	prepareEnvironment(config, traceparent, apiKey = this.getApiKey(config)) {
		const inheritProcessEnv = config.inherit_process_env === true;
		const cliEnv = Object.fromEntries(Object.entries(config.cli_env ?? {}).map(([key, value]) => [key, String(value)]));
		const env = {
			...inheritProcessEnv ? process.env : getMinimalProcessEnv(),
			...cliEnv
		};
		const ignoredProviderEnvKeys = Object.keys(this.env ?? {}).filter((key) => key !== "OPENAI_API_KEY" && key !== "CODEX_API_KEY" && !(key in (config.cli_env ?? {}))).sort();
		if (ignoredProviderEnvKeys.length > 0 && !this.ignoredProviderEnvWarningShown) {
			logger.warn("[CodexSDK] Ignoring promptfoo-level env overrides for the Codex CLI process. Move these keys into config.cli_env if Codex shell commands need them.", { envKeys: ignoredProviderEnvKeys });
			this.ignoredProviderEnvWarningShown = true;
		}
		if (!inheritProcessEnv && !this.omittedProcessEnvWarningShown) {
			const omittedProcessEnvKeys = this.getOmittedOptionalProcessEnvKeys(config, env);
			if (omittedProcessEnvKeys.length > 0) {
				logger.warn("[CodexSDK] Optional Codex CLI process env vars are not inherited by default. Move these keys into config.cli_env or set inherit_process_env: true if Codex CLI commands need them.", { envKeys: omittedProcessEnvKeys });
				this.omittedProcessEnvWarningShown = true;
			}
		}
		const sortedEnv = {};
		for (const key of Object.keys(env).sort()) if (env[key] !== void 0) sortedEnv[key] = env[key];
		applyApiKeyToCliEnv(sortedEnv, config, apiKey);
		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.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;
	}
	getOmittedOptionalProcessEnvKeys(config, env) {
		const shouldWarnForSshEnv = config.network_access_enabled === true || config.web_search_enabled === true || config.web_search_mode === "live";
		return COMMON_OPTIONAL_PROCESS_ENV_KEYS.filter((key) => typeof process.env[key] === "string" && !(key in env) && (shouldWarnForSshEnv || key !== "SSH_AUTH_SOCK" && key !== "GIT_SSH_COMMAND"));
	}
	getResolvedCliConfig(config) {
		if (!config.cli_config && !config.collaboration_mode && !config.model_provider) return;
		return {
			...config.cli_config ?? {},
			...config.model_provider ? { model_provider: config.model_provider } : {},
			...config.collaboration_mode ? { collaboration_mode: config.collaboration_mode } : {}
		};
	}
	getSkillRootPrefixes(env, workingDir) {
		const resolvedWorkingDir = workingDir ? path.resolve(workingDir).replace(/\\/g, "/") : void 0;
		return getCodexSkillRootPrefixes({
			codexHome: env.CODEX_HOME,
			gitRepositoryRoot: resolvedWorkingDir ? this.findGitRepositoryRoot(resolvedWorkingDir) : void 0,
			homeDir: env.HOME || env.USERPROFILE || process.env.HOME || process.env.USERPROFILE,
			workingDir: resolvedWorkingDir
		});
	}
	buildSkillMetadata(items, skillRootPrefixes = []) {
		return buildCodexSkillMetadata(items, skillRootPrefixes, {
			getCommand: (item) => item?.type === "command_execution" && typeof item.command === "string" && item.command.trim() ? item.command : void 0,
			isSuccessfulCommand: (item) => this.isSuccessfulCommandExecution(item)
		});
	}
	isSuccessfulCommandExecution(item) {
		if (item?.type !== "command_execution") return false;
		if (typeof item.status === "string" && item.status !== "completed") return false;
		if (typeof item.exit_code === "number" && item.exit_code !== 0) return false;
		return true;
	}
	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 && !this.isInsideGitRepository(workingDir)) throw new Error(dedent`Working directory ${workingDir} is not inside 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.`);
	}
	isInsideGitRepository(workingDir) {
		return this.findGitRepositoryRoot(workingDir) !== void 0;
	}
	findGitRepositoryRoot(workingDir) {
		let currentDir = path.resolve(workingDir);
		while (true) {
			if (fs.existsSync(path.join(currentDir, ".git"))) return currentDir;
			const parentDir = path.dirname(currentDir);
			if (parentDir === currentDir) return;
			currentDir = parentDir;
		}
	}
	/**
	* Build Codex constructor options from provider config.
	* Used when creating both local (deep-tracing) and cached instances.
	*/
	buildCodexOptions(env, config, apiKey = this.getApiKey(config)) {
		const cliConfig = this.getResolvedCliConfig(config);
		return {
			env,
			...shouldInjectApiKey(config, apiKey) ? { apiKey } : {},
			...config.codex_path_override ? { codexPathOverride: config.codex_path_override } : {},
			...config.base_url ? { baseUrl: config.base_url } : {},
			...cliConfig ? { config: cliConfig } : {}
		};
	}
	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_mode ? { webSearchMode: config.web_search_mode } : {},
			...config.web_search_enabled !== void 0 && !config.web_search_mode ? { webSearchEnabled: config.web_search_enabled } : {},
			...config.approval_policy ? { approvalPolicy: config.approval_policy } : {}
		};
	}
	async getOrCreateThread(config, cacheKey, instanceKey, instance) {
		const threadOptions = this.buildThreadOptions(config);
		if (config.deep_tracing) return instance.startThread(threadOptions);
		if (config.thread_id) {
			const threadIdCacheKey = `${instanceKey}:${config.thread_id}`;
			const cached = this.threads.get(threadIdCacheKey);
			if (cached) return cached;
			const thread = instance.resumeThread(config.thread_id, threadOptions);
			if (config.persist_threads) this.threads.set(threadIdCacheKey, thread);
			return thread;
		}
		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, skillRootPrefixes = []) {
		const { events } = await thread.runStreamed(prompt, runOptions);
		const tracer = trace.getTracer("promptfoo.codex-sdk");
		const state = this.createCodexStreamingState(prompt);
		try {
			for await (const event of events) {
				const eventTime = Date.now();
				if (callOptions?.abortSignal?.aborted) throw this.createAbortError("OpenAI Codex SDK call aborted");
				this.handleStreamingEvent(event, state, tracer, eventTime, skillRootPrefixes);
				state.lastEventTime = eventTime;
			}
			if (!state.turnCompleted) {
				if (state.lastStreamError) {
					if (!state.activeTurnSpan) this.startTurnSpan(state, tracer, state.lastEventTime);
					this.endTurnSpan(state, state.lastEventTime, void 0, state.lastStreamError);
					throw new Error(`Codex stream ended after error: ${state.lastStreamError}`);
				}
				throw new Error("Codex stream ended before turn completion");
			}
		} finally {
			this.endUnclosedStreamingSpans(state);
		}
		return this.buildStreamingTurnResult(state);
	}
	createCodexStreamingState(prompt) {
		return {
			items: [],
			usage: void 0,
			turnCompleted: false,
			lastStreamError: void 0,
			activeSpans: /* @__PURE__ */ new Map(),
			itemStartTimes: /* @__PURE__ */ new Map(),
			lastEventTime: Date.now(),
			reasoningTexts: [],
			conversationMessages: [{
				role: "user",
				content: this.formatPromptInputForTrace(prompt)
			}],
			turnCount: 0,
			activeTurnSpan: void 0,
			activeTurnIndex: 0
		};
	}
	handleStreamingEvent(event, state, tracer, eventTime, skillRootPrefixes) {
		switch (event.type) {
			case "item.started":
				this.handleStreamingItemStarted(event, state, tracer, eventTime);
				return;
			case "item.completed":
				this.handleStreamingItemCompleted(event, state, tracer, eventTime, skillRootPrefixes);
				return;
			case "item.updated":
				this.handleStreamingItemUpdated(event, state, skillRootPrefixes);
				return;
			case "turn.completed":
				state.usage = event.usage;
				state.turnCompleted = true;
				if (!state.activeTurnSpan) this.startTurnSpan(state, tracer, state.lastEventTime);
				this.endTurnSpan(state, eventTime, event.usage);
				logger.debug("Codex turn completed", { usage: state.usage });
				return;
			case "turn.failed": {
				const errorMsg = event.error?.message || "Turn failed";
				if (!state.activeTurnSpan) this.startTurnSpan(state, tracer, state.lastEventTime);
				this.endTurnSpan(state, eventTime, void 0, errorMsg);
				logger.error("Codex turn failed", { error: errorMsg });
				const previousStreamError = state.lastStreamError && state.lastStreamError !== errorMsg ? ` Previous stream error: ${state.lastStreamError}` : "";
				throw new Error(`Codex turn failed: ${errorMsg}${previousStreamError}`);
			}
			case "error": {
				const errorMsg = typeof event.message === "string" && event.message ? event.message : "Stream failed";
				state.lastStreamError = errorMsg;
				logger.debug("Codex stream error event received; waiting for terminal event", { error: errorMsg });
				return;
			}
			case "thread.started": return;
			case "turn.started":
				this.startTurnSpan(state, tracer, eventTime);
				return;
			default: logger.debug("Codex unknown event type", { type: event.type });
		}
	}
	handleStreamingItemStarted(event, state, tracer, eventTime) {
		const item = event.item;
		if (!item) {
			logger.warn("Codex item.started event missing item", { event });
			return;
		}
		if (!state.activeTurnSpan) this.startTurnSpan(state, tracer, eventTime);
		if (!item.id) {
			logger.debug("Codex item.started without id, will create span at completion", { type: item.type });
			return;
		}
		const itemId = String(item.id);
		const span = this.startStreamingItemSpan(tracer, item, itemId, void 0, state.activeTurnIndex > 0 ? state.activeTurnIndex : void 0);
		state.activeSpans.set(itemId, span);
		state.itemStartTimes.set(itemId, eventTime);
		logger.debug("Codex item started", {
			itemId,
			type: item.type
		});
	}
	handleStreamingItemCompleted(event, state, tracer, eventTime, skillRootPrefixes) {
		const item = event.item;
		if (!item) {
			logger.warn("Codex item.completed event missing item", { event });
			return;
		}
		if (!state.activeTurnSpan) this.startTurnSpan(state, tracer, state.lastEventTime);
		const itemId = item.id ? String(item.id) : crypto.randomUUID();
		state.items.push(item);
		this.collectStreamingItemText(item, state);
		const span = state.activeSpans.get(itemId) ?? this.startStreamingItemSpan(tracer, item, itemId, state.lastEventTime, state.activeTurnIndex > 0 ? state.activeTurnIndex : void 0);
		const hadStartEvent = state.activeSpans.has(itemId);
		const startTime = state.itemStartTimes.get(itemId) ?? state.lastEventTime;
		const durationMs = eventTime - startTime;
		this.applyStreamingCompletionAttributes(span, item, skillRootPrefixes, eventTime, startTime, hadStartEvent);
		span.end();
		state.activeSpans.delete(itemId);
		state.itemStartTimes.delete(itemId);
		logger.debug("Codex item completed", {
			itemId,
			type: item.type,
			durationMs
		});
	}
	handleStreamingItemUpdated(event, state, skillRootPrefixes) {
		const item = event.item;
		if (item?.id) {
			const itemId = String(item.id);
			const span = state.activeSpans.get(itemId);
			if (span) this.setStreamingCompletionAttributes(span, item, skillRootPrefixes);
		}
		logger.debug("Codex item updated", {
			itemId: item?.id,
			type: item?.type
		});
	}
	startStreamingItemSpan(tracer, item, itemId, startTime, turnIndex) {
		return tracer.startSpan(this.getSpanNameForItem(item), {
			kind: SpanKind.INTERNAL,
			...startTime === void 0 ? {} : { startTime },
			attributes: {
				"codex.item.id": itemId,
				"codex.item.type": item.type,
				...startTime === void 0 ? {} : { "codex.timing.estimated": true },
				...typeof turnIndex === "number" ? { "gen_ai.turn.index": turnIndex } : {},
				...this.getAttributesForItem(item)
			}
		});
	}
	startTurnSpan(state, tracer, eventTime) {
		openTurnSpan(state, {
			tracer,
			eventTime,
			system: "openai",
			logLabel: "CodexSDK"
		});
	}
	endTurnSpan(state, eventTime, usage, errorMessage) {
		const attributes = {};
		if (usage) {
			const inputTokens = usage.input_tokens ?? usage.inputTokens;
			const outputTokens = usage.output_tokens ?? usage.outputTokens;
			const cachedTokens = usage.cached_input_tokens ?? usage.cachedInputTokens;
			const reasoningTokens = usage.reasoning_output_tokens ?? usage.reasoningOutputTokens;
			if (typeof inputTokens === "number") attributes["gen_ai.usage.input_tokens"] = inputTokens;
			if (typeof outputTokens === "number") attributes["gen_ai.usage.output_tokens"] = outputTokens;
			if (typeof cachedTokens === "number") attributes["gen_ai.usage.cached_tokens"] = cachedTokens;
			if (typeof reasoningTokens === "number") attributes["gen_ai.usage.reasoning_tokens"] = reasoningTokens;
		}
		closeTurnSpan(state, {
			eventTime,
			attributes,
			errorMessage,
			logLabel: "CodexSDK"
		});
	}
	collectStreamingItemText(item, state) {
		if (item.type === "reasoning" && typeof item.text === "string") {
			const sanitizedReasoning = this.sanitizeTraceText(item.text, "Codex reasoning trace event");
			if (sanitizedReasoning) state.reasoningTexts.push(sanitizedReasoning);
		}
		if (item.type === "agent_message" && typeof item.text === "string") state.conversationMessages.push({
			role: "assistant",
			content: this.sanitizeTraceText(item.text, "Codex agent message trace event") ?? ""
		});
	}
	applyStreamingCompletionAttributes(span, item, skillRootPrefixes, eventTime, startTime, hadStartEvent) {
		this.setStreamingCompletionAttributes(span, item, skillRootPrefixes);
		span.setAttribute("codex.duration_ms", eventTime - startTime);
		span.setAttribute("codex.had_start_event", hadStartEvent);
		this.addStreamingSpanEvents(span, item);
		this.setStreamingSpanStatus(span, item);
	}
	setStreamingCompletionAttributes(span, item, skillRootPrefixes) {
		const completionAttrs = this.getCompletionAttributesForItem(item, skillRootPrefixes);
		for (const [key, value] of Object.entries(completionAttrs)) span.setAttribute(key, value);
	}
	addStreamingSpanEvents(span, item) {
		if (item.type === "reasoning" && typeof item.text === "string") span.addEvent("reasoning", { "codex.reasoning.text": this.sanitizeTraceText(item.text, "Codex reasoning span event") ?? "" });
		if (item.type === "agent_message" && typeof item.text === "string") span.addEvent("message", { "codex.message.text": this.sanitizeTraceText(item.text, "Codex agent message span event") ?? "" });
		if (item.type === "command_execution" && typeof item.aggregated_output === "string") span.addEvent("output", { "codex.command.output": this.sanitizeTraceText(item.aggregated_output, "Codex command output span event") ?? "" });
	}
	setStreamingSpanStatus(span, item) {
		if (!this.isStreamingItemError(item)) {
			span.setStatus({ code: SpanStatusCode.OK });
			return;
		}
		span.setStatus({
			code: SpanStatusCode.ERROR,
			message: this.getStreamingItemErrorMessage(item)
		});
	}
	isStreamingItemError(item) {
		return item.status === "failed" || item.type === "error" || item.error !== void 0 || item.type === "command_execution" && typeof item.exit_code === "number" && item.exit_code !== 0;
	}
	getStreamingItemErrorMessage(item) {
		return (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";
	}
	endUnclosedStreamingSpans(state) {
		for (const [itemId, span] of state.activeSpans) {
			logger.warn("Codex item span not properly closed", { itemId });
			span.setStatus({
				code: SpanStatusCode.ERROR,
				message: "Span not properly closed"
			});
			span.end();
		}
		state.activeSpans.clear();
		state.itemStartTimes.clear();
		closeTurnSpan(state, {
			errorMessage: "Turn span not properly closed",
			logLabel: "CodexSDK"
		});
	}
	buildStreamingTurnResult(state) {
		return {
			finalResponse: [...state.items.filter((item) => item.type === "agent_message")].reverse().find((item) => typeof item.text === "string")?.text ?? "",
			items: state.items,
			usage: state.usage,
			reasoningTexts: state.reasoningTexts,
			conversationMessages: state.conversationMessages
		};
	}
	parsePromptInput(prompt) {
		let parsedPrompt;
		try {
			parsedPrompt = JSON.parse(prompt);
		} catch {
			return prompt;
		}
		if (!Array.isArray(parsedPrompt) || parsedPrompt.length === 0 || !parsedPrompt.every((item) => this.isCodexPromptInputItem(item))) return prompt;
		return parsedPrompt;
	}
	isCodexPromptInputItem(item) {
		if (!item || typeof item !== "object" || Array.isArray(item)) return false;
		const keys = Object.keys(item);
		if ("type" in item && item.type === "text") return keys.length === 2 && keys.includes("type") && "text" in item && typeof item.text === "string";
		if ("type" in item && item.type === "local_image") return keys.length === 2 && keys.includes("type") && "path" in item && typeof item.path === "string";
		return false;
	}
	formatPromptInputForTrace(prompt) {
		if (typeof prompt === "string") return prompt;
		return prompt.map((item) => item.type === "text" ? item.text : `[local_image: ${item.path}]`).join("\n");
	}
	/**
	* 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" ? (this.sanitizeTraceText(item.query, "Codex web search span name") ?? "").slice(0, 30) : ""}"`;
			case "todo_list": return "todo update";
			case "error": return "error";
			case "collaboration_tool_call": return `collab ${typeof item.tool === "string" ? item.tool : "unknown"}`;
			case "spawn_agent": return `spawn ${typeof item.role === "string" ? item.role : "agent"}`;
			case "send_input": return "send input";
			case "agent_wait": return "wait";
			default: return `codex.${item.type || "unknown"}`;
		}
	}
	/**
	* Get attributes for a Codex item at start
	*/
	getSkillTraceAttributes(item, skillRootPrefixes = [], options = {}) {
		if (item?.type !== "command_execution") return {};
		if (options.requireSuccessfulCommand && !this.isSuccessfulCommandExecution(item)) return {};
		const command = typeof item.command === "string" && item.command.trim() ? item.command : void 0;
		const skillCandidates = /* @__PURE__ */ new Map();
		if (command) for (const skill of extractCodexSkillPathCandidates(command, skillRootPrefixes)) skillCandidates.set(skill.path, skill);
		if (skillCandidates.size === 0) return {};
		const skills = Array.from(skillCandidates.values());
		const attrs = {
			"promptfoo.skill.count": skills.length,
			"promptfoo.skill.names": skills.map((skill) => skill.name).join(","),
			"promptfoo.skill.paths": skills.map((skill) => skill.path).join(",")
		};
		if (skills.length === 1) {
			attrs["promptfoo.skill.name"] = skills[0].name;
			attrs["promptfoo.skill.path"] = skills[0].path;
		}
		return attrs;
	}
	getAttributesForItem(item) {
		const attrs = {};
		switch (item.type) {
			case "command_execution":
				if (typeof item.command === "string") attrs["codex.command"] = this.sanitizeTraceText(item.command, "Codex command trace attribute") ?? "";
				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;
				{
					const serializedArgs = this.serializeItemValue(item.arguments ?? item.args ?? item.input);
					if (serializedArgs) attrs["codex.mcp.input"] = serializedArgs;
				}
				break;
			case "web_search":
				if (typeof item.query === "string") attrs["codex.search.query"] = this.sanitizeTraceText(item.query, "Codex web search query trace attribute") ?? "";
				break;
			case "collaboration_tool_call":
				if (typeof item.tool === "string") attrs["codex.collab.tool"] = item.tool;
				if (typeof item.target_thread_id === "string") attrs["codex.collab.target_thread"] = item.target_thread_id;
				break;
			case "spawn_agent":
				if (typeof item.role === "string") attrs["codex.collab.role"] = item.role;
				if (typeof item.thread_id === "string") attrs["codex.collab.spawned_thread"] = item.thread_id;
				break;
			case "send_input":
				if (typeof item.target_thread_id === "string") attrs["codex.collab.target_thread"] = item.target_thread_id;
				break;
		}
		return attrs;
	}
	serializeItemValue(value) {
		if (typeof value === "string") {
			const trimmed = value.trim();
			if (!trimmed) return;
			try {
				return JSON.stringify(this.redactTracePii(sanitizeObject(JSON.parse(trimmed))));
			} catch {
				return this.redactTracePii(sanitizeObject(trimmed, { context: "Codex MCP trace input" }));
			}
		}
		if (value === void 0 || value === null) return;
		try {
			return JSON.stringify(this.redactTracePii(sanitizeObject(value, { context: "Codex MCP trace input" })));
		} catch {
			return;
		}
	}
	sanitizeTraceText(value, context) {
		const sanitized = this.redactTracePii(sanitizeObject(value, { context }));
		if (typeof sanitized === "string") return sanitized;
		if (sanitized === void 0 || sanitized === null) return;
		try {
			return JSON.stringify(sanitized);
		} catch {
			return;
		}
	}
	redactTracePii(value) {
		if (typeof value === "string") return value.replace(/\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi, REDACTED).replace(/\b(?:sk-(?:proj-)?[A-Za-z0-9_-]{20,}|sk-ant-[A-Za-z0-9_-]{20,}|AKIA[A-Z0-9]{16}|AIza[A-Za-z0-9_-]{35}|Bearer\s+[A-Za-z0-9._~+/-]{20,}|Basic\s+[A-Za-z0-9+/=]{20,})\b/g, REDACTED).replace(/\b(api[_-]?key|token|password|secret|authorization|auth)\s*([=:])(\s*)(["']?)[^\s"'`]+(\4)/gi, (_match, key, separator, spacing, quote) => `${key}${separator}${spacing}${quote}${REDACTED}${quote}`);
		if (Array.isArray(value)) return value.map((item) => this.redactTracePii(item));
		if (value && typeof value === "object") return Object.fromEntries(Object.entries(value).map(([key, entryValue]) => [key, normalizeFieldName(key).includes("email") ? REDACTED : this.redactTracePii(entryValue)]));
		return value;
	}
	/**
	* Get attributes for a Codex item at completion
	*/
	getCompletionAttributesForItem(item, skillRootPrefixes = []) {
		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"] = this.sanitizeTraceText(item.aggregated_output, "Codex command output trace attribute") ?? "";
				Object.assign(attrs, this.getSkillTraceAttributes(item, skillRootPrefixes, { requireSuccessfulCommand: true }));
				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"] = this.sanitizeTraceText(item.error.message, "Codex MCP error trace attribute") ?? "";
				{
					const serializedArgs = this.serializeItemValue(item.arguments ?? item.args ?? item.input);
					if (serializedArgs) attrs["codex.mcp.input"] = serializedArgs;
				}
				break;
			case "agent_message":
				if (typeof item.text === "string") attrs["codex.message"] = this.sanitizeTraceText(item.text, "Codex agent message trace attribute") ?? "";
				break;
			case "reasoning":
				if (typeof item.text === "string") attrs["codex.reasoning"] = this.sanitizeTraceText(item.text, "Codex reasoning trace attribute") ?? "";
				break;
			case "error":
				if (typeof item.message === "string") attrs["codex.error"] = this.sanitizeTraceText(item.message, "Codex error trace attribute") ?? "";
				break;
		}
		return attrs;
	}
	generateInstanceKey(env, config) {
		const keyData = {
			env,
			base_url: config.base_url,
			cli_config: this.getResolvedCliConfig(config),
			codex_path_override: config.codex_path_override
		};
		return `openai:codex-sdk:instance:${crypto.createHash("sha256").update(JSON.stringify(keyData)).digest("hex")}`;
	}
	generateCacheKey(config, prompt, instanceKey) {
		const keyData = {
			instanceKey,
			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,
			web_search_mode: config.web_search_mode,
			approval_policy: config.approval_policy,
			prompt
		};
		return `openai:codex-sdk:${crypto.createHash("sha256").update(JSON.stringify(keyData)).digest("hex")}`;
	}
	getThreadRunQueueKey(config, cacheKey, instanceKey) {
		if (config.deep_tracing) return;
		if (config.thread_id) return `${instanceKey}:${config.thread_id}`;
		if (config.persist_threads && cacheKey) return cacheKey;
	}
	async runSerializedThreadTurn(queueKey, abortSignal, executeTurn) {
		if (!queueKey) return executeTurn();
		const previousRun = this.threadRunQueues.get(queueKey) ?? Promise.resolve();
		let releaseCurrentRun = () => {};
		const currentRun = new Promise((resolve) => {
			releaseCurrentRun = resolve;
		});
		const queuedRun = previousRun.catch(() => void 0).then(() => currentRun);
		this.threadRunQueues.set(queueKey, queuedRun);
		queuedRun.finally(() => {
			if (this.threadRunQueues.get(queueKey) === queuedRun) this.threadRunQueues.delete(queueKey);
		});
		try {
			await this.waitForPreviousThreadRun(previousRun, abortSignal);
			return await executeTurn();
		} finally {
			releaseCurrentRun();
		}
	}
	async waitForPreviousThreadRun(previousRun, abortSignal) {
		const previousRunDone = previousRun.catch(() => void 0);
		if (!abortSignal) {
			await previousRunDone;
			return;
		}
		if (abortSignal.aborted) throw this.createAbortError("Codex thread turn wait aborted");
		let onAbort;
		const abortPromise = new Promise((_, reject) => {
			onAbort = () => reject(this.createAbortError("Codex thread turn wait aborted"));
			abortSignal.addEventListener("abort", onAbort, { once: true });
		});
		try {
			await Promise.race([previousRunDone, abortPromise]);
		} finally {
			if (onAbort) abortSignal.removeEventListener("abort", onAbort);
		}
	}
	createAbortError(message) {
		const error = new Error(message);
		error.name = "AbortError";
		return error;
	}
	async callApi(prompt, context, callOptions) {
		const config = renderVarsInObject({
			...this.config,
			...context?.prompt?.config
		}, context?.vars);
		const requestedModel = typeof config.model === "string" && config.model ? config.model : void 0;
		return withGenAISpan(this.buildCodexSpanContext(prompt, context, requestedModel), () => this.callApiInternal(prompt, context, callOptions, config), (response) => this.extractCodexSpanResult(response, requestedModel));
	}
	buildCodexSpanContext(prompt, context, requestedModel) {
		return {
			system: "openai",
			operationName: "chat",
			model: requestedModel ?? "codex",
			providerId: this.id(),
			evalId: context?.evaluationId || context?.test?.metadata?.evaluationId,
			testIndex: typeof context?.test?.vars?.__testIdx === "number" ? context.test.vars.__testIdx : void 0,
			promptLabel: context?.prompt?.label,
			traceparent: context?.traceparent,
			requestBody: prompt
		};
	}
	extractCodexSpanResult(response, requestedModel) {
		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;
		if (requestedModel) result.responseModel = requestedModel;
		this.setCodexSpanResponseBody(result, response.output);
		this.setCodexSpanRawAttributes(result, response.raw);
		return result;
	}
	setCodexSpanResponseBody(result, output) {
		if (output === void 0) return;
		try {
			result.responseBody = typeof output === "string" ? output : JSON.stringify(output);
		} catch {
			result.responseBody = "[unable to serialize output]";
		}
	}
	setCodexSpanRawAttributes(result, raw) {
		if (!raw) return;
		try {
			const rawData = typeof raw === "string" ? JSON.parse(raw) : raw;
			if (rawData.reasoningTexts?.length > 0) result.additionalAttributes = {
				...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) result.additionalAttributes = {
				...result.additionalAttributes,
				"codex.items.total": rawData.items.length,
				"codex.items.breakdown": JSON.stringify(this.getCodexItemCounts(rawData.items))
			};
		} catch {}
	}
	getCodexItemCounts(items) {
		const itemCounts = {};
		for (const item of items) itemCounts[item.type] = (itemCounts[item.type] || 0) + 1;
		return itemCounts;
	}
	/**
	* 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, rawConfig) {
		let config;
		try {
			config = parseCodexConfig(rawConfig, { stripUnknownKeys: true });
		} catch (error) {
			const errorMessage = error instanceof Error ? error.message : String(error);
			logger.error("Error calling OpenAI Codex SDK", { error: errorMessage });
			return { error: `Error calling OpenAI Codex SDK: ${errorMessage}` };
		}
		const currentTraceparent = getTraceparent();
		const apiKey = this.getApiKey(config);
		const workingDirectory = resolveAgenticWorkingDir(config.working_dir, state.basePath) ?? process.cwd();
		const additionalDirectories = config.additional_directories?.map((directory) => resolveAgenticWorkingDir(directory, state.basePath) ?? directory);
		const resolvedConfig = {
			...config,
			working_dir: workingDirectory,
			...additionalDirectories ? { additional_directories: additionalDirectories } : {}
		};
		const env = this.prepareEnvironment(resolvedConfig, currentTraceparent, apiKey);
		const skillRootPrefixes = this.getSkillRootPrefixes(env, resolvedConfig.working_dir);
		const promptInput = this.parsePromptInput(prompt);
		if (apiKey) logger.debug("[CodexSDK] Using explicit API credentials from promptfoo config/environment");
		else logger.debug("[CodexSDK] No explicit API credentials configured; deferring auth resolution to Codex SDK login state");
		if (callOptions?.abortSignal?.aborted) return { error: "OpenAI Codex SDK call aborted before it started" };
		let localInstance = void 0;
		let useLocalInstance = false;
		let cacheKey = void 0;
		try {
			this.validateWorkingDirectory(resolvedConfig.working_dir, resolvedConfig.skip_git_repo_check);
			const codexInstance = await this.getCodexInstanceForTurn(env, resolvedConfig, apiKey);
			const activeInstance = codexInstance.activeInstance;
			localInstance = codexInstance.localInstance;
			useLocalInstance = codexInstance.useLocalInstance;
			const promptCacheBasis = context?.prompt?.raw ?? prompt;
			cacheKey = this.generateCacheKey(resolvedConfig, promptCacheBasis, codexInstance.instanceKey);
			const { turn, sessionId } = await this.executeCodexTurn(activeInstance, codexInstance.instanceKey, cacheKey, promptInput, resolvedConfig, callOptions, skillRootPrefixes);
			return this.buildCodexProviderResponse(turn, sessionId, skillRootPrefixes, resolvedConfig);
		} catch (error) {
			if (error instanceof Error && error.name === "AbortError" || callOptions?.abortSignal?.aborted) {
				logger.warn("OpenAI Codex SDK call aborted");
				return { error: "OpenAI Codex SDK call aborted" };
			}
			const errorMessage = error instanceof Error ? error.message : String(error);
			logger.error("Error calling OpenAI Codex SDK", { error: errorMessage });
			return buildCodexRateLimitResponse(error, errorMessage) ?? { error: `Error calling OpenAI Codex SDK: ${errorMessage}` };
		} finally {
			await this.cleanupCodexTurn(resolvedConfig, cacheKey, useLocalInstance, localInstance);
		}
	}
	buildCodexRunOptions(config, callOptions) {
		return {
			...config.output_schema ? { outputSchema: config.output_schema } : {},
			...callOptions?.abortSignal ? { signal: callOptions.abortSignal } : {}
		};
	}
	async getCodexInstanceForTurn(env, resolvedConfig, apiKey) {
		if (!this.codexModule) this.codexModule = await loadCodexSDK();
		const stableEnv = { ...env };
		delete stableEnv.TRACEPARENT;
		const instanceKey = this.generateInstanceKey(stableEnv, resolvedConfig);
		if (resolvedConfig.deep_tracing) {
			this.warnOnceForDeepTracingThreadOptions(resolvedConfig);
			const localInstance = new this.codexModule.Codex(this.buildCodexOptions(env, resolvedConfig, apiKey));
			return {
				activeInstance: localInstance,
				instanceKey,
				localInstance,
				useLocalInstance: true
			};
		}
		let activeInstance = this.codexInstances.get(instanceKey);
		if (!activeInstance) {
			activeInstance = new this.codexModule.Codex(this.buildCodexOptions(env, resolvedConfig, apiKey));
			this.codexInstances.set(instanceKey, activeInstance);
		}
		return {
			activeInstance,
			instanceKey,
			localInstance: void 0,
			useLocalInstance: false
		};
	}
	warnOnceForDeepTracingThreadOptions(resolvedConfig) {
		if (this.deepTracingWarningShown || !resolvedConfig.persist_threads && !resolvedConfig.thread_id && (resolvedConfig.thread_pool_size ?? 0) <= 1) return;
		logger.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;
	}
	async executeCodexTurn(activeInstance, instanceKey, cacheKey, promptInput, resolvedConfig, callOptions, skillRootPrefixes) {
		const queueKey = this.getThreadRunQueueKey(resolvedConfig, cacheKey, instanceKey);
		const runOptions = this.buildCodexRunOptions(resolvedConfig, callOptions);
		return this.runSerializedThreadTurn(queueKey, callOptions?.abortSignal, async () => {
			const thread = await this.getOrCreateThread(resolvedConfig, cacheKey, instanceKey, activeInstance);
			return {
				turn: resolvedConfig.enable_streaming ? await this.runStreaming(thread, promptInput, runOptions, callOptions, skillRootPrefixes) : await thread.run(promptInput, runOptions),
				sessionId: thread.id || "unknown"
			};
		});
	}
	buildCodexProviderResponse(turn, sessionId, skillRootPrefixes, resolvedConfig) {
		const output = turn.finalResponse || "";
		const tokenUsage = this.buildCodexTokenUsage(turn.usage);
		logger.debug("OpenAI Codex SDK response", {
			output,
			usage: turn.usage
		});
		return {
			output,
			tokenUsage,
			cost: this.calculateCodexResponseCost(tokenUsage, resolvedConfig.model),
			metadata: this.buildCodexResponseMetadata(turn.items, skillRootPrefixes),
			raw: JSON.stringify(turn),
			sessionId
		};
	}
	buildCodexResponseMetadata(items, skillRootPrefixes) {
		const skillMetadata = this.buildSkillMetadata(items, skillRootPrefixes);
		if (!skillMetadata) return;
		return getCodexSkillMetadataFields(skillMetadata);
	}
	buildCodexTokenUsage(turnUsage) {
		if (!turnUsage) return;
		return {
			prompt: turnUsage.input_tokens,
			completion: turnUsage.output_tokens,
			total: turnUsage.input_tokens + turnUsage.output_tokens,
			cached: turnUsage.cached_input_tokens || 0,
			...typeof turnUsage.reasoning_output_tokens === "number" ? { completionDetails: { reasoning: turnUsage.reasoning_output_tokens } } : {}
		};
	}
	calculateCodexResponseCost(tokenUsage, model) {
		return calculateOpenAIUsageCostFromTokenUsage(model, tokenUsage);
	}
	async cleanupCodexTurn(resolvedConfig, cacheKey, useLocalInstance, localInstance) {
		if (!resolvedConfig.deep_tracing && !resolvedConfig.persist_threads && !resolvedConfig.thread_id && cacheKey) this.threads.delete(cacheKey);
		if (!useLocalInstance || !localInstance) return;
		try {
			await this.destroyInstance(localInstance);
		} catch (cleanupError) {
			logger.debug("[CodexSDK] Error cleaning up local instance", { error: cleanupError });
		}
	}
};
//#endregion
export { OpenAICodexSDKProvider as t };

//# sourceMappingURL=codex-sdk-tY9AZhDN.js.map