@ai-sdk/harness-codex 1.0.134 → 1.0.136
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/CHANGELOG.md +21 -0
- package/dist/bridge/index.mjs +1983 -2194
- package/dist/bridge/index.mjs.map +1 -1
- package/dist/index.d.ts +1478 -1476
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1103 -1371
- package/dist/index.js.map +1 -1
- package/package.json +8 -7
- package/src/codex-harness.ts +5 -4
- package/src/index.ts +1 -0
package/dist/bridge/index.mjs
CHANGED
|
@@ -1,2300 +1,2089 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { env as procEnv, pid, stdout } from "process";
|
|
1
|
+
import { appendFile, mkdir, writeFile } from "node:fs/promises";
|
|
2
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
3
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
4
|
+
import { argv, env, pid, stdout } from "node:process";
|
|
6
5
|
import { WebSocketServer } from "ws";
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
import { spawn } from "node:child_process";
|
|
8
|
+
import { isDeepStrictEqual } from "node:util";
|
|
9
|
+
import path from "node:path";
|
|
10
|
+
//#region ../harness/dist/bridge/index.js
|
|
11
|
+
const DEBUG_LEVEL_WEIGHT = {
|
|
12
|
+
error: 0,
|
|
13
|
+
warn: 1,
|
|
14
|
+
info: 2,
|
|
15
|
+
debug: 3,
|
|
16
|
+
trace: 4
|
|
13
17
|
};
|
|
18
|
+
/** Exact-or-dotted-prefix subsystem match (`'bridge'` matches `'bridge.turn'`). */
|
|
14
19
|
function subsystemMatches(filters, subsystem) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
(filter) => subsystem === filter || subsystem.startsWith(`${filter}.`)
|
|
18
|
-
);
|
|
20
|
+
if (!filters || filters.length === 0) return true;
|
|
21
|
+
return filters.some((filter) => subsystem === filter || subsystem.startsWith(`${filter}.`));
|
|
19
22
|
}
|
|
20
23
|
function formatBridgeError(err) {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
return { message: String(err) };
|
|
24
|
+
if (err instanceof Error) return {
|
|
25
|
+
name: err.name,
|
|
26
|
+
message: err.message,
|
|
27
|
+
stack: err.stack
|
|
28
|
+
};
|
|
29
|
+
if (typeof err === "string") return { message: err };
|
|
30
|
+
if (err !== null && typeof err === "object") try {
|
|
31
|
+
return { message: JSON.stringify(err) };
|
|
32
|
+
} catch {}
|
|
33
|
+
return { message: String(err) };
|
|
34
34
|
}
|
|
35
35
|
function createBridgeUserMessageQueue(options) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
value: void 0
|
|
123
|
-
});
|
|
124
|
-
}
|
|
125
|
-
return new Promise(
|
|
126
|
-
(resolve) => {
|
|
127
|
-
waiters.push(resolve);
|
|
128
|
-
}
|
|
129
|
-
);
|
|
130
|
-
}
|
|
131
|
-
};
|
|
132
|
-
}
|
|
133
|
-
};
|
|
36
|
+
const messages = [];
|
|
37
|
+
const waiters = [];
|
|
38
|
+
const entries = /* @__PURE__ */ new Map();
|
|
39
|
+
let closed = false;
|
|
40
|
+
let pendingCount = 0;
|
|
41
|
+
const enqueue = (input) => {
|
|
42
|
+
const existing = entries.get(input.messageId);
|
|
43
|
+
if (existing != null) {
|
|
44
|
+
if (existing.response != null) options.respond(existing.response);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
let settled = false;
|
|
48
|
+
const settle = (response) => {
|
|
49
|
+
if (settled) return;
|
|
50
|
+
settled = true;
|
|
51
|
+
pendingCount--;
|
|
52
|
+
const entry = entries.get(input.messageId);
|
|
53
|
+
if (entry != null) entry.response = response;
|
|
54
|
+
options.respond(response);
|
|
55
|
+
};
|
|
56
|
+
const message = {
|
|
57
|
+
messageId: input.messageId,
|
|
58
|
+
text: input.text,
|
|
59
|
+
accept: () => {
|
|
60
|
+
settle({
|
|
61
|
+
type: "user-message-response",
|
|
62
|
+
messageId: input.messageId,
|
|
63
|
+
accepted: true
|
|
64
|
+
});
|
|
65
|
+
},
|
|
66
|
+
reject: (error) => {
|
|
67
|
+
settle({
|
|
68
|
+
type: "user-message-response",
|
|
69
|
+
messageId: input.messageId,
|
|
70
|
+
accepted: false,
|
|
71
|
+
error: { message: formatBridgeError(error).message }
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
entries.set(input.messageId, { reject: message.reject });
|
|
76
|
+
pendingCount++;
|
|
77
|
+
if (closed) {
|
|
78
|
+
message.reject(/* @__PURE__ */ new Error("The bridge turn is no longer accepting user messages."));
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const waiter = waiters.shift();
|
|
82
|
+
if (waiter != null) waiter({
|
|
83
|
+
done: false,
|
|
84
|
+
value: message
|
|
85
|
+
});
|
|
86
|
+
else messages.push(message);
|
|
87
|
+
};
|
|
88
|
+
const close = (error) => {
|
|
89
|
+
if (closed) return;
|
|
90
|
+
closed = true;
|
|
91
|
+
const reason = error ?? /* @__PURE__ */ new Error("The bridge turn ended before accepting the user message.");
|
|
92
|
+
for (const entry of entries.values()) if (entry.response == null) entry.reject(reason);
|
|
93
|
+
messages.length = 0;
|
|
94
|
+
while (waiters.length > 0) waiters.shift()({
|
|
95
|
+
done: true,
|
|
96
|
+
value: void 0
|
|
97
|
+
});
|
|
98
|
+
};
|
|
99
|
+
return {
|
|
100
|
+
get pendingCount() {
|
|
101
|
+
return pendingCount;
|
|
102
|
+
},
|
|
103
|
+
enqueue,
|
|
104
|
+
close,
|
|
105
|
+
[Symbol.asyncIterator]() {
|
|
106
|
+
return { next: () => {
|
|
107
|
+
const message = messages.shift();
|
|
108
|
+
if (message != null) return Promise.resolve({
|
|
109
|
+
done: false,
|
|
110
|
+
value: message
|
|
111
|
+
});
|
|
112
|
+
if (closed) return Promise.resolve({
|
|
113
|
+
done: true,
|
|
114
|
+
value: void 0
|
|
115
|
+
});
|
|
116
|
+
return new Promise((resolve) => {
|
|
117
|
+
waiters.push(resolve);
|
|
118
|
+
});
|
|
119
|
+
} };
|
|
120
|
+
}
|
|
121
|
+
};
|
|
134
122
|
}
|
|
135
123
|
function parseEnvList(value) {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
124
|
+
if (!value) return void 0;
|
|
125
|
+
const items = value.split(",").map((item) => item.trim()).filter(Boolean);
|
|
126
|
+
return items.length > 0 ? items : void 0;
|
|
139
127
|
}
|
|
140
|
-
|
|
141
|
-
|
|
128
|
+
const ENV_TRUTHY = /* @__PURE__ */ new Set([
|
|
129
|
+
"1",
|
|
130
|
+
"true",
|
|
131
|
+
"yes",
|
|
132
|
+
"on"
|
|
133
|
+
]);
|
|
134
|
+
const WS_OPEN = 1;
|
|
142
135
|
async function runBridge(options) {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
);
|
|
574
|
-
options.onListening?.(currentBoundPort);
|
|
575
|
-
});
|
|
576
|
-
wss.on("connection", (ws, req) => {
|
|
577
|
-
const url = new URL(req.url ?? "/", "http://localhost");
|
|
578
|
-
if (url.searchParams.get("agent_bridge_token") !== expectedToken) {
|
|
579
|
-
ws.close(1008, "unauthorized");
|
|
580
|
-
return;
|
|
581
|
-
}
|
|
582
|
-
sendControl(ws, {
|
|
583
|
-
type: "bridge-hello",
|
|
584
|
-
state: currentTurnState,
|
|
585
|
-
lastSeq: seqCounter,
|
|
586
|
-
capabilities: { experimental_userMessageResponses: true }
|
|
587
|
-
});
|
|
588
|
-
ws.on("message", (raw) => {
|
|
589
|
-
let parsed;
|
|
590
|
-
try {
|
|
591
|
-
const text = typeof raw === "string" ? raw : Buffer.from(raw).toString("utf8");
|
|
592
|
-
parsed = JSON.parse(text);
|
|
593
|
-
} catch (err) {
|
|
594
|
-
sendControl(ws, {
|
|
595
|
-
type: "error",
|
|
596
|
-
error: `protocol parse error: ${err.message}`
|
|
597
|
-
});
|
|
598
|
-
return;
|
|
599
|
-
}
|
|
600
|
-
void handleInbound(parsed, ws);
|
|
601
|
-
});
|
|
602
|
-
ws.on("close", () => {
|
|
603
|
-
if (activeSocket === ws) {
|
|
604
|
-
activeSocket = void 0;
|
|
605
|
-
}
|
|
606
|
-
});
|
|
607
|
-
ws.on("error", () => {
|
|
608
|
-
});
|
|
609
|
-
});
|
|
610
|
-
process.on("uncaughtException", (err) => {
|
|
611
|
-
emitError({ error: err, message: "uncaught exception" });
|
|
612
|
-
});
|
|
613
|
-
process.on("unhandledRejection", (err) => {
|
|
614
|
-
emitError({ error: err, message: "unhandled rejection" });
|
|
615
|
-
});
|
|
616
|
-
await new Promise((resolve, reject) => {
|
|
617
|
-
if (wss.address() != null) {
|
|
618
|
-
resolve();
|
|
619
|
-
return;
|
|
620
|
-
}
|
|
621
|
-
wss.once("listening", resolve);
|
|
622
|
-
wss.once("error", reject);
|
|
623
|
-
});
|
|
624
|
-
return {
|
|
625
|
-
port: currentBoundPort,
|
|
626
|
-
close: () => new Promise((resolve) => {
|
|
627
|
-
wss.close(() => resolve());
|
|
628
|
-
})
|
|
629
|
-
};
|
|
136
|
+
const { bridgeType, bridgeStateDir, onStart, onStop, onDestroy } = options;
|
|
137
|
+
const teardownGraceMs = options.turnTeardownGraceMs ?? 1e4;
|
|
138
|
+
const expectedToken = options.token ?? env.BRIDGE_CHANNEL_TOKEN ?? "";
|
|
139
|
+
const bridgeWsPort = options.port ?? parseInt(env.BRIDGE_WS_PORT ?? "0", 10);
|
|
140
|
+
const bridgeMetaPath = `${bridgeStateDir}/bridge-meta.json`;
|
|
141
|
+
const startConfigPath = `${bridgeStateDir}/start-config.json`;
|
|
142
|
+
const rerunStartConfigPath = `${bridgeStateDir}/rerun-start-config.json`;
|
|
143
|
+
const eventLogPath = `${bridgeStateDir}/event-log.ndjson`;
|
|
144
|
+
try {
|
|
145
|
+
await mkdir(bridgeStateDir, { recursive: true });
|
|
146
|
+
} catch {}
|
|
147
|
+
let currentBoundPort = 0;
|
|
148
|
+
let currentTurnState = "init";
|
|
149
|
+
let activeSocket;
|
|
150
|
+
let isFirstTurn = true;
|
|
151
|
+
let turnAbort;
|
|
152
|
+
let currentUserMessages;
|
|
153
|
+
/**
|
|
154
|
+
* Settles when the in-flight turn has fully wound down — `onStart`
|
|
155
|
+
* returned or threw AND its completion state was recorded. `undefined`
|
|
156
|
+
* between turns. A new `start` fences on this so turns never overlap.
|
|
157
|
+
*/
|
|
158
|
+
let activeTurn;
|
|
159
|
+
let debugConfig;
|
|
160
|
+
let consoleCaptureInstalled = false;
|
|
161
|
+
const envDebugEnabled = ENV_TRUTHY.has((env.HARNESS_DEBUG ?? "").toLowerCase());
|
|
162
|
+
let seqCounter = 0;
|
|
163
|
+
let eventLog = [];
|
|
164
|
+
let diskBuffer = "";
|
|
165
|
+
let flushPromise = null;
|
|
166
|
+
const flushEventsToDisk = async () => {
|
|
167
|
+
while (diskBuffer.length > 0) {
|
|
168
|
+
const buf = diskBuffer;
|
|
169
|
+
diskBuffer = "";
|
|
170
|
+
await appendFile(eventLogPath, buf).catch(() => {});
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
const scheduleEventFlush = () => {
|
|
174
|
+
if (flushPromise) return;
|
|
175
|
+
flushPromise = new Promise((resolve) => {
|
|
176
|
+
setImmediate(() => {
|
|
177
|
+
flushEventsToDisk().finally(resolve);
|
|
178
|
+
});
|
|
179
|
+
}).finally(() => {
|
|
180
|
+
flushPromise = null;
|
|
181
|
+
if (diskBuffer.length > 0) scheduleEventFlush();
|
|
182
|
+
});
|
|
183
|
+
};
|
|
184
|
+
const flushPendingEventsToDisk = async () => {
|
|
185
|
+
if (diskBuffer.length > 0 && !flushPromise) scheduleEventFlush();
|
|
186
|
+
let inFlight = flushPromise;
|
|
187
|
+
while (inFlight) {
|
|
188
|
+
await inFlight;
|
|
189
|
+
inFlight = flushPromise;
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
if (env.BRIDGE_REPLAY_FROM_DISK === "1" && existsSync(eventLogPath)) try {
|
|
193
|
+
eventLog = readFileSync(eventLogPath, "utf8").split("\n").map((line) => line.trim()).filter(Boolean).map((line) => ({
|
|
194
|
+
seq: JSON.parse(line).seq,
|
|
195
|
+
line
|
|
196
|
+
}));
|
|
197
|
+
seqCounter = eventLog.at(-1)?.seq ?? 0;
|
|
198
|
+
} catch {
|
|
199
|
+
eventLog = [];
|
|
200
|
+
seqCounter = 0;
|
|
201
|
+
}
|
|
202
|
+
const pendingToolResults = /* @__PURE__ */ new Map();
|
|
203
|
+
const bufferedToolResults = [];
|
|
204
|
+
const pendingToolApprovals = /* @__PURE__ */ new Map();
|
|
205
|
+
const writeBridgeMeta = async (state) => {
|
|
206
|
+
try {
|
|
207
|
+
await writeFile(bridgeMetaPath, JSON.stringify({
|
|
208
|
+
type: bridgeType,
|
|
209
|
+
port: currentBoundPort,
|
|
210
|
+
state,
|
|
211
|
+
pid
|
|
212
|
+
}));
|
|
213
|
+
} catch {}
|
|
214
|
+
};
|
|
215
|
+
const writeStartConfig = async (start) => {
|
|
216
|
+
try {
|
|
217
|
+
const serialized = JSON.stringify(start);
|
|
218
|
+
await writeFile(startConfigPath, serialized);
|
|
219
|
+
if (!existsSync(rerunStartConfigPath)) await writeFile(rerunStartConfigPath, serialized);
|
|
220
|
+
} catch {}
|
|
221
|
+
};
|
|
222
|
+
const emit = (event) => {
|
|
223
|
+
const seq = ++seqCounter;
|
|
224
|
+
const line = JSON.stringify({
|
|
225
|
+
...event,
|
|
226
|
+
seq
|
|
227
|
+
});
|
|
228
|
+
eventLog.push({
|
|
229
|
+
seq,
|
|
230
|
+
line
|
|
231
|
+
});
|
|
232
|
+
diskBuffer += `${line}\n`;
|
|
233
|
+
scheduleEventFlush();
|
|
234
|
+
if (activeSocket?.readyState === WS_OPEN) try {
|
|
235
|
+
activeSocket.send(line);
|
|
236
|
+
} catch {}
|
|
237
|
+
};
|
|
238
|
+
const replay = (ws, afterSeq) => {
|
|
239
|
+
for (const entry of eventLog) if (entry.seq > afterSeq && ws.readyState === WS_OPEN) ws.send(entry.line);
|
|
240
|
+
};
|
|
241
|
+
const shouldEmitDebugEvent = (level, subsystem) => {
|
|
242
|
+
if (!debugConfig?.enabled) return false;
|
|
243
|
+
const threshold = debugConfig.level ?? "debug";
|
|
244
|
+
if (DEBUG_LEVEL_WEIGHT[level] > DEBUG_LEVEL_WEIGHT[threshold]) return false;
|
|
245
|
+
return subsystemMatches(debugConfig.subsystems, subsystem);
|
|
246
|
+
};
|
|
247
|
+
const rawStdoutWrite = process.stdout.write.bind(process.stdout);
|
|
248
|
+
const rawStderrWrite = process.stderr.write.bind(process.stderr);
|
|
249
|
+
const writeErrorToStderr = (input) => {
|
|
250
|
+
try {
|
|
251
|
+
const formatted = formatBridgeError(input.error);
|
|
252
|
+
rawStderrWrite(`[harness:${bridgeType}:error] ${input.message}: ${formatted.message}\n`);
|
|
253
|
+
if (formatted.stack) rawStderrWrite(`${formatted.stack}\n`);
|
|
254
|
+
} catch {}
|
|
255
|
+
};
|
|
256
|
+
const emitWarning = (input) => {
|
|
257
|
+
try {
|
|
258
|
+
for (const line of input.message.split("\n")) if (line.trim().length > 0) rawStderrWrite(`[harness:${bridgeType}:warn] ${line}\n`);
|
|
259
|
+
} catch {}
|
|
260
|
+
};
|
|
261
|
+
const emitError = (input) => {
|
|
262
|
+
writeErrorToStderr({
|
|
263
|
+
message: input.message ?? "bridge error",
|
|
264
|
+
error: input.error
|
|
265
|
+
});
|
|
266
|
+
emit({
|
|
267
|
+
type: "error",
|
|
268
|
+
error: serialiseError(input.error)
|
|
269
|
+
});
|
|
270
|
+
};
|
|
271
|
+
const installConsoleCapture = () => {
|
|
272
|
+
if (consoleCaptureInstalled) return;
|
|
273
|
+
consoleCaptureInstalled = true;
|
|
274
|
+
const buffers = {
|
|
275
|
+
stdout: "",
|
|
276
|
+
stderr: ""
|
|
277
|
+
};
|
|
278
|
+
const patch = (stream, raw) => (chunk, encoding, cb) => {
|
|
279
|
+
if (debugConfig?.enabled) try {
|
|
280
|
+
const enc = typeof encoding === "string" ? encoding : "utf8";
|
|
281
|
+
const text = typeof chunk === "string" ? chunk : Buffer.from(chunk).toString(enc);
|
|
282
|
+
const parts = (buffers[stream] + text.replace(/\r\n/g, "\n")).split("\n");
|
|
283
|
+
buffers[stream] = parts.pop() ?? "";
|
|
284
|
+
for (const line of parts) {
|
|
285
|
+
const trimmed = line.replace(/\s+$/, "");
|
|
286
|
+
if (trimmed) emit({
|
|
287
|
+
type: "sandbox-log",
|
|
288
|
+
source: bridgeType,
|
|
289
|
+
stream,
|
|
290
|
+
line: trimmed
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
} catch {}
|
|
294
|
+
return raw(chunk, encoding, cb);
|
|
295
|
+
};
|
|
296
|
+
process.stdout.write = patch("stdout", rawStdoutWrite);
|
|
297
|
+
process.stderr.write = patch("stderr", rawStderrWrite);
|
|
298
|
+
};
|
|
299
|
+
const handleInbound = async (msg, ws) => {
|
|
300
|
+
switch (msg.type) {
|
|
301
|
+
case "start": {
|
|
302
|
+
for (;;) {
|
|
303
|
+
const pendingTurn = activeTurn;
|
|
304
|
+
if (pendingTurn == null) break;
|
|
305
|
+
turnAbort?.abort();
|
|
306
|
+
currentUserMessages?.close(/* @__PURE__ */ new Error("A new bridge turn replaced the active turn."));
|
|
307
|
+
let graceTimer;
|
|
308
|
+
const settled = await Promise.race([pendingTurn.then(() => true), new Promise((resolve) => {
|
|
309
|
+
graceTimer = setTimeout(() => resolve(false), teardownGraceMs);
|
|
310
|
+
graceTimer.unref?.();
|
|
311
|
+
})]);
|
|
312
|
+
clearTimeout(graceTimer);
|
|
313
|
+
if (!settled) break;
|
|
314
|
+
}
|
|
315
|
+
let turnFinished;
|
|
316
|
+
const thisTurn = new Promise((resolve) => turnFinished = resolve);
|
|
317
|
+
activeTurn = thisTurn;
|
|
318
|
+
activeSocket = ws;
|
|
319
|
+
const firstTurn = isFirstTurn;
|
|
320
|
+
isFirstTurn = false;
|
|
321
|
+
eventLog = [];
|
|
322
|
+
diskBuffer = "";
|
|
323
|
+
writeFile(eventLogPath, "").catch(() => {});
|
|
324
|
+
turnAbort = new AbortController();
|
|
325
|
+
currentTurnState = "running";
|
|
326
|
+
writeStartConfig(msg);
|
|
327
|
+
writeBridgeMeta("running");
|
|
328
|
+
const startDebug = msg.debug;
|
|
329
|
+
debugConfig = {
|
|
330
|
+
enabled: startDebug?.enabled ?? envDebugEnabled,
|
|
331
|
+
level: startDebug?.level ?? env.HARNESS_DEBUG_LEVEL,
|
|
332
|
+
subsystems: startDebug?.subsystems ?? parseEnvList(env.HARNESS_DEBUG_SUBSYSTEMS)
|
|
333
|
+
};
|
|
334
|
+
if (debugConfig.enabled) installConsoleCapture();
|
|
335
|
+
const userMessages = createBridgeUserMessageQueue({ respond: emit });
|
|
336
|
+
const turn = {
|
|
337
|
+
emit,
|
|
338
|
+
requestToolResult: (requestInput) => {
|
|
339
|
+
const request = typeof requestInput === "string" ? { toolCallId: requestInput } : requestInput;
|
|
340
|
+
const bufferedIndex = bufferedToolResults.findIndex((buffered) => buffered.toolCallId === request.toolCallId || request.matches?.(buffered.result) === true);
|
|
341
|
+
if (bufferedIndex >= 0) return Promise.resolve(bufferedToolResults.splice(bufferedIndex, 1)[0].result);
|
|
342
|
+
return new Promise((resolve) => {
|
|
343
|
+
pendingToolResults.set(request.toolCallId, {
|
|
344
|
+
resolve,
|
|
345
|
+
matches: request.matches
|
|
346
|
+
});
|
|
347
|
+
});
|
|
348
|
+
},
|
|
349
|
+
requestToolApproval: (approvalId) => new Promise((resolve) => {
|
|
350
|
+
pendingToolApprovals.set(approvalId, resolve);
|
|
351
|
+
}),
|
|
352
|
+
experimental_userMessages: userMessages,
|
|
353
|
+
abortSignal: turnAbort.signal,
|
|
354
|
+
firstTurn,
|
|
355
|
+
bridgeLog: (input) => {
|
|
356
|
+
const level = input.level ?? "debug";
|
|
357
|
+
if (!shouldEmitDebugEvent(level, input.subsystem)) return;
|
|
358
|
+
emit({
|
|
359
|
+
type: "debug-event",
|
|
360
|
+
level,
|
|
361
|
+
subsystem: input.subsystem,
|
|
362
|
+
message: input.message,
|
|
363
|
+
...input.attrs ? { attrs: input.attrs } : {},
|
|
364
|
+
...input.error !== void 0 ? { error: formatBridgeError(input.error) } : {}
|
|
365
|
+
});
|
|
366
|
+
},
|
|
367
|
+
emitWarning,
|
|
368
|
+
emitError
|
|
369
|
+
};
|
|
370
|
+
currentUserMessages = userMessages;
|
|
371
|
+
try {
|
|
372
|
+
await onStart(msg, turn);
|
|
373
|
+
} catch (err) {
|
|
374
|
+
emitError({
|
|
375
|
+
error: err,
|
|
376
|
+
message: "bridge turn failed"
|
|
377
|
+
});
|
|
378
|
+
} finally {
|
|
379
|
+
userMessages.close();
|
|
380
|
+
if (currentUserMessages === userMessages) currentUserMessages = void 0;
|
|
381
|
+
if (activeTurn === thisTurn) {
|
|
382
|
+
activeTurn = void 0;
|
|
383
|
+
currentTurnState = "waiting";
|
|
384
|
+
writeBridgeMeta("waiting");
|
|
385
|
+
}
|
|
386
|
+
turnFinished();
|
|
387
|
+
}
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
case "tool-result": {
|
|
391
|
+
const result = {
|
|
392
|
+
output: msg.output,
|
|
393
|
+
isError: msg.isError,
|
|
394
|
+
toolResult: msg.toolResult
|
|
395
|
+
};
|
|
396
|
+
const exactPending = pendingToolResults.get(msg.toolCallId);
|
|
397
|
+
const matchingPending = exactPending == null ? Array.from(pendingToolResults.entries()).find(([, pending]) => pending.matches?.(result) === true) : void 0;
|
|
398
|
+
const pending = exactPending ?? matchingPending?.[1];
|
|
399
|
+
const pendingId = exactPending != null ? msg.toolCallId : matchingPending?.[0];
|
|
400
|
+
if (pending != null && pendingId != null) {
|
|
401
|
+
pendingToolResults.delete(pendingId);
|
|
402
|
+
pending.resolve(result);
|
|
403
|
+
} else bufferedToolResults.push({
|
|
404
|
+
toolCallId: msg.toolCallId,
|
|
405
|
+
result
|
|
406
|
+
});
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
case "tool-approval-response": {
|
|
410
|
+
const resolver = pendingToolApprovals.get(msg.approvalId);
|
|
411
|
+
if (resolver) {
|
|
412
|
+
pendingToolApprovals.delete(msg.approvalId);
|
|
413
|
+
resolver({
|
|
414
|
+
approved: msg.approved,
|
|
415
|
+
reason: msg.reason
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
case "user-message": {
|
|
421
|
+
const messageId = msg.messageId ?? randomUUID();
|
|
422
|
+
if (currentUserMessages == null) {
|
|
423
|
+
sendControl(ws, {
|
|
424
|
+
type: "user-message-response",
|
|
425
|
+
messageId,
|
|
426
|
+
accepted: false,
|
|
427
|
+
error: { message: "The bridge has no active turn to steer." }
|
|
428
|
+
});
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
if (ws !== activeSocket) {
|
|
432
|
+
sendControl(ws, {
|
|
433
|
+
type: "user-message-response",
|
|
434
|
+
messageId,
|
|
435
|
+
accepted: false,
|
|
436
|
+
error: { message: "The connection does not own the active bridge turn." }
|
|
437
|
+
});
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
currentUserMessages.enqueue({
|
|
441
|
+
messageId,
|
|
442
|
+
text: msg.text
|
|
443
|
+
});
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
446
|
+
case "abort":
|
|
447
|
+
turnAbort?.abort();
|
|
448
|
+
return;
|
|
449
|
+
case "resume":
|
|
450
|
+
activeSocket = ws;
|
|
451
|
+
replay(ws, msg.lastSeenEventId);
|
|
452
|
+
return;
|
|
453
|
+
case "destroy":
|
|
454
|
+
currentTurnState = "done";
|
|
455
|
+
writeBridgeMeta("done");
|
|
456
|
+
await onDestroy?.();
|
|
457
|
+
drainThenExit(ws, 1e3, "destroy");
|
|
458
|
+
return;
|
|
459
|
+
case "stop":
|
|
460
|
+
currentTurnState = "done";
|
|
461
|
+
writeBridgeMeta("done");
|
|
462
|
+
sendControl(ws, {
|
|
463
|
+
type: "bridge-stop",
|
|
464
|
+
data: await onStop?.() ?? {}
|
|
465
|
+
});
|
|
466
|
+
drainThenExit(ws, 1e3, "stop");
|
|
467
|
+
}
|
|
468
|
+
};
|
|
469
|
+
writeBridgeMeta("init");
|
|
470
|
+
const wss = new WebSocketServer({
|
|
471
|
+
port: bridgeWsPort,
|
|
472
|
+
host: "0.0.0.0"
|
|
473
|
+
});
|
|
474
|
+
const exit = () => {
|
|
475
|
+
if (options.onExit) {
|
|
476
|
+
options.onExit();
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
479
|
+
wss.close(() => process.exit(0));
|
|
480
|
+
setTimeout(() => process.exit(0), 1e3).unref();
|
|
481
|
+
};
|
|
482
|
+
const drainThenExit = (ws, code, reason) => {
|
|
483
|
+
const start = Date.now();
|
|
484
|
+
const tick = () => {
|
|
485
|
+
if (ws.bufferedAmount === 0 || ws.readyState !== WS_OPEN || Date.now() - start >= 5e3) {
|
|
486
|
+
flushPendingEventsToDisk().finally(() => {
|
|
487
|
+
try {
|
|
488
|
+
ws.close(code, reason);
|
|
489
|
+
} finally {
|
|
490
|
+
exit();
|
|
491
|
+
}
|
|
492
|
+
});
|
|
493
|
+
return;
|
|
494
|
+
}
|
|
495
|
+
setTimeout(tick, 10).unref();
|
|
496
|
+
};
|
|
497
|
+
tick();
|
|
498
|
+
};
|
|
499
|
+
wss.on("listening", () => {
|
|
500
|
+
const addr = wss.address();
|
|
501
|
+
currentBoundPort = typeof addr === "object" && addr ? addr.port : 0;
|
|
502
|
+
currentTurnState = "waiting";
|
|
503
|
+
writeBridgeMeta("waiting");
|
|
504
|
+
stdout.write(JSON.stringify({
|
|
505
|
+
type: "bridge-ready",
|
|
506
|
+
port: currentBoundPort
|
|
507
|
+
}) + "\n");
|
|
508
|
+
options.onListening?.(currentBoundPort);
|
|
509
|
+
});
|
|
510
|
+
wss.on("connection", (ws, req) => {
|
|
511
|
+
if (new URL(req.url ?? "/", "http://localhost").searchParams.get("agent_bridge_token") !== expectedToken) {
|
|
512
|
+
ws.close(1008, "unauthorized");
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
515
|
+
sendControl(ws, {
|
|
516
|
+
type: "bridge-hello",
|
|
517
|
+
state: currentTurnState,
|
|
518
|
+
lastSeq: seqCounter,
|
|
519
|
+
capabilities: { experimental_userMessageResponses: true }
|
|
520
|
+
});
|
|
521
|
+
ws.on("message", (raw) => {
|
|
522
|
+
let parsed;
|
|
523
|
+
try {
|
|
524
|
+
const text = typeof raw === "string" ? raw : Buffer.from(raw).toString("utf8");
|
|
525
|
+
parsed = JSON.parse(text);
|
|
526
|
+
} catch (err) {
|
|
527
|
+
sendControl(ws, {
|
|
528
|
+
type: "error",
|
|
529
|
+
error: `protocol parse error: ${err.message}`
|
|
530
|
+
});
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
handleInbound(parsed, ws);
|
|
534
|
+
});
|
|
535
|
+
ws.on("close", () => {
|
|
536
|
+
if (activeSocket === ws) activeSocket = void 0;
|
|
537
|
+
});
|
|
538
|
+
ws.on("error", () => {});
|
|
539
|
+
});
|
|
540
|
+
process.on("uncaughtException", (err) => {
|
|
541
|
+
emitError({
|
|
542
|
+
error: err,
|
|
543
|
+
message: "uncaught exception"
|
|
544
|
+
});
|
|
545
|
+
});
|
|
546
|
+
process.on("unhandledRejection", (err) => {
|
|
547
|
+
emitError({
|
|
548
|
+
error: err,
|
|
549
|
+
message: "unhandled rejection"
|
|
550
|
+
});
|
|
551
|
+
});
|
|
552
|
+
await new Promise((resolve, reject) => {
|
|
553
|
+
if (wss.address() != null) {
|
|
554
|
+
resolve();
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
557
|
+
wss.once("listening", resolve);
|
|
558
|
+
wss.once("error", reject);
|
|
559
|
+
});
|
|
560
|
+
return {
|
|
561
|
+
port: currentBoundPort,
|
|
562
|
+
close: () => new Promise((resolve) => {
|
|
563
|
+
wss.close(() => resolve());
|
|
564
|
+
})
|
|
565
|
+
};
|
|
630
566
|
}
|
|
631
567
|
function sendControl(socket, message) {
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
} catch {
|
|
636
|
-
}
|
|
637
|
-
}
|
|
568
|
+
if (socket?.readyState === WS_OPEN) try {
|
|
569
|
+
socket.send(JSON.stringify(message));
|
|
570
|
+
} catch {}
|
|
638
571
|
}
|
|
639
572
|
function serialiseError(err) {
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
573
|
+
if (err instanceof Error) return {
|
|
574
|
+
name: err.name,
|
|
575
|
+
message: err.message,
|
|
576
|
+
stack: err.stack
|
|
577
|
+
};
|
|
578
|
+
return err;
|
|
644
579
|
}
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
}
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
const disableEnvironments = disabled.bash && disabled.apply_patch && disabled.view_image;
|
|
663
|
-
return {
|
|
664
|
-
disabled,
|
|
665
|
-
disableEnvironments,
|
|
666
|
-
denyApplyPatchWithHook: disabled.apply_patch && !disableEnvironments,
|
|
667
|
-
webSearchMode: webSearch && !disabled.webSearch ? "live" : "disabled"
|
|
668
|
-
};
|
|
580
|
+
//#endregion
|
|
581
|
+
//#region src/bridge/codex-tool-filtering.ts
|
|
582
|
+
function resolveCodexBuiltinToolPolicy({ builtinToolFiltering, webSearch }) {
|
|
583
|
+
const included = (toolName) => builtinToolFiltering == null || (builtinToolFiltering.mode === "allow" ? builtinToolFiltering.toolNames.includes(toolName) : !builtinToolFiltering.toolNames.includes(toolName));
|
|
584
|
+
const disabled = {
|
|
585
|
+
bash: !included("bash"),
|
|
586
|
+
webSearch: !included("webSearch"),
|
|
587
|
+
apply_patch: !included("apply_patch"),
|
|
588
|
+
view_image: !included("view_image")
|
|
589
|
+
};
|
|
590
|
+
const disableEnvironments = disabled.bash && disabled.apply_patch && disabled.view_image;
|
|
591
|
+
return {
|
|
592
|
+
disabled,
|
|
593
|
+
disableEnvironments,
|
|
594
|
+
denyApplyPatchWithHook: disabled.apply_patch && !disableEnvironments,
|
|
595
|
+
webSearchMode: webSearch && !disabled.webSearch ? "live" : "disabled"
|
|
596
|
+
};
|
|
669
597
|
}
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
import { spawn } from "child_process";
|
|
598
|
+
//#endregion
|
|
599
|
+
//#region src/bridge/codex-app-server-client.ts
|
|
673
600
|
var CodexAppServerRequestError = class extends Error {
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
601
|
+
constructor({ method, error }) {
|
|
602
|
+
super(`Codex app-server ${method} failed: ${error.message}`);
|
|
603
|
+
this.name = "CodexAppServerRequestError";
|
|
604
|
+
this.method = method;
|
|
605
|
+
this.code = error.code;
|
|
606
|
+
this.data = error.data;
|
|
607
|
+
}
|
|
681
608
|
};
|
|
682
609
|
var CodexAppServerClient = class {
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
}
|
|
866
|
-
failPending(error) {
|
|
867
|
-
for (const pending of this.pending.values()) pending.reject(error);
|
|
868
|
-
this.pending.clear();
|
|
869
|
-
}
|
|
870
|
-
fail(error) {
|
|
871
|
-
this.failPending(error);
|
|
872
|
-
if (this.failure !== void 0) return;
|
|
873
|
-
this.failure = error;
|
|
874
|
-
this.resolveFailure(error);
|
|
875
|
-
}
|
|
876
|
-
async waitForExit({
|
|
877
|
-
timeoutMs
|
|
878
|
-
}) {
|
|
879
|
-
if (this.exitResult != null) return true;
|
|
880
|
-
return Promise.race([
|
|
881
|
-
this.exited.then(() => true),
|
|
882
|
-
new Promise((resolve) => {
|
|
883
|
-
const timer = setTimeout(() => resolve(false), timeoutMs);
|
|
884
|
-
timer.unref?.();
|
|
885
|
-
})
|
|
886
|
-
]);
|
|
887
|
-
}
|
|
610
|
+
constructor({ executable, args, cwd, env, onNotification, onRequest, onStderr }) {
|
|
611
|
+
this.pending = /* @__PURE__ */ new Map();
|
|
612
|
+
this.nextRequestId = 1;
|
|
613
|
+
this.stdoutBuffer = "";
|
|
614
|
+
this.parseQueue = Promise.resolve();
|
|
615
|
+
this.closed = false;
|
|
616
|
+
this.resolveFailure = () => {};
|
|
617
|
+
this.exitResult = null;
|
|
618
|
+
this.onNotification = onNotification;
|
|
619
|
+
this.onRequest = onRequest;
|
|
620
|
+
this.onStderr = onStderr;
|
|
621
|
+
this.failed = new Promise((resolve) => {
|
|
622
|
+
this.resolveFailure = resolve;
|
|
623
|
+
});
|
|
624
|
+
this.child = spawn(executable, args, {
|
|
625
|
+
cwd,
|
|
626
|
+
env,
|
|
627
|
+
stdio: [
|
|
628
|
+
"pipe",
|
|
629
|
+
"pipe",
|
|
630
|
+
"pipe"
|
|
631
|
+
]
|
|
632
|
+
});
|
|
633
|
+
this.exited = new Promise((resolve) => {
|
|
634
|
+
this.child.once("exit", (code, signal) => {
|
|
635
|
+
this.exitResult = {
|
|
636
|
+
code,
|
|
637
|
+
signal
|
|
638
|
+
};
|
|
639
|
+
this.fail(/* @__PURE__ */ new Error(`Codex app-server exited (code ${code ?? "null"}, signal ${signal ?? "null"}).`));
|
|
640
|
+
resolve(this.exitResult);
|
|
641
|
+
});
|
|
642
|
+
});
|
|
643
|
+
this.child.once("error", (error) => this.fail(error));
|
|
644
|
+
this.child.stdout.setEncoding("utf8");
|
|
645
|
+
this.child.stdout.on("data", (chunk) => this.consumeStdout(String(chunk)));
|
|
646
|
+
this.child.stderr.setEncoding("utf8");
|
|
647
|
+
this.child.stderr.on("data", (chunk) => this.onStderr(String(chunk)));
|
|
648
|
+
}
|
|
649
|
+
async initialize({ clientName, clientVersion }) {
|
|
650
|
+
await this.request({
|
|
651
|
+
method: "initialize",
|
|
652
|
+
params: {
|
|
653
|
+
clientInfo: {
|
|
654
|
+
name: clientName,
|
|
655
|
+
version: clientVersion
|
|
656
|
+
},
|
|
657
|
+
capabilities: { experimentalApi: true }
|
|
658
|
+
}
|
|
659
|
+
});
|
|
660
|
+
this.notify({ method: "initialized" });
|
|
661
|
+
}
|
|
662
|
+
request({ method, params }) {
|
|
663
|
+
if (this.closed || this.exitResult != null) return Promise.reject(/* @__PURE__ */ new Error("Codex app-server is not running."));
|
|
664
|
+
const id = this.nextRequestId++;
|
|
665
|
+
const response = new Promise((resolve, reject) => {
|
|
666
|
+
this.pending.set(id, {
|
|
667
|
+
method,
|
|
668
|
+
resolve,
|
|
669
|
+
reject
|
|
670
|
+
});
|
|
671
|
+
});
|
|
672
|
+
try {
|
|
673
|
+
this.write({
|
|
674
|
+
id,
|
|
675
|
+
method,
|
|
676
|
+
...params === void 0 ? {} : { params }
|
|
677
|
+
});
|
|
678
|
+
} catch (error) {
|
|
679
|
+
this.pending.delete(id);
|
|
680
|
+
return Promise.reject(error);
|
|
681
|
+
}
|
|
682
|
+
return response;
|
|
683
|
+
}
|
|
684
|
+
notify({ method, params }) {
|
|
685
|
+
this.write({
|
|
686
|
+
method,
|
|
687
|
+
...params === void 0 ? {} : { params }
|
|
688
|
+
});
|
|
689
|
+
}
|
|
690
|
+
async close() {
|
|
691
|
+
if (this.closed) return;
|
|
692
|
+
this.closed = true;
|
|
693
|
+
this.child.stdin.end();
|
|
694
|
+
if (await this.waitForExit({ timeoutMs: 500 })) return;
|
|
695
|
+
this.child.kill("SIGTERM");
|
|
696
|
+
if (await this.waitForExit({ timeoutMs: 1e3 })) return;
|
|
697
|
+
this.child.kill("SIGKILL");
|
|
698
|
+
await this.waitForExit({ timeoutMs: 1e3 });
|
|
699
|
+
}
|
|
700
|
+
waitUntilExit() {
|
|
701
|
+
return this.exited;
|
|
702
|
+
}
|
|
703
|
+
waitUntilFailure() {
|
|
704
|
+
return this.failed.then((error) => {
|
|
705
|
+
throw error;
|
|
706
|
+
});
|
|
707
|
+
}
|
|
708
|
+
write(message) {
|
|
709
|
+
if (this.closed || this.exitResult != null || !this.child.stdin.writable) throw new Error("Codex app-server is not writable.");
|
|
710
|
+
this.child.stdin.write(`${JSON.stringify(message)}\n`);
|
|
711
|
+
}
|
|
712
|
+
consumeStdout(chunk) {
|
|
713
|
+
this.stdoutBuffer += chunk;
|
|
714
|
+
let newline = this.stdoutBuffer.indexOf("\n");
|
|
715
|
+
while (newline !== -1) {
|
|
716
|
+
const line = this.stdoutBuffer.slice(0, newline).trim();
|
|
717
|
+
this.stdoutBuffer = this.stdoutBuffer.slice(newline + 1);
|
|
718
|
+
if (line.length > 0) this.parseQueue = this.parseQueue.then(() => this.processLine(line)).catch((error) => this.fail(error));
|
|
719
|
+
newline = this.stdoutBuffer.indexOf("\n");
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
async processLine(line) {
|
|
723
|
+
let message;
|
|
724
|
+
try {
|
|
725
|
+
message = JSON.parse(line);
|
|
726
|
+
} catch {
|
|
727
|
+
throw new Error("Codex app-server wrote an invalid JSON-RPC message.");
|
|
728
|
+
}
|
|
729
|
+
if (!isRecord(message)) throw new Error("Codex app-server wrote an invalid JSON-RPC message.");
|
|
730
|
+
if (typeof message.method === "string") {
|
|
731
|
+
if (isJsonRpcId(message.id)) this.handleServerRequest({
|
|
732
|
+
id: message.id,
|
|
733
|
+
method: message.method,
|
|
734
|
+
...message.params === void 0 ? {} : { params: message.params }
|
|
735
|
+
});
|
|
736
|
+
else this.onNotification({
|
|
737
|
+
method: message.method,
|
|
738
|
+
...message.params === void 0 ? {} : { params: message.params }
|
|
739
|
+
});
|
|
740
|
+
return;
|
|
741
|
+
}
|
|
742
|
+
if (!isJsonRpcId(message.id)) throw new Error("Codex app-server response is missing a valid id.");
|
|
743
|
+
const pending = this.pending.get(message.id);
|
|
744
|
+
if (pending == null) return;
|
|
745
|
+
this.pending.delete(message.id);
|
|
746
|
+
if (isJsonRpcError(message.error)) {
|
|
747
|
+
pending.reject(new CodexAppServerRequestError({
|
|
748
|
+
method: pending.method,
|
|
749
|
+
error: message.error
|
|
750
|
+
}));
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
753
|
+
if (!Object.prototype.hasOwnProperty.call(message, "result")) {
|
|
754
|
+
pending.reject(/* @__PURE__ */ new Error(`Codex app-server ${pending.method} response has no result.`));
|
|
755
|
+
return;
|
|
756
|
+
}
|
|
757
|
+
pending.resolve(message.result);
|
|
758
|
+
}
|
|
759
|
+
async handleServerRequest(request) {
|
|
760
|
+
try {
|
|
761
|
+
const result = await this.onRequest(request);
|
|
762
|
+
if (!this.closed && this.exitResult == null) this.write({
|
|
763
|
+
id: request.id,
|
|
764
|
+
result
|
|
765
|
+
});
|
|
766
|
+
} catch (error) {
|
|
767
|
+
if (!this.closed && this.exitResult == null) this.write({
|
|
768
|
+
id: request.id,
|
|
769
|
+
error: {
|
|
770
|
+
code: -32603,
|
|
771
|
+
message: error instanceof Error ? error.message : String(error)
|
|
772
|
+
}
|
|
773
|
+
});
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
failPending(error) {
|
|
777
|
+
for (const pending of this.pending.values()) pending.reject(error);
|
|
778
|
+
this.pending.clear();
|
|
779
|
+
}
|
|
780
|
+
fail(error) {
|
|
781
|
+
this.failPending(error);
|
|
782
|
+
if (this.failure !== void 0) return;
|
|
783
|
+
this.failure = error;
|
|
784
|
+
this.resolveFailure(error);
|
|
785
|
+
}
|
|
786
|
+
async waitForExit({ timeoutMs }) {
|
|
787
|
+
if (this.exitResult != null) return true;
|
|
788
|
+
return Promise.race([this.exited.then(() => true), new Promise((resolve) => {
|
|
789
|
+
setTimeout(() => resolve(false), timeoutMs).unref?.();
|
|
790
|
+
})]);
|
|
791
|
+
}
|
|
888
792
|
};
|
|
889
793
|
function isRecord(value) {
|
|
890
|
-
|
|
794
|
+
return value != null && typeof value === "object" && !Array.isArray(value);
|
|
891
795
|
}
|
|
892
796
|
function isJsonRpcId(value) {
|
|
893
|
-
|
|
797
|
+
return typeof value === "string" || typeof value === "number";
|
|
894
798
|
}
|
|
895
799
|
function isJsonRpcError(value) {
|
|
896
|
-
|
|
800
|
+
return isRecord(value) && typeof value.code === "number" && typeof value.message === "string";
|
|
897
801
|
}
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
function createAppServerEventHandler({
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
802
|
+
//#endregion
|
|
803
|
+
//#region src/bridge/create-app-server-event-handler.ts
|
|
804
|
+
function createAppServerEventHandler({ stepTracker, emitStreamEvent, emitWarning, emitError }) {
|
|
805
|
+
let activeThreadId;
|
|
806
|
+
let activeTurnId;
|
|
807
|
+
let settled = false;
|
|
808
|
+
let accumulatedUsage = emptyUsageBreakdown();
|
|
809
|
+
let lastCumulativeUsageKey;
|
|
810
|
+
const textByItem = /* @__PURE__ */ new Map();
|
|
811
|
+
const reasoningByItem = /* @__PURE__ */ new Map();
|
|
812
|
+
const nativeToolCalls = /* @__PURE__ */ new Map();
|
|
813
|
+
let resolveCompletion = () => {};
|
|
814
|
+
const completion = new Promise((resolve) => {
|
|
815
|
+
resolveCompletion = resolve;
|
|
816
|
+
});
|
|
817
|
+
const announceThread = (threadId) => {
|
|
818
|
+
if (activeThreadId != null) return;
|
|
819
|
+
activeThreadId = threadId;
|
|
820
|
+
emitStreamEvent({
|
|
821
|
+
type: "thread.started",
|
|
822
|
+
thread_id: threadId
|
|
823
|
+
});
|
|
824
|
+
};
|
|
825
|
+
const handleItem = ({ eventType, params }) => {
|
|
826
|
+
if (!matchesActiveTurn({
|
|
827
|
+
params,
|
|
828
|
+
activeThreadId,
|
|
829
|
+
activeTurnId
|
|
830
|
+
})) return;
|
|
831
|
+
const item = asRecord$2(params.item);
|
|
832
|
+
if (item == null || typeof item.type !== "string") return;
|
|
833
|
+
if (eventType === "item.completed" && typeof item.id === "string") {
|
|
834
|
+
const call = nativeToolCalls.get(item.id);
|
|
835
|
+
if (call?.name === "apply_patch" && item.type === "fileChange") call.successConfirmed = item.status === "completed";
|
|
836
|
+
else if (call?.name === "view_image" && item.type === "imageView") call.successConfirmed = true;
|
|
837
|
+
}
|
|
838
|
+
const normalized = normalizeItem({
|
|
839
|
+
item,
|
|
840
|
+
textByItem,
|
|
841
|
+
reasoningByItem
|
|
842
|
+
});
|
|
843
|
+
if (normalized == null) return;
|
|
844
|
+
if (normalized.type === "dynamic_tool_call") {
|
|
845
|
+
stepTracker.observeEvent({
|
|
846
|
+
event: {
|
|
847
|
+
type: eventType,
|
|
848
|
+
item: normalized
|
|
849
|
+
},
|
|
850
|
+
itemId: normalized.id
|
|
851
|
+
});
|
|
852
|
+
return;
|
|
853
|
+
}
|
|
854
|
+
emitStreamEvent({
|
|
855
|
+
type: eventType,
|
|
856
|
+
item: normalized
|
|
857
|
+
});
|
|
858
|
+
};
|
|
859
|
+
const emitNativeToolResult = ({ callId, result }) => {
|
|
860
|
+
const call = nativeToolCalls.get(callId);
|
|
861
|
+
if (call == null || call.resultEmitted) return;
|
|
862
|
+
call.resultEmitted = true;
|
|
863
|
+
emitStreamEvent({
|
|
864
|
+
type: "item.completed",
|
|
865
|
+
item: {
|
|
866
|
+
type: "native_tool",
|
|
867
|
+
id: callId,
|
|
868
|
+
tool: call.name,
|
|
869
|
+
result,
|
|
870
|
+
...!call.successConfirmed ? { isError: true } : {}
|
|
871
|
+
}
|
|
872
|
+
});
|
|
873
|
+
};
|
|
874
|
+
const handleRawItem = (params) => {
|
|
875
|
+
if (!matchesActiveTurn({
|
|
876
|
+
params,
|
|
877
|
+
activeThreadId,
|
|
878
|
+
activeTurnId
|
|
879
|
+
})) return;
|
|
880
|
+
const item = asRecord$2(params.item);
|
|
881
|
+
if (item == null) return;
|
|
882
|
+
const toolCall = normalizeNativeToolCall({ item });
|
|
883
|
+
if (toolCall != null) {
|
|
884
|
+
if (nativeToolCalls.has(toolCall.callId)) return;
|
|
885
|
+
nativeToolCalls.set(toolCall.callId, {
|
|
886
|
+
name: toolCall.name,
|
|
887
|
+
resultEmitted: false,
|
|
888
|
+
successConfirmed: false
|
|
889
|
+
});
|
|
890
|
+
emitStreamEvent({
|
|
891
|
+
type: "item.started",
|
|
892
|
+
item: {
|
|
893
|
+
type: "native_tool",
|
|
894
|
+
id: toolCall.callId,
|
|
895
|
+
tool: toolCall.name,
|
|
896
|
+
input: toolCall.input
|
|
897
|
+
}
|
|
898
|
+
});
|
|
899
|
+
return;
|
|
900
|
+
}
|
|
901
|
+
if (typeof item.call_id !== "string") return;
|
|
902
|
+
const call = nativeToolCalls.get(item.call_id);
|
|
903
|
+
if (call == null || (call.name === "apply_patch" ? item.type !== "custom_tool_call_output" : item.type !== "function_call_output") || !Object.prototype.hasOwnProperty.call(item, "output")) return;
|
|
904
|
+
emitNativeToolResult({
|
|
905
|
+
callId: item.call_id,
|
|
906
|
+
result: call.name === "view_image" && call.successConfirmed && Array.isArray(item.output) && item.output.length === 0 ? "Image viewed." : item.output
|
|
907
|
+
});
|
|
908
|
+
};
|
|
909
|
+
return {
|
|
910
|
+
announceThread,
|
|
911
|
+
setTurnId(turnId) {
|
|
912
|
+
activeTurnId = turnId;
|
|
913
|
+
},
|
|
914
|
+
handle(notification) {
|
|
915
|
+
const params = asRecord$2(notification.params);
|
|
916
|
+
if (notification.method === "thread/started") {
|
|
917
|
+
const thread = asRecord$2(params?.thread);
|
|
918
|
+
if (typeof thread?.id === "string") announceThread(thread.id);
|
|
919
|
+
return;
|
|
920
|
+
}
|
|
921
|
+
if (notification.method === "turn/started") {
|
|
922
|
+
const turn = asRecord$2(params?.turn);
|
|
923
|
+
if (params?.threadId === activeThreadId && typeof turn?.id === "string") activeTurnId = turn.id;
|
|
924
|
+
return;
|
|
925
|
+
}
|
|
926
|
+
if (notification.method === "item/started" && params != null) {
|
|
927
|
+
handleItem({
|
|
928
|
+
eventType: "item.started",
|
|
929
|
+
params
|
|
930
|
+
});
|
|
931
|
+
return;
|
|
932
|
+
}
|
|
933
|
+
if (notification.method === "item/completed" && params != null) {
|
|
934
|
+
handleItem({
|
|
935
|
+
eventType: "item.completed",
|
|
936
|
+
params
|
|
937
|
+
});
|
|
938
|
+
return;
|
|
939
|
+
}
|
|
940
|
+
if (notification.method === "rawResponseItem/completed" && params != null) {
|
|
941
|
+
handleRawItem(params);
|
|
942
|
+
return;
|
|
943
|
+
}
|
|
944
|
+
if (notification.method === "item/agentMessage/delta" && params != null && matchesActiveTurn({
|
|
945
|
+
params,
|
|
946
|
+
activeThreadId,
|
|
947
|
+
activeTurnId
|
|
948
|
+
}) && typeof params.itemId === "string" && typeof params.delta === "string") {
|
|
949
|
+
const text = (textByItem.get(params.itemId) ?? "") + params.delta;
|
|
950
|
+
textByItem.set(params.itemId, text);
|
|
951
|
+
emitStreamEvent({
|
|
952
|
+
type: "item.updated",
|
|
953
|
+
item: {
|
|
954
|
+
type: "agent_message",
|
|
955
|
+
id: params.itemId,
|
|
956
|
+
text
|
|
957
|
+
}
|
|
958
|
+
});
|
|
959
|
+
return;
|
|
960
|
+
}
|
|
961
|
+
if (notification.method === "item/reasoning/summaryTextDelta" && params != null && matchesActiveTurn({
|
|
962
|
+
params,
|
|
963
|
+
activeThreadId,
|
|
964
|
+
activeTurnId
|
|
965
|
+
}) && typeof params.itemId === "string" && typeof params.delta === "string") {
|
|
966
|
+
const text = (reasoningByItem.get(params.itemId) ?? "") + params.delta;
|
|
967
|
+
reasoningByItem.set(params.itemId, text);
|
|
968
|
+
emitStreamEvent({
|
|
969
|
+
type: "item.updated",
|
|
970
|
+
item: {
|
|
971
|
+
type: "reasoning",
|
|
972
|
+
id: params.itemId,
|
|
973
|
+
text
|
|
974
|
+
}
|
|
975
|
+
});
|
|
976
|
+
return;
|
|
977
|
+
}
|
|
978
|
+
if (notification.method === "thread/tokenUsage/updated" && params != null && matchesActiveTurn({
|
|
979
|
+
params,
|
|
980
|
+
activeThreadId,
|
|
981
|
+
activeTurnId
|
|
982
|
+
})) {
|
|
983
|
+
const tokenUsage = asRecord$2(params.tokenUsage);
|
|
984
|
+
const total = readUsageBreakdown(tokenUsage?.total);
|
|
985
|
+
const last = readUsageBreakdown(tokenUsage?.last);
|
|
986
|
+
if (total != null && last != null) {
|
|
987
|
+
const key = usageKey(total);
|
|
988
|
+
if (key !== lastCumulativeUsageKey) {
|
|
989
|
+
lastCumulativeUsageKey = key;
|
|
990
|
+
accumulatedUsage = addUsage({
|
|
991
|
+
total: accumulatedUsage,
|
|
992
|
+
increment: last
|
|
993
|
+
});
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
return;
|
|
997
|
+
}
|
|
998
|
+
if (notification.method === "error" && params != null) {
|
|
999
|
+
if (!matchesActiveTurn({
|
|
1000
|
+
params,
|
|
1001
|
+
activeThreadId,
|
|
1002
|
+
activeTurnId
|
|
1003
|
+
})) return;
|
|
1004
|
+
const error = asRecord$2(params.error);
|
|
1005
|
+
const message = typeof error?.message === "string" ? error.message : "Codex app-server reported an error.";
|
|
1006
|
+
if (params.willRetry === true) emitWarning({ message });
|
|
1007
|
+
else emitError({
|
|
1008
|
+
error: message,
|
|
1009
|
+
message: "codex turn failed"
|
|
1010
|
+
});
|
|
1011
|
+
return;
|
|
1012
|
+
}
|
|
1013
|
+
if ((notification.method === "warning" || notification.method === "configWarning" || notification.method === "deprecationNotice") && typeof params?.message === "string") {
|
|
1014
|
+
emitWarning({ message: params.message });
|
|
1015
|
+
return;
|
|
1016
|
+
}
|
|
1017
|
+
if (notification.method === "turn/completed" && params != null && matchesActiveTurn({
|
|
1018
|
+
params,
|
|
1019
|
+
activeThreadId,
|
|
1020
|
+
activeTurnId
|
|
1021
|
+
})) {
|
|
1022
|
+
if (settled) return;
|
|
1023
|
+
settled = true;
|
|
1024
|
+
const turn = asRecord$2(params.turn);
|
|
1025
|
+
const status = typeof turn?.status === "string" ? turn.status : "failed";
|
|
1026
|
+
const turnError = asRecord$2(turn?.error);
|
|
1027
|
+
if (status === "completed") for (const [callId, call] of nativeToolCalls) {
|
|
1028
|
+
if (call.resultEmitted) continue;
|
|
1029
|
+
emitNativeToolResult({
|
|
1030
|
+
callId,
|
|
1031
|
+
result: call.successConfirmed ? call.name === "apply_patch" ? "Patch applied." : "Image viewed." : "Codex did not report the tool result."
|
|
1032
|
+
});
|
|
1033
|
+
}
|
|
1034
|
+
emitStreamEvent({
|
|
1035
|
+
type: "turn.completed",
|
|
1036
|
+
usage: toLegacyUsage(accumulatedUsage)
|
|
1037
|
+
});
|
|
1038
|
+
resolveCompletion({
|
|
1039
|
+
status,
|
|
1040
|
+
...typeof turnError?.message === "string" ? { error: turnError.message } : {}
|
|
1041
|
+
});
|
|
1042
|
+
}
|
|
1043
|
+
},
|
|
1044
|
+
waitForCompletion: () => completion
|
|
1045
|
+
};
|
|
1109
1046
|
}
|
|
1110
|
-
function normalizeNativeToolCall({
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
}
|
|
1123
|
-
if (item.type === "function_call" && item.name === "view_image" && typeof item.arguments === "string") {
|
|
1124
|
-
return {
|
|
1125
|
-
callId: item.call_id,
|
|
1126
|
-
name: "view_image",
|
|
1127
|
-
input: item.arguments
|
|
1128
|
-
};
|
|
1129
|
-
}
|
|
1130
|
-
return void 0;
|
|
1047
|
+
function normalizeNativeToolCall({ item }) {
|
|
1048
|
+
if (item.namespace != null || typeof item.call_id !== "string" || item.call_id.length === 0) return;
|
|
1049
|
+
if (item.type === "custom_tool_call" && item.name === "apply_patch" && typeof item.input === "string") return {
|
|
1050
|
+
callId: item.call_id,
|
|
1051
|
+
name: "apply_patch",
|
|
1052
|
+
input: JSON.stringify(item.input)
|
|
1053
|
+
};
|
|
1054
|
+
if (item.type === "function_call" && item.name === "view_image" && typeof item.arguments === "string") return {
|
|
1055
|
+
callId: item.call_id,
|
|
1056
|
+
name: "view_image",
|
|
1057
|
+
input: item.arguments
|
|
1058
|
+
};
|
|
1131
1059
|
}
|
|
1132
|
-
function normalizeItem({
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1060
|
+
function normalizeItem({ item, textByItem, reasoningByItem }) {
|
|
1061
|
+
const id = typeof item.id === "string" ? item.id : void 0;
|
|
1062
|
+
if (item.type === "agentMessage") {
|
|
1063
|
+
const text = typeof item.text === "string" ? item.text : "";
|
|
1064
|
+
if (id != null) textByItem.set(id, text);
|
|
1065
|
+
return {
|
|
1066
|
+
type: "agent_message",
|
|
1067
|
+
id,
|
|
1068
|
+
text
|
|
1069
|
+
};
|
|
1070
|
+
}
|
|
1071
|
+
if (item.type === "reasoning") {
|
|
1072
|
+
const summary = stringArray(item.summary).join("\n\n");
|
|
1073
|
+
const content = stringArray(item.content).join("\n\n");
|
|
1074
|
+
const text = summary || reasoningByItem.get(id ?? "") || content;
|
|
1075
|
+
if (id != null) reasoningByItem.set(id, text);
|
|
1076
|
+
return {
|
|
1077
|
+
type: "reasoning",
|
|
1078
|
+
id,
|
|
1079
|
+
text
|
|
1080
|
+
};
|
|
1081
|
+
}
|
|
1082
|
+
if (item.type === "commandExecution") return {
|
|
1083
|
+
type: "command_execution",
|
|
1084
|
+
id,
|
|
1085
|
+
command: typeof item.command === "string" ? item.command : "",
|
|
1086
|
+
exit_code: typeof item.exitCode === "number" ? item.exitCode : void 0,
|
|
1087
|
+
aggregated_output: typeof item.aggregatedOutput === "string" ? item.aggregatedOutput : void 0,
|
|
1088
|
+
status: normalizeCommandStatus(item.status)
|
|
1089
|
+
};
|
|
1090
|
+
if (item.type === "mcpToolCall") {
|
|
1091
|
+
const result = asRecord$2(item.result);
|
|
1092
|
+
return {
|
|
1093
|
+
type: "mcp_tool_call",
|
|
1094
|
+
id,
|
|
1095
|
+
server: typeof item.server === "string" ? item.server : void 0,
|
|
1096
|
+
tool: typeof item.tool === "string" ? item.tool : void 0,
|
|
1097
|
+
arguments: item.arguments,
|
|
1098
|
+
result: result == null ? item.result : {
|
|
1099
|
+
content: result.content,
|
|
1100
|
+
structured_content: result.structuredContent
|
|
1101
|
+
},
|
|
1102
|
+
error: asRecord$2(item.error)
|
|
1103
|
+
};
|
|
1104
|
+
}
|
|
1105
|
+
if (item.type === "dynamicToolCall") return {
|
|
1106
|
+
type: "dynamic_tool_call",
|
|
1107
|
+
id
|
|
1108
|
+
};
|
|
1109
|
+
if (item.type === "webSearch") return {
|
|
1110
|
+
type: "web_search",
|
|
1111
|
+
id,
|
|
1112
|
+
query: typeof item.query === "string" ? item.query : void 0,
|
|
1113
|
+
action: asRecord$2(item.action) ?? void 0,
|
|
1114
|
+
result: item.results
|
|
1115
|
+
};
|
|
1116
|
+
if (item.type === "fileChange") return {
|
|
1117
|
+
type: "file_change",
|
|
1118
|
+
id,
|
|
1119
|
+
changes: Array.isArray(item.changes) ? item.changes.flatMap((change) => {
|
|
1120
|
+
const value = asRecord$2(change);
|
|
1121
|
+
const kind = asRecord$2(value?.kind)?.type;
|
|
1122
|
+
return typeof value?.path === "string" && (kind === "add" || kind === "delete" || kind === "update") ? [{
|
|
1123
|
+
path: value.path,
|
|
1124
|
+
kind
|
|
1125
|
+
}] : [];
|
|
1126
|
+
}) : []
|
|
1127
|
+
};
|
|
1128
|
+
if (item.type === "plan") return {
|
|
1129
|
+
type: "todo_list",
|
|
1130
|
+
id
|
|
1131
|
+
};
|
|
1200
1132
|
}
|
|
1201
1133
|
function normalizeCommandStatus(value) {
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1134
|
+
if (value === "completed") return "completed";
|
|
1135
|
+
if (value === "inProgress") return "in_progress";
|
|
1136
|
+
return "failed";
|
|
1205
1137
|
}
|
|
1206
|
-
function matchesActiveTurn({
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
}) {
|
|
1211
|
-
const nestedTurn = asRecord(params.turn);
|
|
1212
|
-
const turnId = typeof params.turnId === "string" ? params.turnId : typeof nestedTurn?.id === "string" ? nestedTurn.id : void 0;
|
|
1213
|
-
return activeThreadId != null && activeTurnId != null && params.threadId === activeThreadId && turnId === activeTurnId;
|
|
1138
|
+
function matchesActiveTurn({ params, activeThreadId, activeTurnId }) {
|
|
1139
|
+
const nestedTurn = asRecord$2(params.turn);
|
|
1140
|
+
const turnId = typeof params.turnId === "string" ? params.turnId : typeof nestedTurn?.id === "string" ? nestedTurn.id : void 0;
|
|
1141
|
+
return activeThreadId != null && activeTurnId != null && params.threadId === activeThreadId && turnId === activeTurnId;
|
|
1214
1142
|
}
|
|
1215
|
-
function asRecord(value) {
|
|
1216
|
-
|
|
1143
|
+
function asRecord$2(value) {
|
|
1144
|
+
return value != null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
1217
1145
|
}
|
|
1218
1146
|
function stringArray(value) {
|
|
1219
|
-
|
|
1147
|
+
return Array.isArray(value) ? value.filter((entry) => typeof entry === "string") : [];
|
|
1220
1148
|
}
|
|
1221
1149
|
function emptyUsageBreakdown() {
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1150
|
+
return {
|
|
1151
|
+
inputTokens: 0,
|
|
1152
|
+
cachedInputTokens: 0,
|
|
1153
|
+
cacheWriteInputTokens: 0,
|
|
1154
|
+
outputTokens: 0
|
|
1155
|
+
};
|
|
1228
1156
|
}
|
|
1229
1157
|
function readUsageBreakdown(value) {
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1158
|
+
const usage = asRecord$2(value);
|
|
1159
|
+
if (usage == null) return void 0;
|
|
1160
|
+
return {
|
|
1161
|
+
inputTokens: numberOrZero(usage.inputTokens),
|
|
1162
|
+
cachedInputTokens: numberOrZero(usage.cachedInputTokens),
|
|
1163
|
+
cacheWriteInputTokens: numberOrZero(usage.cacheWriteInputTokens),
|
|
1164
|
+
outputTokens: numberOrZero(usage.outputTokens)
|
|
1165
|
+
};
|
|
1238
1166
|
}
|
|
1239
|
-
function addUsage({
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
cacheWriteInputTokens: total.cacheWriteInputTokens + increment.cacheWriteInputTokens,
|
|
1247
|
-
outputTokens: total.outputTokens + increment.outputTokens
|
|
1248
|
-
};
|
|
1167
|
+
function addUsage({ total, increment }) {
|
|
1168
|
+
return {
|
|
1169
|
+
inputTokens: total.inputTokens + increment.inputTokens,
|
|
1170
|
+
cachedInputTokens: total.cachedInputTokens + increment.cachedInputTokens,
|
|
1171
|
+
cacheWriteInputTokens: total.cacheWriteInputTokens + increment.cacheWriteInputTokens,
|
|
1172
|
+
outputTokens: total.outputTokens + increment.outputTokens
|
|
1173
|
+
};
|
|
1249
1174
|
}
|
|
1250
1175
|
function usageKey(usage) {
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1176
|
+
return [
|
|
1177
|
+
usage.inputTokens,
|
|
1178
|
+
usage.cachedInputTokens,
|
|
1179
|
+
usage.cacheWriteInputTokens,
|
|
1180
|
+
usage.outputTokens
|
|
1181
|
+
].join(":");
|
|
1257
1182
|
}
|
|
1258
1183
|
function toLegacyUsage(usage) {
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1184
|
+
return {
|
|
1185
|
+
input_tokens: usage.inputTokens,
|
|
1186
|
+
cached_input_tokens: usage.cachedInputTokens,
|
|
1187
|
+
cache_write_input_tokens: usage.cacheWriteInputTokens,
|
|
1188
|
+
output_tokens: usage.outputTokens
|
|
1189
|
+
};
|
|
1265
1190
|
}
|
|
1266
1191
|
function numberOrZero(value) {
|
|
1267
|
-
|
|
1192
|
+
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
1268
1193
|
}
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
hooks.state = {
|
|
1321
|
-
...asRecord2(hooks.state),
|
|
1322
|
-
[key]: { enabled: true, trusted_hash: hash }
|
|
1323
|
-
};
|
|
1324
|
-
return {
|
|
1325
|
-
cliOverrides: [`hooks=${toTomlInline(hooks)}`, "features.hooks=true"],
|
|
1326
|
-
threadConfig,
|
|
1327
|
-
key,
|
|
1328
|
-
hash,
|
|
1329
|
-
command: COMMAND
|
|
1330
|
-
};
|
|
1194
|
+
//#endregion
|
|
1195
|
+
//#region src/bridge/codex-tool-filtering-hook.ts
|
|
1196
|
+
const MATCHER = "^apply_patch$";
|
|
1197
|
+
const TIMEOUT_SECONDS = 10;
|
|
1198
|
+
const COMMAND = "printf '%s\\n' \"Tool 'apply_patch' is inactive due to the HarnessAgent tool filtering policy.\" >&2; exit 2";
|
|
1199
|
+
function createTrustedApplyPatchHook({ codexConfig }) {
|
|
1200
|
+
const threadConfig = { ...codexConfig };
|
|
1201
|
+
const hooks = { ...asRecord$1(threadConfig.hooks) };
|
|
1202
|
+
delete threadConfig.hooks;
|
|
1203
|
+
for (const [key, value] of Object.entries(threadConfig)) {
|
|
1204
|
+
if (!key.startsWith("hooks.")) continue;
|
|
1205
|
+
const name = key.slice(6);
|
|
1206
|
+
if (name.startsWith("state.")) hooks.state = {
|
|
1207
|
+
...asRecord$1(hooks.state),
|
|
1208
|
+
[name.slice(6)]: value
|
|
1209
|
+
};
|
|
1210
|
+
else hooks[name] = value;
|
|
1211
|
+
delete threadConfig[key];
|
|
1212
|
+
}
|
|
1213
|
+
const userGroups = hooks.PreToolUse ?? [];
|
|
1214
|
+
if (!Array.isArray(userGroups)) throw new Error("Codex config hooks.PreToolUse must be an array.");
|
|
1215
|
+
const handler = {
|
|
1216
|
+
type: "command",
|
|
1217
|
+
command: COMMAND,
|
|
1218
|
+
timeout: TIMEOUT_SECONDS,
|
|
1219
|
+
async: false
|
|
1220
|
+
};
|
|
1221
|
+
const hash = `sha256:${createHash("sha256").update(JSON.stringify(canonicalize({
|
|
1222
|
+
event_name: "pre_tool_use",
|
|
1223
|
+
matcher: MATCHER,
|
|
1224
|
+
hooks: [handler]
|
|
1225
|
+
}))).digest("hex")}`;
|
|
1226
|
+
const key = `${process.platform === "win32" ? path.win32.resolve("C:\\", "<session-flags>/config.toml") : path.posix.resolve("/", "<session-flags>/config.toml")}:pre_tool_use:${userGroups.length}:0`;
|
|
1227
|
+
hooks.PreToolUse = [...userGroups, {
|
|
1228
|
+
matcher: MATCHER,
|
|
1229
|
+
hooks: [handler]
|
|
1230
|
+
}];
|
|
1231
|
+
hooks.state = {
|
|
1232
|
+
...asRecord$1(hooks.state),
|
|
1233
|
+
[key]: {
|
|
1234
|
+
enabled: true,
|
|
1235
|
+
trusted_hash: hash
|
|
1236
|
+
}
|
|
1237
|
+
};
|
|
1238
|
+
return {
|
|
1239
|
+
cliOverrides: [`hooks=${toTomlInline(hooks)}`, "features.hooks=true"],
|
|
1240
|
+
threadConfig,
|
|
1241
|
+
key,
|
|
1242
|
+
hash,
|
|
1243
|
+
command: COMMAND
|
|
1244
|
+
};
|
|
1331
1245
|
}
|
|
1332
|
-
function assertTrustedApplyPatchHook({
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
const record = asRecord2(entry);
|
|
1339
|
-
const matches = Array.isArray(record?.hooks) ? record.hooks.filter((item) => asRecord2(item)?.key === hook.key) : [];
|
|
1340
|
-
const match = matches.length === 1 ? asRecord2(matches[0]) : void 0;
|
|
1341
|
-
if (!match || match.eventName !== "preToolUse" || match.handlerType !== "command" || match.command !== hook.command || match.matcher !== MATCHER || match.currentHash !== hook.hash || match.enabled !== true || match.trustStatus !== "trusted" || Array.isArray(record?.errors) && record.errors.length > 0) {
|
|
1342
|
-
throw new Error(
|
|
1343
|
-
"Codex app-server did not load the trusted apply_patch filtering hook."
|
|
1344
|
-
);
|
|
1345
|
-
}
|
|
1246
|
+
function assertTrustedApplyPatchHook({ response, hook }) {
|
|
1247
|
+
const data = asRecord$1(response)?.data;
|
|
1248
|
+
const record = asRecord$1(Array.isArray(data) && data.length === 1 ? data[0] : null);
|
|
1249
|
+
const matches = Array.isArray(record?.hooks) ? record.hooks.filter((item) => asRecord$1(item)?.key === hook.key) : [];
|
|
1250
|
+
const match = matches.length === 1 ? asRecord$1(matches[0]) : void 0;
|
|
1251
|
+
if (!match || match.eventName !== "preToolUse" || match.handlerType !== "command" || match.command !== hook.command || match.matcher !== MATCHER || match.currentHash !== hook.hash || match.enabled !== true || match.trustStatus !== "trusted" || Array.isArray(record?.errors) && record.errors.length > 0) throw new Error("Codex app-server did not load the trusted apply_patch filtering hook.");
|
|
1346
1252
|
}
|
|
1347
1253
|
function canonicalize(value) {
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
Object.entries(value).sort(([a], [b]) => a.localeCompare(b)).map(([key, item]) => [key, canonicalize(item)])
|
|
1352
|
-
);
|
|
1353
|
-
}
|
|
1354
|
-
return value;
|
|
1254
|
+
if (Array.isArray(value)) return value.map(canonicalize);
|
|
1255
|
+
if (value != null && typeof value === "object") return Object.fromEntries(Object.entries(value).sort(([a], [b]) => a.localeCompare(b)).map(([key, item]) => [key, canonicalize(item)]));
|
|
1256
|
+
return value;
|
|
1355
1257
|
}
|
|
1356
1258
|
function toTomlInline(value) {
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
return `[${value.map(toTomlInline).join(",")}]`;
|
|
1364
|
-
}
|
|
1365
|
-
if (value != null && typeof value === "object") {
|
|
1366
|
-
return `{${Object.entries(value).filter(([, item]) => item !== void 0).map(([key, item]) => `${JSON.stringify(key)}=${toTomlInline(item)}`).join(",")}}`;
|
|
1367
|
-
}
|
|
1368
|
-
throw new Error("Codex hook configuration contains an unsupported value.");
|
|
1259
|
+
if (typeof value === "string") return JSON.stringify(value);
|
|
1260
|
+
if (typeof value === "boolean") return String(value);
|
|
1261
|
+
if (typeof value === "number" && Number.isFinite(value)) return String(value);
|
|
1262
|
+
if (Array.isArray(value)) return `[${value.map(toTomlInline).join(",")}]`;
|
|
1263
|
+
if (value != null && typeof value === "object") return `{${Object.entries(value).filter(([, item]) => item !== void 0).map(([key, item]) => `${JSON.stringify(key)}=${toTomlInline(item)}`).join(",")}}`;
|
|
1264
|
+
throw new Error("Codex hook configuration contains an unsupported value.");
|
|
1369
1265
|
}
|
|
1370
|
-
function
|
|
1371
|
-
|
|
1266
|
+
function asRecord$1(value) {
|
|
1267
|
+
return value != null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
1372
1268
|
}
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
new URL("./node_modules/@openai/codex/bin/codex.js", import.meta.url)
|
|
1377
|
-
);
|
|
1269
|
+
//#endregion
|
|
1270
|
+
//#region src/bridge/codex-app-server-driver.ts
|
|
1271
|
+
const CODEX_CLI_PATH = fileURLToPath(new URL("./node_modules/@openai/codex/bin/codex.js", import.meta.url));
|
|
1378
1272
|
function createCodexAppServerRuntime() {
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
const turnResponse = await raceWithProcess({
|
|
1635
|
-
operation: runningClient.request({
|
|
1636
|
-
method: "turn/start",
|
|
1637
|
-
params: createTurnParams({
|
|
1638
|
-
threadId: currentTurn.threadId,
|
|
1639
|
-
start,
|
|
1640
|
-
codexModel,
|
|
1641
|
-
disableEnvironments: toolPolicy.disableEnvironments
|
|
1642
|
-
})
|
|
1643
|
-
})
|
|
1644
|
-
});
|
|
1645
|
-
currentTurn.turnId = readNestedString({
|
|
1646
|
-
value: turnResponse,
|
|
1647
|
-
path: ["turn", "id"],
|
|
1648
|
-
method: "turn/start"
|
|
1649
|
-
});
|
|
1650
|
-
handler.setTurnId(currentTurn.turnId);
|
|
1651
|
-
void userMessageLoop();
|
|
1652
|
-
const result = await raceWithProcess({
|
|
1653
|
-
operation: handler.waitForCompletion()
|
|
1654
|
-
});
|
|
1655
|
-
keepClient = true;
|
|
1656
|
-
assertSuccessfulTurn(result);
|
|
1657
|
-
} catch (error) {
|
|
1658
|
-
if (turn.abortSignal.aborted && currentTurn.turnId != null) {
|
|
1659
|
-
let timer;
|
|
1660
|
-
try {
|
|
1661
|
-
keepClient = await Promise.race([
|
|
1662
|
-
handler.waitForCompletion().then(() => true),
|
|
1663
|
-
new Promise((resolve) => {
|
|
1664
|
-
timer = setTimeout(() => resolve(false), 5e3);
|
|
1665
|
-
}),
|
|
1666
|
-
clientFailure.then(
|
|
1667
|
-
() => false,
|
|
1668
|
-
() => false
|
|
1669
|
-
)
|
|
1670
|
-
]);
|
|
1671
|
-
} finally {
|
|
1672
|
-
clearTimeout(timer);
|
|
1673
|
-
}
|
|
1674
|
-
}
|
|
1675
|
-
throw error;
|
|
1676
|
-
} finally {
|
|
1677
|
-
turn.experimental_userMessages.close();
|
|
1678
|
-
stopSteering();
|
|
1679
|
-
removeAbortListener();
|
|
1680
|
-
if (activeTurn === currentTurn) activeTurn = void 0;
|
|
1681
|
-
if (!keepClient) await close();
|
|
1682
|
-
}
|
|
1683
|
-
};
|
|
1684
|
-
return { runTurn: runTurn2, close };
|
|
1273
|
+
let client;
|
|
1274
|
+
let loadedThreadId;
|
|
1275
|
+
let loadedConfig;
|
|
1276
|
+
let activeTurn;
|
|
1277
|
+
const close = async () => {
|
|
1278
|
+
const previous = client;
|
|
1279
|
+
client = void 0;
|
|
1280
|
+
loadedThreadId = void 0;
|
|
1281
|
+
loadedConfig = void 0;
|
|
1282
|
+
await previous?.close();
|
|
1283
|
+
};
|
|
1284
|
+
const runTurn = async (options) => {
|
|
1285
|
+
const { start, turn, emit, workdir, threadId, codexModel, codexConfig } = options;
|
|
1286
|
+
if (activeTurn != null) throw new Error("A Codex turn is already active.");
|
|
1287
|
+
const toolPolicy = resolveCodexBuiltinToolPolicy({
|
|
1288
|
+
builtinToolFiltering: start.builtinToolFiltering,
|
|
1289
|
+
webSearch: start.webSearch
|
|
1290
|
+
});
|
|
1291
|
+
const trustedHook = toolPolicy.denyApplyPatchWithHook ? createTrustedApplyPatchHook({ codexConfig }) : void 0;
|
|
1292
|
+
const nextConfig = {
|
|
1293
|
+
...trustedHook?.threadConfig ?? codexConfig,
|
|
1294
|
+
web_search: toolPolicy.webSearchMode,
|
|
1295
|
+
...toolPolicy.disabled.bash || toolPolicy.disabled.view_image ? { features: {
|
|
1296
|
+
...asRecord(codexConfig.features),
|
|
1297
|
+
...toolPolicy.disabled.bash ? { shell_tool: false } : {},
|
|
1298
|
+
...toolPolicy.disabled.view_image ? { view_image: false } : {}
|
|
1299
|
+
} } : {}
|
|
1300
|
+
};
|
|
1301
|
+
const processConfig = {
|
|
1302
|
+
thread: nextConfig,
|
|
1303
|
+
cliOverrides: trustedHook?.cliOverrides ?? []
|
|
1304
|
+
};
|
|
1305
|
+
if (client != null && !isDeepStrictEqual(loadedConfig, processConfig)) await close();
|
|
1306
|
+
const handler = createAppServerEventHandler({
|
|
1307
|
+
stepTracker: options.stepTracker,
|
|
1308
|
+
emitStreamEvent: options.emitStreamEvent,
|
|
1309
|
+
emitWarning: turn.emitWarning,
|
|
1310
|
+
emitError: turn.emitError
|
|
1311
|
+
});
|
|
1312
|
+
const dynamicTools = createDynamicTools(start.tools ?? []);
|
|
1313
|
+
let rejectProtocolFailure = () => {};
|
|
1314
|
+
const protocolFailure = new Promise((_, reject) => {
|
|
1315
|
+
rejectProtocolFailure = reject;
|
|
1316
|
+
});
|
|
1317
|
+
const currentTurn = {
|
|
1318
|
+
threadId,
|
|
1319
|
+
turnId: void 0,
|
|
1320
|
+
handler,
|
|
1321
|
+
dynamicTools,
|
|
1322
|
+
options,
|
|
1323
|
+
fail: rejectProtocolFailure
|
|
1324
|
+
};
|
|
1325
|
+
activeTurn = currentTurn;
|
|
1326
|
+
let initialized = false;
|
|
1327
|
+
if (client == null) {
|
|
1328
|
+
const created = new CodexAppServerClient({
|
|
1329
|
+
executable: process.execPath,
|
|
1330
|
+
args: createCodexAppServerArgs({ cliOverrides: trustedHook?.cliOverrides }),
|
|
1331
|
+
cwd: workdir,
|
|
1332
|
+
env: process.env,
|
|
1333
|
+
onNotification: (notification) => {
|
|
1334
|
+
if (client !== created || activeTurn == null) return;
|
|
1335
|
+
const params = asRecord(notification.params);
|
|
1336
|
+
if (notification.method === "turn/started" && params?.threadId === activeTurn.threadId && activeTurn.turnId == null) {
|
|
1337
|
+
const turnId = asRecord(params?.turn)?.id;
|
|
1338
|
+
if (typeof turnId === "string") activeTurn.turnId = turnId;
|
|
1339
|
+
}
|
|
1340
|
+
activeTurn.handler.handle(notification);
|
|
1341
|
+
},
|
|
1342
|
+
onRequest: (request) => {
|
|
1343
|
+
const active = activeTurn;
|
|
1344
|
+
const params = asRecord(request.params);
|
|
1345
|
+
if (client !== created || active == null || params?.threadId !== active.threadId || params?.turnId !== active.turnId || active.turnId == null) return Promise.reject(/* @__PURE__ */ new Error("Codex app-server requested a tool for an inactive turn."));
|
|
1346
|
+
return handleAppServerRequest({
|
|
1347
|
+
request,
|
|
1348
|
+
dynamicTools: active.dynamicTools,
|
|
1349
|
+
emit: active.options.emit,
|
|
1350
|
+
requestToolResult: active.options.turn.requestToolResult
|
|
1351
|
+
}).catch((error) => {
|
|
1352
|
+
active.fail(error);
|
|
1353
|
+
throw error;
|
|
1354
|
+
});
|
|
1355
|
+
},
|
|
1356
|
+
onStderr: (text) => {
|
|
1357
|
+
const message = text.trim();
|
|
1358
|
+
if (message.length > 0) activeTurn?.options.turn.bridgeLog({
|
|
1359
|
+
level: "debug",
|
|
1360
|
+
subsystem: "codex.app-server.stderr",
|
|
1361
|
+
message
|
|
1362
|
+
});
|
|
1363
|
+
}
|
|
1364
|
+
});
|
|
1365
|
+
client = created;
|
|
1366
|
+
loadedConfig = processConfig;
|
|
1367
|
+
initialized = true;
|
|
1368
|
+
}
|
|
1369
|
+
const runningClient = client;
|
|
1370
|
+
const clientFailure = runningClient.waitUntilFailure();
|
|
1371
|
+
const exitFailure = runningClient.waitUntilExit().then(({ code, signal }) => {
|
|
1372
|
+
throw new Error(`Codex app-server exited before the turn completed (code ${code ?? "null"}, signal ${signal ?? "null"}).`);
|
|
1373
|
+
});
|
|
1374
|
+
let removeAbortListener = () => {};
|
|
1375
|
+
const abortFailure = new Promise((_, reject) => {
|
|
1376
|
+
const onAbort = () => {
|
|
1377
|
+
if (currentTurn.threadId != null && currentTurn.turnId != null) runningClient.request({
|
|
1378
|
+
method: "turn/interrupt",
|
|
1379
|
+
params: {
|
|
1380
|
+
threadId: currentTurn.threadId,
|
|
1381
|
+
turnId: currentTurn.turnId
|
|
1382
|
+
}
|
|
1383
|
+
}).catch(() => {});
|
|
1384
|
+
reject(turn.abortSignal.reason ?? new DOMException("Aborted", "AbortError"));
|
|
1385
|
+
};
|
|
1386
|
+
if (turn.abortSignal.aborted) onAbort();
|
|
1387
|
+
else {
|
|
1388
|
+
turn.abortSignal.addEventListener("abort", onAbort, { once: true });
|
|
1389
|
+
removeAbortListener = () => turn.abortSignal.removeEventListener("abort", onAbort);
|
|
1390
|
+
}
|
|
1391
|
+
});
|
|
1392
|
+
const raceWithProcess = ({ operation }) => Promise.race([
|
|
1393
|
+
operation,
|
|
1394
|
+
abortFailure,
|
|
1395
|
+
protocolFailure,
|
|
1396
|
+
clientFailure,
|
|
1397
|
+
exitFailure
|
|
1398
|
+
]);
|
|
1399
|
+
let stopSteering = () => {};
|
|
1400
|
+
const steeringStopped = new Promise((resolve) => {
|
|
1401
|
+
stopSteering = resolve;
|
|
1402
|
+
});
|
|
1403
|
+
const userMessageLoop = async () => {
|
|
1404
|
+
for await (const message of turn.experimental_userMessages) try {
|
|
1405
|
+
if (asRecord(await Promise.race([raceWithProcess({ operation: runningClient.request({
|
|
1406
|
+
method: "turn/steer",
|
|
1407
|
+
params: {
|
|
1408
|
+
threadId: currentTurn.threadId,
|
|
1409
|
+
expectedTurnId: currentTurn.turnId,
|
|
1410
|
+
clientUserMessageId: message.messageId,
|
|
1411
|
+
input: [{
|
|
1412
|
+
type: "text",
|
|
1413
|
+
text: message.text,
|
|
1414
|
+
text_elements: []
|
|
1415
|
+
}]
|
|
1416
|
+
}
|
|
1417
|
+
}) }), steeringStopped.then(() => {
|
|
1418
|
+
throw new Error("The Codex turn ended before accepting the user message.");
|
|
1419
|
+
})]))?.turnId !== currentTurn.turnId) throw new Error("Codex app-server turn/steer returned an unexpected turn ID.");
|
|
1420
|
+
message.accept();
|
|
1421
|
+
} catch (error) {
|
|
1422
|
+
message.reject(error);
|
|
1423
|
+
}
|
|
1424
|
+
};
|
|
1425
|
+
let keepClient = false;
|
|
1426
|
+
try {
|
|
1427
|
+
if (initialized) await raceWithProcess({ operation: runningClient.initialize({
|
|
1428
|
+
clientName: "ai-sdk-harness-codex",
|
|
1429
|
+
clientVersion: "1"
|
|
1430
|
+
}) });
|
|
1431
|
+
if (start.restartThread && loadedThreadId != null) {
|
|
1432
|
+
await raceWithProcess({ operation: runningClient.request({
|
|
1433
|
+
method: "thread/unsubscribe",
|
|
1434
|
+
params: { threadId: loadedThreadId }
|
|
1435
|
+
}) });
|
|
1436
|
+
loadedThreadId = void 0;
|
|
1437
|
+
}
|
|
1438
|
+
if (trustedHook != null) assertTrustedApplyPatchHook({
|
|
1439
|
+
response: await raceWithProcess({ operation: runningClient.request({
|
|
1440
|
+
method: "hooks/list",
|
|
1441
|
+
params: {}
|
|
1442
|
+
}) }),
|
|
1443
|
+
hook: trustedHook
|
|
1444
|
+
});
|
|
1445
|
+
const threadMethod = threadId == null ? "thread/start" : "thread/resume";
|
|
1446
|
+
if (loadedThreadId !== threadId || threadId == null) {
|
|
1447
|
+
const threadResponse = await raceWithProcess({ operation: runningClient.request({
|
|
1448
|
+
method: threadMethod,
|
|
1449
|
+
params: threadId == null ? {
|
|
1450
|
+
...createThreadParams({
|
|
1451
|
+
start,
|
|
1452
|
+
workdir,
|
|
1453
|
+
codexModel,
|
|
1454
|
+
codexConfig: nextConfig
|
|
1455
|
+
}),
|
|
1456
|
+
dynamicTools: dynamicTools.specs,
|
|
1457
|
+
experimentalRawEvents: true
|
|
1458
|
+
} : {
|
|
1459
|
+
threadId,
|
|
1460
|
+
...createThreadParams({
|
|
1461
|
+
start,
|
|
1462
|
+
workdir,
|
|
1463
|
+
codexModel,
|
|
1464
|
+
codexConfig: nextConfig
|
|
1465
|
+
}),
|
|
1466
|
+
excludeTurns: true
|
|
1467
|
+
}
|
|
1468
|
+
}) });
|
|
1469
|
+
assertCodexThreadPermissions({
|
|
1470
|
+
response: threadResponse,
|
|
1471
|
+
method: threadMethod
|
|
1472
|
+
});
|
|
1473
|
+
currentTurn.threadId = readNestedString({
|
|
1474
|
+
value: threadResponse,
|
|
1475
|
+
path: ["thread", "id"],
|
|
1476
|
+
method: threadMethod
|
|
1477
|
+
});
|
|
1478
|
+
loadedThreadId = currentTurn.threadId;
|
|
1479
|
+
}
|
|
1480
|
+
handler.announceThread(currentTurn.threadId);
|
|
1481
|
+
emit({ type: "stream-start" });
|
|
1482
|
+
currentTurn.turnId = readNestedString({
|
|
1483
|
+
value: await raceWithProcess({ operation: runningClient.request({
|
|
1484
|
+
method: "turn/start",
|
|
1485
|
+
params: createTurnParams({
|
|
1486
|
+
threadId: currentTurn.threadId,
|
|
1487
|
+
start,
|
|
1488
|
+
codexModel,
|
|
1489
|
+
disableEnvironments: toolPolicy.disableEnvironments
|
|
1490
|
+
})
|
|
1491
|
+
}) }),
|
|
1492
|
+
path: ["turn", "id"],
|
|
1493
|
+
method: "turn/start"
|
|
1494
|
+
});
|
|
1495
|
+
handler.setTurnId(currentTurn.turnId);
|
|
1496
|
+
userMessageLoop();
|
|
1497
|
+
const result = await raceWithProcess({ operation: handler.waitForCompletion() });
|
|
1498
|
+
keepClient = true;
|
|
1499
|
+
assertSuccessfulTurn(result);
|
|
1500
|
+
} catch (error) {
|
|
1501
|
+
if (turn.abortSignal.aborted && currentTurn.turnId != null) {
|
|
1502
|
+
let timer;
|
|
1503
|
+
try {
|
|
1504
|
+
keepClient = await Promise.race([
|
|
1505
|
+
handler.waitForCompletion().then(() => true),
|
|
1506
|
+
new Promise((resolve) => {
|
|
1507
|
+
timer = setTimeout(() => resolve(false), 5e3);
|
|
1508
|
+
}),
|
|
1509
|
+
clientFailure.then(() => false, () => false)
|
|
1510
|
+
]);
|
|
1511
|
+
} finally {
|
|
1512
|
+
clearTimeout(timer);
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1515
|
+
throw error;
|
|
1516
|
+
} finally {
|
|
1517
|
+
turn.experimental_userMessages.close();
|
|
1518
|
+
stopSteering();
|
|
1519
|
+
removeAbortListener();
|
|
1520
|
+
if (activeTurn === currentTurn) activeTurn = void 0;
|
|
1521
|
+
if (!keepClient) await close();
|
|
1522
|
+
}
|
|
1523
|
+
};
|
|
1524
|
+
return {
|
|
1525
|
+
runTurn,
|
|
1526
|
+
close
|
|
1527
|
+
};
|
|
1685
1528
|
}
|
|
1686
|
-
function createCodexAppServerArgs({
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
"--stdio"
|
|
1698
|
-
];
|
|
1529
|
+
function createCodexAppServerArgs({ cliOverrides = [] } = {}) {
|
|
1530
|
+
return [
|
|
1531
|
+
CODEX_CLI_PATH,
|
|
1532
|
+
"--config",
|
|
1533
|
+
"sandbox_mode=\"danger-full-access\"",
|
|
1534
|
+
"--config",
|
|
1535
|
+
"approval_policy=\"never\"",
|
|
1536
|
+
...cliOverrides.flatMap((override) => ["--config", override]),
|
|
1537
|
+
"app-server",
|
|
1538
|
+
"--stdio"
|
|
1539
|
+
];
|
|
1699
1540
|
}
|
|
1700
|
-
function createThreadParams({
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
config: {
|
|
1712
|
-
...codexConfig,
|
|
1713
|
-
web_search: codexConfig.web_search ?? (start.webSearch ? "live" : "disabled")
|
|
1714
|
-
}
|
|
1715
|
-
};
|
|
1541
|
+
function createThreadParams({ start, workdir, codexModel, codexConfig }) {
|
|
1542
|
+
return {
|
|
1543
|
+
...codexModel == null ? {} : { model: codexModel },
|
|
1544
|
+
cwd: workdir,
|
|
1545
|
+
approvalPolicy: "never",
|
|
1546
|
+
sandbox: "danger-full-access",
|
|
1547
|
+
config: {
|
|
1548
|
+
...codexConfig,
|
|
1549
|
+
web_search: codexConfig.web_search ?? (start.webSearch ? "live" : "disabled")
|
|
1550
|
+
}
|
|
1551
|
+
};
|
|
1716
1552
|
}
|
|
1717
|
-
function createTurnParams({
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
};
|
|
1553
|
+
function createTurnParams({ threadId, start, codexModel, disableEnvironments = false }) {
|
|
1554
|
+
return {
|
|
1555
|
+
threadId,
|
|
1556
|
+
...disableEnvironments ? { environments: [] } : {},
|
|
1557
|
+
input: [{
|
|
1558
|
+
type: "text",
|
|
1559
|
+
text: start.prompt,
|
|
1560
|
+
text_elements: []
|
|
1561
|
+
}],
|
|
1562
|
+
approvalPolicy: "never",
|
|
1563
|
+
sandboxPolicy: {
|
|
1564
|
+
type: "externalSandbox",
|
|
1565
|
+
networkAccess: "enabled"
|
|
1566
|
+
},
|
|
1567
|
+
...codexModel == null ? {} : { model: codexModel },
|
|
1568
|
+
...start.reasoningEffort == null ? {} : { effort: start.reasoningEffort },
|
|
1569
|
+
...start.responseFormat?.type === "json" && start.responseFormat.schema != null ? { outputSchema: start.responseFormat.schema } : {}
|
|
1570
|
+
};
|
|
1736
1571
|
}
|
|
1737
|
-
function assertCodexThreadPermissions({
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
const value = asRecord3(response);
|
|
1742
|
-
const sandbox = asRecord3(value?.sandbox);
|
|
1743
|
-
if (value?.approvalPolicy !== "never" || sandbox?.type !== "dangerFullAccess") {
|
|
1744
|
-
throw new Error(
|
|
1745
|
-
`Codex app-server ${method} did not disable approvals and its platform sandbox.`
|
|
1746
|
-
);
|
|
1747
|
-
}
|
|
1572
|
+
function assertCodexThreadPermissions({ response, method }) {
|
|
1573
|
+
const value = asRecord(response);
|
|
1574
|
+
const sandbox = asRecord(value?.sandbox);
|
|
1575
|
+
if (value?.approvalPolicy !== "never" || sandbox?.type !== "dangerFullAccess") throw new Error(`Codex app-server ${method} did not disable approvals and its platform sandbox.`);
|
|
1748
1576
|
}
|
|
1749
1577
|
function createDynamicTools(tools) {
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
};
|
|
1774
|
-
});
|
|
1775
|
-
return { specs, originalNameByAlias };
|
|
1578
|
+
const validOriginalNames = new Set(tools.map((tool) => tool.name).filter(isValidDynamicToolName));
|
|
1579
|
+
const usedAliases = /* @__PURE__ */ new Set();
|
|
1580
|
+
const originalNameByAlias = /* @__PURE__ */ new Map();
|
|
1581
|
+
return {
|
|
1582
|
+
specs: tools.map((tool, index) => {
|
|
1583
|
+
let alias = tool.name;
|
|
1584
|
+
if (!isValidDynamicToolName(alias) || usedAliases.has(alias)) {
|
|
1585
|
+
const base = `ai_sdk_tool_${createHash("sha256").update(tool.name).digest("hex").slice(0, 16)}`;
|
|
1586
|
+
alias = base;
|
|
1587
|
+
let suffix = index;
|
|
1588
|
+
while (usedAliases.has(alias) || validOriginalNames.has(alias)) alias = `${base}_${suffix++}`;
|
|
1589
|
+
}
|
|
1590
|
+
usedAliases.add(alias);
|
|
1591
|
+
originalNameByAlias.set(alias, tool.name);
|
|
1592
|
+
return {
|
|
1593
|
+
type: "function",
|
|
1594
|
+
name: alias,
|
|
1595
|
+
description: tool.description ?? "",
|
|
1596
|
+
inputSchema: tool.inputSchema ?? {}
|
|
1597
|
+
};
|
|
1598
|
+
}),
|
|
1599
|
+
originalNameByAlias
|
|
1600
|
+
};
|
|
1776
1601
|
}
|
|
1777
1602
|
function isValidDynamicToolName(name) {
|
|
1778
|
-
|
|
1603
|
+
return name.length > 0 && name.length <= 128 && /^[a-zA-Z0-9_-]+$/.test(name) && name !== "mcp" && !name.startsWith("mcp__");
|
|
1779
1604
|
}
|
|
1780
|
-
async function handleAppServerRequest({
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
const result = await requestToolResult(params.callId);
|
|
1809
|
-
emit({
|
|
1810
|
-
type: "tool-result",
|
|
1811
|
-
toolCallId: params.callId,
|
|
1812
|
-
toolName: originalToolName,
|
|
1813
|
-
result: result.output ?? null,
|
|
1814
|
-
isError: result.isError === true
|
|
1815
|
-
});
|
|
1816
|
-
return {
|
|
1817
|
-
contentItems: [
|
|
1818
|
-
{ type: "inputText", text: serializeToolOutput(result.output) }
|
|
1819
|
-
],
|
|
1820
|
-
success: result.isError !== true
|
|
1821
|
-
};
|
|
1605
|
+
async function handleAppServerRequest({ request, dynamicTools, emit, requestToolResult }) {
|
|
1606
|
+
if (request.method !== "item/tool/call") throw new Error(`Codex app-server requested unsupported method '${request.method}'.`);
|
|
1607
|
+
const params = asRecord(request.params);
|
|
1608
|
+
if (params == null || typeof params.callId !== "string" || typeof params.tool !== "string") throw new Error("Codex app-server sent an invalid dynamic tool request.");
|
|
1609
|
+
const originalToolName = dynamicTools.originalNameByAlias.get(params.tool);
|
|
1610
|
+
if (originalToolName == null) throw new Error(`Codex app-server requested unknown dynamic tool '${params.tool}'.`);
|
|
1611
|
+
emit({
|
|
1612
|
+
type: "tool-call",
|
|
1613
|
+
toolCallId: params.callId,
|
|
1614
|
+
toolName: originalToolName,
|
|
1615
|
+
input: JSON.stringify(params.arguments ?? {}),
|
|
1616
|
+
providerExecuted: false
|
|
1617
|
+
});
|
|
1618
|
+
const result = await requestToolResult(params.callId);
|
|
1619
|
+
emit({
|
|
1620
|
+
type: "tool-result",
|
|
1621
|
+
toolCallId: params.callId,
|
|
1622
|
+
toolName: originalToolName,
|
|
1623
|
+
result: result.output ?? null,
|
|
1624
|
+
isError: result.isError === true
|
|
1625
|
+
});
|
|
1626
|
+
return {
|
|
1627
|
+
contentItems: [{
|
|
1628
|
+
type: "inputText",
|
|
1629
|
+
text: serializeToolOutput(result.output)
|
|
1630
|
+
}],
|
|
1631
|
+
success: result.isError !== true
|
|
1632
|
+
};
|
|
1822
1633
|
}
|
|
1823
1634
|
function serializeToolOutput(output) {
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1635
|
+
if (typeof output === "string") return output;
|
|
1636
|
+
try {
|
|
1637
|
+
return JSON.stringify(output) ?? String(output);
|
|
1638
|
+
} catch {
|
|
1639
|
+
return String(output);
|
|
1640
|
+
}
|
|
1830
1641
|
}
|
|
1831
|
-
function readNestedString({
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
let current = value;
|
|
1837
|
-
for (const segment of path2) current = asRecord3(current)?.[segment];
|
|
1838
|
-
if (typeof current !== "string" || current.length === 0) {
|
|
1839
|
-
throw new Error(`Codex app-server ${method} returned an invalid response.`);
|
|
1840
|
-
}
|
|
1841
|
-
return current;
|
|
1642
|
+
function readNestedString({ value, path, method }) {
|
|
1643
|
+
let current = value;
|
|
1644
|
+
for (const segment of path) current = asRecord(current)?.[segment];
|
|
1645
|
+
if (typeof current !== "string" || current.length === 0) throw new Error(`Codex app-server ${method} returned an invalid response.`);
|
|
1646
|
+
return current;
|
|
1842
1647
|
}
|
|
1843
1648
|
function assertSuccessfulTurn(result) {
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
}
|
|
1848
|
-
throw new Error(
|
|
1849
|
-
result.error ?? `Codex turn ended with status '${result.status}'.`
|
|
1850
|
-
);
|
|
1649
|
+
if (result.status === "completed") return;
|
|
1650
|
+
if (result.status === "interrupted") throw new DOMException("Codex turn was interrupted.", "AbortError");
|
|
1651
|
+
throw new Error(result.error ?? `Codex turn ended with status '${result.status}'.`);
|
|
1851
1652
|
}
|
|
1852
|
-
function
|
|
1853
|
-
|
|
1653
|
+
function asRecord(value) {
|
|
1654
|
+
return value != null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
1854
1655
|
}
|
|
1855
|
-
|
|
1856
|
-
|
|
1656
|
+
//#endregion
|
|
1657
|
+
//#region src/bridge/codex-step-tracker.ts
|
|
1857
1658
|
function createCodexStepTracker(input) {
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1659
|
+
let stepOpen = false;
|
|
1660
|
+
const pendingToolItemIds = /* @__PURE__ */ new Set();
|
|
1661
|
+
const finishStep = () => {
|
|
1662
|
+
if (!stepOpen || pendingToolItemIds.size > 0) return;
|
|
1663
|
+
input.send({
|
|
1664
|
+
type: "finish-step",
|
|
1665
|
+
finishReason: {
|
|
1666
|
+
unified: "stop",
|
|
1667
|
+
raw: "stop"
|
|
1668
|
+
},
|
|
1669
|
+
usage: defaultUsage(),
|
|
1670
|
+
harnessMetadata: { codex: { inferredStep: true } }
|
|
1671
|
+
});
|
|
1672
|
+
stepOpen = false;
|
|
1673
|
+
};
|
|
1674
|
+
return {
|
|
1675
|
+
observeEvent({ event, itemId }) {
|
|
1676
|
+
const item = event.item;
|
|
1677
|
+
if (!item || !isStepItem(item)) return;
|
|
1678
|
+
stepOpen = true;
|
|
1679
|
+
if (isToolStepItem(item)) {
|
|
1680
|
+
if (event.type === "item.started" && itemId) pendingToolItemIds.add(itemId);
|
|
1681
|
+
else if (event.type === "item.completed") {
|
|
1682
|
+
if (itemId) pendingToolItemIds.delete(itemId);
|
|
1683
|
+
finishStep();
|
|
1684
|
+
}
|
|
1685
|
+
}
|
|
1686
|
+
},
|
|
1687
|
+
finishTurn() {
|
|
1688
|
+
pendingToolItemIds.clear();
|
|
1689
|
+
finishStep();
|
|
1690
|
+
}
|
|
1691
|
+
};
|
|
1889
1692
|
}
|
|
1890
1693
|
function isStepItem(item) {
|
|
1891
|
-
|
|
1694
|
+
return isModelStepItem(item) || isToolStepItem(item);
|
|
1892
1695
|
}
|
|
1893
1696
|
function isModelStepItem(item) {
|
|
1894
|
-
|
|
1697
|
+
return item.type === "reasoning" || item.type === "agent_message";
|
|
1895
1698
|
}
|
|
1896
1699
|
function isToolStepItem(item) {
|
|
1897
|
-
|
|
1700
|
+
return item.type === "command_execution" || item.type === "native_tool" || item.type === "mcp_tool_call" || item.type === "dynamic_tool_call" || item.type === "web_search" || item.type === "file_change" || item.type === "todo_list";
|
|
1898
1701
|
}
|
|
1899
1702
|
function defaultUsage() {
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1703
|
+
return {
|
|
1704
|
+
inputTokens: {
|
|
1705
|
+
total: 0,
|
|
1706
|
+
noCache: 0,
|
|
1707
|
+
cacheRead: 0,
|
|
1708
|
+
cacheWrite: 0
|
|
1709
|
+
},
|
|
1710
|
+
outputTokens: {
|
|
1711
|
+
total: 0,
|
|
1712
|
+
text: 0
|
|
1713
|
+
}
|
|
1714
|
+
};
|
|
1904
1715
|
}
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
web_search: "webSearch"
|
|
1716
|
+
//#endregion
|
|
1717
|
+
//#region src/bridge/create-emit-stream-event.ts
|
|
1718
|
+
const NATIVE_TO_COMMON = {
|
|
1719
|
+
shell: "bash",
|
|
1720
|
+
web_search: "webSearch"
|
|
1911
1721
|
};
|
|
1912
1722
|
function toCommonName(nativeName) {
|
|
1913
|
-
|
|
1723
|
+
return NATIVE_TO_COMMON[nativeName] ?? nativeName;
|
|
1914
1724
|
}
|
|
1915
1725
|
function getMcpToolName(item) {
|
|
1916
|
-
|
|
1917
|
-
|
|
1726
|
+
const toolName = item.tool ?? "unknown";
|
|
1727
|
+
return item.server != null && item.server.length > 0 ? `mcp__${item.server}__${toolName}` : toolName;
|
|
1918
1728
|
}
|
|
1919
|
-
function createEmitStreamEvent({
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
1729
|
+
function createEmitStreamEvent({ send, stepTracker, setTurnUsage, setThreadId, emitWarning, emitError }) {
|
|
1730
|
+
const textByItem = /* @__PURE__ */ new Map();
|
|
1731
|
+
const reasoningByItem = /* @__PURE__ */ new Map();
|
|
1732
|
+
const emittedWebSearchToolCalls = /* @__PURE__ */ new Set();
|
|
1733
|
+
return (event) => {
|
|
1734
|
+
if (event.type === "thread.started" && typeof event.thread_id === "string") {
|
|
1735
|
+
setThreadId(event.thread_id);
|
|
1736
|
+
send({
|
|
1737
|
+
type: "bridge-thread",
|
|
1738
|
+
threadId: event.thread_id
|
|
1739
|
+
});
|
|
1740
|
+
}
|
|
1741
|
+
if (event.type === "turn.completed") {
|
|
1742
|
+
if (event.usage) setTurnUsage(mapUsage(event.usage));
|
|
1743
|
+
stepTracker.finishTurn();
|
|
1744
|
+
return;
|
|
1745
|
+
}
|
|
1746
|
+
if (event.type === "turn.failed") {
|
|
1747
|
+
emitError({
|
|
1748
|
+
error: event.error?.message ?? "codex turn failed",
|
|
1749
|
+
message: "codex turn failed"
|
|
1750
|
+
});
|
|
1751
|
+
return;
|
|
1752
|
+
}
|
|
1753
|
+
if (event.type === "error") {
|
|
1754
|
+
emitError({
|
|
1755
|
+
error: event.message ?? "codex error",
|
|
1756
|
+
message: "codex stream error"
|
|
1757
|
+
});
|
|
1758
|
+
return;
|
|
1759
|
+
}
|
|
1760
|
+
if (!event.item) return;
|
|
1761
|
+
const item = event.item;
|
|
1762
|
+
const id = item.id ?? randomUUID();
|
|
1763
|
+
const observeStep = () => {
|
|
1764
|
+
stepTracker.observeEvent({
|
|
1765
|
+
event,
|
|
1766
|
+
itemId: id
|
|
1767
|
+
});
|
|
1768
|
+
};
|
|
1769
|
+
if (item.type === "native_tool" && item.tool != null) {
|
|
1770
|
+
if (event.type === "item.started" && item.input != null) send({
|
|
1771
|
+
type: "tool-call",
|
|
1772
|
+
toolCallId: id,
|
|
1773
|
+
toolName: item.tool,
|
|
1774
|
+
input: item.input,
|
|
1775
|
+
providerExecuted: true
|
|
1776
|
+
});
|
|
1777
|
+
else if (event.type === "item.completed") send({
|
|
1778
|
+
type: "tool-result",
|
|
1779
|
+
toolCallId: id,
|
|
1780
|
+
toolName: item.tool,
|
|
1781
|
+
result: item.result,
|
|
1782
|
+
...item.isError ? { isError: true } : {}
|
|
1783
|
+
});
|
|
1784
|
+
stepTracker.observeEvent({
|
|
1785
|
+
event,
|
|
1786
|
+
itemId: `native-tool:${id}`
|
|
1787
|
+
});
|
|
1788
|
+
return;
|
|
1789
|
+
}
|
|
1790
|
+
if (item.type === "agent_message" && typeof item.text === "string") {
|
|
1791
|
+
if (!textByItem.has(id)) {
|
|
1792
|
+
send({
|
|
1793
|
+
type: "text-start",
|
|
1794
|
+
id
|
|
1795
|
+
});
|
|
1796
|
+
textByItem.set(id, "");
|
|
1797
|
+
}
|
|
1798
|
+
const last = textByItem.get(id) ?? "";
|
|
1799
|
+
const next = item.text;
|
|
1800
|
+
if (next.length > last.length) {
|
|
1801
|
+
send({
|
|
1802
|
+
type: "text-delta",
|
|
1803
|
+
id,
|
|
1804
|
+
delta: next.slice(last.length)
|
|
1805
|
+
});
|
|
1806
|
+
textByItem.set(id, next);
|
|
1807
|
+
}
|
|
1808
|
+
if (event.type === "item.completed") send({
|
|
1809
|
+
type: "text-end",
|
|
1810
|
+
id
|
|
1811
|
+
});
|
|
1812
|
+
observeStep();
|
|
1813
|
+
return;
|
|
1814
|
+
}
|
|
1815
|
+
if (item.type === "reasoning" && typeof item.text === "string") {
|
|
1816
|
+
if (!reasoningByItem.has(id)) {
|
|
1817
|
+
send({
|
|
1818
|
+
type: "reasoning-start",
|
|
1819
|
+
id
|
|
1820
|
+
});
|
|
1821
|
+
reasoningByItem.set(id, "");
|
|
1822
|
+
}
|
|
1823
|
+
const last = reasoningByItem.get(id) ?? "";
|
|
1824
|
+
const next = item.text;
|
|
1825
|
+
if (next.length > last.length) {
|
|
1826
|
+
send({
|
|
1827
|
+
type: "reasoning-delta",
|
|
1828
|
+
id,
|
|
1829
|
+
delta: next.slice(last.length)
|
|
1830
|
+
});
|
|
1831
|
+
reasoningByItem.set(id, next);
|
|
1832
|
+
}
|
|
1833
|
+
if (event.type === "item.completed") send({
|
|
1834
|
+
type: "reasoning-end",
|
|
1835
|
+
id
|
|
1836
|
+
});
|
|
1837
|
+
observeStep();
|
|
1838
|
+
return;
|
|
1839
|
+
}
|
|
1840
|
+
if (item.type === "command_execution") {
|
|
1841
|
+
const nativeName = "shell";
|
|
1842
|
+
if (event.type === "item.started") send({
|
|
1843
|
+
type: "tool-call",
|
|
1844
|
+
toolCallId: id,
|
|
1845
|
+
toolName: toCommonName(nativeName),
|
|
1846
|
+
nativeName,
|
|
1847
|
+
input: JSON.stringify({ command: item.command ?? "" }),
|
|
1848
|
+
providerExecuted: true
|
|
1849
|
+
});
|
|
1850
|
+
else if (event.type === "item.completed") send({
|
|
1851
|
+
type: "tool-result",
|
|
1852
|
+
toolCallId: id,
|
|
1853
|
+
toolName: toCommonName(nativeName),
|
|
1854
|
+
result: {
|
|
1855
|
+
exitCode: item.exit_code ?? null,
|
|
1856
|
+
output: item.aggregated_output ?? "",
|
|
1857
|
+
status: item.status ?? "completed"
|
|
1858
|
+
}
|
|
1859
|
+
});
|
|
1860
|
+
observeStep();
|
|
1861
|
+
return;
|
|
1862
|
+
}
|
|
1863
|
+
if (item.type === "mcp_tool_call") {
|
|
1864
|
+
const toolName = getMcpToolName(item);
|
|
1865
|
+
if (event.type === "item.started") send({
|
|
1866
|
+
type: "tool-call",
|
|
1867
|
+
toolCallId: id,
|
|
1868
|
+
toolName,
|
|
1869
|
+
nativeName: toolName,
|
|
1870
|
+
input: JSON.stringify(item.arguments ?? {}),
|
|
1871
|
+
providerExecuted: true,
|
|
1872
|
+
dynamic: true
|
|
1873
|
+
});
|
|
1874
|
+
else if (event.type === "item.completed") send({
|
|
1875
|
+
type: "tool-result",
|
|
1876
|
+
toolCallId: id,
|
|
1877
|
+
toolName,
|
|
1878
|
+
result: extractMcpToolCallResult(item),
|
|
1879
|
+
dynamic: true
|
|
1880
|
+
});
|
|
1881
|
+
observeStep();
|
|
1882
|
+
return;
|
|
1883
|
+
}
|
|
1884
|
+
if (item.type === "web_search") {
|
|
1885
|
+
const nativeName = "web_search";
|
|
1886
|
+
const query = getWebSearchQuery(item);
|
|
1887
|
+
if (event.type === "item.started" || event.type === "item.updated") {
|
|
1888
|
+
if (query !== void 0) emitWebSearchToolCall({
|
|
1889
|
+
id,
|
|
1890
|
+
query,
|
|
1891
|
+
nativeName,
|
|
1892
|
+
emittedWebSearchToolCalls,
|
|
1893
|
+
send
|
|
1894
|
+
});
|
|
1895
|
+
} else if (event.type === "item.completed") {
|
|
1896
|
+
emitWebSearchToolCall({
|
|
1897
|
+
id,
|
|
1898
|
+
query: query ?? "",
|
|
1899
|
+
nativeName,
|
|
1900
|
+
emittedWebSearchToolCalls,
|
|
1901
|
+
send
|
|
1902
|
+
});
|
|
1903
|
+
send({
|
|
1904
|
+
type: "tool-result",
|
|
1905
|
+
toolCallId: id,
|
|
1906
|
+
toolName: toCommonName(nativeName),
|
|
1907
|
+
result: item.result ?? item.action ?? null
|
|
1908
|
+
});
|
|
1909
|
+
emittedWebSearchToolCalls.delete(id);
|
|
1910
|
+
}
|
|
1911
|
+
observeStep();
|
|
1912
|
+
return;
|
|
1913
|
+
}
|
|
1914
|
+
if (item.type === "file_change" && event.type === "item.completed") {
|
|
1915
|
+
for (const change of item.changes ?? []) send({
|
|
1916
|
+
type: "file-change",
|
|
1917
|
+
event: change.kind === "add" ? "create" : change.kind === "delete" ? "delete" : "modify",
|
|
1918
|
+
path: change.path
|
|
1919
|
+
});
|
|
1920
|
+
observeStep();
|
|
1921
|
+
return;
|
|
1922
|
+
}
|
|
1923
|
+
if (item.type === "error" && event.type === "item.completed") emitWarning({ message: typeof item.message === "string" && item.message.trim() ? item.message : "codex reported a non-fatal error item" });
|
|
1924
|
+
};
|
|
2109
1925
|
}
|
|
2110
1926
|
function getWebSearchQuery(item) {
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
}
|
|
2114
|
-
if (typeof item.action?.query === "string" && item.action.query.length > 0) {
|
|
2115
|
-
return item.action.query;
|
|
2116
|
-
}
|
|
1927
|
+
if (typeof item.query === "string" && item.query.length > 0) return item.query;
|
|
1928
|
+
if (typeof item.action?.query === "string" && item.action.query.length > 0) return item.action.query;
|
|
2117
1929
|
}
|
|
2118
|
-
function emitWebSearchToolCall({
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
toolCallId: id,
|
|
2130
|
-
toolName: toCommonName(nativeName),
|
|
2131
|
-
nativeName,
|
|
2132
|
-
input: JSON.stringify({ query }),
|
|
2133
|
-
providerExecuted: true
|
|
2134
|
-
});
|
|
1930
|
+
function emitWebSearchToolCall({ id, query, nativeName, emittedWebSearchToolCalls, send }) {
|
|
1931
|
+
if (emittedWebSearchToolCalls.has(id)) return;
|
|
1932
|
+
emittedWebSearchToolCalls.add(id);
|
|
1933
|
+
send({
|
|
1934
|
+
type: "tool-call",
|
|
1935
|
+
toolCallId: id,
|
|
1936
|
+
toolName: toCommonName(nativeName),
|
|
1937
|
+
nativeName,
|
|
1938
|
+
input: JSON.stringify({ query }),
|
|
1939
|
+
providerExecuted: true
|
|
1940
|
+
});
|
|
2135
1941
|
}
|
|
2136
1942
|
function extractMcpToolCallResult(item) {
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
if (result.structured_content !== void 0 && result.structured_content !== null) {
|
|
2142
|
-
return result.structured_content;
|
|
2143
|
-
}
|
|
2144
|
-
return result.content ?? null;
|
|
1943
|
+
if (item.result === void 0 || item.result === null || typeof item.result !== "object") return item.error?.message ? { error: item.error.message } : null;
|
|
1944
|
+
const result = item.result;
|
|
1945
|
+
if (result.structured_content !== void 0 && result.structured_content !== null) return result.structured_content;
|
|
1946
|
+
return result.content ?? null;
|
|
2145
1947
|
}
|
|
2146
1948
|
function mapUsage(usage) {
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
1949
|
+
const input = usage.input_tokens ?? 0;
|
|
1950
|
+
const cacheRead = usage.cached_input_tokens ?? 0;
|
|
1951
|
+
const cacheWrite = usage.cache_write_input_tokens ?? 0;
|
|
1952
|
+
return {
|
|
1953
|
+
inputTokens: {
|
|
1954
|
+
total: input,
|
|
1955
|
+
noCache: Math.max(0, input - cacheRead - cacheWrite),
|
|
1956
|
+
cacheRead,
|
|
1957
|
+
cacheWrite
|
|
1958
|
+
},
|
|
1959
|
+
outputTokens: {
|
|
1960
|
+
total: usage.output_tokens ?? 0,
|
|
1961
|
+
text: usage.output_tokens ?? 0
|
|
1962
|
+
}
|
|
1963
|
+
};
|
|
2162
1964
|
}
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
1965
|
+
//#endregion
|
|
1966
|
+
//#region src/bridge/index.ts
|
|
1967
|
+
const args = parseArgs(argv.slice(2));
|
|
1968
|
+
const workdir = requireArg({
|
|
1969
|
+
value: args.workdir,
|
|
1970
|
+
name: "--workdir"
|
|
1971
|
+
});
|
|
1972
|
+
const bridgeStateDir = requireArg({
|
|
1973
|
+
value: args.bridgeStateDir,
|
|
1974
|
+
name: "--bridge-state-dir"
|
|
2171
1975
|
});
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
1976
|
+
const HARNESS_CLIENT_APP = env.AI_SDK_HARNESS_CLIENT_APP;
|
|
1977
|
+
const threadState = { id: void 0 };
|
|
1978
|
+
const appServer = createCodexAppServerRuntime();
|
|
2175
1979
|
await runBridge({
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
1980
|
+
bridgeType: "codex",
|
|
1981
|
+
bridgeStateDir,
|
|
1982
|
+
onStart: runTurn,
|
|
1983
|
+
onStop: async () => {
|
|
1984
|
+
const data = threadState.id ? { threadId: threadState.id } : {};
|
|
1985
|
+
await appServer.close();
|
|
1986
|
+
return data;
|
|
1987
|
+
},
|
|
1988
|
+
onDestroy: () => appServer.close()
|
|
2185
1989
|
});
|
|
2186
1990
|
async function runTurn(start, turn) {
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
1991
|
+
const emit = (msg) => turn.emit(msg);
|
|
1992
|
+
if (start.restartThread) threadState.id = void 0;
|
|
1993
|
+
else if (typeof start.resumeThreadId === "string" && start.resumeThreadId.length > 0) threadState.id = start.resumeThreadId;
|
|
1994
|
+
const runtime = resolveCodexRuntime({ start });
|
|
1995
|
+
let turnUsage = defaultUsage();
|
|
1996
|
+
const stepTracker = createCodexStepTracker({ send: emit });
|
|
1997
|
+
const emitStreamEvent = createEmitStreamEvent({
|
|
1998
|
+
send: emit,
|
|
1999
|
+
stepTracker,
|
|
2000
|
+
setTurnUsage: (usage) => turnUsage = usage,
|
|
2001
|
+
setThreadId: (threadId) => threadState.id = threadId,
|
|
2002
|
+
emitWarning: turn.emitWarning,
|
|
2003
|
+
emitError: turn.emitError
|
|
2004
|
+
});
|
|
2005
|
+
try {
|
|
2006
|
+
await appServer.runTurn({
|
|
2007
|
+
start,
|
|
2008
|
+
turn,
|
|
2009
|
+
emit,
|
|
2010
|
+
workdir,
|
|
2011
|
+
threadId: threadState.id,
|
|
2012
|
+
codexModel: runtime.codexModel,
|
|
2013
|
+
codexConfig: runtime.codexConfig,
|
|
2014
|
+
stepTracker,
|
|
2015
|
+
emitStreamEvent
|
|
2016
|
+
});
|
|
2017
|
+
} catch (error) {
|
|
2018
|
+
if (!turn.abortSignal.aborted) turn.emitError({
|
|
2019
|
+
error,
|
|
2020
|
+
message: "codex turn failed"
|
|
2021
|
+
});
|
|
2022
|
+
return;
|
|
2023
|
+
}
|
|
2024
|
+
emit({
|
|
2025
|
+
type: "finish",
|
|
2026
|
+
finishReason: {
|
|
2027
|
+
unified: "stop",
|
|
2028
|
+
raw: "stop"
|
|
2029
|
+
},
|
|
2030
|
+
totalUsage: turnUsage
|
|
2031
|
+
});
|
|
2227
2032
|
}
|
|
2228
2033
|
function resolveCodexRuntime({ start }) {
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
"User-Agent": HARNESS_CLIENT_APP,
|
|
2264
|
-
"x-client-app": HARNESS_CLIENT_APP
|
|
2265
|
-
} : {}
|
|
2266
|
-
}
|
|
2267
|
-
} : {}
|
|
2268
|
-
}
|
|
2269
|
-
};
|
|
2270
|
-
}
|
|
2271
|
-
if (start.mcpServers != null) {
|
|
2272
|
-
codexConfig.mcp_servers = start.mcpServers;
|
|
2273
|
-
}
|
|
2274
|
-
return { codexConfig, codexModel };
|
|
2034
|
+
const codexConfig = {
|
|
2035
|
+
...start.codexConfig,
|
|
2036
|
+
developer_instructions: [start.instructions, "Only respond with your `final` message once you have fully addressed the user request."].filter((instruction) => Boolean(instruction)).join("\n\n"),
|
|
2037
|
+
model_reasoning_summary: "detailed"
|
|
2038
|
+
};
|
|
2039
|
+
const gatewayBaseUrl = env.AI_GATEWAY_BASE_URL;
|
|
2040
|
+
const hasGatewayAuth = Boolean(env.AI_GATEWAY_API_KEY || gatewayBaseUrl);
|
|
2041
|
+
if (hasGatewayAuth && !gatewayBaseUrl) throw new Error("AI Gateway auth was selected but AI_GATEWAY_BASE_URL is missing from the Codex bridge environment.");
|
|
2042
|
+
const apiBaseUrl = hasGatewayAuth ? gatewayBaseUrl : env.OPENAI_BASE_URL ?? (env.CODEX_API_KEY != null || start.headers != null ? "https://api.openai.com/v1" : void 0);
|
|
2043
|
+
const codexModel = start.model && hasGatewayAuth && !start.model.includes("/") ? `openai/${start.model}` : start.model;
|
|
2044
|
+
if (hasGatewayAuth && codexModel?.startsWith("openai/")) codexConfig.model_supports_reasoning_summaries = true;
|
|
2045
|
+
if (apiBaseUrl) {
|
|
2046
|
+
codexConfig.preferred_auth_method = "apikey";
|
|
2047
|
+
codexConfig.model_provider = "agent_bridge_openai";
|
|
2048
|
+
codexConfig.model_providers = { agent_bridge_openai: {
|
|
2049
|
+
name: env.CODEX_MODEL_PROVIDER_NAME || "Agent Bridge OpenAI",
|
|
2050
|
+
base_url: apiBaseUrl,
|
|
2051
|
+
env_key: "CODEX_API_KEY",
|
|
2052
|
+
wire_api: "responses",
|
|
2053
|
+
supports_websockets: false,
|
|
2054
|
+
...start.headers != null || hasGatewayAuth && HARNESS_CLIENT_APP ? { http_headers: {
|
|
2055
|
+
...start.headers,
|
|
2056
|
+
...hasGatewayAuth && HARNESS_CLIENT_APP ? {
|
|
2057
|
+
"User-Agent": HARNESS_CLIENT_APP,
|
|
2058
|
+
"x-client-app": HARNESS_CLIENT_APP
|
|
2059
|
+
} : {}
|
|
2060
|
+
} } : {}
|
|
2061
|
+
} };
|
|
2062
|
+
}
|
|
2063
|
+
if (start.mcpServers != null) codexConfig.mcp_servers = start.mcpServers;
|
|
2064
|
+
return {
|
|
2065
|
+
codexConfig,
|
|
2066
|
+
codexModel
|
|
2067
|
+
};
|
|
2275
2068
|
}
|
|
2276
|
-
function parseArgs(
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
} else if (args2[i] === "--bridge-state-dir" && i + 1 < args2.length) {
|
|
2282
|
-
out.bridgeStateDir = args2[++i];
|
|
2283
|
-
}
|
|
2284
|
-
}
|
|
2285
|
-
return out;
|
|
2069
|
+
function parseArgs(args) {
|
|
2070
|
+
const out = {};
|
|
2071
|
+
for (let i = 0; i < args.length; i++) if (args[i] === "--workdir" && i + 1 < args.length) out.workdir = args[++i];
|
|
2072
|
+
else if (args[i] === "--bridge-state-dir" && i + 1 < args.length) out.bridgeStateDir = args[++i];
|
|
2073
|
+
return out;
|
|
2286
2074
|
}
|
|
2287
2075
|
function emitFatal(message) {
|
|
2288
|
-
|
|
2289
|
-
|
|
2076
|
+
stdout.write(JSON.stringify({
|
|
2077
|
+
type: "bridge-fatal",
|
|
2078
|
+
message
|
|
2079
|
+
}) + "\n");
|
|
2080
|
+
process.exit(1);
|
|
2290
2081
|
}
|
|
2291
|
-
function requireArg({
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
}) {
|
|
2295
|
-
if (!value) {
|
|
2296
|
-
emitFatal(`Missing ${name} argument.`);
|
|
2297
|
-
}
|
|
2298
|
-
return value;
|
|
2082
|
+
function requireArg({ value, name }) {
|
|
2083
|
+
if (!value) emitFatal(`Missing ${name} argument.`);
|
|
2084
|
+
return value;
|
|
2299
2085
|
}
|
|
2086
|
+
//#endregion
|
|
2087
|
+
export {};
|
|
2088
|
+
|
|
2300
2089
|
//# sourceMappingURL=index.mjs.map
|