@ai-sdk/harness-codex 1.0.79 → 1.0.81
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 +16 -0
- package/dist/bridge/index.mjs +142 -8
- package/dist/bridge/index.mjs.map +1 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +83 -26
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/bridge/index.ts +0 -2
- package/src/codex-harness.ts +111 -30
package/src/codex-harness.ts
CHANGED
|
@@ -24,8 +24,10 @@ import {
|
|
|
24
24
|
createBridgeStartupError,
|
|
25
25
|
drainBridgeProcessStream,
|
|
26
26
|
forwardBridgeProcessStream,
|
|
27
|
+
getRestrictedSandboxSession,
|
|
27
28
|
markBridgeStarting,
|
|
28
29
|
maskSandboxCredentials,
|
|
30
|
+
resolveSandboxDefaultWorkingDirectory,
|
|
29
31
|
resolveSandboxHomeDir,
|
|
30
32
|
SandboxChannel,
|
|
31
33
|
shellQuote,
|
|
@@ -35,7 +37,7 @@ import {
|
|
|
35
37
|
} from '@ai-sdk/harness/utils';
|
|
36
38
|
import {
|
|
37
39
|
type Experimental_SandboxProcess,
|
|
38
|
-
type Experimental_SandboxSession,
|
|
40
|
+
type Experimental_SandboxSession as SandboxSession,
|
|
39
41
|
} from '@ai-sdk/provider-utils';
|
|
40
42
|
import { WebSocket } from 'ws';
|
|
41
43
|
import { z } from 'zod/v4';
|
|
@@ -118,6 +120,11 @@ export type CodexHarnessSettings = {
|
|
|
118
120
|
* is reserved for something else.
|
|
119
121
|
*/
|
|
120
122
|
readonly port?: number;
|
|
123
|
+
/**
|
|
124
|
+
* Override the host endpoint used to connect to the sandbox bridge. Required
|
|
125
|
+
* together with `port` when using a basic sandbox session.
|
|
126
|
+
*/
|
|
127
|
+
readonly portEndpoint?: HarnessV1PortEndpoint;
|
|
121
128
|
/** Maximum milliseconds to wait for the bridge to advertise its port. Defaults to 120000. */
|
|
122
129
|
readonly startupTimeoutMs?: number;
|
|
123
130
|
/**
|
|
@@ -208,10 +215,33 @@ export function createCodex(
|
|
|
208
215
|
});
|
|
209
216
|
}
|
|
210
217
|
const sandboxSession = startOpts.sandboxSession;
|
|
218
|
+
const toolSafeSandboxSession =
|
|
219
|
+
getRestrictedSandboxSession(sandboxSession);
|
|
220
|
+
const sandboxId = 'id' in sandboxSession ? sandboxSession.id : undefined;
|
|
221
|
+
validateBasicSandboxSettings({
|
|
222
|
+
sandboxSession,
|
|
223
|
+
port: settings.port,
|
|
224
|
+
portEndpoint: settings.portEndpoint,
|
|
225
|
+
});
|
|
226
|
+
if (settings.mintBridgeToken != null && sandboxId == null) {
|
|
227
|
+
throw new HarnessCapabilityUnsupportedError({
|
|
228
|
+
harnessId: 'codex',
|
|
229
|
+
message:
|
|
230
|
+
'The codex harness cannot use `mintBridgeToken` with a sandbox session that does not expose an id.',
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
const defaultWorkingDirectory =
|
|
234
|
+
await resolveSandboxDefaultWorkingDirectory({
|
|
235
|
+
sandboxSession,
|
|
236
|
+
abortSignal: startOpts.abortSignal,
|
|
237
|
+
});
|
|
211
238
|
const authenticationMode = resolveCodexAuthenticationMode(settings.auth);
|
|
212
239
|
const resolvedAuthEnvironment = resolveCodexEnv(settings.auth);
|
|
213
240
|
let sandboxAuthEnvironment = resolvedAuthEnvironment;
|
|
214
|
-
if (
|
|
241
|
+
if (
|
|
242
|
+
'addRequestTransformations' in sandboxSession &&
|
|
243
|
+
sandboxSession.addRequestTransformations != null
|
|
244
|
+
) {
|
|
215
245
|
const requestTransformations = createCodexRequestTransformations(
|
|
216
246
|
resolvedAuthEnvironment,
|
|
217
247
|
authenticationMode,
|
|
@@ -242,10 +272,8 @@ export function createCodex(
|
|
|
242
272
|
} else {
|
|
243
273
|
warnCredentialBrokeringUnavailable();
|
|
244
274
|
}
|
|
245
|
-
const session = sandboxSession.restricted();
|
|
246
|
-
const sandboxId = sandboxSession.id;
|
|
247
275
|
const bootstrapDir = path.posix.resolve(
|
|
248
|
-
|
|
276
|
+
defaultWorkingDirectory,
|
|
249
277
|
BOOTSTRAP_DIR,
|
|
250
278
|
);
|
|
251
279
|
const lifecycleState = startOpts.continueFrom ?? startOpts.resumeFrom;
|
|
@@ -266,7 +294,7 @@ export function createCodex(
|
|
|
266
294
|
const coords = resumeData?.bridge;
|
|
267
295
|
|
|
268
296
|
const workDir = startOpts.sessionWorkDir;
|
|
269
|
-
const sessionDataDir = `${
|
|
297
|
+
const sessionDataDir = `${defaultWorkingDirectory}/.agent-runs/${startOpts.sessionId}`;
|
|
270
298
|
const bridgeStateDir = `${sessionDataDir}/bridge`;
|
|
271
299
|
const cliShimDir = `${sessionDataDir}/codex`;
|
|
272
300
|
const cliShimPath = `${cliShimDir}/${CLI_SHIM_FILENAME}`;
|
|
@@ -300,9 +328,10 @@ export function createCodex(
|
|
|
300
328
|
*/
|
|
301
329
|
if (coords) {
|
|
302
330
|
try {
|
|
303
|
-
const endpoint = await
|
|
331
|
+
const endpoint = await resolveBridgeEndpoint({
|
|
332
|
+
sandboxSession,
|
|
333
|
+
override: settings.portEndpoint,
|
|
304
334
|
port: coords.port,
|
|
305
|
-
protocol: 'ws',
|
|
306
335
|
});
|
|
307
336
|
const attachEndpoint = withBridgeToken({
|
|
308
337
|
endpoint,
|
|
@@ -355,7 +384,7 @@ export function createCodex(
|
|
|
355
384
|
: undefined;
|
|
356
385
|
if (coords && isContinue) {
|
|
357
386
|
const logRaw = await Promise.resolve(
|
|
358
|
-
|
|
387
|
+
toolSafeSandboxSession.readTextFile({
|
|
359
388
|
path: `${bridgeStateDir}/event-log.ndjson`,
|
|
360
389
|
abortSignal: startOpts.abortSignal,
|
|
361
390
|
}),
|
|
@@ -365,15 +394,18 @@ export function createCodex(
|
|
|
365
394
|
}
|
|
366
395
|
}
|
|
367
396
|
|
|
368
|
-
const port = resolveBridgePort(
|
|
397
|
+
const port = resolveBridgePort({
|
|
398
|
+
sandboxSession,
|
|
399
|
+
override: settings.port,
|
|
400
|
+
});
|
|
369
401
|
const token =
|
|
370
402
|
settings.mintBridgeToken == null
|
|
371
403
|
? randomBytes(32).toString('hex')
|
|
372
|
-
: settings.mintBridgeToken(sandboxId);
|
|
404
|
+
: settings.mintBridgeToken(sandboxId!);
|
|
373
405
|
const codexSkillSetup =
|
|
374
406
|
startOpts.skills && startOpts.skills.length > 0
|
|
375
407
|
? await writeCodexSkills({
|
|
376
|
-
sandbox:
|
|
408
|
+
sandbox: toolSafeSandboxSession,
|
|
377
409
|
skills: startOpts.skills,
|
|
378
410
|
abortSignal: startOpts.abortSignal,
|
|
379
411
|
})
|
|
@@ -395,20 +427,20 @@ export function createCodex(
|
|
|
395
427
|
};
|
|
396
428
|
|
|
397
429
|
if (respawnStrategy === undefined) {
|
|
398
|
-
await
|
|
430
|
+
await toolSafeSandboxSession.run({
|
|
399
431
|
command: `mkdir -p ${shellQuote(workDir)} ${shellQuote(bridgeStateDir)}`,
|
|
400
432
|
abortSignal: startOpts.abortSignal,
|
|
401
433
|
});
|
|
402
434
|
}
|
|
403
435
|
|
|
404
436
|
await markBridgeStarting({
|
|
405
|
-
sandbox:
|
|
437
|
+
sandbox: toolSafeSandboxSession,
|
|
406
438
|
bridgeStateDir,
|
|
407
439
|
bridgeType: 'codex',
|
|
408
440
|
abortSignal: startOpts.abortSignal,
|
|
409
441
|
});
|
|
410
442
|
|
|
411
|
-
const proc = await
|
|
443
|
+
const proc = await toolSafeSandboxSession.spawn({
|
|
412
444
|
command: `node ${shellQuote(`${bootstrapDir}/bridge.mjs`)} --workdir ${shellQuote(workDir)} --bridge-state-dir ${shellQuote(bridgeStateDir)} --cli-shim-dir ${shellQuote(cliShimDir)}`,
|
|
413
445
|
env,
|
|
414
446
|
abortSignal: startOpts.abortSignal,
|
|
@@ -423,7 +455,7 @@ export function createCodex(
|
|
|
423
455
|
|
|
424
456
|
const { port: boundPort } = await waitForBridgeReady({
|
|
425
457
|
proc,
|
|
426
|
-
sandbox:
|
|
458
|
+
sandbox: toolSafeSandboxSession,
|
|
427
459
|
bridgeStateDir,
|
|
428
460
|
bridgeType: 'codex',
|
|
429
461
|
timeoutMs,
|
|
@@ -447,9 +479,10 @@ export function createCodex(
|
|
|
447
479
|
});
|
|
448
480
|
void drainBridgeProcessStream(proc.stdout);
|
|
449
481
|
|
|
450
|
-
const endpoint = await
|
|
482
|
+
const endpoint = await resolveBridgeEndpoint({
|
|
483
|
+
sandboxSession,
|
|
484
|
+
override: settings.portEndpoint,
|
|
451
485
|
port: boundPort,
|
|
452
|
-
protocol: 'ws',
|
|
453
486
|
});
|
|
454
487
|
const bridgeEndpoint = withBridgeToken({ endpoint, token });
|
|
455
488
|
|
|
@@ -493,12 +526,17 @@ export function createCodex(
|
|
|
493
526
|
};
|
|
494
527
|
}
|
|
495
528
|
|
|
496
|
-
function resolveBridgePort(
|
|
497
|
-
sandboxSession
|
|
498
|
-
override
|
|
499
|
-
|
|
529
|
+
function resolveBridgePort({
|
|
530
|
+
sandboxSession,
|
|
531
|
+
override,
|
|
532
|
+
}: {
|
|
533
|
+
sandboxSession: HarnessV1NetworkSandboxSession | SandboxSession;
|
|
534
|
+
override: number | undefined;
|
|
535
|
+
}): number {
|
|
500
536
|
if (override !== undefined) return override;
|
|
501
|
-
if (sandboxSession.ports.length > 0)
|
|
537
|
+
if ('ports' in sandboxSession && sandboxSession.ports.length > 0) {
|
|
538
|
+
return sandboxSession.ports[0];
|
|
539
|
+
}
|
|
502
540
|
throw new HarnessCapabilityUnsupportedError({
|
|
503
541
|
harnessId: 'codex',
|
|
504
542
|
message:
|
|
@@ -507,12 +545,58 @@ function resolveBridgePort(
|
|
|
507
545
|
});
|
|
508
546
|
}
|
|
509
547
|
|
|
548
|
+
function validateBasicSandboxSettings({
|
|
549
|
+
sandboxSession,
|
|
550
|
+
port,
|
|
551
|
+
portEndpoint,
|
|
552
|
+
}: {
|
|
553
|
+
sandboxSession: HarnessV1NetworkSandboxSession | SandboxSession;
|
|
554
|
+
port: number | undefined;
|
|
555
|
+
portEndpoint: HarnessV1PortEndpoint | undefined;
|
|
556
|
+
}): void {
|
|
557
|
+
if ('getPortEndpoint' in sandboxSession) return;
|
|
558
|
+
if (port == null) {
|
|
559
|
+
throw new HarnessCapabilityUnsupportedError({
|
|
560
|
+
harnessId: 'codex',
|
|
561
|
+
message:
|
|
562
|
+
'The codex harness requires an explicit `port` when using a basic sandbox session.',
|
|
563
|
+
});
|
|
564
|
+
}
|
|
565
|
+
if (portEndpoint == null) {
|
|
566
|
+
throw new HarnessCapabilityUnsupportedError({
|
|
567
|
+
harnessId: 'codex',
|
|
568
|
+
message:
|
|
569
|
+
'The codex harness requires an explicit `portEndpoint` when using a basic sandbox session.',
|
|
570
|
+
});
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
async function resolveBridgeEndpoint({
|
|
575
|
+
sandboxSession,
|
|
576
|
+
override,
|
|
577
|
+
port,
|
|
578
|
+
}: {
|
|
579
|
+
sandboxSession: HarnessV1NetworkSandboxSession | SandboxSession;
|
|
580
|
+
override: HarnessV1PortEndpoint | undefined;
|
|
581
|
+
port: number;
|
|
582
|
+
}): Promise<HarnessV1PortEndpoint> {
|
|
583
|
+
if (override != null) return override;
|
|
584
|
+
if ('getPortEndpoint' in sandboxSession) {
|
|
585
|
+
return sandboxSession.getPortEndpoint({ port, protocol: 'ws' });
|
|
586
|
+
}
|
|
587
|
+
throw new HarnessCapabilityUnsupportedError({
|
|
588
|
+
harnessId: 'codex',
|
|
589
|
+
message:
|
|
590
|
+
'The codex harness requires an explicit `portEndpoint` when using a basic sandbox session.',
|
|
591
|
+
});
|
|
592
|
+
}
|
|
593
|
+
|
|
510
594
|
async function writeCodexSkills({
|
|
511
595
|
sandbox,
|
|
512
596
|
skills,
|
|
513
597
|
abortSignal,
|
|
514
598
|
}: {
|
|
515
|
-
sandbox:
|
|
599
|
+
sandbox: SandboxSession;
|
|
516
600
|
skills: ReadonlyArray<HarnessV1Skill>;
|
|
517
601
|
abortSignal?: AbortSignal;
|
|
518
602
|
}): Promise<WriteSkillsResult> {
|
|
@@ -609,7 +693,7 @@ function createSession({
|
|
|
609
693
|
rerunContinue: boolean;
|
|
610
694
|
bridgePort: number;
|
|
611
695
|
bridgeToken: string;
|
|
612
|
-
sandboxId: string;
|
|
696
|
+
sandboxId: string | undefined;
|
|
613
697
|
debug: HarnessV1DebugConfig | undefined;
|
|
614
698
|
permissionMode: HarnessV1PermissionMode | undefined;
|
|
615
699
|
}): HarnessV1Session {
|
|
@@ -772,9 +856,6 @@ function createSession({
|
|
|
772
856
|
reason: input.reason,
|
|
773
857
|
});
|
|
774
858
|
},
|
|
775
|
-
submitUserMessage: async text => {
|
|
776
|
-
channel.send({ type: 'user-message', text });
|
|
777
|
-
},
|
|
778
859
|
done,
|
|
779
860
|
};
|
|
780
861
|
|
|
@@ -964,7 +1045,7 @@ function createSession({
|
|
|
964
1045
|
port: bridgePort,
|
|
965
1046
|
token: bridgeToken,
|
|
966
1047
|
lastSeenEventId,
|
|
967
|
-
sandboxId,
|
|
1048
|
+
...(sandboxId == null ? {} : { sandboxId }),
|
|
968
1049
|
},
|
|
969
1050
|
},
|
|
970
1051
|
};
|
|
@@ -1102,7 +1183,7 @@ function createSession({
|
|
|
1102
1183
|
port: bridgePort,
|
|
1103
1184
|
token: bridgeToken,
|
|
1104
1185
|
lastSeenEventId,
|
|
1105
|
-
sandboxId,
|
|
1186
|
+
...(sandboxId == null ? {} : { sandboxId }),
|
|
1106
1187
|
},
|
|
1107
1188
|
},
|
|
1108
1189
|
};
|