@gakr-gakr/codex 0.1.0 → 0.1.1

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 (115) hide show
  1. package/dist/client-DNN2uyJW.js +642 -0
  2. package/dist/client-factory-Bu9OClHJ.js +9 -0
  3. package/dist/command-formatters-BpPOTePl.js +520 -0
  4. package/dist/command-handlers-BBs7Vws9.js +1533 -0
  5. package/dist/compact-CDboBy7o.js +329 -0
  6. package/dist/computer-use-DCZB46Sw.js +367 -0
  7. package/dist/config-CLMSw0p2.js +510 -0
  8. package/dist/doctor-contract-api.js +53 -0
  9. package/dist/harness.js +51 -0
  10. package/dist/index.js +1171 -0
  11. package/dist/media-understanding-provider.js +335 -0
  12. package/dist/models-jLA2SIvd.js +110 -0
  13. package/dist/node-cli-sessions-BLRDs_US.js +1216 -0
  14. package/dist/plugin-activation-CEy_oYpx.js +452 -0
  15. package/dist/prompt-overlay.js +12 -0
  16. package/dist/protocol-C9UWI98H.js +9 -0
  17. package/dist/protocol-validators-BGBspNmF.js +5988 -0
  18. package/dist/provider-catalog.js +84 -0
  19. package/dist/provider-discovery.js +33 -0
  20. package/dist/provider.js +150 -0
  21. package/dist/rate-limit-cache-9LxQdE0K.js +24 -0
  22. package/dist/request-DbSPeTcV.js +89 -0
  23. package/dist/rolldown-runtime-DUslC3ob.js +14 -0
  24. package/dist/run-attempt-BoEwzQCv.js +5463 -0
  25. package/dist/session-binding-e2GFp9VH.js +222 -0
  26. package/dist/shared-client-D7Vy0glq.js +631 -0
  27. package/dist/side-question-BDLuEzFP.js +668 -0
  28. package/dist/test-api.js +49 -0
  29. package/dist/thread-lifecycle-Clo0EHMk.js +1565 -0
  30. package/dist/vision-tools-Cofrv35p.js +1379 -0
  31. package/package.json +16 -1
  32. package/doctor-contract-api.ts +0 -68
  33. package/harness.ts +0 -72
  34. package/index.ts +0 -124
  35. package/media-understanding-provider.ts +0 -521
  36. package/prompt-overlay.ts +0 -21
  37. package/provider-catalog.ts +0 -83
  38. package/provider-discovery.ts +0 -45
  39. package/provider.ts +0 -243
  40. package/src/app-server/app-inventory-cache.ts +0 -324
  41. package/src/app-server/approval-bridge.ts +0 -1211
  42. package/src/app-server/auth-bridge.ts +0 -614
  43. package/src/app-server/capabilities.ts +0 -27
  44. package/src/app-server/client-factory.ts +0 -24
  45. package/src/app-server/client.ts +0 -715
  46. package/src/app-server/compact.ts +0 -512
  47. package/src/app-server/computer-use.ts +0 -683
  48. package/src/app-server/config.ts +0 -1038
  49. package/src/app-server/context-engine-projection.ts +0 -403
  50. package/src/app-server/dynamic-tool-diagnostics.ts +0 -73
  51. package/src/app-server/dynamic-tool-profile.ts +0 -70
  52. package/src/app-server/dynamic-tools.ts +0 -623
  53. package/src/app-server/elicitation-bridge.ts +0 -783
  54. package/src/app-server/event-projector.ts +0 -2065
  55. package/src/app-server/image-payload-sanitizer.ts +0 -167
  56. package/src/app-server/local-runtime-attribution.ts +0 -39
  57. package/src/app-server/managed-binary.ts +0 -193
  58. package/src/app-server/models.ts +0 -172
  59. package/src/app-server/native-hook-relay.ts +0 -150
  60. package/src/app-server/native-subagent-task-mirror.ts +0 -497
  61. package/src/app-server/plugin-activation.ts +0 -283
  62. package/src/app-server/plugin-app-cache-key.ts +0 -74
  63. package/src/app-server/plugin-approval-roundtrip.ts +0 -122
  64. package/src/app-server/plugin-inventory.ts +0 -357
  65. package/src/app-server/plugin-thread-config.ts +0 -455
  66. package/src/app-server/protocol-generated/json/DynamicToolCallParams.json +0 -33
  67. package/src/app-server/protocol-generated/json/v2/ErrorNotification.json +0 -199
  68. package/src/app-server/protocol-generated/json/v2/GetAccountResponse.json +0 -102
  69. package/src/app-server/protocol-generated/json/v2/ModelListResponse.json +0 -227
  70. package/src/app-server/protocol-generated/json/v2/ThreadResumeResponse.json +0 -2630
  71. package/src/app-server/protocol-generated/json/v2/ThreadStartResponse.json +0 -2630
  72. package/src/app-server/protocol-generated/json/v2/TurnCompletedNotification.json +0 -1659
  73. package/src/app-server/protocol-generated/json/v2/TurnStartResponse.json +0 -1655
  74. package/src/app-server/protocol-validators.ts +0 -203
  75. package/src/app-server/protocol.ts +0 -520
  76. package/src/app-server/rate-limit-cache.ts +0 -48
  77. package/src/app-server/rate-limits.ts +0 -583
  78. package/src/app-server/request.ts +0 -73
  79. package/src/app-server/run-attempt.ts +0 -4862
  80. package/src/app-server/session-binding.ts +0 -398
  81. package/src/app-server/session-history.ts +0 -44
  82. package/src/app-server/shared-client.ts +0 -289
  83. package/src/app-server/side-question.ts +0 -1009
  84. package/src/app-server/test-support.ts +0 -48
  85. package/src/app-server/thread-lifecycle.ts +0 -959
  86. package/src/app-server/timeout.ts +0 -9
  87. package/src/app-server/tool-progress-normalization.ts +0 -77
  88. package/src/app-server/trajectory.ts +0 -368
  89. package/src/app-server/transcript-mirror.ts +0 -208
  90. package/src/app-server/transport-stdio.ts +0 -107
  91. package/src/app-server/transport-websocket.ts +0 -90
  92. package/src/app-server/transport.ts +0 -117
  93. package/src/app-server/user-input-bridge.ts +0 -316
  94. package/src/app-server/version.ts +0 -4
  95. package/src/app-server/vision-tools.ts +0 -12
  96. package/src/command-account.ts +0 -544
  97. package/src/command-formatters.ts +0 -426
  98. package/src/command-handlers.ts +0 -2021
  99. package/src/command-plugins-management.ts +0 -137
  100. package/src/command-rpc.ts +0 -142
  101. package/src/commands.ts +0 -65
  102. package/src/conversation-binding-data.ts +0 -124
  103. package/src/conversation-binding.ts +0 -561
  104. package/src/conversation-control.ts +0 -303
  105. package/src/conversation-turn-collector.ts +0 -186
  106. package/src/conversation-turn-input.ts +0 -106
  107. package/src/migration/apply.ts +0 -501
  108. package/src/migration/helpers.ts +0 -55
  109. package/src/migration/plan.ts +0 -461
  110. package/src/migration/provider.ts +0 -41
  111. package/src/migration/source.ts +0 -643
  112. package/src/migration/targets.ts +0 -25
  113. package/src/node-cli-sessions.ts +0 -711
  114. package/test-api.ts +0 -95
  115. package/tsconfig.json +0 -16
