@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
@@ -1,303 +0,0 @@
1
- import { CODEX_CONTROL_METHODS } from "./app-server/capabilities.js";
2
- import {
3
- isCodexFastServiceTier,
4
- resolveCodexAppServerRuntimeOptions,
5
- type CodexAppServerApprovalPolicy,
6
- type CodexAppServerSandboxMode,
7
- } from "./app-server/config.js";
8
- import type { CodexServiceTier, CodexThreadResumeResponse } from "./app-server/protocol.js";
9
- import {
10
- readCodexAppServerBinding,
11
- writeCodexAppServerBinding,
12
- } from "./app-server/session-binding.js";
13
- import { getSharedCodexAppServerClient } from "./app-server/shared-client.js";
14
- import { formatCodexDisplayText } from "./command-formatters.js";
15
-
16
- type ActiveTurn = {
17
- sessionFile: string;
18
- threadId: string;
19
- turnId: string;
20
- };
21
-
22
- type CodexAppServerBindingLookup = NonNullable<Parameters<typeof readCodexAppServerBinding>[1]>;
23
-
24
- type PermissionsMode = "default" | "yolo";
25
-
26
- const CODEX_CONVERSATION_CONTROL_STATE = Symbol.for("autobot.codex.conversationControl");
27
-
28
- function getActiveTurns(): Map<string, ActiveTurn> {
29
- const globalState = globalThis as typeof globalThis & {
30
- [CODEX_CONVERSATION_CONTROL_STATE]?: Map<string, ActiveTurn>;
31
- };
32
- globalState[CODEX_CONVERSATION_CONTROL_STATE] ??= new Map();
33
- return globalState[CODEX_CONVERSATION_CONTROL_STATE];
34
- }
35
-
36
- export function trackCodexConversationActiveTurn(active: ActiveTurn): () => void {
37
- const activeTurns = getActiveTurns();
38
- activeTurns.set(active.sessionFile, active);
39
- return () => {
40
- const current = activeTurns.get(active.sessionFile);
41
- if (current?.turnId === active.turnId) {
42
- activeTurns.delete(active.sessionFile);
43
- }
44
- };
45
- }
46
-
47
- export function readCodexConversationActiveTurn(sessionFile: string): ActiveTurn | undefined {
48
- return getActiveTurns().get(sessionFile);
49
- }
50
-
51
- export async function stopCodexConversationTurn(params: {
52
- sessionFile: string;
53
- pluginConfig?: unknown;
54
- agentDir?: string;
55
- config?: CodexAppServerBindingLookup["config"];
56
- }): Promise<{ stopped: boolean; message: string }> {
57
- const active = readCodexConversationActiveTurn(params.sessionFile);
58
- if (!active) {
59
- return { stopped: false, message: "No active Codex run to stop." };
60
- }
61
- const runtime = resolveCodexAppServerRuntimeOptions({ pluginConfig: params.pluginConfig });
62
- const lookup = buildBindingLookup(params);
63
- const binding = await readCodexAppServerBinding(params.sessionFile, lookup);
64
- const client = await getSharedCodexAppServerClient({
65
- startOptions: runtime.start,
66
- timeoutMs: runtime.requestTimeoutMs,
67
- authProfileId: binding?.authProfileId,
68
- ...lookup,
69
- });
70
- await client.request(
71
- "turn/interrupt",
72
- {
73
- threadId: active.threadId,
74
- turnId: active.turnId,
75
- },
76
- { timeoutMs: runtime.requestTimeoutMs },
77
- );
78
- return { stopped: true, message: "Codex stop requested." };
79
- }
80
-
81
- export async function steerCodexConversationTurn(params: {
82
- sessionFile: string;
83
- message: string;
84
- pluginConfig?: unknown;
85
- agentDir?: string;
86
- config?: CodexAppServerBindingLookup["config"];
87
- }): Promise<{ steered: boolean; message: string }> {
88
- const active = readCodexConversationActiveTurn(params.sessionFile);
89
- const text = params.message.trim();
90
- if (!text) {
91
- return { steered: false, message: "Usage: /codex steer <message>" };
92
- }
93
- if (!active) {
94
- return { steered: false, message: "No active Codex run to steer." };
95
- }
96
- const runtime = resolveCodexAppServerRuntimeOptions({ pluginConfig: params.pluginConfig });
97
- const lookup = buildBindingLookup(params);
98
- const binding = await readCodexAppServerBinding(params.sessionFile, lookup);
99
- const client = await getSharedCodexAppServerClient({
100
- startOptions: runtime.start,
101
- timeoutMs: runtime.requestTimeoutMs,
102
- authProfileId: binding?.authProfileId,
103
- ...lookup,
104
- });
105
- await client.request(
106
- "turn/steer",
107
- {
108
- threadId: active.threadId,
109
- expectedTurnId: active.turnId,
110
- input: [{ type: "text", text, text_elements: [] }],
111
- },
112
- { timeoutMs: runtime.requestTimeoutMs },
113
- );
114
- return { steered: true, message: "Sent steer message to Codex." };
115
- }
116
-
117
- export async function setCodexConversationModel(params: {
118
- sessionFile: string;
119
- model: string;
120
- pluginConfig?: unknown;
121
- agentDir?: string;
122
- config?: CodexAppServerBindingLookup["config"];
123
- }): Promise<string> {
124
- const model = params.model.trim();
125
- if (!model) {
126
- return "Usage: /codex model <model>";
127
- }
128
- const lookup = buildBindingLookup(params);
129
- const binding = await requireThreadBinding(params.sessionFile, lookup);
130
- const runtime = resolveCodexAppServerRuntimeOptions({ pluginConfig: params.pluginConfig });
131
- const response = await resumeThreadWithOverrides({
132
- pluginConfig: params.pluginConfig,
133
- threadId: binding.threadId,
134
- authProfileId: binding.authProfileId,
135
- ...lookup,
136
- model,
137
- });
138
- await writeCodexAppServerBinding(
139
- params.sessionFile,
140
- {
141
- ...binding,
142
- cwd: response.thread.cwd ?? binding.cwd,
143
- model: response.model ?? model,
144
- modelProvider: response.modelProvider ?? binding.modelProvider,
145
- approvalPolicy: binding.approvalPolicy,
146
- sandbox: binding.sandbox,
147
- serviceTier: binding.serviceTier ?? runtime.serviceTier,
148
- },
149
- lookup,
150
- );
151
- return `Codex model set to ${formatCodexDisplayText(response.model ?? model)}.`;
152
- }
153
-
154
- export async function setCodexConversationFastMode(params: {
155
- sessionFile: string;
156
- enabled?: boolean;
157
- pluginConfig?: unknown;
158
- agentDir?: string;
159
- config?: CodexAppServerBindingLookup["config"];
160
- }): Promise<string> {
161
- const lookup = buildBindingLookup(params);
162
- const binding = await requireThreadBinding(params.sessionFile, lookup);
163
- if (params.enabled == null) {
164
- return `Codex fast mode: ${isCodexFastServiceTier(binding.serviceTier) ? "on" : "off"}.`;
165
- }
166
- const serviceTier: CodexServiceTier = params.enabled ? "priority" : "flex";
167
- // Fast mode is sent on each later turn; do not require Codex to accept an
168
- // immediate thread/resume control request just to persist the preference.
169
- await writeCodexAppServerBinding(
170
- params.sessionFile,
171
- {
172
- ...binding,
173
- serviceTier,
174
- },
175
- lookup,
176
- );
177
- return `Codex fast mode ${params.enabled ? "enabled" : "disabled"}.`;
178
- }
179
-
180
- export async function setCodexConversationPermissions(params: {
181
- sessionFile: string;
182
- mode?: PermissionsMode;
183
- pluginConfig?: unknown;
184
- agentDir?: string;
185
- config?: CodexAppServerBindingLookup["config"];
186
- }): Promise<string> {
187
- const lookup = buildBindingLookup(params);
188
- const binding = await requireThreadBinding(params.sessionFile, lookup);
189
- if (!params.mode) {
190
- return `Codex permissions: ${formatPermissionsMode(binding)}.`;
191
- }
192
- const policy = permissionsForMode(params.mode);
193
- // Native bound turns pass these settings at turn/start time, so this command
194
- // can update the local binding even when app-server resume overrides fail.
195
- await writeCodexAppServerBinding(
196
- params.sessionFile,
197
- {
198
- ...binding,
199
- approvalPolicy: policy.approvalPolicy,
200
- sandbox: policy.sandbox,
201
- },
202
- lookup,
203
- );
204
- return `Codex permissions set to ${params.mode === "yolo" ? "full access" : "default"}.`;
205
- }
206
-
207
- export function parseCodexFastModeArg(arg: string | undefined): boolean | undefined {
208
- const normalized = arg?.trim().toLowerCase();
209
- if (!normalized || normalized === "status") {
210
- return undefined;
211
- }
212
- if (normalized === "on" || normalized === "true" || normalized === "fast") {
213
- return true;
214
- }
215
- if (normalized === "off" || normalized === "false" || normalized === "flex") {
216
- return false;
217
- }
218
- return undefined;
219
- }
220
-
221
- export function parseCodexPermissionsModeArg(arg: string | undefined): PermissionsMode | undefined {
222
- const normalized = arg?.trim().toLowerCase();
223
- if (!normalized || normalized === "status") {
224
- return undefined;
225
- }
226
- if (normalized === "yolo" || normalized === "full" || normalized === "full-access") {
227
- return "yolo";
228
- }
229
- if (normalized === "default" || normalized === "guardian") {
230
- return "default";
231
- }
232
- return undefined;
233
- }
234
-
235
- export function formatPermissionsMode(binding: {
236
- approvalPolicy?: CodexAppServerApprovalPolicy;
237
- sandbox?: CodexAppServerSandboxMode;
238
- }): string {
239
- return binding.approvalPolicy === "never" && binding.sandbox === "danger-full-access"
240
- ? "full access"
241
- : "default";
242
- }
243
-
244
- async function requireThreadBinding(sessionFile: string, lookup: CodexAppServerBindingLookup = {}) {
245
- const binding = await readCodexAppServerBinding(sessionFile, lookup);
246
- if (!binding?.threadId) {
247
- throw new Error("No Codex thread is attached to this AutoBot session yet.");
248
- }
249
- return binding;
250
- }
251
-
252
- async function resumeThreadWithOverrides(params: {
253
- pluginConfig?: unknown;
254
- threadId: string;
255
- authProfileId?: string;
256
- agentDir?: string;
257
- config?: CodexAppServerBindingLookup["config"];
258
- model?: string;
259
- approvalPolicy?: CodexAppServerApprovalPolicy;
260
- sandbox?: CodexAppServerSandboxMode;
261
- serviceTier?: CodexServiceTier;
262
- }): Promise<CodexThreadResumeResponse> {
263
- const runtime = resolveCodexAppServerRuntimeOptions({ pluginConfig: params.pluginConfig });
264
- const client = await getSharedCodexAppServerClient({
265
- startOptions: runtime.start,
266
- timeoutMs: runtime.requestTimeoutMs,
267
- authProfileId: params.authProfileId,
268
- ...buildBindingLookup(params),
269
- });
270
- return await client.request(
271
- CODEX_CONTROL_METHODS.resumeThread,
272
- {
273
- threadId: params.threadId,
274
- ...(params.model ? { model: params.model } : {}),
275
- approvalPolicy: params.approvalPolicy ?? runtime.approvalPolicy,
276
- sandbox: params.sandbox ?? runtime.sandbox,
277
- approvalsReviewer: runtime.approvalsReviewer,
278
- ...(params.serviceTier ? { serviceTier: params.serviceTier } : {}),
279
- persistExtendedHistory: true,
280
- },
281
- { timeoutMs: runtime.requestTimeoutMs },
282
- );
283
- }
284
-
285
- function buildBindingLookup(params: {
286
- agentDir?: string;
287
- config?: CodexAppServerBindingLookup["config"];
288
- }): CodexAppServerBindingLookup {
289
- const agentDir = params.agentDir?.trim();
290
- return {
291
- ...(agentDir ? { agentDir } : {}),
292
- ...(params.config ? { config: params.config } : {}),
293
- };
294
- }
295
-
296
- function permissionsForMode(mode: PermissionsMode): {
297
- approvalPolicy: CodexAppServerApprovalPolicy;
298
- sandbox: CodexAppServerSandboxMode;
299
- } {
300
- return mode === "yolo"
301
- ? { approvalPolicy: "never", sandbox: "danger-full-access" }
302
- : { approvalPolicy: "on-request", sandbox: "workspace-write" };
303
- }
@@ -1,186 +0,0 @@
1
- import {
2
- isJsonObject,
3
- type CodexServerNotification,
4
- type JsonObject,
5
- } from "./app-server/protocol.js";
6
-
7
- const MAX_PENDING_NOTIFICATIONS_PER_TURN = 100;
8
-
9
- export function createCodexConversationTurnCollector(threadId: string) {
10
- let turnId: string | undefined;
11
- let completed = false;
12
- let failedError: string | undefined;
13
- let timeout: ReturnType<typeof setTimeout> | undefined;
14
- const assistantTextByItem = new Map<string, string>();
15
- const assistantOrder: string[] = [];
16
- const pendingNotificationsByTurnId = new Map<string, CodexServerNotification[]>();
17
- let resolveCompletion: ((value: { replyText: string }) => void) | undefined;
18
- let rejectCompletion: ((error: Error) => void) | undefined;
19
-
20
- const rememberItem = (itemId: string) => {
21
- if (!assistantOrder.includes(itemId)) {
22
- assistantOrder.push(itemId);
23
- }
24
- };
25
- const collectReplyText = (): string => {
26
- const texts = assistantOrder
27
- .map((itemId) => assistantTextByItem.get(itemId)?.trim())
28
- .filter((text): text is string => Boolean(text));
29
- return texts.at(-1) ?? "";
30
- };
31
- const clearWaitState = () => {
32
- if (timeout) {
33
- clearTimeout(timeout);
34
- timeout = undefined;
35
- }
36
- resolveCompletion = undefined;
37
- rejectCompletion = undefined;
38
- };
39
- const finish = () => {
40
- if (completed) {
41
- return;
42
- }
43
- completed = true;
44
- if (failedError) {
45
- rejectCompletion?.(new Error(failedError));
46
- } else {
47
- resolveCompletion?.({ replyText: collectReplyText() });
48
- }
49
- clearWaitState();
50
- };
51
-
52
- const handleNotification = (notification: CodexServerNotification) => {
53
- const params = isJsonObject(notification.params) ? notification.params : undefined;
54
- if (!params || readString(params, "threadId") !== threadId) {
55
- return;
56
- }
57
- if (!turnId) {
58
- const pendingTurnId = readNotificationTurnId(params);
59
- if (pendingTurnId) {
60
- const pending = pendingNotificationsByTurnId.get(pendingTurnId) ?? [];
61
- if (pending.length < MAX_PENDING_NOTIFICATIONS_PER_TURN) {
62
- pending.push(notification);
63
- pendingNotificationsByTurnId.set(pendingTurnId, pending);
64
- }
65
- }
66
- return;
67
- }
68
- if (!isNotificationForTurn(params, threadId, turnId)) {
69
- return;
70
- }
71
- if (notification.method === "item/agentMessage/delta") {
72
- const itemId = readString(params, "itemId") ?? readString(params, "id") ?? "assistant";
73
- const delta = readTextString(params, "delta");
74
- if (!delta) {
75
- return;
76
- }
77
- rememberItem(itemId);
78
- assistantTextByItem.set(itemId, `${assistantTextByItem.get(itemId) ?? ""}${delta}`);
79
- return;
80
- }
81
- if (notification.method === "item/completed") {
82
- const item = isJsonObject(params.item) ? params.item : undefined;
83
- if (item?.type === "agentMessage") {
84
- const itemId = readString(item, "id") ?? readString(params, "itemId") ?? "assistant";
85
- const text = readTextString(item, "text");
86
- if (text) {
87
- rememberItem(itemId);
88
- assistantTextByItem.set(itemId, text);
89
- }
90
- }
91
- return;
92
- }
93
- if (notification.method === "turn/completed") {
94
- const turn = isJsonObject(params.turn) ? params.turn : undefined;
95
- const status = readString(turn, "status");
96
- if (status === "failed") {
97
- failedError =
98
- readString(readRecord(turn?.error), "message") ?? "codex app-server turn failed";
99
- }
100
- const items = Array.isArray(turn?.items) ? turn.items : [];
101
- for (const item of items) {
102
- if (!isJsonObject(item) || item.type !== "agentMessage") {
103
- continue;
104
- }
105
- const itemId = readString(item, "id") ?? `assistant-${assistantOrder.length + 1}`;
106
- const text = readTextString(item, "text");
107
- if (text) {
108
- rememberItem(itemId);
109
- assistantTextByItem.set(itemId, text);
110
- }
111
- }
112
- finish();
113
- }
114
- };
115
-
116
- return {
117
- setTurnId(nextTurnId: string) {
118
- turnId = nextTurnId;
119
- const pending = pendingNotificationsByTurnId.get(nextTurnId) ?? [];
120
- pendingNotificationsByTurnId.clear();
121
- for (const notification of pending) {
122
- handleNotification(notification);
123
- }
124
- },
125
- handleNotification,
126
- wait(params: { timeoutMs: number }): Promise<{ replyText: string }> {
127
- if (completed) {
128
- return failedError
129
- ? Promise.reject(new Error(failedError))
130
- : Promise.resolve({ replyText: collectReplyText() });
131
- }
132
- return new Promise<{ replyText: string }>((resolve, reject) => {
133
- resolveCompletion = resolve;
134
- rejectCompletion = reject;
135
- timeout = setTimeout(
136
- () => {
137
- completed = true;
138
- reject(new Error("codex app-server bound turn timed out"));
139
- clearWaitState();
140
- },
141
- Math.max(100, params.timeoutMs),
142
- );
143
- timeout.unref?.();
144
- });
145
- },
146
- };
147
- }
148
-
149
- function isNotificationForTurn(
150
- params: JsonObject,
151
- threadId: string,
152
- turnId: string | undefined,
153
- ): boolean {
154
- if (readString(params, "threadId") !== threadId) {
155
- return false;
156
- }
157
- if (!turnId) {
158
- return true;
159
- }
160
- const directTurnId = readString(params, "turnId");
161
- if (directTurnId) {
162
- return directTurnId === turnId;
163
- }
164
- const turn = isJsonObject(params.turn) ? params.turn : undefined;
165
- return readString(turn, "id") === turnId;
166
- }
167
-
168
- function readNotificationTurnId(params: JsonObject): string | undefined {
169
- return readString(params, "turnId") ?? readString(readRecord(params.turn), "id");
170
- }
171
-
172
- function readRecord(value: unknown): Record<string, unknown> | undefined {
173
- return value && typeof value === "object" && !Array.isArray(value)
174
- ? (value as Record<string, unknown>)
175
- : undefined;
176
- }
177
-
178
- function readString(record: Record<string, unknown> | JsonObject | undefined, key: string) {
179
- const value = record?.[key];
180
- return typeof value === "string" && value.trim() ? value.trim() : undefined;
181
- }
182
-
183
- function readTextString(record: Record<string, unknown> | JsonObject | undefined, key: string) {
184
- const value = record?.[key];
185
- return typeof value === "string" && value.length > 0 ? value : undefined;
186
- }
@@ -1,106 +0,0 @@
1
- import path from "node:path";
2
- import { fileURLToPath } from "node:url";
3
- import type { PluginHookInboundClaimEvent } from "autobot/plugin-sdk/plugin-entry";
4
- import type { CodexUserInput } from "./app-server/protocol.js";
5
-
6
- type InboundMedia = {
7
- path?: string;
8
- url?: string;
9
- mimeType?: string;
10
- };
11
-
12
- const IMAGE_EXTENSIONS = new Set([".avif", ".gif", ".jpeg", ".jpg", ".png", ".webp"]);
13
-
14
- export function buildCodexConversationTurnInput(params: {
15
- prompt: string;
16
- event: PluginHookInboundClaimEvent;
17
- }): CodexUserInput[] {
18
- return [
19
- { type: "text", text: params.prompt, text_elements: [] },
20
- ...extractInboundMedia(params.event)
21
- .map(toCodexImageInput)
22
- .filter((item): item is CodexUserInput => item !== undefined),
23
- ];
24
- }
25
-
26
- function extractInboundMedia(event: PluginHookInboundClaimEvent): InboundMedia[] {
27
- const metadata = event.metadata ?? {};
28
- // AutoBot channels expose either local staged files or remote URLs. Keep
29
- // them separate so Codex can receive the cheaper localImage input when a file
30
- // is already present, while still supporting remote-only transports.
31
- const paths = readStringArray(metadata.mediaPaths).concat(readStringArray(metadata.mediaPath));
32
- const urls = readStringArray(metadata.mediaUrls).concat(readStringArray(metadata.mediaUrl));
33
- const mimeTypes = readStringArray(metadata.mediaTypes).concat(
34
- readStringArray(metadata.mediaType),
35
- );
36
- const count = Math.max(paths.length, urls.length, mimeTypes.length);
37
- const media: InboundMedia[] = [];
38
- for (let index = 0; index < count; index += 1) {
39
- media.push({
40
- path: paths[index],
41
- url: urls[index],
42
- mimeType: mimeTypes[index] ?? mimeTypes[0],
43
- });
44
- }
45
- return media;
46
- }
47
-
48
- function toCodexImageInput(media: InboundMedia): CodexUserInput | undefined {
49
- if (!isImageMedia(media)) {
50
- return undefined;
51
- }
52
- const localPath = media.path ?? readLocalMediaPath(media.url);
53
- if (localPath) {
54
- const normalized = normalizeFileUrl(localPath);
55
- return normalized ? { type: "localImage", path: normalized } : undefined;
56
- }
57
- return media.url ? { type: "image", url: media.url } : undefined;
58
- }
59
-
60
- function isImageMedia(media: InboundMedia): boolean {
61
- if (media.mimeType?.toLowerCase().startsWith("image/")) {
62
- return true;
63
- }
64
- const candidate = media.path ?? media.url;
65
- if (!candidate) {
66
- return false;
67
- }
68
- return IMAGE_EXTENSIONS.has(path.extname(candidate.split(/[?#]/, 1)[0] ?? "").toLowerCase());
69
- }
70
-
71
- function normalizeFileUrl(value: string): string | undefined {
72
- if (!value.startsWith("file://")) {
73
- return value;
74
- }
75
- try {
76
- return fileURLToPath(value);
77
- } catch {
78
- return undefined;
79
- }
80
- }
81
-
82
- function readLocalMediaPath(value: string | undefined): string | undefined {
83
- if (!value) {
84
- return undefined;
85
- }
86
- if (value.startsWith("file://")) {
87
- return value;
88
- }
89
- if (value.startsWith("//")) {
90
- return undefined;
91
- }
92
- if (path.isAbsolute(value) || path.win32.isAbsolute(value)) {
93
- return value;
94
- }
95
- return /^[a-z][a-z0-9+.-]*:/i.test(value) ? undefined : value;
96
- }
97
-
98
- function readStringArray(value: unknown): string[] {
99
- if (typeof value === "string" && value.trim()) {
100
- return [value.trim()];
101
- }
102
- if (!Array.isArray(value)) {
103
- return [];
104
- }
105
- return value.map((entry) => (typeof entry === "string" ? entry.trim() : "")).filter(Boolean);
106
- }