@desplega.ai/agent-swarm 1.113.0 → 1.115.0

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 (237) hide show
  1. package/README.md +2 -2
  2. package/dist/{actions-q4n7cz6e.js → actions-17ya5wde.js} +8 -9
  3. package/dist/{app-es4nzc71.js → app-4f718yk0.js} +6 -6
  4. package/dist/{assistant-0x8ey0vs.js → assistant-ah7csnjz.js} +19 -18
  5. package/dist/{boot-reembed-69agkhv6.js → boot-reembed-4jpxhb25.js} +6 -6
  6. package/dist/{boot-reembed-p0ny05t8.js → boot-reembed-qata34xa.js} +5 -5
  7. package/dist/{boot-scrub-logs-yybrkmvr.js → boot-scrub-logs-tv48nn3z.js} +4 -4
  8. package/dist/{claude-adapter-mma9a1yr.js → claude-adapter-3csn5bks.js} +3 -3
  9. package/dist/{claude-managed-adapter-pv2nbqy4.js → claude-managed-adapter-kysmkz89.js} +2 -2
  10. package/dist/cli-0pygq9r1.js +429 -0
  11. package/dist/{cli-hsetkkd3.js → cli-12vb0xg6.js} +33 -9
  12. package/dist/{cli-ncfgsqbd.js → cli-15y7qhzp.js} +262 -33
  13. package/dist/{cli-7ga43spr.js → cli-1yjrbg1f.js} +1 -1
  14. package/dist/{cli-36ymmpmp.js → cli-3samkyap.js} +2 -2
  15. package/dist/cli-52fg7s7x.js +269 -0
  16. package/dist/{cli-hjhvekf4.js → cli-65ekje01.js} +11 -8
  17. package/dist/{cli-h3k622d0.js → cli-6aq8xhvm.js} +90 -3
  18. package/dist/{cli-f3qa069v.js → cli-7xje5amz.js} +1 -1
  19. package/dist/cli-86fdnerz.js +405 -0
  20. package/dist/{cli-9cy6nc5m.js → cli-8fjwym53.js} +1 -1
  21. package/dist/{cli-c3frqf65.js → cli-b23axgz4.js} +3 -3
  22. package/dist/{cli-a0t7vapf.js → cli-bc89my0z.js} +1 -1
  23. package/dist/{cli-bnzbn8j8.js → cli-beea4a3y.js} +73 -24
  24. package/dist/{cli-q21d49ac.js → cli-bw80ck94.js} +1 -1
  25. package/dist/{cli-pwc5e83c.js → cli-cchksf5q.js} +12 -7
  26. package/dist/{cli-30bbaveh.js → cli-cmw9rzy5.js} +2 -2
  27. package/dist/{cli-y6mrptcn.js → cli-cnwssmsr.js} +1 -1
  28. package/dist/{cli-r5g6ngtn.js → cli-dcca6j8m.js} +46 -5
  29. package/dist/{cli-tkmm5nf9.js → cli-e61fs2hp.js} +59 -2
  30. package/dist/{cli-6wscj9z0.js → cli-e7306swv.js} +14 -14
  31. package/dist/{cli-dcfyyh3t.js → cli-ftyf58xj.js} +2 -2
  32. package/dist/{cli-4xyj9jtq.js → cli-hrhye2rm.js} +1062 -478
  33. package/dist/cli-jkhwmmgg.js +136 -0
  34. package/dist/{cli-y4ycrnjc.js → cli-k3y4z3h5.js} +1 -1
  35. package/dist/{cli-6xd0bvf8.js → cli-ma04faw5.js} +1 -1
  36. package/dist/{cli-2fc97xm5.js → cli-mnbgv3w7.js} +4 -4
  37. package/dist/{cli-trmapn9k.js → cli-pet3aev6.js} +95 -10
  38. package/dist/{cli-z46pnksz.js → cli-q1rx7g0d.js} +175 -28
  39. package/dist/{cli-qwgtacd6.js → cli-r9f6ngrf.js} +1244 -336
  40. package/dist/{cli-gtkqc144.js → cli-shvaxw27.js} +13 -7
  41. package/dist/{cli-ywez6yps.js → cli-sq7f72d6.js} +1 -1
  42. package/dist/cli-t0vqevnm.js +393 -0
  43. package/dist/{cli-tbw13sx8.js → cli-tece6zgm.js} +1 -1
  44. package/dist/{cli-bgvskydy.js → cli-tffvrh6j.js} +1 -1
  45. package/dist/cli-wh91zby6.js +187222 -0
  46. package/dist/{cli-anrj584m.js → cli-xz9aq0rf.js} +1 -1
  47. package/dist/{cli-mq580e06.js → cli-zsrdty74.js} +18 -18
  48. package/dist/cli.js +32 -15
  49. package/dist/{codex-adapter-5cmy858w.js → codex-adapter-de3w0nch.js} +10 -10
  50. package/dist/{codex-session-runner-46q8mrba.js → codex-session-runner-bnw8cysk.js} +10 -10
  51. package/dist/{commands-z5dbwta3.js → commands-hch4e2k1.js} +4 -4
  52. package/dist/{db-bbgahh4z.js → db-p3h2j06x.js} +14 -4
  53. package/dist/{handlers-6b44317z.js → handlers-9er4zx9q.js} +17 -31
  54. package/dist/{hook-sbp5fmps.js → hook-4axs756z.js} +7 -7
  55. package/dist/{http-2xz43gz3.js → http-809fvtz2.js} +1934 -886
  56. package/dist/{index-wz1ea15p.js → index-2dgfj9av.js} +3 -3
  57. package/dist/{index-nhat35em.js → index-47em3d2n.js} +14 -11
  58. package/dist/{index-5ghxn8s6.js → index-9fqaktjr.js} +12 -9
  59. package/dist/{index-0gzmqj4k.js → index-9ht8gr56.js} +13 -10
  60. package/dist/{index-vfedgdw6.js → index-rm85cfdx.js} +368 -40
  61. package/dist/{index-ykvs43z4.js → index-t2snazez.js} +4 -4
  62. package/dist/{keepalive-62gfj91d.js → keepalive-3cps6e3e.js} +7 -6
  63. package/dist/{lead-ag0akn0v.js → lead-fkk95wgq.js} +36 -32
  64. package/dist/{maintenance-xxrrzk5x.js → maintenance-fweqh0yj.js} +6 -6
  65. package/dist/{mistral-conversations-26yrnbf1.js → mistral-conversations-7gqnz2wt.js} +7 -7
  66. package/dist/oauth-refresh-sweep-sycqtbph.js +114 -0
  67. package/dist/{onboard-64zyfcbs.js → onboard-cy0rkgvx.js} +2 -2
  68. package/dist/{opencode-adapter-mqsxr170.js → opencode-adapter-p6bxnbm4.js} +2 -2
  69. package/dist/{otel-zjd9zaxs.js → otel-g299k41b.js} +2 -2
  70. package/dist/{otel-impl-b86qseaa.js → otel-impl-wbns8hf7.js} +1 -1
  71. package/dist/{pi-mono-adapter-kxjxpfgy.js → pi-mono-adapter-05sck1ds.js} +17 -109
  72. package/dist/{pricing-refresh-cgtkffpa.js → pricing-refresh-0hvazmkf.js} +6 -6
  73. package/dist/rbac-roles-kfesjy9y.js +28 -0
  74. package/dist/rbac-roles-mwcwhbxc.js +283 -0
  75. package/dist/{seed-pricing-1r1y1xkq.js → seed-pricing-h2kdq4cq.js} +5 -5
  76. package/dist/{setup-mkcgkjf7.js → setup-8506q97w.js} +2 -2
  77. package/dist/{worker-351sze63.js → worker-4r5c6zyb.js} +36 -32
  78. package/openapi.json +1327 -20
  79. package/package.json +3 -3
  80. package/src/agentmail/handlers.ts +16 -6
  81. package/src/agentmail/templates.ts +25 -5
  82. package/src/be/audit-user.ts +28 -4
  83. package/src/be/db-queries/oauth.ts +42 -0
  84. package/src/be/db.ts +314 -28
  85. package/src/be/identity.ts +79 -0
  86. package/src/be/mcp-proxy.ts +132 -0
  87. package/src/be/migrations/109_rbac_roles.sql +79 -0
  88. package/src/be/migrations/111_oauth_credential_bindings.sql +6 -0
  89. package/src/be/migrations/112_script_connections_graphql.sql +96 -0
  90. package/src/be/migrations/113_task_routing_affinity.sql +9 -0
  91. package/src/be/migrations/114_backfill_gpt_5_6_pricing.sql +15 -0
  92. package/src/be/modelsdev-cache.json +135 -0
  93. package/src/be/oauth-credential-bindings.ts +35 -0
  94. package/src/be/oauth-refresh-sweep.ts +159 -0
  95. package/src/be/rbac-audit.ts +52 -13
  96. package/src/be/rbac-roles.ts +424 -0
  97. package/src/be/script-connections.ts +459 -40
  98. package/src/be/script-credential-broker.ts +29 -3
  99. package/src/be/scripts/typecheck.ts +51 -7
  100. package/src/be/seed-scripts/catalog/catalog-report.inline.ts +325 -24
  101. package/src/be/seed-scripts/catalog/catalog-report.ts +325 -24
  102. package/src/be/users.ts +24 -0
  103. package/src/cli.tsx +17 -0
  104. package/src/github/handlers.ts +10 -9
  105. package/src/github/templates.ts +45 -9
  106. package/src/gitlab/handlers.ts +20 -8
  107. package/src/gitlab/templates.ts +18 -6
  108. package/src/heartbeat/heartbeat.ts +143 -21
  109. package/src/http/all-routes.ts +3 -0
  110. package/src/http/approval-requests.ts +12 -0
  111. package/src/http/config.ts +44 -10
  112. package/src/http/core.ts +30 -3
  113. package/src/http/favorites.ts +1 -5
  114. package/src/http/index.ts +25 -1
  115. package/src/http/mcp-oauth.ts +2 -0
  116. package/src/http/mcp-servers.ts +44 -3
  117. package/src/http/oauth-generic.ts +105 -0
  118. package/src/http/poll.ts +20 -3
  119. package/src/http/script-connection-proxy.ts +116 -0
  120. package/src/http/script-connections.ts +1657 -0
  121. package/src/http/scripts.ts +27 -7
  122. package/src/http/skills.ts +11 -0
  123. package/src/http/tasks.ts +27 -13
  124. package/src/http/x.ts +6 -2
  125. package/src/integrations/kapso/inbound.ts +23 -6
  126. package/src/jira/sync.ts +111 -17
  127. package/src/jira/templates.ts +10 -2
  128. package/src/mcp-client/http-client.ts +194 -0
  129. package/src/oauth/app-validation.ts +16 -0
  130. package/src/oauth/ensure-token.ts +69 -14
  131. package/src/oauth/mcp-wrapper.ts +15 -0
  132. package/src/oauth/wrapper.ts +48 -20
  133. package/src/prompts/session-templates.ts +3 -3
  134. package/src/providers/codex-models.ts +100 -30
  135. package/src/providers/modelsdev-reasoning.json +51 -0
  136. package/src/providers/pi-mono-mcp-client.ts +2 -133
  137. package/src/providers/reasoning-effort.ts +11 -5
  138. package/src/rbac/admission.ts +68 -0
  139. package/src/rbac/index.ts +2 -0
  140. package/src/rbac/legacy-policy.ts +12 -0
  141. package/src/rbac/permissions.ts +48 -0
  142. package/src/scheduler/scheduler.ts +9 -1
  143. package/src/scripts-runtime/api-client.ts +151 -19
  144. package/src/scripts-runtime/api-types.ts +38 -6
  145. package/src/scripts-runtime/credential-broker/default-bindings.ts +1 -0
  146. package/src/scripts-runtime/credential-broker/types.ts +8 -0
  147. package/src/scripts-runtime/ctx.ts +11 -1
  148. package/src/scripts-runtime/eval-harness.ts +5 -1
  149. package/src/scripts-runtime/executors/types.ts +2 -1
  150. package/src/scripts-runtime/loader.ts +3 -1
  151. package/src/scripts-runtime/mcp-client.ts +87 -0
  152. package/src/scripts-runtime/sdk-allowlist.ts +1 -0
  153. package/src/scripts-runtime/types/stdlib.d.ts +6 -0
  154. package/src/scripts-runtime/types/swarm-sdk.d.ts +19 -0
  155. package/src/server-user.ts +114 -59
  156. package/src/server.ts +12 -0
  157. package/src/slack/assistant.ts +14 -6
  158. package/src/slack/enrich.ts +17 -0
  159. package/src/slack/handlers.ts +8 -22
  160. package/src/slack/responses.ts +91 -1
  161. package/src/slack/thread-buffer.ts +6 -3
  162. package/src/slack/watcher.ts +9 -6
  163. package/src/tasks/worker-follow-up.ts +100 -3
  164. package/src/tests/agentmail-handlers.test.ts +32 -3
  165. package/src/tests/approval-requests.test.ts +61 -0
  166. package/src/tests/audit-user.test.ts +47 -1
  167. package/src/tests/codex-adapter.test.ts +46 -23
  168. package/src/tests/credential-broker.test.ts +5 -2
  169. package/src/tests/delete-page-tool.test.ts +197 -0
  170. package/src/tests/github-handlers.test.ts +28 -2
  171. package/src/tests/gitlab-handlers.test.ts +125 -0
  172. package/src/tests/heartbeat.test.ts +48 -3
  173. package/src/tests/identity.test.ts +117 -0
  174. package/src/tests/jira-sync.test.ts +99 -1
  175. package/src/tests/kapso-inbound.test.ts +7 -0
  176. package/src/tests/mcp-tools-user.test.ts +60 -6
  177. package/src/tests/model-control.test.ts +8 -2
  178. package/src/tests/oauth-credential-bindings.test.ts +490 -0
  179. package/src/tests/oauth-refresh-sweep.test.ts +160 -0
  180. package/src/tests/pool-affinity.test.ts +401 -0
  181. package/src/tests/prompt-template-remaining.test.ts +2 -2
  182. package/src/tests/rbac-admission-e2e.test.ts +386 -0
  183. package/src/tests/rbac-admission.test.ts +492 -0
  184. package/src/tests/rbac-e2e-helpers.ts +3 -0
  185. package/src/tests/rbac-engine.test.ts +50 -0
  186. package/src/tests/rbac-mcp-admission-e2e.test.ts +362 -0
  187. package/src/tests/rbac-roles.test.ts +328 -0
  188. package/src/tests/reasoning-effort.test.ts +15 -2
  189. package/src/tests/schedule-target-type.test.ts +50 -0
  190. package/src/tests/scheduled-tasks.test.ts +40 -0
  191. package/src/tests/script-apis-mcp.test.ts +37 -13
  192. package/src/tests/script-connections-http.test.ts +1158 -0
  193. package/src/tests/script-connections-mcp.test.ts +894 -0
  194. package/src/tests/script-connections.test.ts +754 -6
  195. package/src/tests/scripts-external-api.test.ts +49 -1
  196. package/src/tests/scripts-runtime-secret-egress.test.ts +1 -0
  197. package/src/tests/scripts-runtime.test.ts +291 -0
  198. package/src/tests/seed-scripts.test.ts +36 -0
  199. package/src/tests/slack-identity-resolution.test.ts +37 -3
  200. package/src/tests/slack-inline-output.test.ts +65 -0
  201. package/src/tests/slack-thread-buffer.test.ts +28 -0
  202. package/src/tests/tool-annotations.test.ts +1 -0
  203. package/src/tests/user-token-rest-auth.test.ts +140 -1
  204. package/src/tests/workflow-swarm-script.test.ts +44 -0
  205. package/src/tests/workflow-triggers-v2.test.ts +300 -1
  206. package/src/tools/credential-bindings/tool.ts +277 -18
  207. package/src/tools/delete-page.ts +145 -0
  208. package/src/tools/mcp-servers/mcp-server-delete.ts +7 -4
  209. package/src/tools/request-human-input.ts +2 -0
  210. package/src/tools/resolve-user.ts +77 -27
  211. package/src/tools/script-apis.ts +62 -3
  212. package/src/tools/script-connections/tool.ts +275 -31
  213. package/src/tools/send-task.ts +15 -1
  214. package/src/tools/task-action.ts +34 -2
  215. package/src/tools/templates.ts +53 -1
  216. package/src/tools/tool-config.ts +2 -1
  217. package/src/tools/utils.ts +2 -0
  218. package/src/tools/workflows/create-workflow.ts +8 -1
  219. package/src/tools/workflows/update-workflow.ts +11 -2
  220. package/src/types.ts +90 -11
  221. package/src/utils/scoped-resource.ts +25 -0
  222. package/src/workflows/executors/human-in-the-loop.ts +1 -0
  223. package/src/workflows/executors/swarm-script.ts +7 -1
  224. package/src/workflows/triggers.ts +146 -18
  225. package/templates/skills/swarm-scripts/content.md +33 -8
  226. package/dist/cli-bgef578c.js +0 -771
  227. package/dist/cli-c9mtm09b.js +0 -43
  228. package/dist/cli-ceewqajw.js +0 -1698
  229. package/dist/{anthropic-messages-8nc6r2dw.js → anthropic-messages-kq3pjhbp.js} +6 -6
  230. package/dist/{azure-openai-responses-6yfzw3qw.js → azure-openai-responses-gm1ejtmn.js} +7 -7
  231. package/dist/{cli-vbraamxr.js → cli-6b4wwmm8.js} +9 -9
  232. package/dist/{cli-2r7s8jhp.js → cli-sznberje.js} +3 -3
  233. package/dist/{google-generative-ai-vfq6ymz9.js → google-generative-ai-hyrra2jm.js} +3 -3
  234. package/dist/{google-vertex-mj5y5rfj.js → google-vertex-8bs2pzgn.js} +3 -3
  235. package/dist/{openai-codex-responses-8h6kvfv8.js → openai-codex-responses-3nkvje79.js} +4 -4
  236. package/dist/{openai-completions-x6kjnqjd.js → openai-completions-0asftkwg.js} +11 -11
  237. package/dist/{openai-responses-7jwyz3ds.js → openai-responses-pmatek45.js} +10 -10
