@use-lattice/litmus 0.121.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (199) hide show
  1. package/LICENSE +19 -0
  2. package/dist/src/accounts-Bt1oJb1Z.cjs +219 -0
  3. package/dist/src/accounts-DjOU8Rm3.js +178 -0
  4. package/dist/src/agentic-utils-D03IiXQc.js +153 -0
  5. package/dist/src/agentic-utils-Dh7xaMQM.cjs +180 -0
  6. package/dist/src/agents-C6BIMlZa.js +231 -0
  7. package/dist/src/agents-DvIpNX1L.cjs +666 -0
  8. package/dist/src/agents-ZP0RP9vV.cjs +231 -0
  9. package/dist/src/agents-maJXdjbR.js +665 -0
  10. package/dist/src/aimlapi-BTbQjG2E.cjs +30 -0
  11. package/dist/src/aimlapi-CwMxqfXP.js +30 -0
  12. package/dist/src/audio-BBUdvsde.cjs +97 -0
  13. package/dist/src/audio-D5DPZ7I-.js +97 -0
  14. package/dist/src/base-BEysXrkq.cjs +222 -0
  15. package/dist/src/base-C451JQfq.js +193 -0
  16. package/dist/src/blobs-BY8MDmpo.js +230 -0
  17. package/dist/src/blobs-BgcNn97m.cjs +256 -0
  18. package/dist/src/cache-BBE_lsTA.cjs +4 -0
  19. package/dist/src/cache-BkrqU5Ba.js +237 -0
  20. package/dist/src/cache-DsCxFlsZ.cjs +297 -0
  21. package/dist/src/chat-CPJWDP6a.cjs +289 -0
  22. package/dist/src/chat-CXX3xzkk.cjs +811 -0
  23. package/dist/src/chat-CcDgZFJ4.js +787 -0
  24. package/dist/src/chat-Dz5ZeGO2.js +289 -0
  25. package/dist/src/chatkit-Dw0mKkML.cjs +1158 -0
  26. package/dist/src/chatkit-swAIVuea.js +1157 -0
  27. package/dist/src/chunk-DEq-mXcV.js +15 -0
  28. package/dist/src/claude-agent-sdk-BXZJtOg6.js +379 -0
  29. package/dist/src/claude-agent-sdk-CkfyjDoG.cjs +383 -0
  30. package/dist/src/cloudflare-ai-BzpJcqUH.js +161 -0
  31. package/dist/src/cloudflare-ai-Cmy_R1y2.cjs +161 -0
  32. package/dist/src/cloudflare-gateway-B9tVQKok.cjs +272 -0
  33. package/dist/src/cloudflare-gateway-DrD3ew3H.js +272 -0
  34. package/dist/src/codex-sdk-Dezj9Nwm.js +1056 -0
  35. package/dist/src/codex-sdk-Dl9D4k5B.cjs +1060 -0
  36. package/dist/src/cometapi-C-9YvCHC.js +54 -0
  37. package/dist/src/cometapi-DHgDKoO2.cjs +54 -0
  38. package/dist/src/completion-B8Ctyxpr.js +120 -0
  39. package/dist/src/completion-Cxrt08sj.cjs +131 -0
  40. package/dist/src/createHash-BwgE13yv.cjs +27 -0
  41. package/dist/src/createHash-DmPQkvBh.js +15 -0
  42. package/dist/src/docker-BiqcTwLv.js +80 -0
  43. package/dist/src/docker-C7tEJnP-.cjs +80 -0
  44. package/dist/src/esm-C62Zofr1.cjs +409 -0
  45. package/dist/src/esm-DMVc93eh.js +379 -0
  46. package/dist/src/evalResult-C3NJPQOo.cjs +301 -0
  47. package/dist/src/evalResult-C7JJAPBb.js +295 -0
  48. package/dist/src/evalResult-DoVTZZWI.cjs +2 -0
  49. package/dist/src/extractor-DnMD3fwt.cjs +391 -0
  50. package/dist/src/extractor-DtlL28vL.js +374 -0
  51. package/dist/src/fetch-BTxakTSg.cjs +1133 -0
  52. package/dist/src/fetch-DQckpUFz.js +928 -0
  53. package/dist/src/fileExtensions-DnqA1y9x.js +85 -0
  54. package/dist/src/fileExtensions-bYh77CN8.cjs +114 -0
  55. package/dist/src/genaiTracer-CyZrmaK0.cjs +268 -0
  56. package/dist/src/genaiTracer-D3fD9dNV.js +256 -0
  57. package/dist/src/graders-BNscxFrU.js +13644 -0
  58. package/dist/src/graders-D2oE9Msq.js +2 -0
  59. package/dist/src/graders-c0Ez_w-9.cjs +2 -0
  60. package/dist/src/graders-d0F2M3e9.cjs +14056 -0
  61. package/dist/src/image-0ZhE0VlR.cjs +280 -0
  62. package/dist/src/image-CWE1pdNv.js +257 -0
  63. package/dist/src/image-D9ZK6hwL.js +163 -0
  64. package/dist/src/image-DKZgZITg.cjs +163 -0
  65. package/dist/src/index.cjs +11366 -0
  66. package/dist/src/index.d.cts +19640 -0
  67. package/dist/src/index.d.ts +19641 -0
  68. package/dist/src/index.js +11306 -0
  69. package/dist/src/invariant-Ddh24eXh.js +25 -0
  70. package/dist/src/invariant-kfQ8Bu82.cjs +30 -0
  71. package/dist/src/knowledgeBase-BgPyGFUd.cjs +122 -0
  72. package/dist/src/knowledgeBase-DyHilYaP.js +122 -0
  73. package/dist/src/litellm-CyMeneHS.js +135 -0
  74. package/dist/src/litellm-DWDF73yF.cjs +135 -0
  75. package/dist/src/logger-C40ZGil9.js +717 -0
  76. package/dist/src/logger-DyfK9PBt.cjs +917 -0
  77. package/dist/src/luma-ray-BAU9X_ep.cjs +315 -0
  78. package/dist/src/luma-ray-nwVseBbv.js +313 -0
  79. package/dist/src/messages-B5ADWTTv.js +245 -0
  80. package/dist/src/messages-BCnZfqrS.cjs +257 -0
  81. package/dist/src/meteor-DLZZ3osF.cjs +134 -0
  82. package/dist/src/meteor-DUiCJRC-.js +134 -0
  83. package/dist/src/modelslab-00cveB8L.cjs +163 -0
  84. package/dist/src/modelslab-D9sCU_L7.js +163 -0
  85. package/dist/src/nova-reel-CTapvqYH.js +276 -0
  86. package/dist/src/nova-reel-DlWuuroF.cjs +278 -0
  87. package/dist/src/nova-sonic-5UPWfeMv.cjs +363 -0
  88. package/dist/src/nova-sonic-BhSwQNym.js +363 -0
  89. package/dist/src/openai-BWrJK9d8.cjs +52 -0
  90. package/dist/src/openai-DumO8WQn.js +47 -0
  91. package/dist/src/openclaw-B8brrjC_.cjs +577 -0
  92. package/dist/src/openclaw-Bkayww9q.js +571 -0
  93. package/dist/src/opencode-sdk-7xjoDNiM.cjs +562 -0
  94. package/dist/src/opencode-sdk-SGwAPxht.js +558 -0
  95. package/dist/src/otlpReceiver-CoAHfAN9.cjs +15 -0
  96. package/dist/src/otlpReceiver-oO3EQwI9.js +14 -0
  97. package/dist/src/providerRegistry-4yjhaEM8.js +45 -0
  98. package/dist/src/providerRegistry-DhV4rJIc.cjs +50 -0
  99. package/dist/src/providers-B5RJVG-7.cjs +33609 -0
  100. package/dist/src/providers-BdmZCLzV.js +33262 -0
  101. package/dist/src/providers-CxtRxn8e.js +2 -0
  102. package/dist/src/providers-DnQLNbx1.cjs +3 -0
  103. package/dist/src/pythonUtils-BD0druiM.cjs +275 -0
  104. package/dist/src/pythonUtils-IBhn5YGR.js +249 -0
  105. package/dist/src/quiverai-BDOwZBsM.cjs +213 -0
  106. package/dist/src/quiverai-D3JTF5lD.js +213 -0
  107. package/dist/src/responses-B2LCDCXZ.js +667 -0
  108. package/dist/src/responses-BvNm4Xv9.cjs +685 -0
  109. package/dist/src/rubyUtils-B0NwnfpY.cjs +245 -0
  110. package/dist/src/rubyUtils-BroxzZ7c.cjs +2 -0
  111. package/dist/src/rubyUtils-hqVw5UvJ.js +222 -0
  112. package/dist/src/sagemaker-Cno2V-Sx.js +689 -0
  113. package/dist/src/sagemaker-fV_KUgs5.cjs +691 -0
  114. package/dist/src/server-BOuAXb06.cjs +238 -0
  115. package/dist/src/server-CtI-EWzm.cjs +2 -0
  116. package/dist/src/server-Cy3DZymt.js +189 -0
  117. package/dist/src/slack-CP8xBePa.js +135 -0
  118. package/dist/src/slack-DSQ1yXVb.cjs +135 -0
  119. package/dist/src/store-BwDDaBjb.cjs +246 -0
  120. package/dist/src/store-DcbLC593.cjs +2 -0
  121. package/dist/src/store-IGpqMIkv.js +240 -0
  122. package/dist/src/tables-3Q2cL7So.cjs +373 -0
  123. package/dist/src/tables-Bi2fjr4W.js +288 -0
  124. package/dist/src/telemetry-Bg2WqF79.js +161 -0
  125. package/dist/src/telemetry-D0x6u5kX.cjs +166 -0
  126. package/dist/src/telemetry-DXNimrI0.cjs +2 -0
  127. package/dist/src/text-B_UCRPp2.js +22 -0
  128. package/dist/src/text-CW1cyrwj.cjs +33 -0
  129. package/dist/src/tokenUsageUtils-NYT-WKS6.js +138 -0
  130. package/dist/src/tokenUsageUtils-bVa1ga6f.cjs +173 -0
  131. package/dist/src/transcription-Cl_W16Pr.js +122 -0
  132. package/dist/src/transcription-yt1EecY8.cjs +124 -0
  133. package/dist/src/transform-BCtGrl_W.cjs +228 -0
  134. package/dist/src/transform-Bv6gG2MJ.cjs +1688 -0
  135. package/dist/src/transform-CY1wbpRy.js +1507 -0
  136. package/dist/src/transform-DU8rUL9P.cjs +2 -0
  137. package/dist/src/transform-yWaShiKr.js +216 -0
  138. package/dist/src/transformersAvailability-BGkzavwb.js +35 -0
  139. package/dist/src/transformersAvailability-DKoRtQLy.cjs +35 -0
  140. package/dist/src/types-5aqHpBwE.cjs +3769 -0
  141. package/dist/src/types-Bn6D9c4U.js +3300 -0
  142. package/dist/src/util-BkKlTkI2.js +293 -0
  143. package/dist/src/util-CTh0bfOm.cjs +1119 -0
  144. package/dist/src/util-D17oBwo7.cjs +328 -0
  145. package/dist/src/util-DsS_-v4p.js +613 -0
  146. package/dist/src/util-DuntT1Ga.js +951 -0
  147. package/dist/src/util-aWjdCYMI.cjs +667 -0
  148. package/dist/src/utils-CisQwpjA.js +94 -0
  149. package/dist/src/utils-yWamDvmz.cjs +123 -0
  150. package/dist/tsconfig.tsbuildinfo +1 -0
  151. package/drizzle/0000_lush_hellion.sql +36 -0
  152. package/drizzle/0001_wide_calypso.sql +3 -0
  153. package/drizzle/0002_tidy_juggernaut.sql +1 -0
  154. package/drizzle/0003_lively_naoko.sql +8 -0
  155. package/drizzle/0004_minor_peter_quill.sql +19 -0
  156. package/drizzle/0005_silky_millenium_guard.sql +2 -0
  157. package/drizzle/0006_harsh_caretaker.sql +42 -0
  158. package/drizzle/0007_cloudy_wong.sql +1 -0
  159. package/drizzle/0008_broad_boomer.sql +2 -0
  160. package/drizzle/0009_strong_marten_broadcloak.sql +19 -0
  161. package/drizzle/0010_needy_bishop.sql +11 -0
  162. package/drizzle/0011_moaning_millenium_guard.sql +1 -0
  163. package/drizzle/0012_late_marten_broadcloak.sql +2 -0
  164. package/drizzle/0013_previous_dormammu.sql +9 -0
  165. package/drizzle/0014_lazy_captain_universe.sql +2 -0
  166. package/drizzle/0015_zippy_wallop.sql +29 -0
  167. package/drizzle/0016_jazzy_zemo.sql +2 -0
  168. package/drizzle/0017_reflective_praxagora.sql +4 -0
  169. package/drizzle/0018_fat_vanisher.sql +22 -0
  170. package/drizzle/0019_new_clint_barton.sql +8 -0
  171. package/drizzle/0020_skinny_maverick.sql +1 -0
  172. package/drizzle/0021_mysterious_madelyne_pryor.sql +13 -0
  173. package/drizzle/0022_sleepy_ultimo.sql +25 -0
  174. package/drizzle/0023_wooden_mandrill.sql +2 -0
  175. package/drizzle/AGENTS.md +68 -0
  176. package/drizzle/CLAUDE.md +1 -0
  177. package/drizzle/meta/0000_snapshot.json +221 -0
  178. package/drizzle/meta/0001_snapshot.json +214 -0
  179. package/drizzle/meta/0002_snapshot.json +221 -0
  180. package/drizzle/meta/0005_snapshot.json +369 -0
  181. package/drizzle/meta/0006_snapshot.json +638 -0
  182. package/drizzle/meta/0007_snapshot.json +640 -0
  183. package/drizzle/meta/0008_snapshot.json +649 -0
  184. package/drizzle/meta/0009_snapshot.json +554 -0
  185. package/drizzle/meta/0010_snapshot.json +619 -0
  186. package/drizzle/meta/0011_snapshot.json +627 -0
  187. package/drizzle/meta/0012_snapshot.json +639 -0
  188. package/drizzle/meta/0013_snapshot.json +717 -0
  189. package/drizzle/meta/0014_snapshot.json +717 -0
  190. package/drizzle/meta/0015_snapshot.json +897 -0
  191. package/drizzle/meta/0016_snapshot.json +1031 -0
  192. package/drizzle/meta/0018_snapshot.json +1210 -0
  193. package/drizzle/meta/0019_snapshot.json +1165 -0
  194. package/drizzle/meta/0020_snapshot.json +1232 -0
  195. package/drizzle/meta/0021_snapshot.json +1311 -0
  196. package/drizzle/meta/0022_snapshot.json +1481 -0
  197. package/drizzle/meta/0023_snapshot.json +1496 -0
  198. package/drizzle/meta/_journal.json +174 -0
  199. package/package.json +240 -0
