@ai-sdk/harness-acp 1.0.14 → 1.0.16

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/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { HarnessV1RequestTransformation, HarnessV1PermissionMode, HarnessV1 } from '@ai-sdk/harness';
1
+ import { HarnessV1RequestTransformation, HarnessV1PermissionMode, HarnessV1PortEndpoint, HarnessV1 } from '@ai-sdk/harness';
2
2
  import { ToolSet } from '@ai-sdk/provider-utils';
3
3
 
4
4
  type ACPMetadata = Record<string, unknown>;
@@ -224,6 +224,11 @@ type ACPHarnessSettings<TBuiltinTools extends ToolSet = {}> = {
224
224
  readonly mcpServers?: Record<string, unknown>;
225
225
  readonly isMcpToolCall?: (toolCall: ACPToolCall) => boolean;
226
226
  readonly port?: number;
227
+ /**
228
+ * Override the host endpoint used to connect to the sandbox bridge. Required
229
+ * together with `port` when using a basic sandbox session.
230
+ */
231
+ readonly portEndpoint?: HarnessV1PortEndpoint;
227
232
  readonly startupTimeoutMs?: number;
228
233
  readonly clientApp?: ACPClientApp;
229
234
  readonly version?: ACPV1Settings['version'];
package/dist/index.js CHANGED
@@ -15,8 +15,10 @@ import {
15
15
  classifyDiskLog,
16
16
  drainBridgeProcessStream,
17
17
  forwardBridgeProcessStream,
18
+ getRestrictedSandboxSession,
18
19
  markBridgeStarting,
19
20
  maskSandboxCredentials,
21
+ resolveSandboxDefaultWorkingDirectory,
20
22
  resolveSandboxHomeDir,
21
23
  SandboxChannel,
22
24
  shellQuote,
@@ -1158,6 +1160,7 @@ function createACPV1({
1158
1160
  settings,
1159
1161
  builtinTools,
1160
1162
  port: portOverride,
1163
+ portEndpoint: portEndpointOverride,
1161
1164
  startupTimeoutMs,
1162
1165
  clientApp,
1163
1166
  lifecycleStateSchema
@@ -1233,6 +1236,24 @@ function createACPV1({
1233
1236
  compatibility: providerAuthenticationCompatibility
1234
1237
  });
1235
1238
  const sandboxSession = startOptions.sandboxSession;
1239
+ const toolSafeSandboxSession = getRestrictedSandboxSession(sandboxSession);
1240
+ const sandboxId = "id" in sandboxSession ? sandboxSession.id : void 0;
1241
+ validateBasicSandboxSettings({
1242
+ sandboxSession,
1243
+ port: portOverride,
1244
+ portEndpoint: portEndpointOverride,
1245
+ harnessId: settings.harnessId
1246
+ });
1247
+ if (settings.mintBridgeToken != null && sandboxId == null) {
1248
+ throw unsupported({
1249
+ harnessId: settings.harnessId,
1250
+ message: `The ${settings.harnessId} ACP harness cannot use \`mintBridgeToken\` with a sandbox session that does not expose an id.`
1251
+ });
1252
+ }
1253
+ const defaultWorkingDirectory = await resolveSandboxDefaultWorkingDirectory({
1254
+ sandboxSession,
1255
+ abortSignal: startOptions.abortSignal
1256
+ });
1236
1257
  const implementationEnvironment = resolveImplementationEnvironment({
1237
1258
  implementation,
1238
1259
  env
@@ -1240,7 +1261,7 @@ function createACPV1({
1240
1261
  let sandboxImplementationEnvironment = implementationEnvironment;
1241
1262
  let sandboxProviderAuthenticationEnvironment = resolvedProviderAuthentication.env;
1242
1263
  let sandboxProviderEnvironment;
1243
- if (settings.credentialBrokering != null && sandboxSession.addRequestTransformations != null) {
1264
+ if (settings.credentialBrokering != null && "addRequestTransformations" in sandboxSession && sandboxSession.addRequestTransformations != null) {
1244
1265
  const providerEnvironment = resolveProviderEnvironment({
1245
1266
  resolvedProviderAuthentication,
1246
1267
  clientApp
@@ -1283,15 +1304,14 @@ function createACPV1({
1283
1304
  } else if (settings.credentialBrokering != null) {
1284
1305
  warnCredentialBrokeringUnavailable();
1285
1306
  }
1286
- const sandbox = sandboxSession.restricted();
1287
1307
  const resolvedBridgeDir = posix.resolve(
1288
- sandboxSession.defaultWorkingDirectory,
1308
+ defaultWorkingDirectory,
1289
1309
  bootstrap.bootstrapDir
1290
1310
  );
1291
1311
  const resolvedImplementationDir = `${resolvedBridgeDir}/implementation`;
1292
1312
  const workDir = startOptions.sessionWorkDir;
1293
1313
  const sandboxHomeDir = await resolveSandboxHomeDir({
1294
- sandbox,
1314
+ sandbox: toolSafeSandboxSession,
1295
1315
  abortSignal: startOptions.abortSignal
1296
1316
  });
1297
1317
  const privateSessionDir = resolveACPPrivateSessionDirectory({
@@ -1313,7 +1333,7 @@ function createACPV1({
1313
1333
  implementationIdentity,
1314
1334
  authenticationProfile,
1315
1335
  lifecycleData,
1316
- sandboxId: sandboxSession.id
1336
+ sandboxId
1317
1337
  });
1318
1338
  }
1319
1339
  const skills = startOptions.skills ?? [];
@@ -1326,7 +1346,7 @@ function createACPV1({
1326
1346
  let skillCatalog = [];
1327
1347
  if (skills.length > 0) {
1328
1348
  skillCatalog = (await materializeACPSkills({
1329
- sandbox,
1349
+ sandbox: toolSafeSandboxSession,
1330
1350
  sandboxHomeDir,
1331
1351
  sessionWorkDir: workDir,
1332
1352
  harnessId: settings.harnessId,
@@ -1362,9 +1382,11 @@ function createACPV1({
1362
1382
  }
1363
1383
  if (coords != null) {
1364
1384
  try {
1365
- const endpoint2 = await sandboxSession.getPortEndpoint({
1385
+ const endpoint2 = await resolveBridgeEndpoint({
1386
+ sandboxSession,
1387
+ override: portEndpointOverride,
1366
1388
  port: coords.port,
1367
- protocol: "ws"
1389
+ harnessId: settings.harnessId
1368
1390
  });
1369
1391
  const attachEndpoint = withBridgeToken({
1370
1392
  endpoint: endpoint2,
@@ -1404,7 +1426,7 @@ function createACPV1({
1404
1426
  acpSessionId: lifecycleData.acpSessionId,
1405
1427
  bridgePort: coords.port,
1406
1428
  bridgeToken: coords.token,
1407
- sandboxId: sandboxSession.id,
1429
+ sandboxId,
1408
1430
  isResume: true,
1409
1431
  turnInFlight: isContinue,
1410
1432
  bridgeStateDir,
@@ -1417,7 +1439,7 @@ function createACPV1({
1417
1439
  } catch (error) {
1418
1440
  if (isContinue) {
1419
1441
  const eventLog = await Promise.resolve(
1420
- sandbox.readTextFile({
1442
+ toolSafeSandboxSession.readTextFile({
1421
1443
  path: `${bridgeStateDir}/event-log.ndjson`,
1422
1444
  abortSignal: startOptions.abortSignal
1423
1445
  })
@@ -1493,18 +1515,18 @@ function createACPV1({
1493
1515
  override: portOverride,
1494
1516
  harnessId: settings.harnessId
1495
1517
  });
1496
- const token = settings.mintBridgeToken == null ? randomBytes(32).toString("hex") : settings.mintBridgeToken(sandboxSession.id);
1497
- await sandbox.run({
1518
+ const token = settings.mintBridgeToken == null ? randomBytes(32).toString("hex") : settings.mintBridgeToken(sandboxId);
1519
+ await toolSafeSandboxSession.run({
1498
1520
  command: `mkdir -p ${shellQuote(workDir)} ${shellQuote(bridgeStateDir)}`,
1499
1521
  abortSignal: startOptions.abortSignal
1500
1522
  });
1501
1523
  await markBridgeStarting({
1502
- sandbox,
1524
+ sandbox: toolSafeSandboxSession,
1503
1525
  bridgeStateDir,
1504
1526
  bridgeType: settings.harnessId,
1505
1527
  abortSignal: startOptions.abortSignal
1506
1528
  });
1507
- const proc = await sandbox.spawn({
1529
+ const proc = await toolSafeSandboxSession.spawn({
1508
1530
  command: `node ${shellQuote(`${resolvedBridgeDir}/bridge.mjs`)} --workdir ${shellQuote(workDir)} --bridge-state-dir ${shellQuote(bridgeStateDir)} --implementation-dir ${shellQuote(resolvedImplementationDir)} --bridge-type ${shellQuote(settings.harnessId)}`,
1509
1531
  env: {
1510
1532
  ...sandboxImplementationEnvironment,
@@ -1533,7 +1555,7 @@ function createACPV1({
1533
1555
  const timeoutMs = startupTimeoutMs ?? 12e4;
1534
1556
  const { port: boundPort } = await waitForBridgeReady({
1535
1557
  proc,
1536
- sandbox,
1558
+ sandbox: toolSafeSandboxSession,
1537
1559
  bridgeStateDir,
1538
1560
  bridgeType: settings.harnessId,
1539
1561
  timeoutMs,
@@ -1554,9 +1576,11 @@ function createACPV1({
1554
1576
  })
1555
1577
  });
1556
1578
  void drainBridgeProcessStream(proc.stdout);
1557
- const endpoint = await sandboxSession.getPortEndpoint({
1579
+ const endpoint = await resolveBridgeEndpoint({
1580
+ sandboxSession,
1581
+ override: portEndpointOverride,
1558
1582
  port: boundPort,
1559
- protocol: "ws"
1583
+ harnessId: settings.harnessId
1560
1584
  });
1561
1585
  const bridgeEndpoint = withBridgeToken({ endpoint, token });
1562
1586
  const channel = new SandboxChannel({
@@ -1634,7 +1658,7 @@ function createACPV1({
1634
1658
  acpSessionId: lifecycleData?.acpSessionId,
1635
1659
  bridgePort: boundPort,
1636
1660
  bridgeToken: token,
1637
- sandboxId: sandboxSession.id,
1661
+ sandboxId,
1638
1662
  isResume,
1639
1663
  turnInFlight: respawnStrategy?.mode === "disk-replay" || respawnStrategy?.mode === "lossy-rerun",
1640
1664
  bridgeStateDir,
@@ -1690,12 +1714,49 @@ function resolveBridgePort({
1690
1714
  harnessId
1691
1715
  }) {
1692
1716
  if (override !== void 0) return override;
1693
- if (sandboxSession.ports.length > 0) return sandboxSession.ports[0];
1717
+ if ("ports" in sandboxSession && sandboxSession.ports.length > 0) {
1718
+ return sandboxSession.ports[0];
1719
+ }
1694
1720
  throw unsupported({
1695
1721
  harnessId,
1696
1722
  message: `The ${harnessId} ACP harness needs a TCP port exposed by the sandbox. Create the sandbox with \`ports: [<port>]\` or configure the harness \`port\`.`
1697
1723
  });
1698
1724
  }
1725
+ function validateBasicSandboxSettings({
1726
+ sandboxSession,
1727
+ port,
1728
+ portEndpoint,
1729
+ harnessId
1730
+ }) {
1731
+ if ("getPortEndpoint" in sandboxSession) return;
1732
+ if (port == null) {
1733
+ throw unsupported({
1734
+ harnessId,
1735
+ message: `The ${harnessId} ACP harness requires an explicit \`port\` when using a basic sandbox session.`
1736
+ });
1737
+ }
1738
+ if (portEndpoint == null) {
1739
+ throw unsupported({
1740
+ harnessId,
1741
+ message: `The ${harnessId} ACP harness requires an explicit \`portEndpoint\` when using a basic sandbox session.`
1742
+ });
1743
+ }
1744
+ }
1745
+ async function resolveBridgeEndpoint({
1746
+ sandboxSession,
1747
+ override,
1748
+ port,
1749
+ harnessId
1750
+ }) {
1751
+ if (override != null) return override;
1752
+ if ("getPortEndpoint" in sandboxSession) {
1753
+ return sandboxSession.getPortEndpoint({ port, protocol: "ws" });
1754
+ }
1755
+ throw unsupported({
1756
+ harnessId,
1757
+ message: `The ${harnessId} ACP harness requires an explicit \`portEndpoint\` when using a basic sandbox session.`
1758
+ });
1759
+ }
1699
1760
  function openWebSocket({
1700
1761
  url,
1701
1762
  headers
@@ -2433,7 +2494,7 @@ function deserializeBridgeError({
2433
2494
  }
2434
2495
 
2435
2496
  // src/version.ts
2436
- var VERSION = true ? "1.0.14" : "0.0.0-test";
2497
+ var VERSION = true ? "1.0.16" : "0.0.0-test";
2437
2498
 
2438
2499
  // src/acp-harness.ts
2439
2500
  var ACP_CLIENT_APP = {
@@ -2496,6 +2557,7 @@ function createACP(settings) {
2496
2557
  settings,
2497
2558
  builtinTools: settings.builtinTools ?? ACP_BUILTIN_TOOLS,
2498
2559
  port: settings.port,
2560
+ portEndpoint: settings.portEndpoint,
2499
2561
  startupTimeoutMs: settings.startupTimeoutMs,
2500
2562
  clientApp,
2501
2563
  lifecycleStateSchema: acpResumeStateSchema