@@ -1,1698 +0,0 @@
1
- import {
2
- summarizeSession
3
- } from "./cli-6wscj9z0.js";
4
- import {
5
- createSwarmEventHandler
6
- } from "./cli-07er7wrs.js";
7
- import {
8
- resolveCodexPrompt
9
- } from "./cli-r94z9p1z.js";
10
- import {
11
- readPkgVersion
12
- } from "./cli-d6n0thxw.js";
13
- import {
14
- buildRatingsFromLlm,
15
- fetchRetrievalsForTask,
16
- postRatings
17
- } from "./cli-dcfyyh3t.js";
18
- import {
19
- CodexOAuthRefreshError,
20
- credentialsToAuthJson,
21
- getValidCodexOAuth
22
- } from "./cli-tk9vyqzh.js";
23
- import {
24
- SessionErrorTracker,
25
- buildOtelTraceparentEnv
26
- } from "./cli-psrqtw2m.js";
27
- import {
28
- CTX_MODE_NUDGE_EVERY
29
- } from "./cli-zm7njwzd.js";
30
- import {
31
- CONTEXT_FORMULA,
32
- clampContextPercent,
33
- computeContextUsedUnified
34
- } from "./cli-tghre7g6.js";
35
- import {
36
- applyReasoningEffort
37
- } from "./cli-tkmm5nf9.js";
38
- import {
39
- getApiKey
40
- } from "./cli-f14fvzag.js";
41
- import {
42
- init_secret_scrubber,
43
- scrubSecrets
44
- } from "./cli-4j2mrzag.js";
45
- import {
46
- __require
47
- } from "./cli-p9swy5t3.js";
48
-
49
- // src/providers/codex-adapter.ts
50
- import { existsSync as nodeExistsSync } from "node:fs";
51
- import os2 from "node:os";
52
- import { join as join2 } from "node:path";
53
-
54
- // node_modules/@openai/codex-sdk/dist/index.js
55
- import { promises as fs } from "fs";
56
- import os from "os";
57
- import path from "path";
58
- import { spawn } from "child_process";
59
- import { statSync } from "fs";
60
- import path2 from "path";
61
- import readline from "readline";
62
- import { createRequire } from "module";
63
- async function createOutputSchemaFile(schema) {
64
- if (schema === undefined) {
65
- return { cleanup: async () => {} };
66
- }
67
- if (!isJsonObject(schema)) {
68
- throw new Error("outputSchema must be a plain JSON object");
69
- }
70
- const schemaDir = await fs.mkdtemp(path.join(os.tmpdir(), "codex-output-schema-"));
71
- const schemaPath = path.join(schemaDir, "schema.json");
72
- const cleanup = async () => {
73
- try {
74
- await fs.rm(schemaDir, { recursive: true, force: true });
75
- } catch {}
76
- };
77
- try {
78
- await fs.writeFile(schemaPath, JSON.stringify(schema), "utf8");
79
- return { schemaPath, cleanup };
80
- } catch (error) {
81
- await cleanup();
82
- throw error;
83
- }
84
- }
85
- function isJsonObject(value) {
86
- return typeof value === "object" && value !== null && !Array.isArray(value);
87
- }
88
- var Thread = class {
89
- _exec;
90
- _options;
91
- _id;
92
- _threadOptions;
93
- get id() {
94
- return this._id;
95
- }
96
- constructor(exec, options, threadOptions, id = null) {
97
- this._exec = exec;
98
- this._options = options;
99
- this._id = id;
100
- this._threadOptions = threadOptions;
101
- }
102
- async runStreamed(input, turnOptions = {}) {
103
- return { events: this.runStreamedInternal(input, turnOptions) };
104
- }
105
- async* runStreamedInternal(input, turnOptions = {}) {
106
- const { schemaPath, cleanup } = await createOutputSchemaFile(turnOptions.outputSchema);
107
- const options = this._threadOptions;
108
- const { prompt, images } = normalizeInput(input);
109
- const generator = this._exec.run({
110
- input: prompt,
111
- baseUrl: this._options.baseUrl,
112
- apiKey: this._options.apiKey,
113
- threadId: this._id,
114
- images,
115
- model: options?.model,
116
- sandboxMode: options?.sandboxMode,
117
- workingDirectory: options?.workingDirectory,
118
- skipGitRepoCheck: options?.skipGitRepoCheck,
119
- outputSchemaFile: schemaPath,
120
- modelReasoningEffort: options?.modelReasoningEffort,
121
- signal: turnOptions.signal,
122
- networkAccessEnabled: options?.networkAccessEnabled,
123
- webSearchMode: options?.webSearchMode,
124
- webSearchEnabled: options?.webSearchEnabled,
125
- approvalPolicy: options?.approvalPolicy,
126
- additionalDirectories: options?.additionalDirectories
127
- });
128
- try {
129
- for await (const item of generator) {
130
- let parsed;
131
- try {
132
- parsed = JSON.parse(item);
133
- } catch (error) {
134
- throw new Error(`Failed to parse item: ${item}`, { cause: error });
135
- }
136
- if (parsed.type === "thread.started") {
137
- this._id = parsed.thread_id;
138
- }
139
- yield parsed;
140
- }
141
- } finally {
142
- await cleanup();
143
- }
144
- }
145
- async run(input, turnOptions = {}) {
146
- const generator = this.runStreamedInternal(input, turnOptions);
147
- const items = [];
148
- let finalResponse = "";
149
- let usage = null;
150
- let turnFailure = null;
151
- for await (const event of generator) {
152
- if (event.type === "item.completed") {
153
- if (event.item.type === "agent_message") {
154
- finalResponse = event.item.text;
155
- }
156
- items.push(event.item);
157
- } else if (event.type === "turn.completed") {
158
- usage = event.usage;
159
- } else if (event.type === "turn.failed") {
160
- turnFailure = event.error;
161
- break;
162
- }
163
- }
164
- if (turnFailure) {
165
- throw new Error(turnFailure.message);
166
- }
167
- return { items, finalResponse, usage };
168
- }
169
- };
170
- function normalizeInput(input) {
171
- if (typeof input === "string") {
172
- return { prompt: input, images: [] };
173
- }
174
- const promptParts = [];
175
- const images = [];
176
- for (const item of input) {
177
- if (item.type === "text") {
178
- promptParts.push(item.text);
179
- } else if (item.type === "local_image") {
180
- images.push(item.path);
181
- }
182
- }
183
- return { prompt: promptParts.join(`
184
-
185
- `), images };
186
- }
187
- var INTERNAL_ORIGINATOR_ENV = "CODEX_INTERNAL_ORIGINATOR_OVERRIDE";
188
- var TYPESCRIPT_SDK_ORIGINATOR = "codex_sdk_ts";
189
- var CODEX_NPM_NAME = "@openai/codex";
190
- var PLATFORM_PACKAGE_BY_TARGET = {
191
- "x86_64-unknown-linux-musl": "@openai/codex-linux-x64",
192
- "aarch64-unknown-linux-musl": "@openai/codex-linux-arm64",
193
- "x86_64-apple-darwin": "@openai/codex-darwin-x64",
194
- "aarch64-apple-darwin": "@openai/codex-darwin-arm64",
195
- "x86_64-pc-windows-msvc": "@openai/codex-win32-x64",
196
- "aarch64-pc-windows-msvc": "@openai/codex-win32-arm64"
197
- };
198
- var moduleRequire = createRequire(import.meta.url);
199
- var CodexExec = class {
200
- executablePath;
201
- pathDirs;
202
- envOverride;
203
- configOverrides;
204
- constructor(executablePath = null, env, configOverrides) {
205
- if (executablePath) {
206
- this.executablePath = executablePath;
207
- this.pathDirs = [];
208
- } else {
209
- const resolved = findCodexPath();
210
- this.executablePath = resolved.executablePath;
211
- this.pathDirs = resolved.pathDirs;
212
- }
213
- this.envOverride = env;
214
- this.configOverrides = configOverrides;
215
- }
216
- async* run(args) {
217
- const commandArgs = ["exec", "--experimental-json"];
218
- if (this.configOverrides) {
219
- for (const override of serializeConfigOverrides(this.configOverrides)) {
220
- commandArgs.push("--config", override);
221
- }
222
- }
223
- if (args.baseUrl) {
224
- commandArgs.push("--config", `openai_base_url=${toTomlValue(args.baseUrl, "openai_base_url")}`);
225
- }
226
- if (args.model) {
227
- commandArgs.push("--model", args.model);
228
- }
229
- if (args.sandboxMode) {
230
- commandArgs.push("--sandbox", args.sandboxMode);
231
- }
232
- if (args.workingDirectory) {
233
- commandArgs.push("--cd", args.workingDirectory);
234
- }
235
- if (args.additionalDirectories?.length) {
236
- for (const dir of args.additionalDirectories) {
237
- commandArgs.push("--add-dir", dir);
238
- }
239
- }
240
- if (args.skipGitRepoCheck) {
241
- commandArgs.push("--skip-git-repo-check");
242
- }
243
- if (args.outputSchemaFile) {
244
- commandArgs.push("--output-schema", args.outputSchemaFile);
245
- }
246
- if (args.modelReasoningEffort) {
247
- commandArgs.push("--config", `model_reasoning_effort="${args.modelReasoningEffort}"`);
248
- }
249
- if (args.networkAccessEnabled !== undefined) {
250
- commandArgs.push("--config", `sandbox_workspace_write.network_access=${args.networkAccessEnabled}`);
251
- }
252
- if (args.webSearchMode) {
253
- commandArgs.push("--config", `web_search="${args.webSearchMode}"`);
254
- } else if (args.webSearchEnabled === true) {
255
- commandArgs.push("--config", `web_search="live"`);
256
- } else if (args.webSearchEnabled === false) {
257
- commandArgs.push("--config", `web_search="disabled"`);
258
- }
259
- if (args.approvalPolicy) {
260
- commandArgs.push("--config", `approval_policy="${args.approvalPolicy}"`);
261
- }
262
- if (args.threadId) {
263
- commandArgs.push("resume", args.threadId);
264
- }
265
- if (args.images?.length) {
266
- for (const image of args.images) {
267
- commandArgs.push("--image", image);
268
- }
269
- }
270
- const env = {};
271
- if (this.envOverride) {
272
- Object.assign(env, this.envOverride);
273
- } else {
274
- for (const [key, value] of Object.entries(process.env)) {
275
- if (value !== undefined) {
276
- env[key] = value;
277
- }
278
- }
279
- }
280
- if (!env[INTERNAL_ORIGINATOR_ENV]) {
281
- env[INTERNAL_ORIGINATOR_ENV] = TYPESCRIPT_SDK_ORIGINATOR;
282
- }
283
- if (args.apiKey) {
284
- env.CODEX_API_KEY = args.apiKey;
285
- }
286
- if (this.pathDirs.length > 0) {
287
- prependPathDirs(env, this.pathDirs);
288
- }
289
- const child = spawn(this.executablePath, commandArgs, {
290
- env,
291
- signal: args.signal
292
- });
293
- let spawnError = null;
294
- child.once("error", (err) => spawnError = err);
295
- if (!child.stdin) {
296
- child.kill();
297
- throw new Error("Child process has no stdin");
298
- }
299
- child.stdin.write(args.input);
300
- child.stdin.end();
301
- if (!child.stdout) {
302
- child.kill();
303
- throw new Error("Child process has no stdout");
304
- }
305
- const stderrChunks = [];
306
- if (child.stderr) {
307
- child.stderr.on("data", (data) => {
308
- stderrChunks.push(data);
309
- });
310
- }
311
- const exitPromise = new Promise((resolve) => {
312
- child.once("exit", (code, signal) => {
313
- resolve({ code, signal });
314
- });
315
- });
316
- const rl = readline.createInterface({
317
- input: child.stdout,
318
- crlfDelay: Infinity
319
- });
320
- try {
321
- for await (const line of rl) {
322
- yield line;
323
- }
324
- if (spawnError)
325
- throw spawnError;
326
- const { code, signal } = await exitPromise;
327
- if (code !== 0 || signal) {
328
- const stderrBuffer = Buffer.concat(stderrChunks);
329
- const detail = signal ? `signal ${signal}` : `code ${code ?? 1}`;
330
- throw new Error(`Codex Exec exited with ${detail}: ${stderrBuffer.toString("utf8")}`);
331
- }
332
- } finally {
333
- rl.close();
334
- child.removeAllListeners();
335
- try {
336
- if (!child.killed)
337
- child.kill();
338
- } catch {}
339
- }
340
- }
341
- };
342
- function serializeConfigOverrides(configOverrides) {
343
- const overrides = [];
344
- flattenConfigOverrides(configOverrides, "", overrides);
345
- return overrides;
346
- }
347
- function flattenConfigOverrides(value, prefix, overrides) {
348
- if (!isPlainObject(value)) {
349
- if (prefix) {
350
- overrides.push(`${prefix}=${toTomlValue(value, prefix)}`);
351
- return;
352
- } else {
353
- throw new Error("Codex config overrides must be a plain object");
354
- }
355
- }
356
- const entries = Object.entries(value);
357
- if (!prefix && entries.length === 0) {
358
- return;
359
- }
360
- if (prefix && entries.length === 0) {
361
- overrides.push(`${prefix}={}`);
362
- return;
363
- }
364
- for (const [key, child] of entries) {
365
- if (!key) {
366
- throw new Error("Codex config override keys must be non-empty strings");
367
- }
368
- if (child === undefined) {
369
- continue;
370
- }
371
- const path3 = prefix ? `${prefix}.${key}` : key;
372
- if (isPlainObject(child)) {
373
- flattenConfigOverrides(child, path3, overrides);
374
- } else {
375
- overrides.push(`${path3}=${toTomlValue(child, path3)}`);
376
- }
377
- }
378
- }
379
- function toTomlValue(value, path3) {
380
- if (typeof value === "string") {
381
- return JSON.stringify(value);
382
- } else if (typeof value === "number") {
383
- if (!Number.isFinite(value)) {
384
- throw new Error(`Codex config override at ${path3} must be a finite number`);
385
- }
386
- return `${value}`;
387
- } else if (typeof value === "boolean") {
388
- return value ? "true" : "false";
389
- } else if (Array.isArray(value)) {
390
- const rendered = value.map((item, index) => toTomlValue(item, `${path3}[${index}]`));
391
- return `[${rendered.join(", ")}]`;
392
- } else if (isPlainObject(value)) {
393
- const parts = [];
394
- for (const [key, child] of Object.entries(value)) {
395
- if (!key) {
396
- throw new Error("Codex config override keys must be non-empty strings");
397
- }
398
- if (child === undefined) {
399
- continue;
400
- }
401
- parts.push(`${formatTomlKey(key)} = ${toTomlValue(child, `${path3}.${key}`)}`);
402
- }
403
- return `{${parts.join(", ")}}`;
404
- } else if (value === null) {
405
- throw new Error(`Codex config override at ${path3} cannot be null`);
406
- } else {
407
- const typeName = typeof value;
408
- throw new Error(`Unsupported Codex config override value at ${path3}: ${typeName}`);
409
- }
410
- }
411
- var TOML_BARE_KEY = /^[A-Za-z0-9_-]+$/;
412
- function formatTomlKey(key) {
413
- return TOML_BARE_KEY.test(key) ? key : JSON.stringify(key);
414
- }
415
- function isPlainObject(value) {
416
- return typeof value === "object" && value !== null && !Array.isArray(value);
417
- }
418
- function findCodexPath() {
419
- const { platform, arch } = process;
420
- let targetTriple = null;
421
- switch (platform) {
422
- case "linux":
423
- case "android":
424
- switch (arch) {
425
- case "x64":
426
- targetTriple = "x86_64-unknown-linux-musl";
427
- break;
428
- case "arm64":
429
- targetTriple = "aarch64-unknown-linux-musl";
430
- break;
431
- default:
432
- break;
433
- }
434
- break;
435
- case "darwin":
436
- switch (arch) {
437
- case "x64":
438
- targetTriple = "x86_64-apple-darwin";
439
- break;
440
- case "arm64":
441
- targetTriple = "aarch64-apple-darwin";
442
- break;
443
- default:
444
- break;
445
- }
446
- break;
447
- case "win32":
448
- switch (arch) {
449
- case "x64":
450
- targetTriple = "x86_64-pc-windows-msvc";
451
- break;
452
- case "arm64":
453
- targetTriple = "aarch64-pc-windows-msvc";
454
- break;
455
- default:
456
- break;
457
- }
458
- break;
459
- default:
460
- break;
461
- }
462
- if (!targetTriple) {
463
- throw new Error(`Unsupported platform: ${platform} (${arch})`);
464
- }
465
- const platformPackage = PLATFORM_PACKAGE_BY_TARGET[targetTriple];
466
- if (!platformPackage) {
467
- throw new Error(`Unsupported target triple: ${targetTriple}`);
468
- }
469
- let vendorRoot;
470
- try {
471
- const codexPackageJsonPath = moduleRequire.resolve(`${CODEX_NPM_NAME}/package.json`);
472
- const codexRequire = createRequire(codexPackageJsonPath);
473
- const platformPackageJsonPath = codexRequire.resolve(`${platformPackage}/package.json`);
474
- vendorRoot = path2.join(path2.dirname(platformPackageJsonPath), "vendor");
475
- } catch {
476
- throw new Error(`Unable to locate Codex CLI binaries. Ensure ${CODEX_NPM_NAME} is installed with optional dependencies.`);
477
- }
478
- const codexBinaryName = process.platform === "win32" ? "codex.exe" : "codex";
479
- const nativePackage = resolveNativePackage(vendorRoot, targetTriple, codexBinaryName);
480
- if (!nativePackage) {
481
- throw new Error(`Unable to locate Codex CLI binaries for ${targetTriple}. Ensure ${CODEX_NPM_NAME} is installed with optional dependencies.`);
482
- }
483
- return nativePackage;
484
- }
485
- function resolveNativePackage(vendorRoot, targetTriple, codexBinaryName) {
486
- const packageRoot = path2.join(vendorRoot, targetTriple);
487
- const packageBinaryPath = path2.join(packageRoot, "bin", codexBinaryName);
488
- if (isFile(packageBinaryPath) && isFile(path2.join(packageRoot, "codex-package.json"))) {
489
- return {
490
- executablePath: packageBinaryPath,
491
- pathDirs: existingDirs(path2.join(packageRoot, "codex-path"))
492
- };
493
- }
494
- const legacyBinaryPath = path2.join(packageRoot, "codex", codexBinaryName);
495
- if (isFile(legacyBinaryPath)) {
496
- return {
497
- executablePath: legacyBinaryPath,
498
- pathDirs: existingDirs(path2.join(packageRoot, "path"))
499
- };
500
- }
501
- return null;
502
- }
503
- function existingDirs(...dirs) {
504
- return dirs.filter(isDirectory);
505
- }
506
- function prependPathDirs(env, pathDirs, platform = process.platform) {
507
- const pathKey = pathEnvKey(env, platform);
508
- if (platform === "win32") {
509
- for (const key of Object.keys(env)) {
510
- if (key.toLowerCase() === "path" && key !== pathKey) {
511
- delete env[key];
512
- }
513
- }
514
- }
515
- const existingEntries = (env[pathKey] ?? "").split(path2.delimiter).filter((entry) => entry.length > 0 && !pathDirs.includes(entry));
516
- env[pathKey] = [...pathDirs, ...existingEntries].join(path2.delimiter);
517
- }
518
- function pathEnvKey(env, platform) {
519
- if (platform !== "win32") {
520
- return "PATH";
521
- }
522
- const matchingKeys = Object.keys(env).filter((key) => key.toLowerCase() === "path");
523
- return matchingKeys.includes("Path") ? "Path" : matchingKeys.at(-1) ?? "PATH";
524
- }
525
- function isFile(filePath) {
526
- try {
527
- return statSync(filePath).isFile();
528
- } catch {
529
- return false;
530
- }
531
- }
532
- function isDirectory(filePath) {
533
- try {
534
- return statSync(filePath).isDirectory();
535
- } catch {
536
- return false;
537
- }
538
- }
539
- var Codex = class {
540
- exec;
541
- options;
542
- constructor(options = {}) {
543
- const { codexPathOverride, env, config } = options;
544
- this.exec = new CodexExec(codexPathOverride, env, config);
545
- this.options = options;
546
- }
547
- startThread(options = {}) {
548
- return new Thread(this.exec, this.options, options);
549
- }
550
- resumeThread(id, options = {}) {
551
- return new Thread(this.exec, this.options, options, id);
552
- }
553
- };
554
-
555
- // src/providers/codex-adapter.ts
556
- init_secret_scrubber();
557
-
558
- // src/providers/codex-agents-md.ts
559
- import { join } from "node:path";
560
- var BLOCK_OPEN = "<swarm_system_prompt>";
561
- var BLOCK_CLOSE = "</swarm_system_prompt>";
562
- var BLOCK_REGEX = /<swarm_system_prompt>[\s\S]*?<\/swarm_system_prompt>\n?/;
563
- var NOOP_HANDLE = {
564
- cleanup: async () => {}
565
- };
566
- async function writeCodexAgentsMd(cwd, systemPrompt) {
567
- if (!cwd || !systemPrompt) {
568
- return NOOP_HANDLE;
569
- }
570
- const agentsMdPath = join(cwd, "AGENTS.md");
571
- const claudeMdPath = join(cwd, "CLAUDE.md");
572
- const block = `${BLOCK_OPEN}
573
- ${systemPrompt}
574
- ${BLOCK_CLOSE}`;
575
- const agentsMdFile = Bun.file(agentsMdPath);
576
- const existingAgentsMdExists = await agentsMdFile.exists();
577
- let createdFresh = false;
578
- let newContent;
579
- if (!existingAgentsMdExists) {
580
- const claudeMdFile = Bun.file(claudeMdPath);
581
- const claudeMdExists = await claudeMdFile.exists();
582
- if (claudeMdExists) {
583
- const claudeContent = await claudeMdFile.text();
584
- newContent = `${block}
585
-
586
- ${claudeContent}`;
587
- } else {
588
- newContent = `${block}
589
- `;
590
- }
591
- createdFresh = true;
592
- } else {
593
- const existingContent = await agentsMdFile.text();
594
- if (BLOCK_REGEX.test(existingContent)) {
595
- newContent = existingContent.replace(BLOCK_REGEX, `${block}
596
- `);
597
- } else {
598
- newContent = `${block}
599
-
600
- ${existingContent}`;
601
- }
602
- }
603
- await Bun.write(agentsMdPath, newContent);
604
- return {
605
- async cleanup() {
606
- try {
607
- if (createdFresh) {
608
- await Bun.$`rm -f ${agentsMdPath}`.quiet().nothrow();
609
- return;
610
- }
611
- const currentFile = Bun.file(agentsMdPath);
612
- if (!await currentFile.exists()) {
613
- return;
614
- }
615
- const currentContent = await currentFile.text();
616
- const stripped = currentContent.replace(BLOCK_REGEX, "");
617
- await Bun.write(agentsMdPath, stripped);
618
- } catch {}
619
- }
620
- };
621
- }
622
-
623
- // src/providers/codex-models.ts
624
- var CODEX_DEFAULT_MODEL = "gpt-5.4";
625
- var CLAUDE_SHORTNAMES = {
626
- fable: "gpt-5.5",
627
- opus: "gpt-5.4",
628
- sonnet: "gpt-5.4",
629
- haiku: "gpt-5.4-mini"
630
- };
631
- function resolveCodexModel(modelStr) {
632
- if (!modelStr)
633
- return CODEX_DEFAULT_MODEL;
634
- const normalized = modelStr.toLowerCase();
635
- return CLAUDE_SHORTNAMES[normalized] ?? normalized;
636
- }
637
- var CODEX_MODEL_CONTEXT_WINDOWS = {
638
- "gpt-5.5": 1050000,
639
- "gpt-5.4": 200000,
640
- "gpt-5.4-mini": 200000,
641
- "gpt-5.3-codex": 1e6,
642
- "gpt-5.2-codex": 200000
643
- };
644
- function getCodexContextWindow(model) {
645
- return CODEX_MODEL_CONTEXT_WINDOWS[model] ?? 200000;
646
- }
647
- var CODEX_MODEL_PRICING = {
648
- "gpt-5.5": {
649
- inputPerMillion: 5,
650
- cachedInputPerMillion: 0.5,
651
- outputPerMillion: 30
652
- },
653
- "gpt-5.4": {
654
- inputPerMillion: 2.5,
655
- cachedInputPerMillion: 0.25,
656
- outputPerMillion: 15
657
- },
658
- "gpt-5.4-mini": {
659
- inputPerMillion: 0.75,
660
- cachedInputPerMillion: 0.075,
661
- outputPerMillion: 4.5
662
- },
663
- "gpt-5.3-codex": {
664
- inputPerMillion: 1.75,
665
- cachedInputPerMillion: 0.175,
666
- outputPerMillion: 14
667
- },
668
- "gpt-5.2-codex": {
669
- inputPerMillion: 1.75,
670
- cachedInputPerMillion: 0.175,
671
- outputPerMillion: 14
672
- }
673
- };
674
- var _warnedUnknownCodexModels = new Set;
675
- function computeCodexCostUsd(model, inputTokens, cachedInputTokens, outputTokens) {
676
- const pricing = CODEX_MODEL_PRICING[model];
677
- if (!pricing) {
678
- if (!_warnedUnknownCodexModels.has(model)) {
679
- _warnedUnknownCodexModels.add(model);
680
- console.warn(`[codex] unpriced model ${JSON.stringify(model)} — adapter cost will report $0; ` + "server-side recompute will tag costSource='unpriced' if the pricing table has no rows.");
681
- }
682
- return 0;
683
- }
684
- const uncachedInput = Math.max(0, inputTokens - cachedInputTokens);
685
- const inputCost = uncachedInput / 1e6 * pricing.inputPerMillion;
686
- const cachedCost = cachedInputTokens / 1e6 * pricing.cachedInputPerMillion;
687
- const outputCost = outputTokens / 1e6 * pricing.outputPerMillion;
688
- return inputCost + cachedCost + outputCost;
689
- }
690
-
691
- // src/providers/codex-swarm-events.ts
692
- function createCodexSwarmEventHandler(opts) {
693
- return createSwarmEventHandler({
694
- ...opts,
695
- sessionIdFallbackPrefix: "codex"
696
- });
697
- }
698
-
699
- // src/providers/codex-adapter.ts
700
- function checkCodexCredentials(env, opts = {}) {
701
- const homeDir = opts.homeDir ?? env.HOME ?? "/root";
702
- const existsSync = opts.fs?.existsSync ?? nodeExistsSync;
703
- const authFile = `${homeDir}/.codex/auth.json`;
704
- if (existsSync(authFile)) {
705
- return { ready: true, missing: [], satisfiedBy: "file" };
706
- }
707
- if (env.OPENAI_API_KEY || env.CODEX_OAUTH) {
708
- return {
709
- ready: true,
710
- missing: [],
711
- satisfiedBy: "side-effect-pending",
712
- hint: "Credential present in env; entrypoint will materialise ~/.codex/auth.json on next boot."
713
- };
714
- }
715
- if (Object.keys(env).some((k) => /^codex_oauth_\d+$/.test(k))) {
716
- return {
717
- ready: true,
718
- missing: [],
719
- satisfiedBy: "side-effect-pending",
720
- hint: "Codex OAuth credential pool configured; runner will materialise auth.json per-task."
721
- };
722
- }
723
- return {
724
- ready: false,
725
- missing: ["OPENAI_API_KEY", "CODEX_OAUTH", authFile],
726
- hint: "Set OPENAI_API_KEY (entrypoint runs `codex login --with-api-key`), or store CODEX_OAUTH in swarm_config, or place a pre-authenticated `~/.codex/auth.json` in the worker home."
727
- };
728
- }
729
- async function resolveCodexAuthMode(config, emit, deps = {}) {
730
- const fsModule = await import("node:fs/promises");
731
- const homedir = deps.homedir ?? os2.homedir.bind(os2);
732
- const fs2 = deps.fs ?? {
733
- readFile: (path3, encoding) => fsModule.readFile(path3, encoding),
734
- mkdir: (path3, opts) => fsModule.mkdir(path3, opts),
735
- writeFile: (path3, data, opts) => fsModule.writeFile(path3, data, opts)
736
- };
737
- const authJsonPath = join2(homedir(), ".codex", "auth.json");
738
- const readAuthMode = async () => {
739
- try {
740
- const raw = await fs2.readFile(authJsonPath, "utf-8");
741
- const parsed = JSON.parse(raw);
742
- return typeof parsed.auth_mode === "string" ? parsed.auth_mode : null;
743
- } catch {
744
- return null;
745
- }
746
- };
747
- let currentMode = await readAuthMode();
748
- const slot = config.codexSlot ?? 0;
749
- const isPoolSlot = config.codexSlot !== undefined;
750
- if (config.apiUrl && config.apiKey && (currentMode !== "chatgpt" || isPoolSlot)) {
751
- let oauthCreds = null;
752
- try {
753
- oauthCreds = await getValidCodexOAuth(config.apiUrl, config.apiKey, slot);
754
- } catch (err) {
755
- if (isPoolSlot) {
756
- throw new Error(buildPoolRevalidationFailureReason(err, slot));
757
- }
758
- const message = err instanceof Error ? err.message : String(err);
759
- emit({
760
- type: "raw_stderr",
761
- content: `[codex] OAuth revalidation failed (non-fatal, non-pool auth): ${message}
762
- `
763
- });
764
- }
765
- if (!oauthCreds && isPoolSlot) {
766
- throw new Error(`[auth-error] Codex pool slot ${slot} revalidation failed: no credentials found in config store — the slot may have just been quarantined; re-run codex-login for this slot if this persists.`);
767
- }
768
- if (oauthCreds) {
769
- try {
770
- const authJson = credentialsToAuthJson(oauthCreds, {
771
- includeRefreshToken: !isPoolSlot
772
- });
773
- await fs2.mkdir(join2(homedir(), ".codex"), { recursive: true, mode: 448 });
774
- await fs2.writeFile(authJsonPath, JSON.stringify(authJson, null, 2), { mode: 384 });
775
- const verb = currentMode === null ? "Restored" : currentMode === "chatgpt" ? "Revalidated" : "Upgraded api-key auth.json to";
776
- emit({
777
- type: "raw_stderr",
778
- content: `[codex] ${verb} OAuth credentials from config store
779
- `
780
- });
781
- currentMode = "chatgpt";
782
- } catch (err) {
783
- const message = err instanceof Error ? err.message : String(err);
784
- emit({
785
- type: "raw_stderr",
786
- content: `[codex] Failed to write auth.json: ${message}
787
- `
788
- });
789
- }
790
- }
791
- }
792
- return currentMode;
793
- }
794
- function buildPoolRevalidationFailureReason(err, slot) {
795
- if (err instanceof CodexOAuthRefreshError) {
796
- if (err.reason === "lock_timeout") {
797
- return `[auth-error] Codex pool slot ${slot} [...${err.keySuffix}] revalidation failed: timed out waiting for the refresh lock — transient, will retry on next task.`;
798
- }
799
- return `[auth-error] Codex pool slot ${slot} [...${err.keySuffix}] revalidation failed: refresh rejected (${err.status ?? "unknown status"} ${err.body ?? ""}) — credential likely revoked; re-run codex-login for this slot.`;
800
- }
801
- const message = err instanceof Error ? err.message : String(err);
802
- return `[auth-error] Codex pool slot ${slot} revalidation failed: ${message}`;
803
- }
804
- async function buildCodexConfig(config, model, emit) {
805
- const mcpServers = {};
806
- mcpServers["agent-swarm"] = {
807
- url: `${config.apiUrl}/mcp`,
808
- http_headers: {
809
- Authorization: `Bearer ${config.apiKey}`,
810
- "X-Agent-ID": config.agentId,
811
- "X-Source-Task-Id": config.taskId ?? ""
812
- },
813
- enabled: true,
814
- startup_timeout_sec: 30,
815
- tool_timeout_sec: 120
816
- };
817
- if (config.apiUrl && config.apiKey && config.agentId) {
818
- try {
819
- const res = await fetch(`${config.apiUrl}/api/agents/${config.agentId}/mcp-servers?resolveSecrets=true`, {
820
- headers: {
821
- Authorization: `Bearer ${config.apiKey}`,
822
- "X-Agent-ID": config.agentId
823
- }
824
- });
825
- if (res.ok) {
826
- const data = await res.json();
827
- for (const srv of data.servers ?? []) {
828
- if (!srv.isActive || !srv.isEnabled)
829
- continue;
830
- if (srv.transport === "stdio") {
831
- if (!srv.command)
832
- continue;
833
- let parsedArgs = [];
834
- try {
835
- parsedArgs = srv.args ? JSON.parse(srv.args) : [];
836
- } catch {}
837
- mcpServers[srv.name] = {
838
- command: srv.command,
839
- args: parsedArgs,
840
- env: srv.resolvedEnv ?? {},
841
- enabled: true,
842
- startup_timeout_sec: 30,
843
- tool_timeout_sec: 120
844
- };
845
- continue;
846
- }
847
- if (srv.transport === "http") {
848
- if (!srv.url)
849
- continue;
850
- let parsedHeaders = {};
851
- try {
852
- parsedHeaders = srv.headers ? JSON.parse(srv.headers) : {};
853
- } catch {}
854
- mcpServers[srv.name] = {
855
- url: srv.url,
856
- http_headers: { ...parsedHeaders, ...srv.resolvedHeaders ?? {} },
857
- enabled: true,
858
- startup_timeout_sec: 30,
859
- tool_timeout_sec: 120
860
- };
861
- continue;
862
- }
863
- if (srv.transport === "sse") {
864
- emit({
865
- type: "raw_stderr",
866
- content: `[codex] Skipping MCP server "${srv.name}": SSE transport is not yet supported by Codex (tracked in openai/codex#2129).
867
- `
868
- });
869
- }
870
- }
871
- } else {
872
- emit({
873
- type: "raw_stderr",
874
- content: `[codex] Failed to fetch installed MCP servers: HTTP ${res.status}. Continuing with only the swarm MCP server.
875
- `
876
- });
877
- }
878
- } catch (err) {
879
- const message = err instanceof Error ? err.message : String(err);
880
- emit({
881
- type: "raw_stderr",
882
- content: `[codex] Failed to fetch installed MCP servers: ${message}. Continuing with only the swarm MCP server.
883
- `
884
- });
885
- }
886
- }
887
- if (process.env.CONTEXT_MODE_DISABLED !== "true") {
888
- mcpServers["context-mode"] = {
889
- command: "context-mode",
890
- enabled: true,
891
- startup_timeout_sec: 30,
892
- tool_timeout_sec: 120
893
- };
894
- }
895
- const reasoningApplication = applyReasoningEffort("codex", model, config.reasoningEffort);
896
- const reasoningConfig = reasoningApplication.kind === "codex-config" ? reasoningApplication.config : {};
897
- return {
898
- model,
899
- approval_policy: "never",
900
- sandbox_mode: "danger-full-access",
901
- skip_git_repo_check: true,
902
- show_raw_agent_reasoning: false,
903
- features: { hooks: true, plugin_hooks: true },
904
- mcp_servers: mcpServers,
905
- ...reasoningConfig
906
- };
907
- }
908
-
909
- class CodexSession {
910
- thread;
911
- config;
912
- agentsMdHandle;
913
- resolvedModel;
914
- contextWindow;
915
- skillsDir;
916
- summarizeDeps;
917
- listeners = [];
918
- eventQueue = [];
919
- logFileHandle;
920
- startedAt = Date.now();
921
- completionPromise;
922
- resolveCompletion;
923
- abortController = null;
924
- transcript = [];
925
- abortRef = { current: null };
926
- _sessionId;
927
- numTurns = 0;
928
- lastUsage = null;
929
- aborted = false;
930
- settled = false;
931
- errorTracker = new SessionErrorTracker;
932
- appliedReasoningEffort;
933
- pendingResult = null;
934
- constructor(thread, config, agentsMdHandle, resolvedModel, initialEvents = [], skillsDir, summarizeDeps = {}) {
935
- this.thread = thread;
936
- this.config = config;
937
- this.agentsMdHandle = agentsMdHandle;
938
- this.resolvedModel = resolvedModel;
939
- this.contextWindow = getCodexContextWindow(resolvedModel);
940
- this.skillsDir = skillsDir ?? process.env.CODEX_SKILLS_DIR ?? join2(os2.homedir(), ".codex", "skills");
941
- this.summarizeDeps = summarizeDeps;
942
- this.logFileHandle = Bun.file(config.logFile).writer();
943
- const reasoningApplication = applyReasoningEffort("codex", resolvedModel, config.reasoningEffort);
944
- this.appliedReasoningEffort = reasoningApplication.kind === "codex-config" ? config.reasoningEffort ?? null : null;
945
- this.completionPromise = new Promise((resolve) => {
946
- this.resolveCompletion = resolve;
947
- });
948
- if (config.taskId && config.apiUrl && config.apiKey) {
949
- this.listeners.push(createCodexSwarmEventHandler({
950
- apiUrl: config.apiUrl,
951
- apiKey: config.apiKey,
952
- agentId: config.agentId,
953
- taskId: config.taskId,
954
- abortRef: this.abortRef
955
- }));
956
- }
957
- for (const event of initialEvents) {
958
- this.emit(event);
959
- }
960
- this.runSession();
961
- }
962
- get sessionId() {
963
- return this._sessionId ?? this.thread.id ?? undefined;
964
- }
965
- onEvent(listener) {
966
- this.listeners.push(listener);
967
- for (const event of this.eventQueue) {
968
- listener(event);
969
- }
970
- this.eventQueue.length = 0;
971
- }
972
- async waitForCompletion() {
973
- return this.completionPromise;
974
- }
975
- async abort(reason) {
976
- this.aborted = true;
977
- this.abortController?.abort(reason ?? "cancelled");
978
- }
979
- emit(event) {
980
- const scrubbed = event.type === "raw_log" || event.type === "raw_stderr" ? { ...event, content: scrubSecrets(event.content) } : event;
981
- try {
982
- this.logFileHandle.write(`${JSON.stringify({ ...scrubbed, timestamp: new Date().toISOString() })}
983
- `);
984
- } catch {}
985
- if (this.listeners.length > 0) {
986
- for (const listener of this.listeners) {
987
- try {
988
- listener(scrubbed);
989
- } catch {}
990
- }
991
- } else {
992
- this.eventQueue.push(scrubbed);
993
- }
994
- }
995
- settle(result) {
996
- if (this.settled)
997
- return;
998
- this.settled = true;
999
- this.pendingResult = { ...result, appliedReasoningEffort: this.appliedReasoningEffort };
1000
- }
1001
- buildCostData(usage, isError) {
1002
- const inputTokens = usage?.input_tokens ?? 0;
1003
- const cachedInputTokens = usage?.cached_input_tokens ?? 0;
1004
- const outputTokens = usage?.output_tokens ?? 0;
1005
- const reasoningOutputTokens = usage?.reasoning_output_tokens ?? 0;
1006
- return {
1007
- sessionId: "",
1008
- taskId: this.config.taskId,
1009
- agentId: this.config.agentId,
1010
- totalCostUsd: computeCodexCostUsd(this.resolvedModel, inputTokens, cachedInputTokens, outputTokens),
1011
- inputTokens,
1012
- outputTokens,
1013
- reasoningOutputTokens,
1014
- cacheReadTokens: cachedInputTokens,
1015
- cacheWriteTokens: undefined,
1016
- durationMs: Date.now() - this.startedAt,
1017
- numTurns: this.numTurns,
1018
- model: this.resolvedModel,
1019
- isError,
1020
- provider: "codex"
1021
- };
1022
- }
1023
- toolNameForItem(item) {
1024
- switch (item.type) {
1025
- case "command_execution":
1026
- return "bash";
1027
- case "file_change": {
1028
- const first = item.changes[0];
1029
- if (!first)
1030
- return "Edit";
1031
- return first.kind === "add" ? "Write" : first.kind === "delete" ? "Delete" : "Edit";
1032
- }
1033
- case "mcp_tool_call":
1034
- return item.tool;
1035
- case "web_search":
1036
- return "WebSearch";
1037
- default:
1038
- return item.type;
1039
- }
1040
- }
1041
- toolArgsForItem(item) {
1042
- switch (item.type) {
1043
- case "command_execution":
1044
- return { command: item.command };
1045
- case "file_change":
1046
- return { changes: item.changes };
1047
- case "mcp_tool_call": {
1048
- const mcpItem = item;
1049
- return { server: mcpItem.server, tool: mcpItem.tool, arguments: mcpItem.arguments };
1050
- }
1051
- case "web_search":
1052
- return { query: item.query };
1053
- default:
1054
- return {};
1055
- }
1056
- }
1057
- isToolItem(item) {
1058
- return item.type === "command_execution" || item.type === "file_change" || item.type === "mcp_tool_call" || item.type === "web_search";
1059
- }
1060
- shortenItemResult(item) {
1061
- switch (item.type) {
1062
- case "command_execution": {
1063
- const cmd = item;
1064
- const stdout = (cmd.aggregated_output ?? "").slice(0, 500);
1065
- return `exit=${cmd.exit_code ?? "?"} status=${cmd.status ?? "?"} stdout=${stdout}`;
1066
- }
1067
- case "file_change": {
1068
- const fc = item;
1069
- const summarised = (fc.changes ?? []).slice(0, 5).map((c) => `${c.kind}:${c.path}`).join(",");
1070
- return `changes=[${summarised}]`;
1071
- }
1072
- case "mcp_tool_call": {
1073
- const mcp = item;
1074
- return `server=${mcp.server} tool=${mcp.tool} status=${mcp.status ?? "?"}`;
1075
- }
1076
- case "web_search": {
1077
- const ws = item;
1078
- return `query=${(ws.query ?? "").slice(0, 200)}`;
1079
- }
1080
- default:
1081
- return JSON.stringify(item).slice(0, 500);
1082
- }
1083
- }
1084
- handleEvent(event) {
1085
- this.emit({ type: "raw_log", content: JSON.stringify(event) });
1086
- switch (event.type) {
1087
- case "thread.started": {
1088
- this._sessionId = event.thread_id;
1089
- const codexVersion = readPkgVersion("@openai/codex-sdk");
1090
- this.emit({
1091
- type: "session_init",
1092
- sessionId: event.thread_id,
1093
- provider: "codex",
1094
- ...codexVersion ? { harnessVariantMeta: { version: codexVersion } } : {}
1095
- });
1096
- break;
1097
- }
1098
- case "turn.started": {
1099
- this.numTurns += 1;
1100
- break;
1101
- }
1102
- case "item.started": {
1103
- if (this.isToolItem(event.item)) {
1104
- this.emit({
1105
- type: "tool_start",
1106
- toolCallId: event.item.id,
1107
- toolName: this.toolNameForItem(event.item),
1108
- args: this.toolArgsForItem(event.item)
1109
- });
1110
- this.transcript.push(`Tool[${this.toolNameForItem(event.item)}] started: ${JSON.stringify(this.toolArgsForItem(event.item)).slice(0, 500)}`);
1111
- }
1112
- break;
1113
- }
1114
- case "item.updated": {
1115
- const updatedItem = event.item;
1116
- if (updatedItem.type === "agent_message") {
1117
- const msg = updatedItem;
1118
- if (msg.text) {
1119
- this.emit({
1120
- type: "custom",
1121
- name: "codex.message_delta",
1122
- data: { itemId: updatedItem.id, text: msg.text }
1123
- });
1124
- }
1125
- }
1126
- break;
1127
- }
1128
- case "item.completed": {
1129
- const { item } = event;
1130
- if (this.isToolItem(item)) {
1131
- this.emit({
1132
- type: "tool_end",
1133
- toolCallId: item.id,
1134
- toolName: this.toolNameForItem(item),
1135
- result: item
1136
- });
1137
- this.transcript.push(`Tool[${this.toolNameForItem(item)}] completed: ${this.shortenItemResult(item)}`);
1138
- break;
1139
- }
1140
- switch (item.type) {
1141
- case "agent_message": {
1142
- const msg = item;
1143
- if (msg.text) {
1144
- this.emit({ type: "message", role: "assistant", content: msg.text });
1145
- this.transcript.push(`Assistant: ${msg.text}`);
1146
- }
1147
- break;
1148
- }
1149
- case "reasoning": {
1150
- const r = item;
1151
- const text = r.text ?? r.summary ?? "";
1152
- if (text) {
1153
- this.emit({
1154
- type: "custom",
1155
- name: "codex.reasoning",
1156
- data: { itemId: r.id, text }
1157
- });
1158
- }
1159
- break;
1160
- }
1161
- case "todo_list": {
1162
- const todo = item;
1163
- this.emit({
1164
- type: "custom",
1165
- name: "codex.todo_list",
1166
- data: { itemId: todo.id, items: todo.items ?? [] }
1167
- });
1168
- break;
1169
- }
1170
- case "error": {
1171
- const errItem = item;
1172
- this.emit({
1173
- type: "error",
1174
- message: this.formatTerminalError(errItem.message).message
1175
- });
1176
- break;
1177
- }
1178
- }
1179
- break;
1180
- }
1181
- case "turn.completed": {
1182
- this.lastUsage = event.usage;
1183
- if (event.usage) {
1184
- const contextUsed = computeContextUsedUnified({
1185
- inputTokens: event.usage.input_tokens,
1186
- cacheReadTokens: 0,
1187
- cacheCreateTokens: 0,
1188
- outputTokens: event.usage.output_tokens
1189
- });
1190
- this.emit({
1191
- type: "context_usage",
1192
- contextUsedTokens: contextUsed,
1193
- contextTotalTokens: this.contextWindow,
1194
- contextPercent: clampContextPercent(contextUsed, this.contextWindow) ?? 0,
1195
- outputTokens: event.usage.output_tokens,
1196
- contextFormula: CONTEXT_FORMULA
1197
- });
1198
- }
1199
- break;
1200
- }
1201
- case "turn.failed": {
1202
- const { message } = this.formatTerminalError(event.error.message);
1203
- this.emit({ type: "error", message });
1204
- break;
1205
- }
1206
- case "error": {
1207
- const { message } = this.formatTerminalError(event.message);
1208
- this.emit({ type: "error", message });
1209
- break;
1210
- }
1211
- }
1212
- }
1213
- formatTerminalError(raw) {
1214
- const normalized = raw.toLowerCase();
1215
- const overflowPatterns = [
1216
- "context length exceeded",
1217
- "maximum context length",
1218
- "too many tokens",
1219
- "input too long",
1220
- "request too large",
1221
- "context_length_exceeded",
1222
- "contextwindowexceeded"
1223
- ];
1224
- if (overflowPatterns.some((p) => normalized.includes(p))) {
1225
- return {
1226
- message: `[context-overflow] Codex turn exceeded the model's context window for ${this.resolvedModel} (${this.contextWindow.toLocaleString()} tokens). Codex does not auto-compact conversation history like Claude does — start a fresh task or split the work into smaller turns. Original error: ${raw}`,
1227
- category: "context_overflow"
1228
- };
1229
- }
1230
- const usageLimitPatterns = ["usage limit", "upgrade to pro", "usagelimitexceeded"];
1231
- if (usageLimitPatterns.some((p) => normalized.includes(p))) {
1232
- return {
1233
- message: `[usage-limit] Codex account quota exhausted — upgrade plan or wait for monthly reset. Original error: ${raw}`,
1234
- category: "usage_limit"
1235
- };
1236
- }
1237
- const rateLimitPatterns = [
1238
- "rate limit",
1239
- "rate_limit",
1240
- "ratelimit",
1241
- "too many requests",
1242
- "http 429",
1243
- " 429 "
1244
- ];
1245
- if (rateLimitPatterns.some((p) => normalized.includes(p))) {
1246
- return {
1247
- message: `[rate-limit] Codex API rate limit hit. Original error: ${raw}`,
1248
- category: "rate_limit"
1249
- };
1250
- }
1251
- const authPatterns = [
1252
- "unauthorized",
1253
- "http 401",
1254
- " 401 ",
1255
- "invalid api key",
1256
- "invalid_api_key",
1257
- "missing api key",
1258
- "no api key",
1259
- "authentication failed",
1260
- "failed to refresh token",
1261
- "invalid 'refresh_token'"
1262
- ];
1263
- if (authPatterns.some((p) => normalized.includes(p))) {
1264
- return {
1265
- message: `[auth-error] Codex authentication failed — check OPENAI_API_KEY or ChatGPT login. Original error: ${raw}`,
1266
- category: "authentication_failed"
1267
- };
1268
- }
1269
- return { message: raw };
1270
- }
1271
- async runSession() {
1272
- this.abortController = new AbortController;
1273
- this.abortRef.current = this.abortController;
1274
- let terminalError;
1275
- let sawTurnCompleted = false;
1276
- try {
1277
- const resolvedPrompt = await resolveCodexPrompt(this.config.prompt, this.skillsDir, (event) => this.emit(event));
1278
- this.transcript = [`User: ${resolvedPrompt}`];
1279
- const streamed = await this.thread.runStreamed(resolvedPrompt, {
1280
- signal: this.abortController.signal
1281
- });
1282
- try {
1283
- for await (const event of streamed.events) {
1284
- this.handleEvent(event);
1285
- if (event.type === "turn.completed") {
1286
- sawTurnCompleted = true;
1287
- }
1288
- if (event.type === "turn.failed" && !terminalError) {
1289
- terminalError = this.formatTerminalError(event.error.message);
1290
- this.errorTracker.processCodexUsageLimitMessage(event.error.message);
1291
- }
1292
- if (event.type === "error" && !terminalError) {
1293
- terminalError = this.formatTerminalError(event.message);
1294
- this.errorTracker.processCodexUsageLimitMessage(event.message);
1295
- }
1296
- }
1297
- } catch (err) {
1298
- if (this.aborted || err instanceof Error && err.name === "AbortError") {
1299
- const abortReason = typeof this.abortController?.signal.reason === "string" ? this.abortController.signal.reason : "cancelled";
1300
- const cost2 = this.buildCostData(this.lastUsage, true);
1301
- this.emit({ type: "result", cost: cost2, isError: true, errorCategory: "cancelled" });
1302
- this.settle({
1303
- exitCode: 130,
1304
- sessionId: this._sessionId,
1305
- cost: cost2,
1306
- isError: true,
1307
- failureReason: abortReason
1308
- });
1309
- return;
1310
- }
1311
- if (terminalError) {
1312
- const cost2 = this.buildCostData(this.lastUsage, true);
1313
- this.emit({
1314
- type: "result",
1315
- cost: cost2,
1316
- isError: true,
1317
- errorCategory: terminalError.category ?? "turn_failed"
1318
- });
1319
- this.settle({
1320
- exitCode: 1,
1321
- sessionId: this._sessionId,
1322
- cost: cost2,
1323
- isError: true,
1324
- failureReason: terminalError.message,
1325
- rateLimitResetAt: this.errorTracker.getRateLimitResetAt(),
1326
- rateLimitWindows: this.errorTracker.getRateLimitWindows()
1327
- });
1328
- return;
1329
- }
1330
- throw err;
1331
- }
1332
- const isError = Boolean(terminalError) || !sawTurnCompleted;
1333
- const cost = this.buildCostData(this.lastUsage, isError);
1334
- this.emit({
1335
- type: "result",
1336
- cost,
1337
- isError,
1338
- errorCategory: terminalError ? terminalError.category ?? "turn_failed" : undefined
1339
- });
1340
- this.settle({
1341
- exitCode: isError ? 1 : 0,
1342
- sessionId: this._sessionId,
1343
- cost,
1344
- isError,
1345
- failureReason: terminalError?.message,
1346
- rateLimitResetAt: this.errorTracker.getRateLimitResetAt(),
1347
- rateLimitWindows: this.errorTracker.getRateLimitWindows()
1348
- });
1349
- } catch (err) {
1350
- const message = err instanceof Error ? err.message : String(err);
1351
- this.emit({ type: "raw_stderr", content: `[codex] Error: ${message}
1352
- ` });
1353
- this.emit({ type: "error", message });
1354
- const cost = this.buildCostData(this.lastUsage, true);
1355
- this.emit({ type: "result", cost, isError: true, errorCategory: "exception" });
1356
- this.settle({
1357
- exitCode: 1,
1358
- sessionId: this._sessionId,
1359
- cost,
1360
- isError: true,
1361
- failureReason: message,
1362
- rateLimitResetAt: this.errorTracker.getRateLimitResetAt(),
1363
- rateLimitWindows: this.errorTracker.getRateLimitWindows()
1364
- });
1365
- } finally {
1366
- const sessionWasAborted = this.aborted || this.abortController?.signal.aborted === true || this.pendingResult?.exitCode === 130;
1367
- if (process.env.SKIP_SESSION_SUMMARY !== "1" && !sessionWasAborted) {
1368
- try {
1369
- await this.summarizeAtEnd();
1370
- } catch (err) {
1371
- console.error("session_summary failed (codex):", err);
1372
- }
1373
- } else if (sessionWasAborted) {
1374
- console.debug("[codex] session aborted — skipping session_summary");
1375
- }
1376
- this.abortRef.current = null;
1377
- try {
1378
- await this.logFileHandle.end();
1379
- } catch {}
1380
- await this.agentsMdHandle.cleanup();
1381
- const finalResult = this.pendingResult ?? {
1382
- exitCode: 1,
1383
- sessionId: this._sessionId,
1384
- cost: this.buildCostData(this.lastUsage, true),
1385
- isError: true,
1386
- failureReason: "session did not settle"
1387
- };
1388
- this.resolveCompletion(finalResult);
1389
- }
1390
- }
1391
- async summarizeAtEnd() {
1392
- const transcriptStr = this.transcript.join(`
1393
- `).slice(-20000);
1394
- const { agentId, taskId, apiUrl, apiKey } = this.config;
1395
- if (!agentId || !taskId || !apiUrl || !apiKey)
1396
- return;
1397
- if (transcriptStr.length <= 100)
1398
- return;
1399
- const _runSummarize = this.summarizeDeps.runSummarize ?? summarizeSession;
1400
- const _fetchRetrievals = this.summarizeDeps.fetchRetrievalsForTask ?? fetchRetrievalsForTask;
1401
- const _postRatings = this.summarizeDeps.postRatings ?? postRatings;
1402
- const _buildRatings = this.summarizeDeps.buildRatingsFromLlm ?? buildRatingsFromLlm;
1403
- const memoryRaters = (process.env.MEMORY_RATERS ?? "").split(",").map((s) => s.trim()).filter(Boolean);
1404
- const wantRatings = memoryRaters.includes("llm");
1405
- const retrievals = wantRatings ? await _fetchRetrievals({ apiUrl, apiKey, agentId, taskId }).catch(() => []) : [];
1406
- const result = await _runSummarize({
1407
- harness: "codex",
1408
- transcript: transcriptStr,
1409
- retrievals,
1410
- taskContext: {
1411
- sourceTaskId: taskId,
1412
- agentId,
1413
- prompt: this.config.prompt
1414
- },
1415
- apiUrl,
1416
- apiKey
1417
- });
1418
- if (!result)
1419
- return;
1420
- const summary = result.summary.trim();
1421
- if (summary.length <= 20 || summary.toLowerCase().includes("no significant learnings")) {
1422
- return;
1423
- }
1424
- const indexResp = await fetch(`${apiUrl}/api/memory/index`, {
1425
- method: "POST",
1426
- headers: {
1427
- "Content-Type": "application/json",
1428
- Authorization: `Bearer ${apiKey}`,
1429
- "X-Agent-ID": agentId
1430
- },
1431
- body: JSON.stringify({
1432
- scope: "agent",
1433
- source: "session_summary",
1434
- sourceTaskId: taskId,
1435
- content: summary,
1436
- name: "session-summary",
1437
- agentId
1438
- })
1439
- });
1440
- if (!indexResp.ok) {
1441
- const text = await indexResp.text().catch(() => "");
1442
- console.error("session_summary: /api/memory/index POST failed (codex):", indexResp.status, text);
1443
- return;
1444
- }
1445
- if (wantRatings && result.ratings && result.ratings.length > 0) {
1446
- const ratingEvents = _buildRatings(result.ratings, retrievals);
1447
- if (ratingEvents.length > 0) {
1448
- await _postRatings({
1449
- apiUrl,
1450
- apiKey,
1451
- agentId,
1452
- taskId,
1453
- events: ratingEvents
1454
- }).catch((err) => console.error("session_summary: postRatings failed (codex):", err));
1455
- }
1456
- }
1457
- }
1458
- }
1459
- async function createInProcessCodexSession(config, opts = {}) {
1460
- const agentsMdHandle = await writeCodexAgentsMd(config.cwd, config.systemPrompt);
1461
- try {
1462
- const resolvedModel = resolveCodexModel(config.model);
1463
- const preSessionEvents = [];
1464
- const bufferedEmit = (event) => {
1465
- preSessionEvents.push(event);
1466
- };
1467
- const mergedConfig = await buildCodexConfig(config, resolvedModel, bufferedEmit);
1468
- const authMode = await resolveCodexAuthMode(config, bufferedEmit);
1469
- const env = {
1470
- PATH: process.env.PATH ?? "",
1471
- HOME: process.env.HOME ?? "",
1472
- ...authMode !== "chatgpt" && process.env.OPENAI_API_KEY ? { OPENAI_API_KEY: process.env.OPENAI_API_KEY } : {},
1473
- ...process.env.NODE_EXTRA_CA_CERTS ? { NODE_EXTRA_CA_CERTS: process.env.NODE_EXTRA_CA_CERTS } : {},
1474
- CONTEXT_MODE_EXTERNAL_MCP_NUDGE_EVERY: CTX_MODE_NUDGE_EVERY,
1475
- ...config.env ?? {},
1476
- ...buildOtelTraceparentEnv(config.env ?? process.env)
1477
- };
1478
- const codexPathOverride = process.env.CODEX_PATH_OVERRIDE;
1479
- const codex = new Codex({
1480
- ...codexPathOverride ? { codexPathOverride } : {},
1481
- env,
1482
- config: mergedConfig
1483
- });
1484
- const threadOptions = {
1485
- workingDirectory: config.cwd,
1486
- skipGitRepoCheck: true,
1487
- sandboxMode: "danger-full-access",
1488
- approvalPolicy: "never",
1489
- model: resolvedModel
1490
- };
1491
- if (config.resumeSessionId) {
1492
- console.warn("[codex-adapter] resumeSessionId ignored — native resume is disabled by deprecation plan");
1493
- }
1494
- const thread = codex.startThread(threadOptions);
1495
- return new CodexSession(thread, config, agentsMdHandle, resolvedModel, preSessionEvents, opts.skillsDir, opts.summarizeDeps ?? {});
1496
- } catch (err) {
1497
- await agentsMdHandle.cleanup();
1498
- throw err;
1499
- }
1500
- }
1501
- function resolveCodexRunnerArgv() {
1502
- const override = process.env.AGENT_SWARM_CODEX_RUNNER_ARGV;
1503
- if (override) {
1504
- try {
1505
- const parsed = JSON.parse(override);
1506
- if (Array.isArray(parsed) && parsed.every((s) => typeof s === "string")) {
1507
- return parsed;
1508
- }
1509
- } catch {}
1510
- }
1511
- const execPath = process.execPath;
1512
- const scriptArg = process.argv[1];
1513
- if (scriptArg && /\.(t|j)sx?$/.test(scriptArg)) {
1514
- return [execPath, scriptArg, "codex-session-runner"];
1515
- }
1516
- return [execPath, "codex-session-runner"];
1517
- }
1518
-
1519
- class CodexSubprocessSession {
1520
- proc;
1521
- listeners = [];
1522
- eventQueue = [];
1523
- completionPromise;
1524
- _sessionId;
1525
- constructor(config, skillsDir) {
1526
- const argv = resolveCodexRunnerArgv();
1527
- const payload = {
1528
- config,
1529
- skillsDir,
1530
- parentOtelEnv: buildOtelTraceparentEnv(config.env ?? process.env)
1531
- };
1532
- const apiKey = getApiKey();
1533
- this.proc = Bun.spawn(argv, {
1534
- env: {
1535
- PATH: process.env.PATH ?? "",
1536
- HOME: process.env.HOME ?? "",
1537
- ...process.env.NODE_EXTRA_CA_CERTS ? { NODE_EXTRA_CA_CERTS: process.env.NODE_EXTRA_CA_CERTS } : {},
1538
- ...process.env.MCP_BASE_URL ? { MCP_BASE_URL: process.env.MCP_BASE_URL } : {},
1539
- ...apiKey ? { AGENT_SWARM_API_KEY: apiKey, API_KEY: apiKey } : {},
1540
- ...process.env.OPENAI_API_KEY ? { OPENAI_API_KEY: process.env.OPENAI_API_KEY } : {},
1541
- ...process.env.OPENROUTER_API_KEY ? { OPENROUTER_API_KEY: process.env.OPENROUTER_API_KEY } : {},
1542
- ...process.env.ANTHROPIC_API_KEY ? { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY } : {},
1543
- ...process.env.CODEX_PATH_OVERRIDE ? { CODEX_PATH_OVERRIDE: process.env.CODEX_PATH_OVERRIDE } : {},
1544
- ...process.env.CODEX_SKILLS_DIR ? { CODEX_SKILLS_DIR: process.env.CODEX_SKILLS_DIR } : {},
1545
- CONTEXT_MODE_EXTERNAL_MCP_NUDGE_EVERY: CTX_MODE_NUDGE_EVERY,
1546
- ...process.env.SKIP_SESSION_SUMMARY ? { SKIP_SESSION_SUMMARY: process.env.SKIP_SESSION_SUMMARY } : {},
1547
- ...process.env.MEMORY_RATERS ? { MEMORY_RATERS: process.env.MEMORY_RATERS } : {}
1548
- },
1549
- stdin: "pipe",
1550
- stdout: "pipe",
1551
- stderr: "pipe"
1552
- });
1553
- const stdin = this.proc.stdin;
1554
- stdin.write(JSON.stringify(payload));
1555
- stdin.end();
1556
- this.completionPromise = this.processStreams();
1557
- }
1558
- get sessionId() {
1559
- return this._sessionId;
1560
- }
1561
- onEvent(listener) {
1562
- this.listeners.push(listener);
1563
- for (const event of this.eventQueue) {
1564
- listener(event);
1565
- }
1566
- this.eventQueue.length = 0;
1567
- }
1568
- async waitForCompletion() {
1569
- return this.completionPromise;
1570
- }
1571
- async abort() {
1572
- this.proc.kill("SIGTERM");
1573
- }
1574
- emit(event) {
1575
- if (event.type === "session_init" && event.sessionId) {
1576
- this._sessionId = event.sessionId;
1577
- }
1578
- if (this.listeners.length > 0) {
1579
- for (const listener of this.listeners) {
1580
- try {
1581
- listener(event);
1582
- } catch {}
1583
- }
1584
- } else {
1585
- this.eventQueue.push(event);
1586
- }
1587
- }
1588
- async processStreams() {
1589
- let result = null;
1590
- let partial = "";
1591
- let stderrTail = "";
1592
- const stdoutPromise = (async () => {
1593
- const stdout = this.proc.stdout;
1594
- if (!stdout)
1595
- return;
1596
- for await (const chunk of stdout) {
1597
- partial += new TextDecoder().decode(chunk);
1598
- const parts = partial.split(`
1599
- `);
1600
- partial = parts.pop() ?? "";
1601
- for (const line of parts) {
1602
- const trimmed = line.trim();
1603
- if (!trimmed)
1604
- continue;
1605
- this.handleLine(trimmed, (r) => {
1606
- result = r;
1607
- });
1608
- }
1609
- }
1610
- if (partial.trim()) {
1611
- this.handleLine(partial.trim(), (r) => {
1612
- result = r;
1613
- });
1614
- partial = "";
1615
- }
1616
- })();
1617
- const stderrPromise = (async () => {
1618
- const stderr = this.proc.stderr;
1619
- if (!stderr)
1620
- return;
1621
- for await (const chunk of stderr) {
1622
- const text = new TextDecoder().decode(chunk);
1623
- stderrTail = (stderrTail + text).slice(-2000);
1624
- this.emit({ type: "raw_stderr", content: text });
1625
- }
1626
- })();
1627
- await Promise.all([stdoutPromise, stderrPromise]);
1628
- const exitCode = await this.proc.exited;
1629
- if (result) {
1630
- return result;
1631
- }
1632
- const stderrHint = stderrTail.trim() ? ` — stderr: ${stderrTail.trim().slice(-500)}` : "";
1633
- return {
1634
- exitCode: exitCode ?? 1,
1635
- sessionId: this._sessionId,
1636
- isError: true,
1637
- failureReason: `codex subprocess exited (code=${exitCode ?? "?"}) without a structured result${stderrHint}`
1638
- };
1639
- }
1640
- handleLine(line, setResult) {
1641
- let msg;
1642
- try {
1643
- msg = JSON.parse(line);
1644
- } catch {
1645
- this.emit({ type: "raw_stderr", content: `${line}
1646
- ` });
1647
- return;
1648
- }
1649
- if (msg.kind === "event" && msg.event) {
1650
- this.emit(msg.event);
1651
- return;
1652
- }
1653
- if (msg.kind === "result" && msg.result) {
1654
- setResult(msg.result);
1655
- return;
1656
- }
1657
- if (msg.kind === "error" && msg.message) {
1658
- this.emit({ type: "error", message: msg.message });
1659
- setResult({
1660
- exitCode: 1,
1661
- sessionId: this._sessionId,
1662
- isError: true,
1663
- failureReason: msg.message
1664
- });
1665
- return;
1666
- }
1667
- }
1668
- }
1669
-
1670
- class CodexAdapter {
1671
- name = "codex";
1672
- traits = { hasMcp: true, hasLocalEnvironment: true };
1673
- skillsDir;
1674
- summarizeDeps;
1675
- bypassSubprocess;
1676
- constructor(opts = {}) {
1677
- this.skillsDir = opts.skillsDir;
1678
- this.summarizeDeps = opts.summarizeDeps ?? {};
1679
- this.bypassSubprocess = opts.bypassSubprocess ?? false;
1680
- }
1681
- async createSession(config) {
1682
- if (this.bypassSubprocess) {
1683
- return createInProcessCodexSession(config, {
1684
- skillsDir: this.skillsDir,
1685
- summarizeDeps: this.summarizeDeps
1686
- });
1687
- }
1688
- return new CodexSubprocessSession(config, this.skillsDir);
1689
- }
1690
- async canResume(_sessionId) {
1691
- return false;
1692
- }
1693
- formatCommand(commandName) {
1694
- return `/${commandName}`;
1695
- }
1696
- }
1697
-
1698
- export { checkCodexCredentials, resolveCodexAuthMode, buildCodexConfig, CodexSession, createInProcessCodexSession, resolveCodexRunnerArgv, CodexAdapter };