@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.
@@ -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
- validateBasicSandboxSettings({
251
+ validateBridgeSandboxSettings({
250
252
  sandboxSession,
251
- port: settings.port,
252
- portEndpoint: settings.portEndpoint,
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 endpoint = await resolveBridgeEndpoint({
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: ({ abortSignal }) =>
394
- openWebSocket({ ...attachEndpoint, abortSignal }),
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
- override: settings.port,
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 endpoint = await resolveBridgeEndpoint({
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: ({ abortSignal }) =>
552
- openWebSocket({ ...bridgeEndpoint, abortSignal }),
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,