@@ -0,0 +1,1216 @@
1
+ import { a as isCodexFastServiceTier, c as resolveCodexAppServerRuntimeOptions, r as codexSandboxPolicyForTurn } from "./config-CLMSw0p2.js";
2
+ import { t as isJsonObject } from "./protocol-C9UWI98H.js";
3
+ import { r as CODEX_CONTROL_METHODS } from "./request-DbSPeTcV.js";
4
+ import { r as formatCodexDisplayText } from "./command-formatters-BpPOTePl.js";
5
+ import { i as getSharedCodexAppServerClient, u as resolveCodexAppServerAuthProfileIdForAgent } from "./shared-client-D7Vy0glq.js";
6
+ import { i as readCodexAppServerBinding, n as isCodexAppServerNativeAuthProfile, o as writeCodexAppServerBinding, r as normalizeCodexAppServerBindingModelProvider, t as clearCodexAppServerBinding } from "./session-binding-e2GFp9VH.js";
7
+ import os from "node:os";
8
+ import { formatErrorMessage } from "autobot/plugin-sdk/agent-harness-runtime";
9
+ import { spawn } from "node:child_process";
10
+ import { materializeWindowsSpawnProgram, resolveWindowsSpawnProgram } from "autobot/plugin-sdk/windows-spawn";
11
+ import fs from "node:fs/promises";
12
+ import path from "node:path";
13
+ import { fileURLToPath } from "node:url";
14
+ import process from "node:process";
15
+ import { resolvePreferredAutoBotTmpDir } from "autobot/plugin-sdk/temp-path";
16
+ //#region extensions/codex/src/conversation-binding-data.ts
17
+ const BINDING_DATA_VERSION = 1;
18
+ function createCodexConversationBindingData(params) {
19
+ const agentDir = params.agentDir?.trim();
20
+ return {
21
+ kind: "codex-app-server-session",
22
+ version: BINDING_DATA_VERSION,
23
+ sessionFile: params.sessionFile,
24
+ workspaceDir: params.workspaceDir,
25
+ ...agentDir ? { agentDir } : {}
26
+ };
27
+ }
28
+ function createCodexCliNodeConversationBindingData(params) {
29
+ const cwd = params.cwd?.trim();
30
+ return {
31
+ kind: "codex-cli-node-session",
32
+ version: BINDING_DATA_VERSION,
33
+ nodeId: params.nodeId,
34
+ sessionId: params.sessionId,
35
+ ...cwd ? { cwd } : {}
36
+ };
37
+ }
38
+ function readCodexConversationBindingData(binding) {
39
+ const data = binding?.data;
40
+ if (!data || typeof data !== "object" || Array.isArray(data)) return;
41
+ return readCodexConversationBindingDataRecord(data);
42
+ }
43
+ function readCodexConversationBindingDataRecord(data) {
44
+ if (data.kind === "codex-cli-node-session") {
45
+ if (data.version !== BINDING_DATA_VERSION || typeof data.nodeId !== "string" || !data.nodeId.trim() || typeof data.sessionId !== "string" || !data.sessionId.trim()) return;
46
+ return {
47
+ kind: "codex-cli-node-session",
48
+ version: BINDING_DATA_VERSION,
49
+ nodeId: data.nodeId.trim(),
50
+ sessionId: data.sessionId.trim(),
51
+ cwd: typeof data.cwd === "string" && data.cwd.trim() ? data.cwd.trim() : void 0
52
+ };
53
+ }
54
+ if (data.kind !== "codex-app-server-session") return;
55
+ if (data.version !== BINDING_DATA_VERSION || typeof data.sessionFile !== "string" || !data.sessionFile.trim()) return;
56
+ return {
57
+ kind: "codex-app-server-session",
58
+ version: BINDING_DATA_VERSION,
59
+ sessionFile: data.sessionFile,
60
+ workspaceDir: typeof data.workspaceDir === "string" && data.workspaceDir.trim() ? data.workspaceDir : process.cwd(),
61
+ agentDir: typeof data.agentDir === "string" && data.agentDir.trim() ? data.agentDir : void 0
62
+ };
63
+ }
64
+ function resolveCodexDefaultWorkspaceDir(pluginConfig) {
65
+ return readString$1(readRecord$1(readRecord$1(pluginConfig)?.appServer), "defaultWorkspaceDir") ?? process.cwd();
66
+ }
67
+ function readRecord$1(value) {
68
+ return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
69
+ }
70
+ function readString$1(record, key) {
71
+ const value = record?.[key];
72
+ return typeof value === "string" && value.trim() ? value.trim() : void 0;
73
+ }
74
+ //#endregion
75
+ //#region extensions/codex/src/conversation-control.ts
76
+ const CODEX_CONVERSATION_CONTROL_STATE = Symbol.for("autobot.codex.conversationControl");
77
+ function getActiveTurns() {
78
+ const globalState = globalThis;
79
+ globalState[CODEX_CONVERSATION_CONTROL_STATE] ??= /* @__PURE__ */ new Map();
80
+ return globalState[CODEX_CONVERSATION_CONTROL_STATE];
81
+ }
82
+ function trackCodexConversationActiveTurn(active) {
83
+ const activeTurns = getActiveTurns();
84
+ activeTurns.set(active.sessionFile, active);
85
+ return () => {
86
+ if (activeTurns.get(active.sessionFile)?.turnId === active.turnId) activeTurns.delete(active.sessionFile);
87
+ };
88
+ }
89
+ function readCodexConversationActiveTurn(sessionFile) {
90
+ return getActiveTurns().get(sessionFile);
91
+ }
92
+ async function stopCodexConversationTurn(params) {
93
+ const active = readCodexConversationActiveTurn(params.sessionFile);
94
+ if (!active) return {
95
+ stopped: false,
96
+ message: "No active Codex run to stop."
97
+ };
98
+ const runtime = resolveCodexAppServerRuntimeOptions({ pluginConfig: params.pluginConfig });
99
+ const lookup = buildBindingLookup(params);
100
+ const binding = await readCodexAppServerBinding(params.sessionFile, lookup);
101
+ await (await getSharedCodexAppServerClient({
102
+ startOptions: runtime.start,
103
+ timeoutMs: runtime.requestTimeoutMs,
104
+ authProfileId: binding?.authProfileId,
105
+ ...lookup
106
+ })).request("turn/interrupt", {
107
+ threadId: active.threadId,
108
+ turnId: active.turnId
109
+ }, { timeoutMs: runtime.requestTimeoutMs });
110
+ return {
111
+ stopped: true,
112
+ message: "Codex stop requested."
113
+ };
114
+ }
115
+ async function steerCodexConversationTurn(params) {
116
+ const active = readCodexConversationActiveTurn(params.sessionFile);
117
+ const text = params.message.trim();
118
+ if (!text) return {
119
+ steered: false,
120
+ message: "Usage: /codex steer <message>"
121
+ };
122
+ if (!active) return {
123
+ steered: false,
124
+ message: "No active Codex run to steer."
125
+ };
126
+ const runtime = resolveCodexAppServerRuntimeOptions({ pluginConfig: params.pluginConfig });
127
+ const lookup = buildBindingLookup(params);
128
+ const binding = await readCodexAppServerBinding(params.sessionFile, lookup);
129
+ await (await getSharedCodexAppServerClient({
130
+ startOptions: runtime.start,
131
+ timeoutMs: runtime.requestTimeoutMs,
132
+ authProfileId: binding?.authProfileId,
133
+ ...lookup
134
+ })).request("turn/steer", {
135
+ threadId: active.threadId,
136
+ expectedTurnId: active.turnId,
137
+ input: [{
138
+ type: "text",
139
+ text,
140
+ text_elements: []
141
+ }]
142
+ }, { timeoutMs: runtime.requestTimeoutMs });
143
+ return {
144
+ steered: true,
145
+ message: "Sent steer message to Codex."
146
+ };
147
+ }
148
+ async function setCodexConversationModel(params) {
149
+ const model = params.model.trim();
150
+ if (!model) return "Usage: /codex model <model>";
151
+ const lookup = buildBindingLookup(params);
152
+ const binding = await requireThreadBinding(params.sessionFile, lookup);
153
+ const runtime = resolveCodexAppServerRuntimeOptions({ pluginConfig: params.pluginConfig });
154
+ const response = await resumeThreadWithOverrides({
155
+ pluginConfig: params.pluginConfig,
156
+ threadId: binding.threadId,
157
+ authProfileId: binding.authProfileId,
158
+ ...lookup,
159
+ model
160
+ });
161
+ await writeCodexAppServerBinding(params.sessionFile, {
162
+ ...binding,
163
+ cwd: response.thread.cwd ?? binding.cwd,
164
+ model: response.model ?? model,
165
+ modelProvider: response.modelProvider ?? binding.modelProvider,
166
+ approvalPolicy: binding.approvalPolicy,
167
+ sandbox: binding.sandbox,
168
+ serviceTier: binding.serviceTier ?? runtime.serviceTier
169
+ }, lookup);
170
+ return `Codex model set to ${formatCodexDisplayText(response.model ?? model)}.`;
171
+ }
172
+ async function setCodexConversationFastMode(params) {
173
+ const lookup = buildBindingLookup(params);
174
+ const binding = await requireThreadBinding(params.sessionFile, lookup);
175
+ if (params.enabled == null) return `Codex fast mode: ${isCodexFastServiceTier(binding.serviceTier) ? "on" : "off"}.`;
176
+ const serviceTier = params.enabled ? "priority" : "flex";
177
+ await writeCodexAppServerBinding(params.sessionFile, {
178
+ ...binding,
179
+ serviceTier
180
+ }, lookup);
181
+ return `Codex fast mode ${params.enabled ? "enabled" : "disabled"}.`;
182
+ }
183
+ async function setCodexConversationPermissions(params) {
184
+ const lookup = buildBindingLookup(params);
185
+ const binding = await requireThreadBinding(params.sessionFile, lookup);
186
+ if (!params.mode) return `Codex permissions: ${formatPermissionsMode(binding)}.`;
187
+ const policy = permissionsForMode(params.mode);
188
+ await writeCodexAppServerBinding(params.sessionFile, {
189
+ ...binding,
190
+ approvalPolicy: policy.approvalPolicy,
191
+ sandbox: policy.sandbox
192
+ }, lookup);
193
+ return `Codex permissions set to ${params.mode === "yolo" ? "full access" : "default"}.`;
194
+ }
195
+ function parseCodexFastModeArg(arg) {
196
+ const normalized = arg?.trim().toLowerCase();
197
+ if (!normalized || normalized === "status") return;
198
+ if (normalized === "on" || normalized === "true" || normalized === "fast") return true;
199
+ if (normalized === "off" || normalized === "false" || normalized === "flex") return false;
200
+ }
201
+ function parseCodexPermissionsModeArg(arg) {
202
+ const normalized = arg?.trim().toLowerCase();
203
+ if (!normalized || normalized === "status") return;
204
+ if (normalized === "yolo" || normalized === "full" || normalized === "full-access") return "yolo";
205
+ if (normalized === "default" || normalized === "guardian") return "default";
206
+ }
207
+ function formatPermissionsMode(binding) {
208
+ return binding.approvalPolicy === "never" && binding.sandbox === "danger-full-access" ? "full access" : "default";
209
+ }
210
+ async function requireThreadBinding(sessionFile, lookup = {}) {
211
+ const binding = await readCodexAppServerBinding(sessionFile, lookup);
212
+ if (!binding?.threadId) throw new Error("No Codex thread is attached to this AutoBot session yet.");
213
+ return binding;
214
+ }
215
+ async function resumeThreadWithOverrides(params) {
216
+ const runtime = resolveCodexAppServerRuntimeOptions({ pluginConfig: params.pluginConfig });
217
+ return await (await getSharedCodexAppServerClient({
218
+ startOptions: runtime.start,
219
+ timeoutMs: runtime.requestTimeoutMs,
220
+ authProfileId: params.authProfileId,
221
+ ...buildBindingLookup(params)
222
+ })).request(CODEX_CONTROL_METHODS.resumeThread, {
223
+ threadId: params.threadId,
224
+ ...params.model ? { model: params.model } : {},
225
+ approvalPolicy: params.approvalPolicy ?? runtime.approvalPolicy,
226
+ sandbox: params.sandbox ?? runtime.sandbox,
227
+ approvalsReviewer: runtime.approvalsReviewer,
228
+ ...params.serviceTier ? { serviceTier: params.serviceTier } : {},
229
+ persistExtendedHistory: true
230
+ }, { timeoutMs: runtime.requestTimeoutMs });
231
+ }
232
+ function buildBindingLookup(params) {
233
+ const agentDir = params.agentDir?.trim();
234
+ return {
235
+ ...agentDir ? { agentDir } : {},
236
+ ...params.config ? { config: params.config } : {}
237
+ };
238
+ }
239
+ function permissionsForMode(mode) {
240
+ return mode === "yolo" ? {
241
+ approvalPolicy: "never",
242
+ sandbox: "danger-full-access"
243
+ } : {
244
+ approvalPolicy: "on-request",
245
+ sandbox: "workspace-write"
246
+ };
247
+ }
248
+ //#endregion
249
+ //#region extensions/codex/src/conversation-turn-collector.ts
250
+ const MAX_PENDING_NOTIFICATIONS_PER_TURN = 100;
251
+ function createCodexConversationTurnCollector(threadId) {
252
+ let turnId;
253
+ let completed = false;
254
+ let failedError;
255
+ let timeout;
256
+ const assistantTextByItem = /* @__PURE__ */ new Map();
257
+ const assistantOrder = [];
258
+ const pendingNotificationsByTurnId = /* @__PURE__ */ new Map();
259
+ let resolveCompletion;
260
+ let rejectCompletion;
261
+ const rememberItem = (itemId) => {
262
+ if (!assistantOrder.includes(itemId)) assistantOrder.push(itemId);
263
+ };
264
+ const collectReplyText = () => {
265
+ return assistantOrder.map((itemId) => assistantTextByItem.get(itemId)?.trim()).filter((text) => Boolean(text)).at(-1) ?? "";
266
+ };
267
+ const clearWaitState = () => {
268
+ if (timeout) {
269
+ clearTimeout(timeout);
270
+ timeout = void 0;
271
+ }
272
+ resolveCompletion = void 0;
273
+ rejectCompletion = void 0;
274
+ };
275
+ const finish = () => {
276
+ if (completed) return;
277
+ completed = true;
278
+ if (failedError) rejectCompletion?.(new Error(failedError));
279
+ else resolveCompletion?.({ replyText: collectReplyText() });
280
+ clearWaitState();
281
+ };
282
+ const handleNotification = (notification) => {
283
+ const params = isJsonObject(notification.params) ? notification.params : void 0;
284
+ if (!params || readString(params, "threadId") !== threadId) return;
285
+ if (!turnId) {
286
+ const pendingTurnId = readNotificationTurnId(params);
287
+ if (pendingTurnId) {
288
+ const pending = pendingNotificationsByTurnId.get(pendingTurnId) ?? [];
289
+ if (pending.length < MAX_PENDING_NOTIFICATIONS_PER_TURN) {
290
+ pending.push(notification);
291
+ pendingNotificationsByTurnId.set(pendingTurnId, pending);
292
+ }
293
+ }
294
+ return;
295
+ }
296
+ if (!isNotificationForTurn(params, threadId, turnId)) return;
297
+ if (notification.method === "item/agentMessage/delta") {
298
+ const itemId = readString(params, "itemId") ?? readString(params, "id") ?? "assistant";
299
+ const delta = readTextString(params, "delta");
300
+ if (!delta) return;
301
+ rememberItem(itemId);
302
+ assistantTextByItem.set(itemId, `${assistantTextByItem.get(itemId) ?? ""}${delta}`);
303
+ return;
304
+ }
305
+ if (notification.method === "item/completed") {
306
+ const item = isJsonObject(params.item) ? params.item : void 0;
307
+ if (item?.type === "agentMessage") {
308
+ const itemId = readString(item, "id") ?? readString(params, "itemId") ?? "assistant";
309
+ const text = readTextString(item, "text");
310
+ if (text) {
311
+ rememberItem(itemId);
312
+ assistantTextByItem.set(itemId, text);
313
+ }
314
+ }
315
+ return;
316
+ }
317
+ if (notification.method === "turn/completed") {
318
+ const turn = isJsonObject(params.turn) ? params.turn : void 0;
319
+ if (readString(turn, "status") === "failed") failedError = readString(readRecord(turn?.error), "message") ?? "codex app-server turn failed";
320
+ const items = Array.isArray(turn?.items) ? turn.items : [];
321
+ for (const item of items) {
322
+ if (!isJsonObject(item) || item.type !== "agentMessage") continue;
323
+ const itemId = readString(item, "id") ?? `assistant-${assistantOrder.length + 1}`;
324
+ const text = readTextString(item, "text");
325
+ if (text) {
326
+ rememberItem(itemId);
327
+ assistantTextByItem.set(itemId, text);
328
+ }
329
+ }
330
+ finish();
331
+ }
332
+ };
333
+ return {
334
+ setTurnId(nextTurnId) {
335
+ turnId = nextTurnId;
336
+ const pending = pendingNotificationsByTurnId.get(nextTurnId) ?? [];
337
+ pendingNotificationsByTurnId.clear();
338
+ for (const notification of pending) handleNotification(notification);
339
+ },
340
+ handleNotification,
341
+ wait(params) {
342
+ if (completed) return failedError ? Promise.reject(new Error(failedError)) : Promise.resolve({ replyText: collectReplyText() });
343
+ return new Promise((resolve, reject) => {
344
+ resolveCompletion = resolve;
345
+ rejectCompletion = reject;
346
+ timeout = setTimeout(() => {
347
+ completed = true;
348
+ reject(/* @__PURE__ */ new Error("codex app-server bound turn timed out"));
349
+ clearWaitState();
350
+ }, Math.max(100, params.timeoutMs));
351
+ timeout.unref?.();
352
+ });
353
+ }
354
+ };
355
+ }
356
+ function isNotificationForTurn(params, threadId, turnId) {
357
+ if (readString(params, "threadId") !== threadId) return false;
358
+ if (!turnId) return true;
359
+ const directTurnId = readString(params, "turnId");
360
+ if (directTurnId) return directTurnId === turnId;
361
+ return readString(isJsonObject(params.turn) ? params.turn : void 0, "id") === turnId;
362
+ }
363
+ function readNotificationTurnId(params) {
364
+ return readString(params, "turnId") ?? readString(readRecord(params.turn), "id");
365
+ }
366
+ function readRecord(value) {
367
+ return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
368
+ }
369
+ function readString(record, key) {
370
+ const value = record?.[key];
371
+ return typeof value === "string" && value.trim() ? value.trim() : void 0;
372
+ }
373
+ function readTextString(record, key) {
374
+ const value = record?.[key];
375
+ return typeof value === "string" && value.length > 0 ? value : void 0;
376
+ }
377
+ //#endregion
378
+ //#region extensions/codex/src/conversation-turn-input.ts
379
+ const IMAGE_EXTENSIONS = new Set([
380
+ ".avif",
381
+ ".gif",
382
+ ".jpeg",
383
+ ".jpg",
384
+ ".png",
385
+ ".webp"
386
+ ]);
387
+ function buildCodexConversationTurnInput(params) {
388
+ return [{
389
+ type: "text",
390
+ text: params.prompt,
391
+ text_elements: []
392
+ }, ...extractInboundMedia(params.event).map(toCodexImageInput).filter((item) => item !== void 0)];
393
+ }
394
+ function extractInboundMedia(event) {
395
+ const metadata = event.metadata ?? {};
396
+ const paths = readStringArray(metadata.mediaPaths).concat(readStringArray(metadata.mediaPath));
397
+ const urls = readStringArray(metadata.mediaUrls).concat(readStringArray(metadata.mediaUrl));
398
+ const mimeTypes = readStringArray(metadata.mediaTypes).concat(readStringArray(metadata.mediaType));
399
+ const count = Math.max(paths.length, urls.length, mimeTypes.length);
400
+ const media = [];
401
+ for (let index = 0; index < count; index += 1) media.push({
402
+ path: paths[index],
403
+ url: urls[index],
404
+ mimeType: mimeTypes[index] ?? mimeTypes[0]
405
+ });
406
+ return media;
407
+ }
408
+ function toCodexImageInput(media) {
409
+ if (!isImageMedia(media)) return;
410
+ const localPath = media.path ?? readLocalMediaPath(media.url);
411
+ if (localPath) {
412
+ const normalized = normalizeFileUrl(localPath);
413
+ return normalized ? {
414
+ type: "localImage",
415
+ path: normalized
416
+ } : void 0;
417
+ }
418
+ return media.url ? {
419
+ type: "image",
420
+ url: media.url
421
+ } : void 0;
422
+ }
423
+ function isImageMedia(media) {
424
+ if (media.mimeType?.toLowerCase().startsWith("image/")) return true;
425
+ const candidate = media.path ?? media.url;
426
+ if (!candidate) return false;
427
+ return IMAGE_EXTENSIONS.has(path.extname(candidate.split(/[?#]/, 1)[0] ?? "").toLowerCase());
428
+ }
429
+ function normalizeFileUrl(value) {
430
+ if (!value.startsWith("file://")) return value;
431
+ try {
432
+ return fileURLToPath(value);
433
+ } catch {
434
+ return;
435
+ }
436
+ }
437
+ function readLocalMediaPath(value) {
438
+ if (!value) return;
439
+ if (value.startsWith("file://")) return value;
440
+ if (value.startsWith("//")) return;
441
+ if (path.isAbsolute(value) || path.win32.isAbsolute(value)) return value;
442
+ return /^[a-z][a-z0-9+.-]*:/i.test(value) ? void 0 : value;
443
+ }
444
+ function readStringArray(value) {
445
+ if (typeof value === "string" && value.trim()) return [value.trim()];
446
+ if (!Array.isArray(value)) return [];
447
+ return value.map((entry) => typeof entry === "string" ? entry.trim() : "").filter(Boolean);
448
+ }
449
+ //#endregion
450
+ //#region extensions/codex/src/conversation-binding.ts
451
+ const DEFAULT_BOUND_TURN_TIMEOUT_MS = 20 * 6e4;
452
+ const CODEX_CONVERSATION_GLOBAL_STATE = Symbol.for("autobot.codex.conversationBinding");
453
+ function getGlobalState() {
454
+ const globalState = globalThis;
455
+ globalState[CODEX_CONVERSATION_GLOBAL_STATE] ??= { queues: /* @__PURE__ */ new Map() };
456
+ return globalState[CODEX_CONVERSATION_GLOBAL_STATE];
457
+ }
458
+ async function startCodexConversationThread(params) {
459
+ const workspaceDir = params.workspaceDir?.trim() || resolveCodexDefaultWorkspaceDir(params.pluginConfig);
460
+ const agentDir = params.agentDir?.trim();
461
+ const agentLookup = buildAgentLookup({
462
+ agentDir,
463
+ config: params.config
464
+ });
465
+ const existingBinding = await readCodexAppServerBinding(params.sessionFile, { ...agentLookup });
466
+ const authProfileId = resolveCodexAppServerAuthProfileIdForAgent({
467
+ authProfileId: params.authProfileId ?? existingBinding?.authProfileId,
468
+ ...agentLookup
469
+ });
470
+ if (params.threadId?.trim()) await attachExistingThread({
471
+ pluginConfig: params.pluginConfig,
472
+ sessionFile: params.sessionFile,
473
+ threadId: params.threadId.trim(),
474
+ workspaceDir,
475
+ ...agentDir ? { agentDir } : {},
476
+ model: params.model,
477
+ modelProvider: params.modelProvider,
478
+ authProfileId,
479
+ approvalPolicy: params.approvalPolicy,
480
+ sandbox: params.sandbox,
481
+ serviceTier: params.serviceTier,
482
+ config: params.config
483
+ });
484
+ else await createThread({
485
+ pluginConfig: params.pluginConfig,
486
+ sessionFile: params.sessionFile,
487
+ workspaceDir,
488
+ ...agentDir ? { agentDir } : {},
489
+ model: params.model,
490
+ modelProvider: params.modelProvider,
491
+ authProfileId,
492
+ approvalPolicy: params.approvalPolicy,
493
+ sandbox: params.sandbox,
494
+ serviceTier: params.serviceTier,
495
+ config: params.config
496
+ });
497
+ return createCodexConversationBindingData({
498
+ sessionFile: params.sessionFile,
499
+ workspaceDir,
500
+ ...agentDir ? { agentDir } : {}
501
+ });
502
+ }
503
+ async function handleCodexConversationInboundClaim(event, ctx, options = {}) {
504
+ const data = readCodexConversationBindingData(ctx.pluginBinding);
505
+ if (!data) return;
506
+ if (event.commandAuthorized !== true) return { handled: true };
507
+ const prompt = event.bodyForAgent?.trim() || event.content?.trim() || "";
508
+ if (!prompt) return { handled: true };
509
+ if (data.kind === "codex-cli-node-session") {
510
+ const resume = options.resumeCodexCliSessionOnNode;
511
+ if (!resume) return {
512
+ handled: true,
513
+ reply: { text: "Codex CLI node binding is unavailable because Gateway node runtime is not attached." }
514
+ };
515
+ try {
516
+ return {
517
+ handled: true,
518
+ reply: (await enqueueBoundTurn(`${data.nodeId}:${data.sessionId}`, async () => {
519
+ return { reply: { text: (await resume({
520
+ nodeId: data.nodeId,
521
+ sessionId: data.sessionId,
522
+ prompt,
523
+ cwd: data.cwd,
524
+ timeoutMs: options.timeoutMs
525
+ })).text.trim() || "Codex completed without a text reply." } };
526
+ })).reply
527
+ };
528
+ } catch (error) {
529
+ return {
530
+ handled: true,
531
+ reply: { text: `Codex CLI node turn failed: ${formatCodexDisplayText(formatErrorMessage(error))}` }
532
+ };
533
+ }
534
+ }
535
+ try {
536
+ return {
537
+ handled: true,
538
+ reply: (await enqueueBoundTurn(data.sessionFile, () => runBoundTurnWithMissingThreadRecovery({
539
+ data,
540
+ prompt,
541
+ event,
542
+ pluginConfig: options.pluginConfig,
543
+ timeoutMs: options.timeoutMs
544
+ }))).reply
545
+ };
546
+ } catch (error) {
547
+ return {
548
+ handled: true,
549
+ reply: { text: `Codex app-server turn failed: ${formatCodexDisplayText(formatErrorMessage(error))}` }
550
+ };
551
+ }
552
+ }
553
+ async function handleCodexConversationBindingResolved(event) {
554
+ if (event.status !== "denied") return;
555
+ const data = readCodexConversationBindingDataRecord(event.request.data ?? {});
556
+ if (!data || data.kind !== "codex-app-server-session") return;
557
+ await clearCodexAppServerBinding(data.sessionFile);
558
+ }
559
+ async function attachExistingThread(params) {
560
+ const runtime = resolveCodexAppServerRuntimeOptions({ pluginConfig: params.pluginConfig });
561
+ const agentLookup = buildAgentLookup({
562
+ agentDir: params.agentDir,
563
+ config: params.config
564
+ });
565
+ const modelProvider = resolveThreadRequestModelProvider({
566
+ authProfileId: params.authProfileId,
567
+ modelProvider: params.modelProvider,
568
+ ...agentLookup
569
+ });
570
+ const response = await (await getSharedCodexAppServerClient({
571
+ startOptions: runtime.start,
572
+ timeoutMs: runtime.requestTimeoutMs,
573
+ authProfileId: params.authProfileId,
574
+ ...agentLookup
575
+ })).request(CODEX_CONTROL_METHODS.resumeThread, {
576
+ threadId: params.threadId,
577
+ ...params.model ? { model: params.model } : {},
578
+ ...modelProvider ? { modelProvider } : {},
579
+ approvalPolicy: params.approvalPolicy ?? runtime.approvalPolicy,
580
+ approvalsReviewer: runtime.approvalsReviewer,
581
+ sandbox: params.sandbox ?? runtime.sandbox,
582
+ ...params.serviceTier ?? runtime.serviceTier ? { serviceTier: params.serviceTier ?? runtime.serviceTier } : {},
583
+ persistExtendedHistory: true
584
+ }, { timeoutMs: runtime.requestTimeoutMs });
585
+ const thread = response.thread;
586
+ const runtimeApprovalPolicy = typeof runtime.approvalPolicy === "string" ? runtime.approvalPolicy : void 0;
587
+ await writeCodexAppServerBinding(params.sessionFile, {
588
+ threadId: thread.id,
589
+ cwd: thread.cwd ?? params.workspaceDir,
590
+ authProfileId: params.authProfileId,
591
+ model: response.model ?? params.model,
592
+ modelProvider: normalizeCodexAppServerBindingModelProvider({
593
+ authProfileId: params.authProfileId,
594
+ modelProvider: response.modelProvider ?? params.modelProvider,
595
+ ...agentLookup
596
+ }),
597
+ approvalPolicy: params.approvalPolicy ?? runtimeApprovalPolicy,
598
+ sandbox: params.sandbox ?? runtime.sandbox,
599
+ serviceTier: params.serviceTier ?? runtime.serviceTier
600
+ }, { ...agentLookup });
601
+ }
602
+ async function createThread(params) {
603
+ const runtime = resolveCodexAppServerRuntimeOptions({ pluginConfig: params.pluginConfig });
604
+ const agentLookup = buildAgentLookup({
605
+ agentDir: params.agentDir,
606
+ config: params.config
607
+ });
608
+ const modelProvider = resolveThreadRequestModelProvider({
609
+ authProfileId: params.authProfileId,
610
+ modelProvider: params.modelProvider,
611
+ ...agentLookup
612
+ });
613
+ const response = await (await getSharedCodexAppServerClient({
614
+ startOptions: runtime.start,
615
+ timeoutMs: runtime.requestTimeoutMs,
616
+ authProfileId: params.authProfileId,
617
+ ...agentLookup
618
+ })).request("thread/start", {
619
+ cwd: params.workspaceDir,
620
+ ...params.model ? { model: params.model } : {},
621
+ ...modelProvider ? { modelProvider } : {},
622
+ approvalPolicy: params.approvalPolicy ?? runtime.approvalPolicy,
623
+ approvalsReviewer: runtime.approvalsReviewer,
624
+ sandbox: params.sandbox ?? runtime.sandbox,
625
+ ...params.serviceTier ?? runtime.serviceTier ? { serviceTier: params.serviceTier ?? runtime.serviceTier } : {},
626
+ developerInstructions: "This Codex thread is bound to an AutoBot conversation. Answer normally; AutoBot will deliver your final response back to the conversation.",
627
+ experimentalRawEvents: true,
628
+ persistExtendedHistory: true
629
+ }, { timeoutMs: runtime.requestTimeoutMs });
630
+ const runtimeApprovalPolicy = typeof runtime.approvalPolicy === "string" ? runtime.approvalPolicy : void 0;
631
+ await writeCodexAppServerBinding(params.sessionFile, {
632
+ threadId: response.thread.id,
633
+ cwd: response.thread.cwd ?? params.workspaceDir,
634
+ authProfileId: params.authProfileId,
635
+ model: response.model ?? params.model,
636
+ modelProvider: normalizeCodexAppServerBindingModelProvider({
637
+ authProfileId: params.authProfileId,
638
+ modelProvider: response.modelProvider ?? params.modelProvider,
639
+ ...agentLookup
640
+ }),
641
+ approvalPolicy: params.approvalPolicy ?? runtimeApprovalPolicy,
642
+ sandbox: params.sandbox ?? runtime.sandbox,
643
+ serviceTier: params.serviceTier ?? runtime.serviceTier
644
+ }, { ...agentLookup });
645
+ }
646
+ async function runBoundTurn(params) {
647
+ const runtime = resolveCodexAppServerRuntimeOptions({ pluginConfig: params.pluginConfig });
648
+ const agentLookup = buildAgentLookup({ agentDir: params.data.agentDir });
649
+ const binding = await readCodexAppServerBinding(params.data.sessionFile, agentLookup);
650
+ const threadId = binding?.threadId;
651
+ if (!threadId) throw new Error("bound Codex conversation has no thread binding");
652
+ const client = await getSharedCodexAppServerClient({
653
+ startOptions: runtime.start,
654
+ timeoutMs: runtime.requestTimeoutMs,
655
+ authProfileId: binding.authProfileId,
656
+ ...agentLookup
657
+ });
658
+ const collector = createCodexConversationTurnCollector(threadId);
659
+ const notificationCleanup = client.addNotificationHandler((notification) => collector.handleNotification(notification));
660
+ const requestCleanup = client.addRequestHandler(async (request) => {
661
+ if (request.method === "item/tool/call") return {
662
+ contentItems: [{
663
+ type: "inputText",
664
+ text: "AutoBot native Codex conversation binding does not expose dynamic AutoBot tools yet."
665
+ }],
666
+ success: false
667
+ };
668
+ if (request.method === "item/commandExecution/requestApproval" || request.method === "item/fileChange/requestApproval") return {
669
+ decision: "decline",
670
+ reason: "AutoBot native Codex conversation binding cannot route interactive approvals yet; use the Codex harness or explicit /acp spawn codex for that workflow."
671
+ };
672
+ if (request.method === "item/permissions/requestApproval") return {
673
+ permissions: {},
674
+ scope: "turn"
675
+ };
676
+ if (request.method.includes("requestApproval")) return {
677
+ decision: "decline",
678
+ reason: "AutoBot native Codex conversation binding cannot route interactive approvals yet; use the Codex harness or explicit /acp spawn codex for that workflow."
679
+ };
680
+ });
681
+ try {
682
+ const turnId = (await client.request("turn/start", {
683
+ threadId,
684
+ input: buildCodexConversationTurnInput({
685
+ prompt: params.prompt,
686
+ event: params.event
687
+ }),
688
+ cwd: binding.cwd || params.data.workspaceDir,
689
+ approvalPolicy: binding.approvalPolicy ?? runtime.approvalPolicy,
690
+ approvalsReviewer: runtime.approvalsReviewer,
691
+ sandboxPolicy: codexSandboxPolicyForTurn(binding.sandbox ?? runtime.sandbox, binding.cwd || params.data.workspaceDir),
692
+ ...binding.model ? { model: binding.model } : {},
693
+ ...binding.serviceTier ?? runtime.serviceTier ? { serviceTier: binding.serviceTier ?? runtime.serviceTier } : {}
694
+ }, { timeoutMs: runtime.requestTimeoutMs })).turn.id;
695
+ const activeCleanup = trackCodexConversationActiveTurn({
696
+ sessionFile: params.data.sessionFile,
697
+ threadId,
698
+ turnId
699
+ });
700
+ collector.setTurnId(turnId);
701
+ return { reply: { text: (await collector.wait({ timeoutMs: params.timeoutMs ?? DEFAULT_BOUND_TURN_TIMEOUT_MS }).finally(activeCleanup)).replyText.trim() || "Codex completed without a text reply." } };
702
+ } finally {
703
+ notificationCleanup();
704
+ requestCleanup();
705
+ }
706
+ }
707
+ async function runBoundTurnWithMissingThreadRecovery(params) {
708
+ try {
709
+ return await runBoundTurn(params);
710
+ } catch (error) {
711
+ if (!isCodexThreadNotFoundError(error)) throw error;
712
+ const agentLookup = buildAgentLookup({ agentDir: params.data.agentDir });
713
+ const binding = await readCodexAppServerBinding(params.data.sessionFile, agentLookup);
714
+ await startCodexConversationThread({
715
+ pluginConfig: params.pluginConfig,
716
+ sessionFile: params.data.sessionFile,
717
+ workspaceDir: binding?.cwd || params.data.workspaceDir,
718
+ ...agentLookup,
719
+ model: binding?.model,
720
+ modelProvider: binding?.modelProvider,
721
+ authProfileId: binding?.authProfileId,
722
+ approvalPolicy: binding?.approvalPolicy,
723
+ sandbox: binding?.sandbox,
724
+ serviceTier: binding?.serviceTier
725
+ });
726
+ return await runBoundTurn(params);
727
+ }
728
+ }
729
+ function isCodexThreadNotFoundError(error) {
730
+ return /\bthread not found:/iu.test(formatErrorMessage(error));
731
+ }
732
+ function enqueueBoundTurn(key, run) {
733
+ const state = getGlobalState();
734
+ const next = (state.queues.get(key) ?? Promise.resolve()).then(run, run);
735
+ const queued = next.then(() => void 0, () => void 0);
736
+ state.queues.set(key, queued);
737
+ next.finally(() => {
738
+ if (state.queues.get(key) === queued) state.queues.delete(key);
739
+ }).catch(() => void 0);
740
+ return next;
741
+ }
742
+ function resolveThreadRequestModelProvider(params) {
743
+ const modelProvider = params.modelProvider?.trim();
744
+ if (!modelProvider || modelProvider.toLowerCase() === "codex") return;
745
+ if (isCodexAppServerNativeAuthProfile(params) && (modelProvider.toLowerCase() === "openai" || modelProvider.toLowerCase() === "openai-codex")) return;
746
+ return modelProvider.toLowerCase() === "openai-codex" ? "openai" : modelProvider;
747
+ }
748
+ function buildAgentLookup(params) {
749
+ const agentDir = params.agentDir?.trim();
750
+ return {
751
+ ...agentDir ? { agentDir } : {},
752
+ ...params.config ? { config: params.config } : {}
753
+ };
754
+ }
755
+ //#endregion
756
+ //#region extensions/codex/src/node-cli-sessions.ts
757
+ const CODEX_CLI_SESSIONS_LIST_COMMAND = "codex.cli.sessions.list";
758
+ const CODEX_CLI_SESSION_RESUME_COMMAND = "codex.cli.session.resume";
759
+ const DEFAULT_SESSION_LIMIT = 10;
760
+ const MAX_SESSION_LIMIT = 50;
761
+ const DEFAULT_RESUME_TIMEOUT_MS = 20 * 6e4;
762
+ const SESSION_ID_PATTERN = /^[A-Za-z0-9._:-]{1,128}$/;
763
+ const activeResumeSessions = /* @__PURE__ */ new Set();
764
+ const DEFAULT_RESUME_SPAWN_RUNTIME = {
765
+ platform: process.platform,
766
+ env: process.env,
767
+ execPath: process.execPath
768
+ };
769
+ function createCodexCliSessionNodeHostCommands() {
770
+ return [{
771
+ command: CODEX_CLI_SESSIONS_LIST_COMMAND,
772
+ cap: "codex-cli-sessions",
773
+ handle: listLocalCodexCliSessions
774
+ }, {
775
+ command: CODEX_CLI_SESSION_RESUME_COMMAND,
776
+ cap: "codex-cli-sessions",
777
+ dangerous: true,
778
+ handle: resumeLocalCodexCliSession
779
+ }];
780
+ }
781
+ function createCodexCliSessionNodeInvokePolicies() {
782
+ return [{
783
+ commands: [CODEX_CLI_SESSIONS_LIST_COMMAND],
784
+ defaultPlatforms: [
785
+ "macos",
786
+ "linux",
787
+ "windows"
788
+ ],
789
+ handle: (ctx) => ctx.invokeNode()
790
+ }, {
791
+ commands: [CODEX_CLI_SESSION_RESUME_COMMAND],
792
+ dangerous: true,
793
+ handle: (ctx) => ctx.invokeNode()
794
+ }];
795
+ }
796
+ async function listCodexCliSessionsOnNode(params) {
797
+ const node = await resolveCodexCliNode({
798
+ runtime: params.runtime,
799
+ requestedNode: params.requestedNode,
800
+ command: CODEX_CLI_SESSIONS_LIST_COMMAND
801
+ });
802
+ return {
803
+ node,
804
+ result: parseCodexCliSessionsListResult(await params.runtime.nodes.invoke({
805
+ nodeId: readNodeId(node),
806
+ command: CODEX_CLI_SESSIONS_LIST_COMMAND,
807
+ params: {
808
+ limit: params.limit,
809
+ filter: params.filter
810
+ },
811
+ timeoutMs: 15e3
812
+ }))
813
+ };
814
+ }
815
+ async function resolveCodexCliSessionForBindingOnNode(params) {
816
+ const listing = await listCodexCliSessionsOnNode({
817
+ runtime: params.runtime,
818
+ requestedNode: params.requestedNode,
819
+ filter: params.sessionId,
820
+ limit: MAX_SESSION_LIMIT
821
+ });
822
+ if (!listing.node.commands?.includes("codex.cli.session.resume")) throw new Error(`Node ${formatNodeLabel(listing.node)} does not expose ${CODEX_CLI_SESSION_RESUME_COMMAND}.`);
823
+ return {
824
+ node: listing.node,
825
+ session: listing.result.sessions.find((session) => session.sessionId === params.sessionId)
826
+ };
827
+ }
828
+ async function resumeCodexCliSessionOnNode(params) {
829
+ const payload = unwrapNodeInvokePayload(await params.runtime.nodes.invoke({
830
+ nodeId: params.nodeId,
831
+ command: CODEX_CLI_SESSION_RESUME_COMMAND,
832
+ params: {
833
+ sessionId: params.sessionId,
834
+ prompt: params.prompt,
835
+ cwd: params.cwd,
836
+ timeoutMs: params.timeoutMs
837
+ },
838
+ timeoutMs: (params.timeoutMs ?? DEFAULT_RESUME_TIMEOUT_MS) + 5e3
839
+ }));
840
+ if (!isRecord(payload) || payload.ok !== true || typeof payload.text !== "string") throw new Error("Codex CLI resume returned an invalid payload.");
841
+ return {
842
+ ok: true,
843
+ sessionId: typeof payload.sessionId === "string" ? payload.sessionId : params.sessionId,
844
+ text: payload.text
845
+ };
846
+ }
847
+ function formatCodexCliSessions(params) {
848
+ if (params.result.sessions.length === 0) return `No Codex CLI sessions returned from ${formatCodexDisplayText(formatNodeLabel(params.node))}.`;
849
+ return [`Codex CLI sessions on ${formatCodexDisplayText(formatNodeLabel(params.node))}:`, ...params.result.sessions.map((session) => {
850
+ const details = [session.cwd, session.updatedAt].filter((value) => Boolean(value));
851
+ return `- ${formatCodexDisplayText(session.sessionId)}${session.lastMessage ? ` - ${formatCodexDisplayText(session.lastMessage)}` : ""}${details.length > 0 ? ` (${details.map(formatCodexDisplayText).join(", ")})` : ""}\n Bind: /codex resume ${formatCodexDisplayText(session.sessionId)} --host ${formatCodexDisplayText(readNodeId(params.node))} --bind here`;
852
+ })].join("\n");
853
+ }
854
+ async function listLocalCodexCliSessions(paramsJSON) {
855
+ const params = readRecordParam(paramsJSON);
856
+ const limit = normalizeLimit(params.limit);
857
+ const filter = typeof params.filter === "string" ? params.filter.trim().toLowerCase() : "";
858
+ const codexHome = resolveCodexHome();
859
+ const summaries = await readHistorySessions(codexHome);
860
+ await hydrateSessionFiles(codexHome, summaries);
861
+ await hydrateSessionsFromSessionFiles(codexHome, summaries);
862
+ const sessions = [...summaries.values()].filter((session) => {
863
+ if (!filter) return true;
864
+ return [
865
+ session.sessionId,
866
+ session.cwd,
867
+ session.lastMessage
868
+ ].some((value) => value?.toLowerCase().includes(filter));
869
+ }).toSorted((a, b) => compareOptionalStringsDesc(a.updatedAt, b.updatedAt)).slice(0, limit);
870
+ return JSON.stringify({
871
+ sessions,
872
+ codexHome
873
+ });
874
+ }
875
+ async function resumeLocalCodexCliSession(paramsJSON) {
876
+ const params = readRecordParam(paramsJSON);
877
+ const sessionId = typeof params.sessionId === "string" ? params.sessionId.trim() : "";
878
+ const prompt = typeof params.prompt === "string" ? params.prompt.trim() : "";
879
+ if (!sessionId || !SESSION_ID_PATTERN.test(sessionId)) throw new Error("Missing or invalid Codex CLI session id.");
880
+ if (!prompt) throw new Error("Missing Codex CLI prompt.");
881
+ if (activeResumeSessions.has(sessionId)) throw new Error(`Codex CLI session ${sessionId} already has an active resume turn.`);
882
+ activeResumeSessions.add(sessionId);
883
+ try {
884
+ const text = await runCodexExecResume({
885
+ sessionId,
886
+ prompt,
887
+ cwd: typeof params.cwd === "string" && params.cwd.trim() ? params.cwd.trim() : void 0,
888
+ timeoutMs: normalizeTimeoutMs(params.timeoutMs)
889
+ });
890
+ return JSON.stringify({
891
+ ok: true,
892
+ sessionId,
893
+ text: text.trim() || "Codex completed without a text reply."
894
+ });
895
+ } finally {
896
+ activeResumeSessions.delete(sessionId);
897
+ }
898
+ }
899
+ async function runCodexExecResume(params) {
900
+ const outputPath = path.join(await fs.mkdtemp(path.join(resolvePreferredAutoBotTmpDir(), "autobot-codex-cli-")), "last-message.txt");
901
+ try {
902
+ const invocation = resolveCodexCliResumeSpawnInvocation([
903
+ "exec",
904
+ "resume",
905
+ "--skip-git-repo-check",
906
+ "--output-last-message",
907
+ outputPath,
908
+ params.sessionId,
909
+ "-"
910
+ ], {
911
+ platform: process.platform,
912
+ env: process.env,
913
+ execPath: process.execPath
914
+ });
915
+ const child = spawn(invocation.command, invocation.args, {
916
+ cwd: params.cwd || process.cwd(),
917
+ stdio: [
918
+ "pipe",
919
+ "pipe",
920
+ "pipe"
921
+ ],
922
+ env: process.env,
923
+ shell: invocation.shell,
924
+ windowsHide: invocation.windowsHide
925
+ });
926
+ const stdout = [];
927
+ const stderr = [];
928
+ let timedOut = false;
929
+ let forceKillTimeout;
930
+ const timeout = setTimeout(() => {
931
+ timedOut = true;
932
+ child.kill("SIGTERM");
933
+ forceKillTimeout = setTimeout(() => child.kill("SIGKILL"), 2e3);
934
+ forceKillTimeout.unref?.();
935
+ }, params.timeoutMs);
936
+ child.stdout.on("data", (chunk) => stdout.push(chunk));
937
+ child.stderr.on("data", (chunk) => stderr.push(chunk));
938
+ child.stdin.end(params.prompt);
939
+ const exitCode = await new Promise((resolve, reject) => {
940
+ child.on("error", reject);
941
+ child.on("exit", (code) => resolve(code));
942
+ }).finally(() => {
943
+ clearTimeout(timeout);
944
+ if (forceKillTimeout) clearTimeout(forceKillTimeout);
945
+ });
946
+ if (timedOut) throw new Error(`codex exec resume timed out after ${String(params.timeoutMs)}ms`);
947
+ if (exitCode !== 0) {
948
+ const message = Buffer.concat(stderr).toString("utf8").trim() || Buffer.concat(stdout).toString("utf8").trim() || `codex exec resume exited with code ${String(exitCode)}`;
949
+ throw new Error(message);
950
+ }
951
+ return await fs.readFile(outputPath, "utf8");
952
+ } finally {
953
+ await fs.rm(path.dirname(outputPath), {
954
+ recursive: true,
955
+ force: true
956
+ });
957
+ }
958
+ }
959
+ function resolveCodexCliResumeSpawnInvocation(args, runtime = DEFAULT_RESUME_SPAWN_RUNTIME) {
960
+ const resolved = materializeWindowsSpawnProgram(resolveWindowsSpawnProgram({
961
+ command: "codex",
962
+ platform: runtime.platform,
963
+ env: runtime.env,
964
+ execPath: runtime.execPath,
965
+ packageName: "@openai/codex"
966
+ }), args);
967
+ return {
968
+ command: resolved.command,
969
+ args: resolved.argv,
970
+ shell: resolved.shell,
971
+ windowsHide: resolved.windowsHide
972
+ };
973
+ }
974
+ async function readHistorySessions(codexHome) {
975
+ const summaries = /* @__PURE__ */ new Map();
976
+ const content = await readFileIfExists(path.join(codexHome, "history.jsonl"));
977
+ if (!content) return summaries;
978
+ for (const line of content.split(/\r?\n/u)) {
979
+ const trimmed = line.trim();
980
+ if (!trimmed) continue;
981
+ let parsed;
982
+ try {
983
+ parsed = JSON.parse(trimmed);
984
+ } catch {
985
+ continue;
986
+ }
987
+ if (!isRecord(parsed) || typeof parsed.session_id !== "string") continue;
988
+ const sessionId = parsed.session_id.trim();
989
+ if (!sessionId) continue;
990
+ const entry = summaries.get(sessionId) ?? {
991
+ sessionId,
992
+ messageCount: 0
993
+ };
994
+ entry.messageCount += 1;
995
+ if (typeof parsed.text === "string" && parsed.text.trim()) entry.lastMessage = truncateText(parsed.text.trim(), 140);
996
+ if (typeof parsed.ts === "number" && Number.isFinite(parsed.ts)) entry.updatedAt = (/* @__PURE__ */ new Date(parsed.ts * 1e3)).toISOString();
997
+ summaries.set(sessionId, entry);
998
+ }
999
+ return summaries;
1000
+ }
1001
+ async function hydrateSessionFiles(codexHome, summaries) {
1002
+ if (summaries.size === 0) return;
1003
+ const files = await findSessionFiles(path.join(codexHome, "sessions"), 4);
1004
+ const pending = new Set(summaries.keys());
1005
+ for (const file of files) {
1006
+ const basename = path.basename(file);
1007
+ const sessionId = [...pending].find((id) => basename.includes(id));
1008
+ if (!sessionId) continue;
1009
+ const entry = summaries.get(sessionId);
1010
+ if (!entry) continue;
1011
+ entry.sessionFile = file;
1012
+ const cwd = readSessionMetaCwd(await readFirstLine(file) ?? "");
1013
+ if (cwd) entry.cwd = cwd;
1014
+ pending.delete(sessionId);
1015
+ if (pending.size === 0) return;
1016
+ }
1017
+ }
1018
+ async function hydrateSessionsFromSessionFiles(codexHome, summaries) {
1019
+ const files = await findSessionFiles(path.join(codexHome, "sessions"), 4);
1020
+ for (const file of files) {
1021
+ const summary = await readSessionFileSummary(file);
1022
+ if (!summary) continue;
1023
+ const existing = summaries.get(summary.sessionId);
1024
+ summaries.set(summary.sessionId, {
1025
+ ...summary,
1026
+ ...existing,
1027
+ cwd: existing?.cwd ?? summary.cwd,
1028
+ sessionFile: existing?.sessionFile ?? summary.sessionFile,
1029
+ updatedAt: existing?.updatedAt ?? summary.updatedAt,
1030
+ lastMessage: existing?.lastMessage ?? summary.lastMessage,
1031
+ messageCount: existing?.messageCount ?? summary.messageCount
1032
+ });
1033
+ }
1034
+ }
1035
+ async function readSessionFileSummary(file) {
1036
+ const content = await readFileIfExists(file);
1037
+ if (!content) return null;
1038
+ let sessionId = "";
1039
+ let cwd;
1040
+ let updatedAt;
1041
+ let lastMessage;
1042
+ let messageCount = 0;
1043
+ for (const line of content.split(/\r?\n/u)) {
1044
+ const trimmed = line.trim();
1045
+ if (!trimmed) continue;
1046
+ let parsed;
1047
+ try {
1048
+ parsed = JSON.parse(trimmed);
1049
+ } catch {
1050
+ continue;
1051
+ }
1052
+ if (!isRecord(parsed)) continue;
1053
+ if (typeof parsed.timestamp === "string" && parsed.timestamp.trim()) updatedAt = parsed.timestamp.trim();
1054
+ if (parsed.type === "session_meta" && isRecord(parsed.payload)) {
1055
+ if (typeof parsed.payload.id === "string" && parsed.payload.id.trim()) sessionId = parsed.payload.id.trim();
1056
+ if (typeof parsed.payload.cwd === "string" && parsed.payload.cwd.trim()) cwd = parsed.payload.cwd.trim();
1057
+ continue;
1058
+ }
1059
+ const messageText = readResponseItemMessageText(parsed);
1060
+ if (messageText) {
1061
+ messageCount += 1;
1062
+ lastMessage = truncateText(messageText, 140);
1063
+ }
1064
+ }
1065
+ if (!sessionId) sessionId = readSessionIdFromFilename(file) ?? "";
1066
+ if (!sessionId) return null;
1067
+ return {
1068
+ sessionId,
1069
+ updatedAt: updatedAt ?? await readFileMtimeIso(file),
1070
+ lastMessage,
1071
+ cwd,
1072
+ sessionFile: file,
1073
+ messageCount
1074
+ };
1075
+ }
1076
+ async function findSessionFiles(dir, maxDepth) {
1077
+ if (maxDepth < 0) return [];
1078
+ let entries;
1079
+ try {
1080
+ entries = await fs.readdir(dir, { withFileTypes: true });
1081
+ } catch {
1082
+ return [];
1083
+ }
1084
+ const files = [];
1085
+ for (const entry of entries) {
1086
+ const entryPath = path.join(dir, entry.name);
1087
+ if (entry.isDirectory()) files.push(...await findSessionFiles(entryPath, maxDepth - 1));
1088
+ else if (entry.isFile() && entry.name.endsWith(".jsonl")) files.push(entryPath);
1089
+ }
1090
+ return files;
1091
+ }
1092
+ function readSessionMetaCwd(line) {
1093
+ try {
1094
+ const parsed = JSON.parse(line);
1095
+ if (!isRecord(parsed) || parsed.type !== "session_meta" || !isRecord(parsed.payload)) return;
1096
+ return typeof parsed.payload.cwd === "string" && parsed.payload.cwd.trim() ? parsed.payload.cwd.trim() : void 0;
1097
+ } catch {
1098
+ return;
1099
+ }
1100
+ }
1101
+ function readResponseItemMessageText(parsed) {
1102
+ if (parsed.type !== "response_item" || !isRecord(parsed.payload)) return;
1103
+ if (parsed.payload.type !== "message") return;
1104
+ if ((typeof parsed.payload.role === "string" ? parsed.payload.role : "") !== "user") return;
1105
+ const parts = (Array.isArray(parsed.payload.content) ? parsed.payload.content : []).flatMap((entry) => {
1106
+ if (!isRecord(entry)) return [];
1107
+ const text = typeof entry.text === "string" ? entry.text : typeof entry.input_text === "string" ? entry.input_text : void 0;
1108
+ return text?.trim() ? [text.trim()] : [];
1109
+ });
1110
+ return parts.length > 0 ? parts.join(" ") : void 0;
1111
+ }
1112
+ function readSessionIdFromFilename(file) {
1113
+ return path.basename(file).match(/[0-9a-f]{8}-[0-9a-f-]{27,}/iu)?.[0];
1114
+ }
1115
+ async function resolveCodexCliNode(params) {
1116
+ const list = await params.runtime.nodes.list(params.requestedNode ? void 0 : { connected: true });
1117
+ const requested = params.requestedNode?.trim();
1118
+ const candidates = list.nodes.filter((node) => {
1119
+ if (requested) return [
1120
+ node.nodeId,
1121
+ node.displayName,
1122
+ node.remoteIp
1123
+ ].some((value) => value === requested);
1124
+ return node.connected === true && node.commands?.includes(params.command);
1125
+ });
1126
+ if (candidates.length === 0) throw new Error(requested ? `Codex CLI node ${requested} was not found.` : "No connected node exposes Codex CLI session commands.");
1127
+ const usable = candidates.filter((node) => node.commands?.includes(params.command));
1128
+ if (usable.length === 0) throw new Error(`Node ${requested ?? "candidate"} does not expose ${params.command}.`);
1129
+ if (usable.length > 1) throw new Error("Multiple Codex CLI-capable nodes connected. Pass --host <node-id>.");
1130
+ return usable[0];
1131
+ }
1132
+ function parseCodexCliSessionsListResult(raw) {
1133
+ const payload = unwrapNodeInvokePayload(raw);
1134
+ if (!isRecord(payload) || !Array.isArray(payload.sessions)) throw new Error("Codex CLI session list returned an invalid payload.");
1135
+ return {
1136
+ codexHome: typeof payload.codexHome === "string" ? payload.codexHome : "",
1137
+ sessions: payload.sessions.flatMap((entry) => {
1138
+ if (!isRecord(entry) || typeof entry.sessionId !== "string") return [];
1139
+ return [{
1140
+ sessionId: entry.sessionId,
1141
+ updatedAt: typeof entry.updatedAt === "string" ? entry.updatedAt : void 0,
1142
+ lastMessage: typeof entry.lastMessage === "string" ? entry.lastMessage : void 0,
1143
+ cwd: typeof entry.cwd === "string" ? entry.cwd : void 0,
1144
+ sessionFile: typeof entry.sessionFile === "string" ? entry.sessionFile : void 0,
1145
+ messageCount: typeof entry.messageCount === "number" && Number.isFinite(entry.messageCount) ? entry.messageCount : 0
1146
+ }];
1147
+ })
1148
+ };
1149
+ }
1150
+ function unwrapNodeInvokePayload(raw) {
1151
+ const record = isRecord(raw) ? raw : {};
1152
+ if (typeof record.payloadJSON === "string" && record.payloadJSON.trim()) try {
1153
+ return JSON.parse(record.payloadJSON);
1154
+ } catch (error) {
1155
+ throw new Error("Codex CLI node command returned malformed payloadJSON.", { cause: error });
1156
+ }
1157
+ if ("payload" in record) return record.payload;
1158
+ return raw;
1159
+ }
1160
+ function readRecordParam(paramsJSON) {
1161
+ if (!paramsJSON?.trim()) return {};
1162
+ try {
1163
+ const parsed = JSON.parse(paramsJSON);
1164
+ return isRecord(parsed) ? parsed : {};
1165
+ } catch {
1166
+ return {};
1167
+ }
1168
+ }
1169
+ function resolveCodexHome() {
1170
+ return process.env.CODEX_HOME?.trim() || path.join(os.homedir(), ".codex");
1171
+ }
1172
+ async function readFileIfExists(file) {
1173
+ try {
1174
+ return await fs.readFile(file, "utf8");
1175
+ } catch {
1176
+ return;
1177
+ }
1178
+ }
1179
+ async function readFirstLine(file) {
1180
+ return (await readFileIfExists(file))?.split(/\r?\n/u)[0];
1181
+ }
1182
+ async function readFileMtimeIso(file) {
1183
+ try {
1184
+ return (await fs.stat(file)).mtime.toISOString();
1185
+ } catch {
1186
+ return;
1187
+ }
1188
+ }
1189
+ function normalizeLimit(value) {
1190
+ return typeof value === "number" && Number.isFinite(value) ? Math.min(MAX_SESSION_LIMIT, Math.max(1, Math.floor(value))) : DEFAULT_SESSION_LIMIT;
1191
+ }
1192
+ function normalizeTimeoutMs(value) {
1193
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.min(60 * 6e4, Math.floor(value)) : DEFAULT_RESUME_TIMEOUT_MS;
1194
+ }
1195
+ function truncateText(value, max) {
1196
+ return value.length > max ? `${value.slice(0, max - 3)}...` : value;
1197
+ }
1198
+ function compareOptionalStringsDesc(a, b) {
1199
+ return (b ?? "").localeCompare(a ?? "");
1200
+ }
1201
+ function readNodeId(node) {
1202
+ if (!node.nodeId) throw new Error("Codex CLI node did not include a node id.");
1203
+ return node.nodeId;
1204
+ }
1205
+ function formatNodeLabel(node) {
1206
+ return [
1207
+ node.displayName,
1208
+ node.nodeId,
1209
+ node.remoteIp
1210
+ ].filter(Boolean).join(" / ") || "node";
1211
+ }
1212
+ function isRecord(value) {
1213
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
1214
+ }
1215
+ //#endregion
1216
+ export { steerCodexConversationTurn as _, resolveCodexCliSessionForBindingOnNode as a, readCodexConversationBindingData as b, handleCodexConversationInboundClaim as c, parseCodexFastModeArg as d, parseCodexPermissionsModeArg as f, setCodexConversationPermissions as g, setCodexConversationModel as h, listCodexCliSessionsOnNode as i, startCodexConversationThread as l, setCodexConversationFastMode as m, createCodexCliSessionNodeInvokePolicies as n, resumeCodexCliSessionOnNode as o, readCodexConversationActiveTurn as p, formatCodexCliSessions as r, handleCodexConversationBindingResolved as s, createCodexCliSessionNodeHostCommands as t, formatPermissionsMode as u, stopCodexConversationTurn as v, resolveCodexDefaultWorkspaceDir as x, createCodexCliNodeConversationBindingData as y };