@@ -0,0 +1,1060 @@
1
+ const require_logger = require("./logger-DyfK9PBt.cjs");
2
+ const require_esm = require("./esm-C62Zofr1.cjs");
3
+ const require_genaiTracer = require("./genaiTracer-CyZrmaK0.cjs");
4
+ const require_providerRegistry = require("./providerRegistry-DhV4rJIc.cjs");
5
+ let fs = require("fs");
6
+ fs = require_logger.__toESM(fs);
7
+ let path = require("path");
8
+ path = require_logger.__toESM(path);
9
+ let dedent = require("dedent");
10
+ dedent = require_logger.__toESM(dedent);
11
+ let zod = require("zod");
12
+ let crypto = require("crypto");
13
+ crypto = require_logger.__toESM(crypto);
14
+ let _opentelemetry_api = require("@opentelemetry/api");
15
+ //#region src/providers/openai/codex-sdk.ts
16
+ const MINIMAL_CLI_ENV_KEYS = [
17
+ "PATH",
18
+ "Path",
19
+ "HOME",
20
+ "USER",
21
+ "USERNAME",
22
+ "USERPROFILE",
23
+ "TMPDIR",
24
+ "TMP",
25
+ "TEMP",
26
+ "SHELL",
27
+ "COMSPEC",
28
+ "SystemRoot",
29
+ "PATHEXT",
30
+ "LANG",
31
+ "LC_ALL",
32
+ "TERM"
33
+ ];
34
+ const COMMON_OPTIONAL_PROCESS_ENV_KEYS = [
35
+ "HTTP_PROXY",
36
+ "HTTPS_PROXY",
37
+ "ALL_PROXY",
38
+ "NO_PROXY",
39
+ "SSL_CERT_FILE",
40
+ "SSL_CERT_DIR",
41
+ "REQUESTS_CA_BUNDLE",
42
+ "NODE_EXTRA_CA_CERTS",
43
+ "SSH_AUTH_SOCK",
44
+ "GIT_SSH_COMMAND"
45
+ ];
46
+ const CodexCliEnvValueSchema = zod.z.union([
47
+ zod.z.string(),
48
+ zod.z.number(),
49
+ zod.z.boolean()
50
+ ]).transform(String);
51
+ const OpenAICodexSDKConfigShape = {
52
+ basePath: zod.z.string().optional(),
53
+ prefix: zod.z.string().optional(),
54
+ suffix: zod.z.string().optional(),
55
+ provider: zod.z.unknown().optional(),
56
+ linkedTargetId: zod.z.string().optional(),
57
+ apiKey: zod.z.string().min(1).optional(),
58
+ base_url: zod.z.string().min(1).optional(),
59
+ working_dir: zod.z.string().min(1).optional(),
60
+ additional_directories: zod.z.array(zod.z.string().min(1)).optional(),
61
+ skip_git_repo_check: zod.z.boolean().optional(),
62
+ codex_path_override: zod.z.string().min(1).optional(),
63
+ model: zod.z.string().min(1).optional(),
64
+ sandbox_mode: zod.z.enum([
65
+ "read-only",
66
+ "workspace-write",
67
+ "danger-full-access"
68
+ ]).optional(),
69
+ model_reasoning_effort: zod.z.enum([
70
+ "minimal",
71
+ "low",
72
+ "medium",
73
+ "high",
74
+ "xhigh"
75
+ ]).optional(),
76
+ network_access_enabled: zod.z.boolean().optional(),
77
+ web_search_enabled: zod.z.boolean().optional(),
78
+ web_search_mode: zod.z.enum([
79
+ "disabled",
80
+ "cached",
81
+ "live"
82
+ ]).optional(),
83
+ collaboration_mode: zod.z.enum(["coding", "plan"]).optional(),
84
+ approval_policy: zod.z.enum([
85
+ "never",
86
+ "on-request",
87
+ "on-failure",
88
+ "untrusted"
89
+ ]).optional(),
90
+ thread_id: zod.z.string().min(1).optional(),
91
+ persist_threads: zod.z.boolean().optional(),
92
+ thread_pool_size: zod.z.number().int().positive().optional(),
93
+ output_schema: zod.z.record(zod.z.string(), zod.z.unknown()).optional(),
94
+ cli_env: zod.z.record(zod.z.string(), CodexCliEnvValueSchema).optional(),
95
+ inherit_process_env: zod.z.boolean().optional(),
96
+ enable_streaming: zod.z.boolean().optional(),
97
+ deep_tracing: zod.z.boolean().optional(),
98
+ cli_config: zod.z.record(zod.z.string(), zod.z.unknown()).optional()
99
+ };
100
+ const OpenAICodexSDKConfigSchema = zod.z.object(OpenAICodexSDKConfigShape).strict();
101
+ const OpenAICodexSDKMergedPromptConfigSchema = zod.z.object(OpenAICodexSDKConfigShape).strip();
102
+ function parseCodexConfig(config, options = {}) {
103
+ const schema = options.stripUnknownKeys ? OpenAICodexSDKMergedPromptConfigSchema : OpenAICodexSDKConfigSchema;
104
+ try {
105
+ return schema.parse(config ?? {});
106
+ } catch (error) {
107
+ if (error instanceof zod.z.ZodError) {
108
+ const issues = error.issues.map((issue) => {
109
+ return `${issue.path.length > 0 ? issue.path.join(".") : "(root)"}: ${issue.message}`;
110
+ }).join("; ");
111
+ throw new Error(`Invalid OpenAI Codex SDK config: ${issues}`);
112
+ }
113
+ throw error;
114
+ }
115
+ }
116
+ function getMinimalProcessEnv() {
117
+ const env = {};
118
+ for (const key of MINIMAL_CLI_ENV_KEYS) {
119
+ const value = process.env[key];
120
+ if (typeof value === "string" && value.length > 0) env[key] = value;
121
+ }
122
+ return env;
123
+ }
124
+ /**
125
+ * Helper to load the OpenAI Codex SDK ESM module
126
+ * Uses resolvePackageEntryPoint to handle ESM-only packages with restrictive exports
127
+ */
128
+ async function loadCodexSDK() {
129
+ const basePaths = [
130
+ require_logger.state.basePath ? path.default.resolve(require_logger.state.basePath) : void 0,
131
+ process.cwd(),
132
+ path.default.resolve(require_esm.getDirectory(), ".."),
133
+ path.default.resolve(require_esm.getDirectory(), "../..")
134
+ ].filter((candidate) => Boolean(candidate));
135
+ let codexPath = null;
136
+ for (const basePath of new Set(basePaths)) {
137
+ codexPath = require_esm.resolvePackageEntryPoint("@openai/codex-sdk", basePath);
138
+ if (codexPath) break;
139
+ }
140
+ if (!codexPath) throw new Error(dedent.default`The @openai/codex-sdk package is required but not installed.
141
+
142
+ To use the OpenAI Codex SDK provider, install it with:
143
+ npm install @openai/codex-sdk
144
+
145
+ Requires Node.js 20.20+ or 22.22+.
146
+
147
+ For more information, see: https://www.promptfoo.dev/docs/providers/openai-codex-sdk/`);
148
+ try {
149
+ return await require_esm.importModule(codexPath);
150
+ } catch (err) {
151
+ require_logger.logger.error(`Failed to load OpenAI Codex SDK: ${err}`);
152
+ if (err.stack) require_logger.logger.error(err.stack);
153
+ throw new Error(dedent.default`Failed to load @openai/codex-sdk.
154
+
155
+ The package was found but could not be loaded. This may be due to:
156
+ - Incompatible Node.js version (requires Node.js 20.20+ or 22.22+)
157
+ - Corrupted installation
158
+
159
+ Try reinstalling:
160
+ npm install @openai/codex-sdk
161
+
162
+ For more information, see: https://www.promptfoo.dev/docs/providers/openai-codex-sdk/`);
163
+ }
164
+ }
165
+ const CODEX_MODEL_PRICING = {
166
+ "gpt-5.4": {
167
+ input: 2.5,
168
+ output: 15,
169
+ cache_read: .25
170
+ },
171
+ "gpt-5.4-pro": {
172
+ input: 30,
173
+ output: 180,
174
+ cache_read: 30
175
+ },
176
+ "gpt-5.3-codex": {
177
+ input: 1.75,
178
+ output: 14,
179
+ cache_read: .175
180
+ },
181
+ "gpt-5.3-codex-spark": {
182
+ input: .5,
183
+ output: 4,
184
+ cache_read: .05
185
+ },
186
+ "gpt-5.2": {
187
+ input: 1.75,
188
+ output: 14,
189
+ cache_read: .175
190
+ },
191
+ "gpt-5.2-codex": {
192
+ input: 1.75,
193
+ output: 14,
194
+ cache_read: .175
195
+ },
196
+ "gpt-5.1-codex": {
197
+ input: 2,
198
+ output: 8,
199
+ cache_read: .2
200
+ },
201
+ "gpt-5.1-codex-max": {
202
+ input: 3,
203
+ output: 12,
204
+ cache_read: .3
205
+ },
206
+ "gpt-5.1-codex-mini": {
207
+ input: .5,
208
+ output: 2,
209
+ cache_read: .05
210
+ },
211
+ "gpt-5-codex": {
212
+ input: 2,
213
+ output: 8,
214
+ cache_read: .2
215
+ },
216
+ "gpt-5-codex-mini": {
217
+ input: .5,
218
+ output: 2,
219
+ cache_read: .05
220
+ },
221
+ "gpt-5": {
222
+ input: 2,
223
+ output: 8,
224
+ cache_read: .2
225
+ }
226
+ };
227
+ var OpenAICodexSDKProvider = class OpenAICodexSDKProvider {
228
+ static OPENAI_MODELS = [
229
+ "gpt-5.4",
230
+ "gpt-5.4-pro",
231
+ "gpt-5.3-codex",
232
+ "gpt-5.3-codex-spark",
233
+ "gpt-5.2",
234
+ "gpt-5.2-codex",
235
+ "gpt-5.1-codex",
236
+ "gpt-5.1-codex-max",
237
+ "gpt-5.1-codex-mini",
238
+ "gpt-5-codex",
239
+ "gpt-5-codex-mini",
240
+ "gpt-5"
241
+ ];
242
+ config;
243
+ env;
244
+ apiKey;
245
+ providerId = "openai:codex-sdk";
246
+ codexModule;
247
+ codexInstances = /* @__PURE__ */ new Map();
248
+ threads = /* @__PURE__ */ new Map();
249
+ threadRunQueues = /* @__PURE__ */ new Map();
250
+ deepTracingWarningShown = false;
251
+ ignoredProviderEnvWarningShown = false;
252
+ omittedProcessEnvWarningShown = false;
253
+ constructor(options = {}) {
254
+ const { config, env, id } = options;
255
+ this.config = parseCodexConfig(config);
256
+ this.env = env;
257
+ this.apiKey = this.getApiKey();
258
+ this.providerId = id ?? this.providerId;
259
+ require_providerRegistry.providerRegistry.register(this);
260
+ if (this.config.model && !OpenAICodexSDKProvider.OPENAI_MODELS.includes(this.config.model)) require_logger.logger.warn(`Using unknown model for OpenAI Codex SDK: ${this.config.model}`);
261
+ }
262
+ id() {
263
+ return this.providerId;
264
+ }
265
+ getApiKey(config = this.config) {
266
+ return config?.apiKey || this.env?.OPENAI_API_KEY || this.env?.CODEX_API_KEY || require_logger.getEnvString("OPENAI_API_KEY") || require_logger.getEnvString("CODEX_API_KEY");
267
+ }
268
+ requiresApiKey() {
269
+ return false;
270
+ }
271
+ toString() {
272
+ return "[OpenAI Codex SDK Provider]";
273
+ }
274
+ /**
275
+ * Safely tear down a Codex instance by calling its cleanup method
276
+ * (destroy, cleanup, or close -- whichever is available).
277
+ */
278
+ async destroyInstance(instance) {
279
+ if (typeof instance.destroy === "function") await instance.destroy();
280
+ else if (typeof instance.cleanup === "function") await instance.cleanup();
281
+ else if (typeof instance.close === "function") await instance.close();
282
+ }
283
+ async cleanup() {
284
+ this.threads.clear();
285
+ this.threadRunQueues.clear();
286
+ for (const instance of this.codexInstances.values()) try {
287
+ await this.destroyInstance(instance);
288
+ } catch (error) {
289
+ require_logger.logger.warn("[CodexSDK] Error during cleanup", { error });
290
+ }
291
+ this.codexInstances.clear();
292
+ }
293
+ async shutdown() {
294
+ try {
295
+ await this.cleanup();
296
+ } finally {
297
+ require_providerRegistry.providerRegistry.unregister(this);
298
+ }
299
+ }
300
+ prepareEnvironment(config, traceparent, apiKey = this.getApiKey(config)) {
301
+ const inheritProcessEnv = config.inherit_process_env === true;
302
+ const cliEnv = Object.fromEntries(Object.entries(config.cli_env ?? {}).map(([key, value]) => [key, String(value)]));
303
+ const env = {
304
+ ...inheritProcessEnv ? process.env : getMinimalProcessEnv(),
305
+ ...cliEnv
306
+ };
307
+ const ignoredProviderEnvKeys = Object.keys(this.env ?? {}).filter((key) => key !== "OPENAI_API_KEY" && key !== "CODEX_API_KEY" && !(key in (config.cli_env ?? {}))).sort();
308
+ if (ignoredProviderEnvKeys.length > 0 && !this.ignoredProviderEnvWarningShown) {
309
+ require_logger.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 });
310
+ this.ignoredProviderEnvWarningShown = true;
311
+ }
312
+ if (!inheritProcessEnv && !this.omittedProcessEnvWarningShown) {
313
+ const omittedProcessEnvKeys = COMMON_OPTIONAL_PROCESS_ENV_KEYS.filter((key) => typeof process.env[key] === "string" && !(key in env));
314
+ if (omittedProcessEnvKeys.length > 0) {
315
+ require_logger.logger.warn("[CodexSDK] Common proxy/SSH/certificate 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 });
316
+ this.omittedProcessEnvWarningShown = true;
317
+ }
318
+ }
319
+ const sortedEnv = {};
320
+ for (const key of Object.keys(env).sort()) if (env[key] !== void 0) sortedEnv[key] = env[key];
321
+ if (apiKey) {
322
+ sortedEnv.OPENAI_API_KEY = apiKey;
323
+ sortedEnv.CODEX_API_KEY = apiKey;
324
+ }
325
+ if (config.deep_tracing) {
326
+ if (!sortedEnv.OTEL_EXPORTER_OTLP_ENDPOINT) sortedEnv.OTEL_EXPORTER_OTLP_ENDPOINT = "http://127.0.0.1:4318";
327
+ if (!sortedEnv.OTEL_EXPORTER_OTLP_PROTOCOL) sortedEnv.OTEL_EXPORTER_OTLP_PROTOCOL = "http/json";
328
+ if (!sortedEnv.OTEL_SERVICE_NAME) sortedEnv.OTEL_SERVICE_NAME = "codex-cli";
329
+ if (!sortedEnv.OTEL_TRACES_EXPORTER) sortedEnv.OTEL_TRACES_EXPORTER = "otlp";
330
+ if (traceparent) sortedEnv.TRACEPARENT = traceparent;
331
+ require_logger.logger.debug("[CodexSDK] Injecting OTEL config for deep tracing", {
332
+ traceparent: traceparent || "(none - CLI will start own trace)",
333
+ endpoint: sortedEnv.OTEL_EXPORTER_OTLP_ENDPOINT,
334
+ userConfigured: {
335
+ endpoint: !!env.OTEL_EXPORTER_OTLP_ENDPOINT,
336
+ protocol: !!env.OTEL_EXPORTER_OTLP_PROTOCOL,
337
+ serviceName: !!env.OTEL_SERVICE_NAME
338
+ }
339
+ });
340
+ } else delete sortedEnv.TRACEPARENT;
341
+ return sortedEnv;
342
+ }
343
+ getResolvedCliConfig(config) {
344
+ if (!config.cli_config && !config.collaboration_mode) return;
345
+ return {
346
+ ...config.cli_config ?? {},
347
+ ...config.collaboration_mode ? { collaboration_mode: config.collaboration_mode } : {}
348
+ };
349
+ }
350
+ getSkillRootPrefixes(env, workingDir) {
351
+ const prefixes = /* @__PURE__ */ new Set();
352
+ const addPrefix = (candidate) => {
353
+ if (!candidate) return;
354
+ const normalized = candidate.replace(/\\/g, "/").replace(/\/+$/g, "");
355
+ if (normalized) prefixes.add(normalized);
356
+ };
357
+ addPrefix(env.CODEX_HOME);
358
+ addPrefix("/etc/codex");
359
+ if (workingDir) {
360
+ const resolvedWorkingDir = path.default.resolve(workingDir).replace(/\\/g, "/");
361
+ addPrefix(path.default.posix.join(resolvedWorkingDir, ".agents"));
362
+ const gitRoot = this.findGitRepositoryRoot(resolvedWorkingDir);
363
+ if (gitRoot) addPrefix(path.default.posix.join(gitRoot.replace(/\\/g, "/"), ".agents"));
364
+ }
365
+ const homeDir = env.HOME || env.USERPROFILE || process.env.HOME || process.env.USERPROFILE;
366
+ if (homeDir) addPrefix(path.default.posix.join(homeDir.replace(/\\/g, "/"), ".codex"));
367
+ return Array.from(prefixes);
368
+ }
369
+ isValidCodexSkillName(name) {
370
+ return /^[A-Za-z0-9._:-]+$/.test(name);
371
+ }
372
+ extractSkillPathCandidates(text, skillRootPrefixes = []) {
373
+ const matches = /* @__PURE__ */ new Map();
374
+ for (const rawToken of text.split(/\s+/)) {
375
+ const token = rawToken.replace(/^[`"'([{<]+|[`"',;:)\]}>]+$/g, "").trim();
376
+ if (!token) continue;
377
+ const normalizedPath = token.replace(/\\/g, "/");
378
+ const repoMatch = normalizedPath.match(/^\.agents\/skills\/([^/\s]+)\/SKILL\.md$/);
379
+ if (repoMatch) {
380
+ if (this.isValidCodexSkillName(repoMatch[1])) matches.set(normalizedPath, {
381
+ name: repoMatch[1],
382
+ path: normalizedPath
383
+ });
384
+ continue;
385
+ }
386
+ const matchingRoot = skillRootPrefixes.find((prefix) => normalizedPath.startsWith(`${prefix}/skills/`));
387
+ if (!matchingRoot) continue;
388
+ const customRootMatch = normalizedPath.slice(matchingRoot.length + 1).match(/^skills\/([^/\s]+)\/SKILL\.md$/);
389
+ if (customRootMatch && this.isValidCodexSkillName(customRootMatch[1])) matches.set(normalizedPath, {
390
+ name: customRootMatch[1],
391
+ path: normalizedPath
392
+ });
393
+ }
394
+ return Array.from(matches.values());
395
+ }
396
+ extractSkillCallsFromItems(items, skillRootPrefixes = [], options = {}) {
397
+ const skillCalls = /* @__PURE__ */ new Map();
398
+ for (const item of items) {
399
+ if (item?.type !== "command_execution") continue;
400
+ if (options.requireSuccessfulCommand && !this.isSuccessfulCommandExecution(item)) continue;
401
+ const command = typeof item.command === "string" && item.command.trim() ? item.command : void 0;
402
+ if (!command) continue;
403
+ for (const skillPath of this.extractSkillPathCandidates(command, skillRootPrefixes)) {
404
+ const existing = skillCalls.get(skillPath.path) ?? {
405
+ name: skillPath.name,
406
+ path: skillPath.path
407
+ };
408
+ skillCalls.set(skillPath.path, existing);
409
+ }
410
+ }
411
+ return Array.from(skillCalls.values()).map((skillCall) => ({
412
+ name: skillCall.name,
413
+ path: skillCall.path,
414
+ source: "heuristic"
415
+ }));
416
+ }
417
+ buildSkillMetadata(items, skillRootPrefixes = []) {
418
+ if (!Array.isArray(items) || items.length === 0) return;
419
+ const attemptedSkillCalls = this.extractSkillCallsFromItems(items, skillRootPrefixes);
420
+ const skillCalls = this.extractSkillCallsFromItems(items, skillRootPrefixes, { requireSuccessfulCommand: true });
421
+ if (skillCalls.length === 0 && attemptedSkillCalls.length <= skillCalls.length) return;
422
+ return {
423
+ attemptedSkillCalls,
424
+ skillCalls
425
+ };
426
+ }
427
+ isSuccessfulCommandExecution(item) {
428
+ if (item?.type !== "command_execution") return false;
429
+ if (typeof item.status === "string" && item.status !== "completed") return false;
430
+ if (typeof item.exit_code === "number" && item.exit_code !== 0) return false;
431
+ return true;
432
+ }
433
+ validateWorkingDirectory(workingDir, skipGitCheck = false) {
434
+ let stats;
435
+ try {
436
+ stats = fs.default.statSync(workingDir);
437
+ } catch (err) {
438
+ throw new Error(`Working directory ${workingDir} does not exist or isn't accessible: ${err.message}`);
439
+ }
440
+ if (!stats.isDirectory()) throw new Error(`Working directory ${workingDir} is not a directory`);
441
+ if (!skipGitCheck && !this.isInsideGitRepository(workingDir)) throw new Error(dedent.default`Working directory ${workingDir} is not inside a Git repository.
442
+
443
+ Codex requires a Git repository by default to prevent unrecoverable errors.
444
+
445
+ To bypass this check, set skip_git_repo_check: true in your provider config.`);
446
+ }
447
+ isInsideGitRepository(workingDir) {
448
+ return this.findGitRepositoryRoot(workingDir) !== void 0;
449
+ }
450
+ findGitRepositoryRoot(workingDir) {
451
+ let currentDir = path.default.resolve(workingDir);
452
+ while (true) {
453
+ if (fs.default.existsSync(path.default.join(currentDir, ".git"))) return currentDir;
454
+ const parentDir = path.default.dirname(currentDir);
455
+ if (parentDir === currentDir) return;
456
+ currentDir = parentDir;
457
+ }
458
+ }
459
+ /**
460
+ * Build Codex constructor options from provider config.
461
+ * Used when creating both local (deep-tracing) and cached instances.
462
+ */
463
+ buildCodexOptions(env, config, apiKey = this.getApiKey(config)) {
464
+ const cliConfig = this.getResolvedCliConfig(config);
465
+ return {
466
+ env,
467
+ ...apiKey ? { apiKey } : {},
468
+ ...config.codex_path_override ? { codexPathOverride: config.codex_path_override } : {},
469
+ ...config.base_url ? { baseUrl: config.base_url } : {},
470
+ ...cliConfig ? { config: cliConfig } : {}
471
+ };
472
+ }
473
+ buildThreadOptions(config) {
474
+ return {
475
+ workingDirectory: config.working_dir,
476
+ skipGitRepoCheck: config.skip_git_repo_check ?? false,
477
+ ...config.model ? { model: config.model } : {},
478
+ ...config.additional_directories?.length ? { additionalDirectories: config.additional_directories } : {},
479
+ ...config.sandbox_mode ? { sandboxMode: config.sandbox_mode } : {},
480
+ ...config.model_reasoning_effort ? { modelReasoningEffort: config.model_reasoning_effort } : {},
481
+ ...config.network_access_enabled === void 0 ? {} : { networkAccessEnabled: config.network_access_enabled },
482
+ ...config.web_search_mode ? { webSearchMode: config.web_search_mode } : {},
483
+ ...config.web_search_enabled !== void 0 && !config.web_search_mode ? { webSearchEnabled: config.web_search_enabled } : {},
484
+ ...config.approval_policy ? { approvalPolicy: config.approval_policy } : {}
485
+ };
486
+ }
487
+ async getOrCreateThread(config, cacheKey, instanceKey, instance) {
488
+ const threadOptions = this.buildThreadOptions(config);
489
+ if (config.deep_tracing) return instance.startThread(threadOptions);
490
+ if (config.thread_id) {
491
+ const threadIdCacheKey = `${instanceKey}:${config.thread_id}`;
492
+ const cached = this.threads.get(threadIdCacheKey);
493
+ if (cached) return cached;
494
+ const thread = instance.resumeThread(config.thread_id, threadOptions);
495
+ if (config.persist_threads) this.threads.set(threadIdCacheKey, thread);
496
+ return thread;
497
+ }
498
+ if (config.persist_threads && cacheKey) {
499
+ const cached = this.threads.get(cacheKey);
500
+ if (cached) return cached;
501
+ const poolSize = config.thread_pool_size ?? 1;
502
+ if (this.threads.size >= poolSize) {
503
+ const oldestKey = this.threads.keys().next().value;
504
+ if (oldestKey) this.threads.delete(oldestKey);
505
+ }
506
+ }
507
+ const thread = instance.startThread(threadOptions);
508
+ if (config.persist_threads && cacheKey) this.threads.set(cacheKey, thread);
509
+ return thread;
510
+ }
511
+ async runStreaming(thread, prompt, runOptions, callOptions, skillRootPrefixes = []) {
512
+ const { events } = await thread.runStreamed(prompt, runOptions);
513
+ const items = [];
514
+ let usage = void 0;
515
+ const tracer = _opentelemetry_api.trace.getTracer("promptfoo.codex-sdk");
516
+ const activeSpans = /* @__PURE__ */ new Map();
517
+ const itemStartTimes = /* @__PURE__ */ new Map();
518
+ let lastEventTime = Date.now();
519
+ const reasoningTexts = [];
520
+ const conversationMessages = [];
521
+ conversationMessages.push({
522
+ role: "user",
523
+ content: prompt
524
+ });
525
+ try {
526
+ for await (const event of events) {
527
+ const eventTime = Date.now();
528
+ if (callOptions?.abortSignal?.aborted) {
529
+ const abortError = /* @__PURE__ */ new Error("AbortError");
530
+ abortError.name = "AbortError";
531
+ throw abortError;
532
+ }
533
+ switch (event.type) {
534
+ case "item.started": {
535
+ const item = event.item;
536
+ if (!item) {
537
+ require_logger.logger.warn("Codex item.started event missing item", { event });
538
+ break;
539
+ }
540
+ if (!item.id) {
541
+ require_logger.logger.debug("Codex item.started without id, will create span at completion", { type: item.type });
542
+ break;
543
+ }
544
+ const itemId = String(item.id);
545
+ const spanName = this.getSpanNameForItem(item);
546
+ const span = tracer.startSpan(spanName, {
547
+ kind: _opentelemetry_api.SpanKind.INTERNAL,
548
+ attributes: {
549
+ "codex.item.id": itemId,
550
+ "codex.item.type": item.type,
551
+ ...this.getAttributesForItem(item)
552
+ }
553
+ });
554
+ activeSpans.set(itemId, span);
555
+ itemStartTimes.set(itemId, eventTime);
556
+ require_logger.logger.debug("Codex item started", {
557
+ itemId,
558
+ type: item.type
559
+ });
560
+ break;
561
+ }
562
+ case "item.completed": {
563
+ const item = event.item;
564
+ if (!item) {
565
+ require_logger.logger.warn("Codex item.completed event missing item", { event });
566
+ break;
567
+ }
568
+ const itemId = item.id ? String(item.id) : crypto.default.randomUUID();
569
+ items.push(item);
570
+ if (item.type === "reasoning" && typeof item.text === "string") {
571
+ const sanitizedReasoning = this.sanitizeTraceText(item.text, "Codex reasoning trace event");
572
+ if (sanitizedReasoning) reasoningTexts.push(sanitizedReasoning);
573
+ }
574
+ if (item.type === "agent_message" && typeof item.text === "string") conversationMessages.push({
575
+ role: "assistant",
576
+ content: this.sanitizeTraceText(item.text, "Codex agent message trace event") ?? ""
577
+ });
578
+ let span = activeSpans.get(itemId);
579
+ const hadStartEvent = span !== void 0;
580
+ if (!span) {
581
+ const spanName = this.getSpanNameForItem(item);
582
+ span = tracer.startSpan(spanName, {
583
+ kind: _opentelemetry_api.SpanKind.INTERNAL,
584
+ startTime: lastEventTime,
585
+ attributes: {
586
+ "codex.item.id": itemId,
587
+ "codex.item.type": item.type,
588
+ "codex.timing.estimated": true,
589
+ ...this.getAttributesForItem(item)
590
+ }
591
+ });
592
+ }
593
+ const completionAttrs = this.getCompletionAttributesForItem(item, skillRootPrefixes);
594
+ for (const [key, value] of Object.entries(completionAttrs)) span.setAttribute(key, value);
595
+ const durationMs = eventTime - (itemStartTimes.get(itemId) || lastEventTime);
596
+ span.setAttribute("codex.duration_ms", durationMs);
597
+ span.setAttribute("codex.had_start_event", hadStartEvent);
598
+ if (item.type === "reasoning" && typeof item.text === "string") {
599
+ const reasoningText = this.sanitizeTraceText(item.text, "Codex reasoning span event");
600
+ span.addEvent("reasoning", { "codex.reasoning.text": reasoningText ?? "" });
601
+ }
602
+ if (item.type === "agent_message" && typeof item.text === "string") {
603
+ const messageText = this.sanitizeTraceText(item.text, "Codex agent message span event");
604
+ span.addEvent("message", { "codex.message.text": messageText ?? "" });
605
+ }
606
+ if (item.type === "command_execution" && typeof item.aggregated_output === "string") {
607
+ const commandOutput = this.sanitizeTraceText(item.aggregated_output, "Codex command output span event");
608
+ span.addEvent("output", { "codex.command.output": commandOutput ?? "" });
609
+ }
610
+ 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({
611
+ code: _opentelemetry_api.SpanStatusCode.ERROR,
612
+ 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"
613
+ });
614
+ else span.setStatus({ code: _opentelemetry_api.SpanStatusCode.OK });
615
+ span.end();
616
+ activeSpans.delete(itemId);
617
+ itemStartTimes.delete(itemId);
618
+ require_logger.logger.debug("Codex item completed", {
619
+ itemId,
620
+ type: item.type,
621
+ durationMs
622
+ });
623
+ break;
624
+ }
625
+ case "item.updated": {
626
+ const item = event.item;
627
+ if (item?.id) {
628
+ const itemId = String(item.id);
629
+ const span = activeSpans.get(itemId);
630
+ if (span) {
631
+ const updatedAttrs = this.getCompletionAttributesForItem(item, skillRootPrefixes);
632
+ for (const [key, value] of Object.entries(updatedAttrs)) span.setAttribute(key, value);
633
+ }
634
+ }
635
+ require_logger.logger.debug("Codex item updated", {
636
+ itemId: item?.id,
637
+ type: item?.type
638
+ });
639
+ break;
640
+ }
641
+ case "turn.completed":
642
+ usage = event.usage;
643
+ require_logger.logger.debug("Codex turn completed", { usage });
644
+ break;
645
+ case "turn.failed": {
646
+ const errorMsg = event.error?.message || "Turn failed";
647
+ require_logger.logger.error("Codex turn failed", { error: errorMsg });
648
+ throw new Error(`Codex turn failed: ${errorMsg}`);
649
+ }
650
+ default: require_logger.logger.debug("Codex unknown event type", { type: event.type });
651
+ }
652
+ lastEventTime = eventTime;
653
+ }
654
+ } finally {
655
+ for (const [itemId, span] of activeSpans) {
656
+ require_logger.logger.warn("Codex item span not properly closed", { itemId });
657
+ span.setStatus({
658
+ code: _opentelemetry_api.SpanStatusCode.ERROR,
659
+ message: "Span not properly closed"
660
+ });
661
+ span.end();
662
+ }
663
+ activeSpans.clear();
664
+ itemStartTimes.clear();
665
+ }
666
+ const agentMessages = items.filter((i) => i.type === "agent_message");
667
+ return {
668
+ finalResponse: agentMessages.length > 0 ? agentMessages.map((i) => i.text).join("\n") : "",
669
+ items,
670
+ usage,
671
+ reasoningTexts,
672
+ conversationMessages
673
+ };
674
+ }
675
+ /**
676
+ * Get a descriptive span name for a Codex item
677
+ */
678
+ getSpanNameForItem(item) {
679
+ switch (item.type) {
680
+ case "command_execution": return `exec ${typeof item.command === "string" ? item.command.split(" ")[0] || "command" : "command"}`;
681
+ case "file_change": return `file ${item.changes?.[0]?.kind || "change"}`;
682
+ case "mcp_tool_call": return `mcp ${typeof item.server === "string" ? item.server : "unknown"}/${typeof item.tool === "string" ? item.tool : "unknown"}`;
683
+ case "agent_message": return "agent response";
684
+ case "reasoning": return "reasoning";
685
+ case "web_search": return `search "${typeof item.query === "string" ? (this.sanitizeTraceText(item.query, "Codex web search span name") ?? "").slice(0, 30) : ""}"`;
686
+ case "todo_list": return "todo update";
687
+ case "error": return "error";
688
+ case "collaboration_tool_call": return `collab ${typeof item.tool === "string" ? item.tool : "unknown"}`;
689
+ case "spawn_agent": return `spawn ${typeof item.role === "string" ? item.role : "agent"}`;
690
+ case "send_input": return "send input";
691
+ case "agent_wait": return "wait";
692
+ default: return `codex.${item.type || "unknown"}`;
693
+ }
694
+ }
695
+ /**
696
+ * Get attributes for a Codex item at start
697
+ */
698
+ getSkillTraceAttributes(item, skillRootPrefixes = [], options = {}) {
699
+ if (item?.type !== "command_execution") return {};
700
+ if (options.requireSuccessfulCommand && !this.isSuccessfulCommandExecution(item)) return {};
701
+ const command = typeof item.command === "string" && item.command.trim() ? item.command : void 0;
702
+ const skillCandidates = /* @__PURE__ */ new Map();
703
+ if (command) for (const skill of this.extractSkillPathCandidates(command, skillRootPrefixes)) skillCandidates.set(skill.path, skill);
704
+ if (skillCandidates.size === 0) return {};
705
+ const skills = Array.from(skillCandidates.values());
706
+ const attrs = {
707
+ "promptfoo.skill.count": skills.length,
708
+ "promptfoo.skill.names": skills.map((skill) => skill.name).join(","),
709
+ "promptfoo.skill.paths": skills.map((skill) => skill.path).join(",")
710
+ };
711
+ if (skills.length === 1) {
712
+ attrs["promptfoo.skill.name"] = skills[0].name;
713
+ attrs["promptfoo.skill.path"] = skills[0].path;
714
+ }
715
+ return attrs;
716
+ }
717
+ getAttributesForItem(item) {
718
+ const attrs = {};
719
+ switch (item.type) {
720
+ case "command_execution":
721
+ if (typeof item.command === "string") attrs["codex.command"] = this.sanitizeTraceText(item.command, "Codex command trace attribute") ?? "";
722
+ break;
723
+ case "mcp_tool_call":
724
+ if (typeof item.server === "string") attrs["codex.mcp.server"] = item.server;
725
+ if (typeof item.tool === "string") attrs["codex.mcp.tool"] = item.tool;
726
+ {
727
+ const serializedArgs = this.serializeItemValue(item.arguments ?? item.args ?? item.input);
728
+ if (serializedArgs) attrs["codex.mcp.input"] = serializedArgs;
729
+ }
730
+ break;
731
+ case "web_search":
732
+ if (typeof item.query === "string") attrs["codex.search.query"] = this.sanitizeTraceText(item.query, "Codex web search query trace attribute") ?? "";
733
+ break;
734
+ case "collaboration_tool_call":
735
+ if (typeof item.tool === "string") attrs["codex.collab.tool"] = item.tool;
736
+ if (typeof item.target_thread_id === "string") attrs["codex.collab.target_thread"] = item.target_thread_id;
737
+ break;
738
+ case "spawn_agent":
739
+ if (typeof item.role === "string") attrs["codex.collab.role"] = item.role;
740
+ if (typeof item.thread_id === "string") attrs["codex.collab.spawned_thread"] = item.thread_id;
741
+ break;
742
+ case "send_input":
743
+ if (typeof item.target_thread_id === "string") attrs["codex.collab.target_thread"] = item.target_thread_id;
744
+ break;
745
+ }
746
+ return attrs;
747
+ }
748
+ serializeItemValue(value) {
749
+ if (typeof value === "string") {
750
+ const trimmed = value.trim();
751
+ if (!trimmed) return;
752
+ try {
753
+ return JSON.stringify(this.redactTracePii(require_logger.sanitizeObject(JSON.parse(trimmed))));
754
+ } catch {
755
+ return this.redactTracePii(require_logger.sanitizeObject(trimmed, { context: "Codex MCP trace input" }));
756
+ }
757
+ }
758
+ if (value === void 0 || value === null) return;
759
+ try {
760
+ return JSON.stringify(this.redactTracePii(require_logger.sanitizeObject(value, { context: "Codex MCP trace input" })));
761
+ } catch {
762
+ return;
763
+ }
764
+ }
765
+ sanitizeTraceText(value, context) {
766
+ const sanitized = this.redactTracePii(require_logger.sanitizeObject(value, { context }));
767
+ if (typeof sanitized === "string") return sanitized;
768
+ if (sanitized === void 0 || sanitized === null) return;
769
+ try {
770
+ return JSON.stringify(sanitized);
771
+ } catch {
772
+ return;
773
+ }
774
+ }
775
+ redactTracePii(value) {
776
+ if (typeof value === "string") return value.replace(/\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi, require_logger.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, require_logger.REDACTED).replace(/\b(api[_-]?key|token|password|secret|authorization|auth)\s*([=:])(\s*)(["']?)[^\s"'`]+(\4)/gi, (_match, key, separator, spacing, quote) => `${key}${separator}${spacing}${quote}${require_logger.REDACTED}${quote}`);
777
+ if (Array.isArray(value)) return value.map((item) => this.redactTracePii(item));
778
+ if (value && typeof value === "object") return Object.fromEntries(Object.entries(value).map(([key, entryValue]) => [key, require_logger.normalizeFieldName(key).includes("email") ? require_logger.REDACTED : this.redactTracePii(entryValue)]));
779
+ return value;
780
+ }
781
+ /**
782
+ * Get attributes for a Codex item at completion
783
+ */
784
+ getCompletionAttributesForItem(item, skillRootPrefixes = []) {
785
+ const attrs = {};
786
+ switch (item.type) {
787
+ case "command_execution":
788
+ if (typeof item.exit_code === "number") attrs["codex.exit_code"] = item.exit_code;
789
+ if (typeof item.status === "string") attrs["codex.status"] = item.status;
790
+ if (typeof item.aggregated_output === "string") attrs["codex.output"] = this.sanitizeTraceText(item.aggregated_output, "Codex command output trace attribute") ?? "";
791
+ Object.assign(attrs, this.getSkillTraceAttributes(item, skillRootPrefixes, { requireSuccessfulCommand: true }));
792
+ break;
793
+ case "file_change":
794
+ if (typeof item.status === "string") attrs["codex.status"] = item.status;
795
+ if (Array.isArray(item.changes) && item.changes.length) {
796
+ attrs["codex.files_changed"] = item.changes.length;
797
+ attrs["codex.files"] = item.changes.map((c) => typeof c?.path === "string" ? c.path : "").filter(Boolean).join(", ");
798
+ }
799
+ break;
800
+ case "mcp_tool_call":
801
+ if (typeof item.status === "string") attrs["codex.status"] = item.status;
802
+ if (typeof item.error?.message === "string") attrs["codex.error"] = this.sanitizeTraceText(item.error.message, "Codex MCP error trace attribute") ?? "";
803
+ {
804
+ const serializedArgs = this.serializeItemValue(item.arguments ?? item.args ?? item.input);
805
+ if (serializedArgs) attrs["codex.mcp.input"] = serializedArgs;
806
+ }
807
+ break;
808
+ case "agent_message":
809
+ if (typeof item.text === "string") attrs["codex.message"] = this.sanitizeTraceText(item.text, "Codex agent message trace attribute") ?? "";
810
+ break;
811
+ case "reasoning":
812
+ if (typeof item.text === "string") attrs["codex.reasoning"] = this.sanitizeTraceText(item.text, "Codex reasoning trace attribute") ?? "";
813
+ break;
814
+ case "error":
815
+ if (typeof item.message === "string") attrs["codex.error"] = this.sanitizeTraceText(item.message, "Codex error trace attribute") ?? "";
816
+ break;
817
+ }
818
+ return attrs;
819
+ }
820
+ generateInstanceKey(env, config) {
821
+ const keyData = {
822
+ env,
823
+ base_url: config.base_url,
824
+ cli_config: this.getResolvedCliConfig(config),
825
+ codex_path_override: config.codex_path_override
826
+ };
827
+ return `openai:codex-sdk:instance:${crypto.default.createHash("sha256").update(JSON.stringify(keyData)).digest("hex")}`;
828
+ }
829
+ generateCacheKey(config, prompt, instanceKey) {
830
+ const keyData = {
831
+ instanceKey,
832
+ working_dir: config.working_dir,
833
+ additional_directories: config.additional_directories,
834
+ model: config.model,
835
+ output_schema: config.output_schema,
836
+ sandbox_mode: config.sandbox_mode,
837
+ model_reasoning_effort: config.model_reasoning_effort,
838
+ network_access_enabled: config.network_access_enabled,
839
+ web_search_enabled: config.web_search_enabled,
840
+ web_search_mode: config.web_search_mode,
841
+ approval_policy: config.approval_policy,
842
+ prompt
843
+ };
844
+ return `openai:codex-sdk:${crypto.default.createHash("sha256").update(JSON.stringify(keyData)).digest("hex")}`;
845
+ }
846
+ getThreadRunQueueKey(config, cacheKey, instanceKey) {
847
+ if (config.deep_tracing) return;
848
+ if (config.thread_id) return `${instanceKey}:${config.thread_id}`;
849
+ if (config.persist_threads && cacheKey) return cacheKey;
850
+ }
851
+ async runSerializedThreadTurn(queueKey, abortSignal, executeTurn) {
852
+ if (!queueKey) return executeTurn();
853
+ const previousRun = this.threadRunQueues.get(queueKey) ?? Promise.resolve();
854
+ let releaseCurrentRun = () => {};
855
+ const currentRun = new Promise((resolve) => {
856
+ releaseCurrentRun = resolve;
857
+ });
858
+ const queuedRun = previousRun.catch(() => void 0).then(() => currentRun);
859
+ this.threadRunQueues.set(queueKey, queuedRun);
860
+ queuedRun.finally(() => {
861
+ if (this.threadRunQueues.get(queueKey) === queuedRun) this.threadRunQueues.delete(queueKey);
862
+ });
863
+ try {
864
+ await this.waitForPreviousThreadRun(previousRun, abortSignal);
865
+ return await executeTurn();
866
+ } finally {
867
+ releaseCurrentRun();
868
+ }
869
+ }
870
+ async waitForPreviousThreadRun(previousRun, abortSignal) {
871
+ const previousRunDone = previousRun.catch(() => void 0);
872
+ if (!abortSignal) {
873
+ await previousRunDone;
874
+ return;
875
+ }
876
+ if (abortSignal.aborted) throw this.createAbortError("Codex thread turn wait aborted");
877
+ let onAbort;
878
+ const abortPromise = new Promise((_, reject) => {
879
+ onAbort = () => reject(this.createAbortError("Codex thread turn wait aborted"));
880
+ abortSignal.addEventListener("abort", onAbort, { once: true });
881
+ });
882
+ try {
883
+ await Promise.race([previousRunDone, abortPromise]);
884
+ } finally {
885
+ if (onAbort) abortSignal.removeEventListener("abort", onAbort);
886
+ }
887
+ }
888
+ createAbortError(message) {
889
+ const error = new Error(message);
890
+ error.name = "AbortError";
891
+ return error;
892
+ }
893
+ async callApi(prompt, context, callOptions) {
894
+ const config = {
895
+ ...this.config,
896
+ ...context?.prompt?.config
897
+ };
898
+ const modelName = typeof config.model === "string" && config.model ? config.model : "codex";
899
+ const spanContext = {
900
+ system: "openai",
901
+ operationName: "chat",
902
+ model: modelName,
903
+ providerId: this.id(),
904
+ evalId: context?.evaluationId || context?.test?.metadata?.evaluationId,
905
+ testIndex: typeof context?.test?.vars?.__testIdx === "number" ? context.test.vars.__testIdx : void 0,
906
+ promptLabel: context?.prompt?.label,
907
+ traceparent: context?.traceparent,
908
+ requestBody: prompt
909
+ };
910
+ const resultExtractor = (response) => {
911
+ const result = {};
912
+ if (response.tokenUsage) result.tokenUsage = response.tokenUsage;
913
+ if (response.sessionId) result.responseId = response.sessionId;
914
+ if (response.cached !== void 0) result.cacheHit = response.cached;
915
+ result.responseModel = modelName;
916
+ if (response.output !== void 0) try {
917
+ result.responseBody = typeof response.output === "string" ? response.output : JSON.stringify(response.output);
918
+ } catch {
919
+ result.responseBody = "[unable to serialize output]";
920
+ }
921
+ if (response.raw) try {
922
+ const rawData = typeof response.raw === "string" ? JSON.parse(response.raw) : response.raw;
923
+ if (rawData.reasoningTexts?.length > 0) result.additionalAttributes = {
924
+ "codex.reasoning.count": rawData.reasoningTexts.length,
925
+ "codex.reasoning.summary": rawData.reasoningTexts.join("\n---\n").slice(0, 2e3)
926
+ };
927
+ if (rawData.conversationMessages?.length > 0) result.additionalAttributes = {
928
+ ...result.additionalAttributes,
929
+ "codex.conversation.message_count": rawData.conversationMessages.length
930
+ };
931
+ if (rawData.items?.length > 0) {
932
+ const itemCounts = {};
933
+ for (const item of rawData.items) itemCounts[item.type] = (itemCounts[item.type] || 0) + 1;
934
+ result.additionalAttributes = {
935
+ ...result.additionalAttributes,
936
+ "codex.items.total": rawData.items.length,
937
+ "codex.items.breakdown": JSON.stringify(itemCounts)
938
+ };
939
+ }
940
+ } catch {}
941
+ return result;
942
+ };
943
+ return require_genaiTracer.withGenAISpan(spanContext, () => this.callApiInternal(prompt, context, callOptions, config), resultExtractor);
944
+ }
945
+ /**
946
+ * Internal implementation of callApi without tracing wrapper.
947
+ * Context is available for future use (e.g., _context?.vars for template rendering,
948
+ * _context?.bustCache for cache control, _context?.debug for debug mode).
949
+ */
950
+ async callApiInternal(prompt, context, callOptions, rawConfig) {
951
+ let config;
952
+ try {
953
+ config = parseCodexConfig(rawConfig, { stripUnknownKeys: true });
954
+ } catch (error) {
955
+ const errorMessage = error instanceof Error ? error.message : String(error);
956
+ require_logger.logger.error("Error calling OpenAI Codex SDK", { error: errorMessage });
957
+ return { error: `Error calling OpenAI Codex SDK: ${errorMessage}` };
958
+ }
959
+ const currentTraceparent = require_genaiTracer.getTraceparent();
960
+ const apiKey = this.getApiKey(config);
961
+ const env = this.prepareEnvironment(config, currentTraceparent, apiKey);
962
+ const skillRootPrefixes = this.getSkillRootPrefixes(env, config.working_dir);
963
+ if (apiKey) require_logger.logger.debug("[CodexSDK] Using explicit API credentials from promptfoo config/environment");
964
+ else require_logger.logger.debug("[CodexSDK] No explicit API credentials configured; deferring auth resolution to Codex SDK login state");
965
+ if (callOptions?.abortSignal?.aborted) return { error: "OpenAI Codex SDK call aborted before it started" };
966
+ let localInstance = void 0;
967
+ const useLocalInstance = config.deep_tracing;
968
+ let activeInstance = void 0;
969
+ let cacheKey = void 0;
970
+ const runOptions = {};
971
+ if (config.output_schema) runOptions.outputSchema = config.output_schema;
972
+ if (callOptions?.abortSignal) runOptions.signal = callOptions.abortSignal;
973
+ try {
974
+ if (config.working_dir) this.validateWorkingDirectory(config.working_dir, config.skip_git_repo_check);
975
+ if (!this.codexModule) this.codexModule = await loadCodexSDK();
976
+ const stableEnv = { ...env };
977
+ delete stableEnv.TRACEPARENT;
978
+ const instanceKey = this.generateInstanceKey(stableEnv, config);
979
+ if (useLocalInstance) {
980
+ if ((config.persist_threads || config.thread_id || (config.thread_pool_size ?? 0) > 1) && !this.deepTracingWarningShown) {
981
+ require_logger.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.");
982
+ this.deepTracingWarningShown = true;
983
+ }
984
+ localInstance = new this.codexModule.Codex(this.buildCodexOptions(env, config, apiKey));
985
+ activeInstance = localInstance;
986
+ } else {
987
+ activeInstance = this.codexInstances.get(instanceKey);
988
+ if (!activeInstance) {
989
+ activeInstance = new this.codexModule.Codex(this.buildCodexOptions(env, config, apiKey));
990
+ this.codexInstances.set(instanceKey, activeInstance);
991
+ }
992
+ }
993
+ const promptCacheBasis = context?.prompt?.raw ?? prompt;
994
+ cacheKey = this.generateCacheKey(config, promptCacheBasis, instanceKey);
995
+ const queueKey = this.getThreadRunQueueKey(config, cacheKey, instanceKey);
996
+ const { turn, sessionId } = await this.runSerializedThreadTurn(queueKey, callOptions?.abortSignal, async () => {
997
+ const thread = await this.getOrCreateThread(config, cacheKey, instanceKey, activeInstance);
998
+ return {
999
+ turn: config.enable_streaming ? await this.runStreaming(thread, prompt, runOptions, callOptions, skillRootPrefixes) : await thread.run(prompt, runOptions),
1000
+ sessionId: thread.id || "unknown"
1001
+ };
1002
+ });
1003
+ const output = turn.finalResponse || "";
1004
+ const raw = JSON.stringify(turn);
1005
+ const skillMetadata = this.buildSkillMetadata(turn.items, skillRootPrefixes);
1006
+ const metadata = skillMetadata ? {
1007
+ ...skillMetadata.skillCalls.length > 0 ? { skillCalls: skillMetadata.skillCalls } : {},
1008
+ ...skillMetadata.attemptedSkillCalls.length > skillMetadata.skillCalls.length ? { attemptedSkillCalls: skillMetadata.attemptedSkillCalls } : {}
1009
+ } : void 0;
1010
+ const tokenUsage = turn.usage ? {
1011
+ prompt: turn.usage.input_tokens,
1012
+ completion: turn.usage.output_tokens,
1013
+ total: turn.usage.input_tokens + turn.usage.output_tokens,
1014
+ cached: turn.usage.cached_input_tokens || 0
1015
+ } : void 0;
1016
+ let cost = 0;
1017
+ if (tokenUsage && config.model) {
1018
+ const pricing = CODEX_MODEL_PRICING[config.model];
1019
+ if (pricing) {
1020
+ const cachedTokens = tokenUsage.cached || 0;
1021
+ const inputCost = ((tokenUsage.prompt || 0) - cachedTokens) * (pricing.input / 1e6);
1022
+ const cacheReadCost = cachedTokens * (pricing.cache_read / 1e6);
1023
+ const outputCost = (tokenUsage.completion || 0) * (pricing.output / 1e6);
1024
+ cost = inputCost + cacheReadCost + outputCost;
1025
+ }
1026
+ }
1027
+ require_logger.logger.debug("OpenAI Codex SDK response", {
1028
+ output,
1029
+ usage: turn.usage
1030
+ });
1031
+ return {
1032
+ output,
1033
+ tokenUsage,
1034
+ cost,
1035
+ metadata,
1036
+ raw,
1037
+ sessionId
1038
+ };
1039
+ } catch (error) {
1040
+ if (error instanceof Error && error.name === "AbortError" || callOptions?.abortSignal?.aborted) {
1041
+ require_logger.logger.warn("OpenAI Codex SDK call aborted");
1042
+ return { error: "OpenAI Codex SDK call aborted" };
1043
+ }
1044
+ const errorMessage = error instanceof Error ? error.message : String(error);
1045
+ require_logger.logger.error("Error calling OpenAI Codex SDK", { error: errorMessage });
1046
+ return { error: `Error calling OpenAI Codex SDK: ${errorMessage}` };
1047
+ } finally {
1048
+ if (!config.deep_tracing && !config.persist_threads && !config.thread_id && cacheKey) this.threads.delete(cacheKey);
1049
+ if (useLocalInstance && localInstance) try {
1050
+ await this.destroyInstance(localInstance);
1051
+ } catch (cleanupError) {
1052
+ require_logger.logger.debug("[CodexSDK] Error cleaning up local instance", { error: cleanupError });
1053
+ }
1054
+ }
1055
+ }
1056
+ };
1057
+ //#endregion
1058
+ exports.OpenAICodexSDKProvider = OpenAICodexSDKProvider;
1059
+
1060
+ //# sourceMappingURL=codex-sdk-Dl9D4k5B.cjs.map