@ai-sdk/harness-codex 1.0.165 → 1.0.167
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 +22 -0
- package/README.md +4 -43
- package/dist/bridge/index.mjs +16 -1
- package/dist/bridge/index.mjs.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +22 -100
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/src/bridge/codex-app-server-driver.ts +30 -1
- package/src/codex-harness.ts +25 -147
package/src/codex-harness.ts
CHANGED
|
@@ -14,7 +14,6 @@ import {
|
|
|
14
14
|
type HarnessV1PromptControl,
|
|
15
15
|
type HarnessV1PortEndpoint,
|
|
16
16
|
type HarnessV1ResumeSessionState,
|
|
17
|
-
type HarnessV1NetworkSandboxSession,
|
|
18
17
|
type HarnessV1PermissionMode,
|
|
19
18
|
type HarnessV1Session,
|
|
20
19
|
type HarnessV1Skill,
|
|
@@ -26,6 +25,7 @@ import {
|
|
|
26
25
|
applyCredentialForwarding,
|
|
27
26
|
classifyDiskLog,
|
|
28
27
|
createBridgeToken,
|
|
28
|
+
createBridgeWebSocketConnect,
|
|
29
29
|
resolveSandboxCredentialEnvironment,
|
|
30
30
|
createBridgeErrorHandler,
|
|
31
31
|
createBridgeStartupError,
|
|
@@ -35,12 +35,14 @@ import {
|
|
|
35
35
|
forwardBridgeProcessStream,
|
|
36
36
|
getRestrictedSandboxSession,
|
|
37
37
|
markBridgeStarting,
|
|
38
|
+
resolveBridgeEndpoint,
|
|
39
|
+
resolveBridgePort,
|
|
38
40
|
resolveSandboxHomeDir,
|
|
39
41
|
SandboxChannel,
|
|
40
42
|
shellQuote,
|
|
43
|
+
validateBridgeSandboxSettings,
|
|
41
44
|
warnCredentialBrokeringUnavailable,
|
|
42
45
|
waitForBridgeReady,
|
|
43
|
-
withBridgeToken,
|
|
44
46
|
writeSkills,
|
|
45
47
|
type SandboxChannelReconnectOptions,
|
|
46
48
|
} from '@ai-sdk/harness/utils';
|
|
@@ -246,18 +248,13 @@ export function createCodex(
|
|
|
246
248
|
const toolSafeSandboxSession =
|
|
247
249
|
getRestrictedSandboxSession(sandboxSession);
|
|
248
250
|
const sandboxId = 'id' in sandboxSession ? sandboxSession.id : undefined;
|
|
249
|
-
|
|
251
|
+
validateBridgeSandboxSettings({
|
|
250
252
|
sandboxSession,
|
|
251
|
-
|
|
252
|
-
|
|
253
|
+
sandboxId,
|
|
254
|
+
portOverride: settings.port,
|
|
255
|
+
portEndpointOverride: settings.portEndpoint,
|
|
256
|
+
mintBridgeToken: settings.mintBridgeToken,
|
|
253
257
|
});
|
|
254
|
-
if (settings.mintBridgeToken != null && sandboxId == null) {
|
|
255
|
-
throw new HarnessCapabilityUnsupportedError({
|
|
256
|
-
harnessId: 'codex',
|
|
257
|
-
message:
|
|
258
|
-
'The codex harness cannot use `mintBridgeToken` with a sandbox session that does not expose an id.',
|
|
259
|
-
});
|
|
260
|
-
}
|
|
261
258
|
const lifecycleState = startOpts.continueFrom ?? startOpts.resumeFrom;
|
|
262
259
|
const isResume = lifecycleState != null;
|
|
263
260
|
const isContinue = startOpts.continueFrom != null;
|
|
@@ -380,18 +377,17 @@ export function createCodex(
|
|
|
380
377
|
*/
|
|
381
378
|
if (coords) {
|
|
382
379
|
try {
|
|
383
|
-
const
|
|
380
|
+
const attachEndpoint = await resolveBridgeEndpoint({
|
|
384
381
|
sandboxSession,
|
|
385
|
-
override: settings.portEndpoint,
|
|
386
382
|
port: coords.port,
|
|
387
|
-
|
|
388
|
-
const attachEndpoint = withBridgeToken({
|
|
389
|
-
endpoint,
|
|
383
|
+
portEndpointOverride: settings.portEndpoint,
|
|
390
384
|
token: coords.token,
|
|
391
385
|
});
|
|
392
386
|
const attachChannel: CodexChannel = new SandboxChannel({
|
|
393
|
-
connect: ({
|
|
394
|
-
|
|
387
|
+
connect: createBridgeWebSocketConnect({
|
|
388
|
+
endpoint: attachEndpoint,
|
|
389
|
+
webSocketConstructor: WebSocket,
|
|
390
|
+
}),
|
|
395
391
|
outboundSchema: outboundMessageSchema,
|
|
396
392
|
initialLastSeenEventId: coords.lastSeenEventId,
|
|
397
393
|
onDiagnostic,
|
|
@@ -455,7 +451,7 @@ export function createCodex(
|
|
|
455
451
|
|
|
456
452
|
const port = resolveBridgePort({
|
|
457
453
|
sandboxSession,
|
|
458
|
-
|
|
454
|
+
portOverride: settings.port,
|
|
459
455
|
});
|
|
460
456
|
const token =
|
|
461
457
|
settings.mintBridgeToken == null
|
|
@@ -540,16 +536,20 @@ export function createCodex(
|
|
|
540
536
|
});
|
|
541
537
|
void drainBridgeProcessStream(proc.stdout);
|
|
542
538
|
|
|
543
|
-
const
|
|
539
|
+
const bridgeEndpoint = await resolveBridgeEndpoint({
|
|
544
540
|
sandboxSession,
|
|
545
|
-
override: settings.portEndpoint,
|
|
546
541
|
port: boundPort,
|
|
542
|
+
portEndpointOverride: settings.portEndpoint,
|
|
543
|
+
token,
|
|
547
544
|
});
|
|
548
|
-
const bridgeEndpoint = withBridgeToken({ endpoint, token });
|
|
549
545
|
|
|
550
546
|
const channel: CodexChannel = new SandboxChannel({
|
|
551
|
-
connect: ({
|
|
552
|
-
|
|
547
|
+
connect: createBridgeWebSocketConnect({
|
|
548
|
+
endpoint: bridgeEndpoint,
|
|
549
|
+
webSocketConstructor: WebSocket,
|
|
550
|
+
openRetryTimeoutMs: timeoutMs,
|
|
551
|
+
startAbortSignal: startOpts.abortSignal,
|
|
552
|
+
}),
|
|
553
553
|
outboundSchema: outboundMessageSchema,
|
|
554
554
|
onDiagnostic,
|
|
555
555
|
onBridgeError,
|
|
@@ -594,128 +594,6 @@ export function createCodex(
|
|
|
594
594
|
};
|
|
595
595
|
}
|
|
596
596
|
|
|
597
|
-
function resolveBridgePort({
|
|
598
|
-
sandboxSession,
|
|
599
|
-
override,
|
|
600
|
-
}: {
|
|
601
|
-
sandboxSession: HarnessV1NetworkSandboxSession | SandboxSession;
|
|
602
|
-
override: number | undefined;
|
|
603
|
-
}): number {
|
|
604
|
-
if (override !== undefined) return override;
|
|
605
|
-
if ('ports' in sandboxSession && sandboxSession.ports.length > 0) {
|
|
606
|
-
return sandboxSession.ports[0];
|
|
607
|
-
}
|
|
608
|
-
throw new HarnessCapabilityUnsupportedError({
|
|
609
|
-
harnessId: 'codex',
|
|
610
|
-
message:
|
|
611
|
-
'The codex harness needs a TCP port exposed by the sandbox. ' +
|
|
612
|
-
'Create the sandbox with `ports: [<port>]` or pass `createCodex({ port })`.',
|
|
613
|
-
});
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
function validateBasicSandboxSettings({
|
|
617
|
-
sandboxSession,
|
|
618
|
-
port,
|
|
619
|
-
portEndpoint,
|
|
620
|
-
}: {
|
|
621
|
-
sandboxSession: HarnessV1NetworkSandboxSession | SandboxSession;
|
|
622
|
-
port: number | undefined;
|
|
623
|
-
portEndpoint: HarnessV1PortEndpoint | undefined;
|
|
624
|
-
}): void {
|
|
625
|
-
if ('getPortEndpoint' in sandboxSession) return;
|
|
626
|
-
if (port == null) {
|
|
627
|
-
throw new HarnessCapabilityUnsupportedError({
|
|
628
|
-
harnessId: 'codex',
|
|
629
|
-
message:
|
|
630
|
-
'The codex harness requires an explicit `port` when using a basic sandbox session.',
|
|
631
|
-
});
|
|
632
|
-
}
|
|
633
|
-
if (portEndpoint == null) {
|
|
634
|
-
throw new HarnessCapabilityUnsupportedError({
|
|
635
|
-
harnessId: 'codex',
|
|
636
|
-
message:
|
|
637
|
-
'The codex harness requires an explicit `portEndpoint` when using a basic sandbox session.',
|
|
638
|
-
});
|
|
639
|
-
}
|
|
640
|
-
}
|
|
641
|
-
|
|
642
|
-
async function resolveBridgeEndpoint({
|
|
643
|
-
sandboxSession,
|
|
644
|
-
override,
|
|
645
|
-
port,
|
|
646
|
-
}: {
|
|
647
|
-
sandboxSession: HarnessV1NetworkSandboxSession | SandboxSession;
|
|
648
|
-
override: HarnessV1PortEndpoint | undefined;
|
|
649
|
-
port: number;
|
|
650
|
-
}): Promise<HarnessV1PortEndpoint> {
|
|
651
|
-
if (override != null) return override;
|
|
652
|
-
if ('getPortEndpoint' in sandboxSession) {
|
|
653
|
-
return sandboxSession.getPortEndpoint({ port, protocol: 'ws' });
|
|
654
|
-
}
|
|
655
|
-
throw new HarnessCapabilityUnsupportedError({
|
|
656
|
-
harnessId: 'codex',
|
|
657
|
-
message:
|
|
658
|
-
'The codex harness requires an explicit `portEndpoint` when using a basic sandbox session.',
|
|
659
|
-
});
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
function openWebSocket({
|
|
663
|
-
url,
|
|
664
|
-
headers,
|
|
665
|
-
abortSignal,
|
|
666
|
-
}: HarnessV1PortEndpoint & { abortSignal: AbortSignal }): Promise<WebSocket> {
|
|
667
|
-
return new Promise((resolve, reject) => {
|
|
668
|
-
if (abortSignal.aborted) {
|
|
669
|
-
reject(abortSignal.reason ?? new Error('WebSocket connection aborted'));
|
|
670
|
-
return;
|
|
671
|
-
}
|
|
672
|
-
|
|
673
|
-
const ws = new WebSocket(url, {
|
|
674
|
-
headers: headers == null ? undefined : { ...headers },
|
|
675
|
-
});
|
|
676
|
-
|
|
677
|
-
let settled = false;
|
|
678
|
-
const cleanup = () => {
|
|
679
|
-
abortSignal.removeEventListener('abort', onAbort);
|
|
680
|
-
ws.off('open', onOpen);
|
|
681
|
-
ws.off('error', onError);
|
|
682
|
-
};
|
|
683
|
-
const rejectWithCleanup = (error: unknown) => {
|
|
684
|
-
if (settled) return;
|
|
685
|
-
settled = true;
|
|
686
|
-
cleanup();
|
|
687
|
-
ws.once('error', () => {});
|
|
688
|
-
try {
|
|
689
|
-
ws.terminate();
|
|
690
|
-
} catch {
|
|
691
|
-
try {
|
|
692
|
-
ws.close();
|
|
693
|
-
} catch {
|
|
694
|
-
// best-effort
|
|
695
|
-
}
|
|
696
|
-
}
|
|
697
|
-
reject(error);
|
|
698
|
-
};
|
|
699
|
-
const onOpen = () => {
|
|
700
|
-
if (settled) return;
|
|
701
|
-
settled = true;
|
|
702
|
-
cleanup();
|
|
703
|
-
resolve(ws);
|
|
704
|
-
};
|
|
705
|
-
const onError = (err: Error) => {
|
|
706
|
-
rejectWithCleanup(err);
|
|
707
|
-
};
|
|
708
|
-
const onAbort = () =>
|
|
709
|
-
rejectWithCleanup(
|
|
710
|
-
abortSignal.reason ?? new Error('WebSocket connection aborted'),
|
|
711
|
-
);
|
|
712
|
-
|
|
713
|
-
ws.once('open', onOpen);
|
|
714
|
-
ws.once('error', onError);
|
|
715
|
-
abortSignal.addEventListener('abort', onAbort, { once: true });
|
|
716
|
-
});
|
|
717
|
-
}
|
|
718
|
-
|
|
719
597
|
function createSession({
|
|
720
598
|
sessionId,
|
|
721
599
|
channel,
|