@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.
- package/README.md +2 -2
- package/dist/{actions-q4n7cz6e.js → actions-17ya5wde.js} +8 -9
- package/dist/{app-es4nzc71.js → app-4f718yk0.js} +6 -6
- package/dist/{assistant-0x8ey0vs.js → assistant-ah7csnjz.js} +19 -18
- package/dist/{boot-reembed-69agkhv6.js → boot-reembed-4jpxhb25.js} +6 -6
- package/dist/{boot-reembed-p0ny05t8.js → boot-reembed-qata34xa.js} +5 -5
- package/dist/{boot-scrub-logs-yybrkmvr.js → boot-scrub-logs-tv48nn3z.js} +4 -4
- package/dist/{claude-adapter-mma9a1yr.js → claude-adapter-3csn5bks.js} +3 -3
- package/dist/{claude-managed-adapter-pv2nbqy4.js → claude-managed-adapter-kysmkz89.js} +2 -2
- package/dist/cli-0pygq9r1.js +429 -0
- package/dist/{cli-hsetkkd3.js → cli-12vb0xg6.js} +33 -9
- package/dist/{cli-ncfgsqbd.js → cli-15y7qhzp.js} +262 -33
- package/dist/{cli-7ga43spr.js → cli-1yjrbg1f.js} +1 -1
- package/dist/{cli-36ymmpmp.js → cli-3samkyap.js} +2 -2
- package/dist/cli-52fg7s7x.js +269 -0
- package/dist/{cli-hjhvekf4.js → cli-65ekje01.js} +11 -8
- package/dist/{cli-h3k622d0.js → cli-6aq8xhvm.js} +90 -3
- package/dist/{cli-f3qa069v.js → cli-7xje5amz.js} +1 -1
- package/dist/cli-86fdnerz.js +405 -0
- package/dist/{cli-9cy6nc5m.js → cli-8fjwym53.js} +1 -1
- package/dist/{cli-c3frqf65.js → cli-b23axgz4.js} +3 -3
- package/dist/{cli-a0t7vapf.js → cli-bc89my0z.js} +1 -1
- package/dist/{cli-bnzbn8j8.js → cli-beea4a3y.js} +73 -24
- package/dist/{cli-q21d49ac.js → cli-bw80ck94.js} +1 -1
- package/dist/{cli-pwc5e83c.js → cli-cchksf5q.js} +12 -7
- package/dist/{cli-30bbaveh.js → cli-cmw9rzy5.js} +2 -2
- package/dist/{cli-y6mrptcn.js → cli-cnwssmsr.js} +1 -1
- package/dist/{cli-r5g6ngtn.js → cli-dcca6j8m.js} +46 -5
- package/dist/{cli-tkmm5nf9.js → cli-e61fs2hp.js} +59 -2
- package/dist/{cli-6wscj9z0.js → cli-e7306swv.js} +14 -14
- package/dist/{cli-dcfyyh3t.js → cli-ftyf58xj.js} +2 -2
- package/dist/{cli-4xyj9jtq.js → cli-hrhye2rm.js} +1062 -478
- package/dist/cli-jkhwmmgg.js +136 -0
- package/dist/{cli-y4ycrnjc.js → cli-k3y4z3h5.js} +1 -1
- package/dist/{cli-6xd0bvf8.js → cli-ma04faw5.js} +1 -1
- package/dist/{cli-2fc97xm5.js → cli-mnbgv3w7.js} +4 -4
- package/dist/{cli-trmapn9k.js → cli-pet3aev6.js} +95 -10
- package/dist/{cli-z46pnksz.js → cli-q1rx7g0d.js} +175 -28
- package/dist/{cli-qwgtacd6.js → cli-r9f6ngrf.js} +1244 -336
- package/dist/{cli-gtkqc144.js → cli-shvaxw27.js} +13 -7
- package/dist/{cli-ywez6yps.js → cli-sq7f72d6.js} +1 -1
- package/dist/cli-t0vqevnm.js +393 -0
- package/dist/{cli-tbw13sx8.js → cli-tece6zgm.js} +1 -1
- package/dist/{cli-bgvskydy.js → cli-tffvrh6j.js} +1 -1
- package/dist/cli-wh91zby6.js +187222 -0
- package/dist/{cli-anrj584m.js → cli-xz9aq0rf.js} +1 -1
- package/dist/{cli-mq580e06.js → cli-zsrdty74.js} +18 -18
- package/dist/cli.js +32 -15
- package/dist/{codex-adapter-5cmy858w.js → codex-adapter-de3w0nch.js} +10 -10
- package/dist/{codex-session-runner-46q8mrba.js → codex-session-runner-bnw8cysk.js} +10 -10
- package/dist/{commands-z5dbwta3.js → commands-hch4e2k1.js} +4 -4
- package/dist/{db-bbgahh4z.js → db-p3h2j06x.js} +14 -4
- package/dist/{handlers-6b44317z.js → handlers-9er4zx9q.js} +17 -31
- package/dist/{hook-sbp5fmps.js → hook-4axs756z.js} +7 -7
- package/dist/{http-2xz43gz3.js → http-809fvtz2.js} +1934 -886
- package/dist/{index-wz1ea15p.js → index-2dgfj9av.js} +3 -3
- package/dist/{index-nhat35em.js → index-47em3d2n.js} +14 -11
- package/dist/{index-5ghxn8s6.js → index-9fqaktjr.js} +12 -9
- package/dist/{index-0gzmqj4k.js → index-9ht8gr56.js} +13 -10
- package/dist/{index-vfedgdw6.js → index-rm85cfdx.js} +368 -40
- package/dist/{index-ykvs43z4.js → index-t2snazez.js} +4 -4
- package/dist/{keepalive-62gfj91d.js → keepalive-3cps6e3e.js} +7 -6
- package/dist/{lead-ag0akn0v.js → lead-fkk95wgq.js} +36 -32
- package/dist/{maintenance-xxrrzk5x.js → maintenance-fweqh0yj.js} +6 -6
- package/dist/{mistral-conversations-26yrnbf1.js → mistral-conversations-7gqnz2wt.js} +7 -7
- package/dist/oauth-refresh-sweep-sycqtbph.js +114 -0
- package/dist/{onboard-64zyfcbs.js → onboard-cy0rkgvx.js} +2 -2
- package/dist/{opencode-adapter-mqsxr170.js → opencode-adapter-p6bxnbm4.js} +2 -2
- package/dist/{otel-zjd9zaxs.js → otel-g299k41b.js} +2 -2
- package/dist/{otel-impl-b86qseaa.js → otel-impl-wbns8hf7.js} +1 -1
- package/dist/{pi-mono-adapter-kxjxpfgy.js → pi-mono-adapter-05sck1ds.js} +17 -109
- package/dist/{pricing-refresh-cgtkffpa.js → pricing-refresh-0hvazmkf.js} +6 -6
- package/dist/rbac-roles-kfesjy9y.js +28 -0
- package/dist/rbac-roles-mwcwhbxc.js +283 -0
- package/dist/{seed-pricing-1r1y1xkq.js → seed-pricing-h2kdq4cq.js} +5 -5
- package/dist/{setup-mkcgkjf7.js → setup-8506q97w.js} +2 -2
- package/dist/{worker-351sze63.js → worker-4r5c6zyb.js} +36 -32
- package/openapi.json +1327 -20
- package/package.json +3 -3
- package/src/agentmail/handlers.ts +16 -6
- package/src/agentmail/templates.ts +25 -5
- package/src/be/audit-user.ts +28 -4
- package/src/be/db-queries/oauth.ts +42 -0
- package/src/be/db.ts +314 -28
- package/src/be/identity.ts +79 -0
- package/src/be/mcp-proxy.ts +132 -0
- package/src/be/migrations/109_rbac_roles.sql +79 -0
- package/src/be/migrations/111_oauth_credential_bindings.sql +6 -0
- package/src/be/migrations/112_script_connections_graphql.sql +96 -0
- package/src/be/migrations/113_task_routing_affinity.sql +9 -0
- package/src/be/migrations/114_backfill_gpt_5_6_pricing.sql +15 -0
- package/src/be/modelsdev-cache.json +135 -0
- package/src/be/oauth-credential-bindings.ts +35 -0
- package/src/be/oauth-refresh-sweep.ts +159 -0
- package/src/be/rbac-audit.ts +52 -13
- package/src/be/rbac-roles.ts +424 -0
- package/src/be/script-connections.ts +459 -40
- package/src/be/script-credential-broker.ts +29 -3
- package/src/be/scripts/typecheck.ts +51 -7
- package/src/be/seed-scripts/catalog/catalog-report.inline.ts +325 -24
- package/src/be/seed-scripts/catalog/catalog-report.ts +325 -24
- package/src/be/users.ts +24 -0
- package/src/cli.tsx +17 -0
- package/src/github/handlers.ts +10 -9
- package/src/github/templates.ts +45 -9
- package/src/gitlab/handlers.ts +20 -8
- package/src/gitlab/templates.ts +18 -6
- package/src/heartbeat/heartbeat.ts +143 -21
- package/src/http/all-routes.ts +3 -0
- package/src/http/approval-requests.ts +12 -0
- package/src/http/config.ts +44 -10
- package/src/http/core.ts +30 -3
- package/src/http/favorites.ts +1 -5
- package/src/http/index.ts +25 -1
- package/src/http/mcp-oauth.ts +2 -0
- package/src/http/mcp-servers.ts +44 -3
- package/src/http/oauth-generic.ts +105 -0
- package/src/http/poll.ts +20 -3
- package/src/http/script-connection-proxy.ts +116 -0
- package/src/http/script-connections.ts +1657 -0
- package/src/http/scripts.ts +27 -7
- package/src/http/skills.ts +11 -0
- package/src/http/tasks.ts +27 -13
- package/src/http/x.ts +6 -2
- package/src/integrations/kapso/inbound.ts +23 -6
- package/src/jira/sync.ts +111 -17
- package/src/jira/templates.ts +10 -2
- package/src/mcp-client/http-client.ts +194 -0
- package/src/oauth/app-validation.ts +16 -0
- package/src/oauth/ensure-token.ts +69 -14
- package/src/oauth/mcp-wrapper.ts +15 -0
- package/src/oauth/wrapper.ts +48 -20
- package/src/prompts/session-templates.ts +3 -3
- package/src/providers/codex-models.ts +100 -30
- package/src/providers/modelsdev-reasoning.json +51 -0
- package/src/providers/pi-mono-mcp-client.ts +2 -133
- package/src/providers/reasoning-effort.ts +11 -5
- package/src/rbac/admission.ts +68 -0
- package/src/rbac/index.ts +2 -0
- package/src/rbac/legacy-policy.ts +12 -0
- package/src/rbac/permissions.ts +48 -0
- package/src/scheduler/scheduler.ts +9 -1
- package/src/scripts-runtime/api-client.ts +151 -19
- package/src/scripts-runtime/api-types.ts +38 -6
- package/src/scripts-runtime/credential-broker/default-bindings.ts +1 -0
- package/src/scripts-runtime/credential-broker/types.ts +8 -0
- package/src/scripts-runtime/ctx.ts +11 -1
- package/src/scripts-runtime/eval-harness.ts +5 -1
- package/src/scripts-runtime/executors/types.ts +2 -1
- package/src/scripts-runtime/loader.ts +3 -1
- package/src/scripts-runtime/mcp-client.ts +87 -0
- package/src/scripts-runtime/sdk-allowlist.ts +1 -0
- package/src/scripts-runtime/types/stdlib.d.ts +6 -0
- package/src/scripts-runtime/types/swarm-sdk.d.ts +19 -0
- package/src/server-user.ts +114 -59
- package/src/server.ts +12 -0
- package/src/slack/assistant.ts +14 -6
- package/src/slack/enrich.ts +17 -0
- package/src/slack/handlers.ts +8 -22
- package/src/slack/responses.ts +91 -1
- package/src/slack/thread-buffer.ts +6 -3
- package/src/slack/watcher.ts +9 -6
- package/src/tasks/worker-follow-up.ts +100 -3
- package/src/tests/agentmail-handlers.test.ts +32 -3
- package/src/tests/approval-requests.test.ts +61 -0
- package/src/tests/audit-user.test.ts +47 -1
- package/src/tests/codex-adapter.test.ts +46 -23
- package/src/tests/credential-broker.test.ts +5 -2
- package/src/tests/delete-page-tool.test.ts +197 -0
- package/src/tests/github-handlers.test.ts +28 -2
- package/src/tests/gitlab-handlers.test.ts +125 -0
- package/src/tests/heartbeat.test.ts +48 -3
- package/src/tests/identity.test.ts +117 -0
- package/src/tests/jira-sync.test.ts +99 -1
- package/src/tests/kapso-inbound.test.ts +7 -0
- package/src/tests/mcp-tools-user.test.ts +60 -6
- package/src/tests/model-control.test.ts +8 -2
- package/src/tests/oauth-credential-bindings.test.ts +490 -0
- package/src/tests/oauth-refresh-sweep.test.ts +160 -0
- package/src/tests/pool-affinity.test.ts +401 -0
- package/src/tests/prompt-template-remaining.test.ts +2 -2
- package/src/tests/rbac-admission-e2e.test.ts +386 -0
- package/src/tests/rbac-admission.test.ts +492 -0
- package/src/tests/rbac-e2e-helpers.ts +3 -0
- package/src/tests/rbac-engine.test.ts +50 -0
- package/src/tests/rbac-mcp-admission-e2e.test.ts +362 -0
- package/src/tests/rbac-roles.test.ts +328 -0
- package/src/tests/reasoning-effort.test.ts +15 -2
- package/src/tests/schedule-target-type.test.ts +50 -0
- package/src/tests/scheduled-tasks.test.ts +40 -0
- package/src/tests/script-apis-mcp.test.ts +37 -13
- package/src/tests/script-connections-http.test.ts +1158 -0
- package/src/tests/script-connections-mcp.test.ts +894 -0
- package/src/tests/script-connections.test.ts +754 -6
- package/src/tests/scripts-external-api.test.ts +49 -1
- package/src/tests/scripts-runtime-secret-egress.test.ts +1 -0
- package/src/tests/scripts-runtime.test.ts +291 -0
- package/src/tests/seed-scripts.test.ts +36 -0
- package/src/tests/slack-identity-resolution.test.ts +37 -3
- package/src/tests/slack-inline-output.test.ts +65 -0
- package/src/tests/slack-thread-buffer.test.ts +28 -0
- package/src/tests/tool-annotations.test.ts +1 -0
- package/src/tests/user-token-rest-auth.test.ts +140 -1
- package/src/tests/workflow-swarm-script.test.ts +44 -0
- package/src/tests/workflow-triggers-v2.test.ts +300 -1
- package/src/tools/credential-bindings/tool.ts +277 -18
- package/src/tools/delete-page.ts +145 -0
- package/src/tools/mcp-servers/mcp-server-delete.ts +7 -4
- package/src/tools/request-human-input.ts +2 -0
- package/src/tools/resolve-user.ts +77 -27
- package/src/tools/script-apis.ts +62 -3
- package/src/tools/script-connections/tool.ts +275 -31
- package/src/tools/send-task.ts +15 -1
- package/src/tools/task-action.ts +34 -2
- package/src/tools/templates.ts +53 -1
- package/src/tools/tool-config.ts +2 -1
- package/src/tools/utils.ts +2 -0
- package/src/tools/workflows/create-workflow.ts +8 -1
- package/src/tools/workflows/update-workflow.ts +11 -2
- package/src/types.ts +90 -11
- package/src/utils/scoped-resource.ts +25 -0
- package/src/workflows/executors/human-in-the-loop.ts +1 -0
- package/src/workflows/executors/swarm-script.ts +7 -1
- package/src/workflows/triggers.ts +146 -18
- package/templates/skills/swarm-scripts/content.md +33 -8
- package/dist/cli-bgef578c.js +0 -771
- package/dist/cli-c9mtm09b.js +0 -43
- package/dist/cli-ceewqajw.js +0 -1698
- package/dist/{anthropic-messages-8nc6r2dw.js → anthropic-messages-kq3pjhbp.js} +6 -6
- package/dist/{azure-openai-responses-6yfzw3qw.js → azure-openai-responses-gm1ejtmn.js} +7 -7
- package/dist/{cli-vbraamxr.js → cli-6b4wwmm8.js} +9 -9
- package/dist/{cli-2r7s8jhp.js → cli-sznberje.js} +3 -3
- package/dist/{google-generative-ai-vfq6ymz9.js → google-generative-ai-hyrra2jm.js} +3 -3
- package/dist/{google-vertex-mj5y5rfj.js → google-vertex-8bs2pzgn.js} +3 -3
- package/dist/{openai-codex-responses-8h6kvfv8.js → openai-codex-responses-3nkvje79.js} +4 -4
- package/dist/{openai-completions-x6kjnqjd.js → openai-completions-0asftkwg.js} +11 -11
- package/dist/{openai-responses-7jwyz3ds.js → openai-responses-pmatek45.js} +10 -10
package/dist/cli-ceewqajw.js
DELETED
|
@@ -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 };
|