@ai-sdk/harness-codex 1.0.119 → 1.0.120
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 +11 -0
- package/dist/bridge/index.mjs +46 -10
- package/dist/bridge/index.mjs.map +1 -1
- package/dist/index.d.ts +8 -0
- package/dist/index.js +42 -8
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/bridge/create-emit-stream-event.ts +54 -10
- package/src/codex-harness.ts +54 -6
|
@@ -77,6 +77,7 @@ export function createEmitStreamEvent({
|
|
|
77
77
|
}): (event: CodexEvent) => void {
|
|
78
78
|
const textByItem = new Map<string, string>();
|
|
79
79
|
const reasoningByItem = new Map<string, string>();
|
|
80
|
+
const emittedWebSearchToolCalls = new Set<string>();
|
|
80
81
|
|
|
81
82
|
return event => {
|
|
82
83
|
if (
|
|
@@ -206,24 +207,32 @@ export function createEmitStreamEvent({
|
|
|
206
207
|
|
|
207
208
|
if (item.type === 'web_search') {
|
|
208
209
|
const nativeName = 'web_search';
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
210
|
+
const query = getWebSearchQuery(item);
|
|
211
|
+
if (event.type === 'item.started' || event.type === 'item.updated') {
|
|
212
|
+
if (query !== undefined) {
|
|
213
|
+
emitWebSearchToolCall({
|
|
214
|
+
id,
|
|
215
|
+
query,
|
|
216
|
+
nativeName,
|
|
217
|
+
emittedWebSearchToolCalls,
|
|
218
|
+
send,
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
} else if (event.type === 'item.completed') {
|
|
222
|
+
emitWebSearchToolCall({
|
|
223
|
+
id,
|
|
224
|
+
query: query ?? '',
|
|
214
225
|
nativeName,
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
}),
|
|
218
|
-
providerExecuted: true,
|
|
226
|
+
emittedWebSearchToolCalls,
|
|
227
|
+
send,
|
|
219
228
|
});
|
|
220
|
-
} else if (event.type === 'item.completed') {
|
|
221
229
|
send({
|
|
222
230
|
type: 'tool-result',
|
|
223
231
|
toolCallId: id,
|
|
224
232
|
toolName: toCommonName(nativeName),
|
|
225
233
|
result: item.result ?? item.action ?? null,
|
|
226
234
|
});
|
|
235
|
+
emittedWebSearchToolCalls.delete(id);
|
|
227
236
|
}
|
|
228
237
|
observeStep();
|
|
229
238
|
return;
|
|
@@ -256,6 +265,41 @@ export function createEmitStreamEvent({
|
|
|
256
265
|
};
|
|
257
266
|
}
|
|
258
267
|
|
|
268
|
+
function getWebSearchQuery(item: CodexItem): string | undefined {
|
|
269
|
+
if (typeof item.query === 'string' && item.query.length > 0) {
|
|
270
|
+
return item.query;
|
|
271
|
+
}
|
|
272
|
+
if (typeof item.action?.query === 'string' && item.action.query.length > 0) {
|
|
273
|
+
return item.action.query;
|
|
274
|
+
}
|
|
275
|
+
return undefined;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function emitWebSearchToolCall({
|
|
279
|
+
id,
|
|
280
|
+
query,
|
|
281
|
+
nativeName,
|
|
282
|
+
emittedWebSearchToolCalls,
|
|
283
|
+
send,
|
|
284
|
+
}: {
|
|
285
|
+
id: string;
|
|
286
|
+
query: string;
|
|
287
|
+
nativeName: string;
|
|
288
|
+
emittedWebSearchToolCalls: Set<string>;
|
|
289
|
+
send: Emit;
|
|
290
|
+
}): void {
|
|
291
|
+
if (emittedWebSearchToolCalls.has(id)) return;
|
|
292
|
+
emittedWebSearchToolCalls.add(id);
|
|
293
|
+
send({
|
|
294
|
+
type: 'tool-call',
|
|
295
|
+
toolCallId: id,
|
|
296
|
+
toolName: toCommonName(nativeName),
|
|
297
|
+
nativeName,
|
|
298
|
+
input: JSON.stringify({ query }),
|
|
299
|
+
providerExecuted: true,
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
|
|
259
303
|
function extractMcpToolCallResult(item: CodexItem): unknown {
|
|
260
304
|
if (
|
|
261
305
|
item.result === undefined ||
|
package/src/codex-harness.ts
CHANGED
|
@@ -39,6 +39,7 @@ import {
|
|
|
39
39
|
waitForBridgeReady,
|
|
40
40
|
withBridgeToken,
|
|
41
41
|
writeSkills,
|
|
42
|
+
type SandboxChannelReconnectOptions,
|
|
42
43
|
} from '@ai-sdk/harness/utils';
|
|
43
44
|
import {
|
|
44
45
|
type Experimental_SandboxProcess,
|
|
@@ -130,6 +131,13 @@ export type CodexHarnessSettings = {
|
|
|
130
131
|
readonly portEndpoint?: HarnessV1PortEndpoint;
|
|
131
132
|
/** Maximum milliseconds to wait for the bridge to advertise its port. Defaults to 120000. */
|
|
132
133
|
readonly startupTimeoutMs?: number;
|
|
134
|
+
/**
|
|
135
|
+
* Configures reconnection attempts after an established bridge connection
|
|
136
|
+
* drops. The reconnect window includes connection establishment and
|
|
137
|
+
* backoff delays. Defaults to 30 seconds with exponential backoff from 50
|
|
138
|
+
* milliseconds up to 2 seconds.
|
|
139
|
+
*/
|
|
140
|
+
readonly reconnect?: SandboxChannelReconnectOptions;
|
|
133
141
|
/**
|
|
134
142
|
* Creates the authentication token used by the sandbox bridge. Defaults to
|
|
135
143
|
* a random 32-byte hexadecimal token.
|
|
@@ -370,11 +378,13 @@ export function createCodex(
|
|
|
370
378
|
token: coords.token,
|
|
371
379
|
});
|
|
372
380
|
const attachChannel: CodexChannel = new SandboxChannel({
|
|
373
|
-
connect: () =>
|
|
381
|
+
connect: ({ abortSignal }) =>
|
|
382
|
+
openWebSocket({ ...attachEndpoint, abortSignal }),
|
|
374
383
|
outboundSchema: outboundMessageSchema,
|
|
375
384
|
initialLastSeenEventId: coords.lastSeenEventId,
|
|
376
385
|
onDiagnostic,
|
|
377
386
|
onBridgeError,
|
|
387
|
+
reconnect: settings.reconnect,
|
|
378
388
|
});
|
|
379
389
|
await attachChannel.open(isContinue ? { resume: true } : undefined);
|
|
380
390
|
return createSession({
|
|
@@ -526,10 +536,12 @@ export function createCodex(
|
|
|
526
536
|
const bridgeEndpoint = withBridgeToken({ endpoint, token });
|
|
527
537
|
|
|
528
538
|
const channel: CodexChannel = new SandboxChannel({
|
|
529
|
-
connect: () =>
|
|
539
|
+
connect: ({ abortSignal }) =>
|
|
540
|
+
openWebSocket({ ...bridgeEndpoint, abortSignal }),
|
|
530
541
|
outboundSchema: outboundMessageSchema,
|
|
531
542
|
onDiagnostic,
|
|
532
543
|
onBridgeError,
|
|
544
|
+
reconnect: settings.reconnect,
|
|
533
545
|
// In replay mode the respawned bridge reloaded the finished turn from
|
|
534
546
|
// disk; seed the cursor and resume so it streams the tail (incl.
|
|
535
547
|
// `finish`).
|
|
@@ -638,21 +650,57 @@ async function resolveBridgeEndpoint({
|
|
|
638
650
|
function openWebSocket({
|
|
639
651
|
url,
|
|
640
652
|
headers,
|
|
641
|
-
|
|
653
|
+
abortSignal,
|
|
654
|
+
}: HarnessV1PortEndpoint & { abortSignal: AbortSignal }): Promise<WebSocket> {
|
|
642
655
|
return new Promise((resolve, reject) => {
|
|
656
|
+
if (abortSignal.aborted) {
|
|
657
|
+
reject(abortSignal.reason ?? new Error('WebSocket connection aborted'));
|
|
658
|
+
return;
|
|
659
|
+
}
|
|
660
|
+
|
|
643
661
|
const ws = new WebSocket(url, {
|
|
644
662
|
headers: headers == null ? undefined : { ...headers },
|
|
645
663
|
});
|
|
646
|
-
|
|
664
|
+
|
|
665
|
+
let settled = false;
|
|
666
|
+
const cleanup = () => {
|
|
667
|
+
abortSignal.removeEventListener('abort', onAbort);
|
|
668
|
+
ws.off('open', onOpen);
|
|
647
669
|
ws.off('error', onError);
|
|
670
|
+
};
|
|
671
|
+
const rejectWithCleanup = (error: unknown) => {
|
|
672
|
+
if (settled) return;
|
|
673
|
+
settled = true;
|
|
674
|
+
cleanup();
|
|
675
|
+
ws.once('error', () => {});
|
|
676
|
+
try {
|
|
677
|
+
ws.terminate();
|
|
678
|
+
} catch {
|
|
679
|
+
try {
|
|
680
|
+
ws.close();
|
|
681
|
+
} catch {
|
|
682
|
+
// best-effort
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
reject(error);
|
|
686
|
+
};
|
|
687
|
+
const onOpen = () => {
|
|
688
|
+
if (settled) return;
|
|
689
|
+
settled = true;
|
|
690
|
+
cleanup();
|
|
648
691
|
resolve(ws);
|
|
649
692
|
};
|
|
650
693
|
const onError = (err: Error) => {
|
|
651
|
-
|
|
652
|
-
reject(err);
|
|
694
|
+
rejectWithCleanup(err);
|
|
653
695
|
};
|
|
696
|
+
const onAbort = () =>
|
|
697
|
+
rejectWithCleanup(
|
|
698
|
+
abortSignal.reason ?? new Error('WebSocket connection aborted'),
|
|
699
|
+
);
|
|
700
|
+
|
|
654
701
|
ws.once('open', onOpen);
|
|
655
702
|
ws.once('error', onError);
|
|
703
|
+
abortSignal.addEventListener('abort', onAbort, { once: true });
|
|
656
704
|
});
|
|
657
705
|
}
|
|
658
706
|
|