@ai-sdk/harness-acp 1.0.7 → 1.0.9

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 { HarnessV1PermissionMode, HarnessV1 } from '@ai-sdk/harness';
1
+ import { HarnessV1RequestTransformation, HarnessV1PermissionMode, HarnessV1 } from '@ai-sdk/harness';
2
2
  import { ToolSet } from '@ai-sdk/provider-utils';
3
3
 
4
4
  type ACPMetadata = Record<string, unknown>;
@@ -139,6 +139,9 @@ type ACPProviderAuthentication = {
139
139
  readonly env: Readonly<Record<string, ACPProfileValue>>;
140
140
  };
141
141
  };
142
+ type ACPCredentialBrokering = ({ env, }: {
143
+ env: Readonly<Record<string, string>>;
144
+ }) => ReadonlyArray<HarnessV1RequestTransformation>;
142
145
  type ACPPermissionModeTarget = {
143
146
  readonly type: 'session-mode';
144
147
  readonly modeId: string;
@@ -171,6 +174,8 @@ type ACPV1Settings = {
171
174
  readonly executable: string;
172
175
  readonly args?: ReadonlyArray<string>;
173
176
  readonly forwardEnv?: ReadonlyArray<string>;
177
+ readonly credentialEnv?: ReadonlyArray<string>;
178
+ readonly credentialBrokering?: ACPCredentialBrokering;
174
179
  /**
175
180
  * Runtime environment values that are safe to persist in bootstrap and
176
181
  * lifecycle compatibility identity.
@@ -219,6 +224,8 @@ type ACPHarnessSettings<TBuiltinTools extends ToolSet = {}> = {
219
224
  readonly executable: ACPV1Settings['executable'];
220
225
  readonly args?: ACPV1Settings['args'];
221
226
  readonly forwardEnv?: ACPV1Settings['forwardEnv'];
227
+ readonly credentialEnv?: ACPV1Settings['credentialEnv'];
228
+ readonly credentialBrokering?: ACPV1Settings['credentialBrokering'];
222
229
  readonly env?: ACPV1Settings['env'];
223
230
  readonly authentication?: ACPV1Settings['authentication'];
224
231
  readonly providerAuthentication?: ACPV1Settings['providerAuthentication'];
@@ -232,4 +239,4 @@ declare function createACP<TBuiltinTools extends ToolSet = {}>(settings: ACPHarn
232
239
 
233
240
  declare const VERSION: string;
234
241
 
235
- export { type ACPAnnotations, type ACPAudioContent, type ACPAuthOptions, type ACPAuthentication, type ACPBlobResourceContents, type ACPClientApp, type ACPContentBlock, type ACPEmbeddedResource, type ACPHarnessSettings, type ACPImageContent, type ACPInstructionMapping, type ACPMetadata, type ACPNpmLockedSource, type ACPNpmSimpleSource, type ACPPermissionModeMapping, type ACPPermissionModeTarget, type ACPProfileValue, type ACPProviderAuthentication, type ACPProviderAuthenticationMode, type ACPResourceLink, type ACPRole, type ACPSerializablePrimitive, type ACPSerializableValue, type ACPSource, type ACPTextContent, type ACPTextResourceContents, type ACPToolCall, type ACPToolCallContent, type ACPToolCallLocation, type ACPToolCallStatus, type ACPToolKind, type ACPV1Settings, type ACPValueSource, VERSION, createACP };
242
+ export { type ACPAnnotations, type ACPAudioContent, type ACPAuthOptions, type ACPAuthentication, type ACPBlobResourceContents, type ACPClientApp, type ACPContentBlock, type ACPCredentialBrokering, type ACPEmbeddedResource, type ACPHarnessSettings, type ACPImageContent, type ACPInstructionMapping, type ACPMetadata, type ACPNpmLockedSource, type ACPNpmSimpleSource, type ACPPermissionModeMapping, type ACPPermissionModeTarget, type ACPProfileValue, type ACPProviderAuthentication, type ACPProviderAuthenticationMode, type ACPResourceLink, type ACPRole, type ACPSerializablePrimitive, type ACPSerializableValue, type ACPSource, type ACPTextContent, type ACPTextResourceContents, type ACPToolCall, type ACPToolCallContent, type ACPToolCallLocation, type ACPToolCallStatus, type ACPToolKind, type ACPV1Settings, type ACPValueSource, VERSION, createACP };
package/dist/index.js CHANGED
@@ -3,9 +3,7 @@ import { z as z4 } from "zod/v4";
3
3
 
4
4
  // src/v1/acp-v1-harness.ts
5
5
  import { createHash as createHash5, randomBytes } from "crypto";
6
- import { readFile } from "fs/promises";
7
6
  import { posix } from "path";
8
- import { fileURLToPath } from "url";
9
7
  import {
10
8
  HarnessCapabilityUnsupportedError as HarnessCapabilityUnsupportedError2,
11
9
  harnessV1DiagnosticFromBridgeFrame
@@ -18,9 +16,11 @@ import {
18
16
  drainBridgeProcessStream,
19
17
  forwardBridgeProcessStream,
20
18
  markBridgeStarting,
19
+ maskSandboxCredentials,
21
20
  resolveSandboxHomeDir,
22
21
  SandboxChannel,
23
22
  shellQuote,
23
+ warnCredentialBrokeringUnavailable,
24
24
  waitForBridgeReady
25
25
  } from "@ai-sdk/harness/utils";
26
26
  import {
@@ -182,6 +182,7 @@ function createACPV1Implementation({
182
182
  executable: settings.executable,
183
183
  args: settings.args,
184
184
  forwardEnv: settings.forwardEnv,
185
+ credentialEnv: settings.credentialEnv,
185
186
  env: settings.env
186
187
  };
187
188
  }
@@ -207,14 +208,28 @@ function validateACPV1Implementation(implementation) {
207
208
  );
208
209
  }
209
210
  validateForwardEnvironment({ forwardEnv: implementation.forwardEnv });
211
+ validateForwardEnvironment({ forwardEnv: implementation.credentialEnv });
210
212
  validateEnvironment({ env: implementation.env });
211
213
  const forwardedKeys = new Set(implementation.forwardEnv ?? []);
214
+ const credentialKeys = new Set(implementation.credentialEnv ?? []);
215
+ for (const key of credentialKeys) {
216
+ if (forwardedKeys.has(key)) {
217
+ throw new Error(
218
+ `ACP runtime environment key ${JSON.stringify(key)} cannot be configured in both forwardEnv and credentialEnv.`
219
+ );
220
+ }
221
+ }
212
222
  for (const key of Object.keys(implementation.env ?? {})) {
213
223
  if (forwardedKeys.has(key)) {
214
224
  throw new Error(
215
225
  `ACP runtime environment key ${JSON.stringify(key)} cannot be configured in both forwardEnv and env.`
216
226
  );
217
227
  }
228
+ if (credentialKeys.has(key)) {
229
+ throw new Error(
230
+ `ACP runtime environment key ${JSON.stringify(key)} cannot be configured in both credentialEnv and env.`
231
+ );
232
+ }
218
233
  }
219
234
  }
220
235
  function createImplementationManifest({
@@ -278,6 +293,9 @@ function createImplementationIdentity({
278
293
  const forwardedEnvironment = [
279
294
  ...new Set(implementation.forwardEnv ?? [])
280
295
  ].sort();
296
+ const credentialEnvironment = [
297
+ ...new Set(implementation.credentialEnv ?? [])
298
+ ].sort();
281
299
  const literalEnvironment = Object.fromEntries(
282
300
  Object.entries(implementation.env ?? {}).sort(([left], [right]) => left.localeCompare(right)).map(([key, value]) => [key, { value }])
283
301
  );
@@ -290,6 +308,7 @@ function createImplementationIdentity({
290
308
  clientApp,
291
309
  environment: {
292
310
  forwarded: forwardedEnvironment,
311
+ credential: credentialEnvironment,
293
312
  literal: literalEnvironment
294
313
  },
295
314
  providerAuthentication: providerAuthentication ?? null,
@@ -309,7 +328,10 @@ function resolveImplementationEnvironment({
309
328
  env
310
329
  }) {
311
330
  const forwardedEnvironment = {};
312
- for (const name of implementation.forwardEnv ?? []) {
331
+ for (const name of [
332
+ ...implementation.forwardEnv ?? [],
333
+ ...implementation.credentialEnv ?? []
334
+ ]) {
313
335
  const value = env[name];
314
336
  if (value != null && value.length > 0) {
315
337
  forwardedEnvironment[name] = value;
@@ -365,6 +387,7 @@ function getImplementationEnvironmentKeys({
365
387
  return [
366
388
  .../* @__PURE__ */ new Set([
367
389
  ...implementation.forwardEnv ?? [],
390
+ ...implementation.credentialEnv ?? [],
368
391
  ...Object.keys(implementation.env ?? {})
369
392
  ])
370
393
  ].sort();
@@ -384,6 +407,103 @@ function sortValue({ value }) {
384
407
  return value;
385
408
  }
386
409
 
410
+ // src/v1/acp-bootstrap.ts
411
+ import { readFile } from "fs/promises";
412
+ import { fileURLToPath } from "url";
413
+ function createACPBootstrap({
414
+ harnessId,
415
+ implementation
416
+ }) {
417
+ const bootstrapDir = `.harness-bootstrap/${harnessId}`;
418
+ let cachedBootstrap;
419
+ return {
420
+ bootstrapDir,
421
+ getBootstrap: async () => {
422
+ if (cachedBootstrap != null) return cachedBootstrap;
423
+ const [bridgePackage, bridgeLock, bridge, hostToolMCP] = await Promise.all([
424
+ readBridgeAsset({ name: "package.json" }),
425
+ readBridgeAsset({ name: "pnpm-lock.yaml" }),
426
+ readBridgeAsset({ name: "index.mjs" }),
427
+ readBridgeAsset({ name: "host-tool-mcp.mjs" })
428
+ ]);
429
+ const implementationLock = getImplementationLockfile({
430
+ implementation
431
+ });
432
+ cachedBootstrap = {
433
+ harnessId,
434
+ bootstrapDir,
435
+ files: [
436
+ {
437
+ path: `${bootstrapDir}/package.json`,
438
+ content: bridgePackage
439
+ },
440
+ {
441
+ path: `${bootstrapDir}/pnpm-lock.yaml`,
442
+ content: bridgeLock
443
+ },
444
+ { path: `${bootstrapDir}/bridge.mjs`, content: bridge },
445
+ {
446
+ path: `${bootstrapDir}/host-tool-mcp.mjs`,
447
+ content: hostToolMCP
448
+ },
449
+ {
450
+ path: `${bootstrapDir}/implementation/package.json`,
451
+ content: createImplementationManifest({ implementation })
452
+ },
453
+ {
454
+ path: `${bootstrapDir}/implementation/implementation.json`,
455
+ content: createImplementationDescriptor({ implementation })
456
+ },
457
+ ...implementationLock == null ? [] : [
458
+ {
459
+ path: `${bootstrapDir}/implementation/pnpm-lock.yaml`,
460
+ content: implementationLock
461
+ }
462
+ ]
463
+ ],
464
+ commands: [
465
+ {
466
+ command: "pnpm install --frozen-lockfile --store-dir .pnpm-store"
467
+ },
468
+ {
469
+ command: createImplementationInstallCommand({
470
+ implementationDir: "implementation",
471
+ storeDir: "../.pnpm-store",
472
+ implementation
473
+ })
474
+ }
475
+ ]
476
+ };
477
+ return cachedBootstrap;
478
+ }
479
+ };
480
+ }
481
+ async function readBridgeAsset({ name }) {
482
+ const candidates = resolveBridgeAssetCandidates({
483
+ name,
484
+ moduleUrl: import.meta.url
485
+ });
486
+ let lastError;
487
+ for (const url of candidates) {
488
+ try {
489
+ return await readFile(fileURLToPath(url), "utf8");
490
+ } catch (error) {
491
+ if (error.code !== "ENOENT") throw error;
492
+ lastError = error;
493
+ }
494
+ }
495
+ throw lastError ?? new Error(`ACP bridge asset not found: ${name}`);
496
+ }
497
+ function resolveBridgeAssetCandidates({
498
+ name,
499
+ moduleUrl
500
+ }) {
501
+ return [
502
+ new URL(`./bridge/${name}`, moduleUrl),
503
+ new URL(`../bridge/${name}`, moduleUrl)
504
+ ];
505
+ }
506
+
387
507
  // src/v1/acp-v1-bridge-protocol.ts
388
508
  import {
389
509
  harnessV1BridgeInboundCommandSchemas,
@@ -622,22 +742,120 @@ var providerAuthenticationSchema = z3.discriminatedUnion("type", [
622
742
  var bridgeConfigurationSchema = z3.object({
623
743
  authentication: authenticationSchema.optional(),
624
744
  providerAuthentication: providerAuthenticationSchema.optional(),
745
+ providerEnvironment: z3.record(z3.string(), z3.string()).optional(),
625
746
  sessionMeta: serializableRecordSchema.optional()
626
747
  });
627
748
  function createACPBridgeEnvironment({
628
749
  authentication,
629
750
  providerAuthentication,
751
+ providerEnvironment,
630
752
  sessionMeta
631
753
  }) {
632
754
  return {
633
755
  [ACP_BRIDGE_CONFIGURATION_ENV]: JSON.stringify({
634
756
  ...authentication == null ? {} : { authentication },
635
757
  ...providerAuthentication == null ? {} : { providerAuthentication },
758
+ ...providerEnvironment == null ? {} : { providerEnvironment },
636
759
  ...sessionMeta == null ? {} : { sessionMeta }
637
760
  })
638
761
  };
639
762
  }
640
763
 
764
+ // src/v1/bridge/profile-values.ts
765
+ function resolveACPProfileValue({
766
+ value,
767
+ gateway
768
+ }) {
769
+ if (Array.isArray(value)) {
770
+ return value.map((item) => resolveACPProfileValue({ value: item, gateway }));
771
+ }
772
+ if (value !== null && typeof value === "object") {
773
+ if (isValueSource(value)) {
774
+ const sourceValue = resolveSource({ source: value.$source, gateway });
775
+ const resolved = `${value.prefix ?? ""}${sourceValue}`;
776
+ return value.ensureSuffix == null ? `${resolved}${value.suffix ?? ""}` : ensureSuffix({
777
+ value: resolved,
778
+ suffix: value.ensureSuffix
779
+ });
780
+ }
781
+ return Object.fromEntries(
782
+ Object.entries(value).map(([key, item]) => [
783
+ key,
784
+ resolveACPProfileValue({ value: item, gateway })
785
+ ])
786
+ );
787
+ }
788
+ return value;
789
+ }
790
+ function isValueSource(value) {
791
+ return "$source" in value && typeof value.$source === "string";
792
+ }
793
+ function ensureSuffix({
794
+ value,
795
+ suffix
796
+ }) {
797
+ const normalized = value.replace(/\/+$/, "");
798
+ return normalized.endsWith(suffix) ? normalized : `${normalized}${suffix}`;
799
+ }
800
+ function resolveSource({
801
+ source,
802
+ gateway
803
+ }) {
804
+ switch (source) {
805
+ case "gateway-api-key":
806
+ return gateway.apiKey;
807
+ case "gateway-base-url":
808
+ return gateway.baseUrl;
809
+ case "gateway-authorization":
810
+ return `Bearer ${gateway.apiKey}`;
811
+ case "client-app":
812
+ return `${gateway.clientAppName}/${gateway.clientAppVersion}`;
813
+ case "client-app-name":
814
+ return gateway.clientAppName;
815
+ case "client-app-version":
816
+ return gateway.clientAppVersion;
817
+ }
818
+ }
819
+
820
+ // src/v1/bridge/protocol-configuration.ts
821
+ function resolveACPLaunchEnvironment({
822
+ providerAuthentication,
823
+ gateway
824
+ }) {
825
+ if (providerAuthentication?.type !== "ai-gateway") {
826
+ return {};
827
+ }
828
+ const resolved = asRecord(
829
+ resolveACPProfileValue({
830
+ value: providerAuthentication.env,
831
+ gateway: requireGateway({ gateway })
832
+ })
833
+ );
834
+ return Object.fromEntries(
835
+ Object.entries(resolved).map(([key, value]) => [
836
+ key,
837
+ typeof value === "string" ? value : JSON.stringify(value)
838
+ ])
839
+ );
840
+ }
841
+ function requireGateway({
842
+ gateway
843
+ }) {
844
+ if (gateway == null) {
845
+ throw new Error("ACP Gateway profile values are unavailable.");
846
+ }
847
+ return gateway;
848
+ }
849
+ function asRecord(value) {
850
+ if (!isRecord(value)) {
851
+ throw new Error("ACP profile data must resolve to an object.");
852
+ }
853
+ return value;
854
+ }
855
+ function isRecord(value) {
856
+ return value != null && typeof value === "object" && !Array.isArray(value);
857
+ }
858
+
641
859
  // src/v1/acp-v1-turn-start-config.ts
642
860
  import { createHash as createHash3 } from "crypto";
643
861
  function createACPTurnStartConfig({
@@ -925,6 +1143,11 @@ function createACPV1({
925
1143
  clientApp,
926
1144
  lifecycleStateSchema
927
1145
  }) {
1146
+ if (settings.credentialEnv == null !== (settings.credentialBrokering == null)) {
1147
+ throw new Error(
1148
+ "ACP credentialEnv and credentialBrokering must be configured together."
1149
+ );
1150
+ }
928
1151
  if (settings.mcpServers != null && Object.prototype.hasOwnProperty.call(
929
1152
  settings.mcpServers,
930
1153
  "ai-sdk-harness-tools"
@@ -940,8 +1163,10 @@ function createACPV1({
940
1163
  }
941
1164
  const implementation = createACPV1Implementation({ settings });
942
1165
  validateACPV1Implementation(implementation);
943
- const BOOTSTRAP_DIR = `.harness-bootstrap/${settings.harnessId}`;
944
- let cachedBootstrap;
1166
+ const bootstrap = createACPBootstrap({
1167
+ harnessId: settings.harnessId,
1168
+ implementation
1169
+ });
945
1170
  const permissionModeMapping = isCompletePermissionModeMapping({
946
1171
  value: settings.permissionModeMapping
947
1172
  }) ? settings.permissionModeMapping : void 0;
@@ -952,68 +1177,7 @@ function createACPV1({
952
1177
  supportsBuiltinToolApprovals: true,
953
1178
  supportsBuiltinToolFiltering: false,
954
1179
  lifecycleStateSchema,
955
- getBootstrap: async () => {
956
- if (cachedBootstrap != null) return cachedBootstrap;
957
- const [bridgePackage, bridgeLock, bridge, hostToolMCP] = await Promise.all([
958
- readBridgeAsset({ name: "package.json" }),
959
- readBridgeAsset({ name: "pnpm-lock.yaml" }),
960
- readBridgeAsset({ name: "index.mjs" }),
961
- readBridgeAsset({ name: "host-tool-mcp.mjs" })
962
- ]);
963
- const implementationLock = getImplementationLockfile({
964
- implementation
965
- });
966
- cachedBootstrap = {
967
- harnessId: settings.harnessId,
968
- bootstrapDir: BOOTSTRAP_DIR,
969
- files: [
970
- {
971
- path: `${BOOTSTRAP_DIR}/package.json`,
972
- content: bridgePackage
973
- },
974
- {
975
- path: `${BOOTSTRAP_DIR}/pnpm-lock.yaml`,
976
- content: bridgeLock
977
- },
978
- { path: `${BOOTSTRAP_DIR}/bridge.mjs`, content: bridge },
979
- {
980
- path: `${BOOTSTRAP_DIR}/host-tool-mcp.mjs`,
981
- content: hostToolMCP
982
- },
983
- {
984
- path: `${BOOTSTRAP_DIR}/implementation/package.json`,
985
- content: createImplementationManifest({
986
- implementation
987
- })
988
- },
989
- {
990
- path: `${BOOTSTRAP_DIR}/implementation/implementation.json`,
991
- content: createImplementationDescriptor({
992
- implementation
993
- })
994
- },
995
- ...implementationLock == null ? [] : [
996
- {
997
- path: `${BOOTSTRAP_DIR}/implementation/pnpm-lock.yaml`,
998
- content: implementationLock
999
- }
1000
- ]
1001
- ],
1002
- commands: [
1003
- {
1004
- command: "pnpm install --frozen-lockfile --store-dir .pnpm-store"
1005
- },
1006
- {
1007
- command: createImplementationInstallCommand({
1008
- implementationDir: "implementation",
1009
- storeDir: "../.pnpm-store",
1010
- implementation
1011
- })
1012
- }
1013
- ]
1014
- };
1015
- return cachedBootstrap;
1016
- },
1180
+ getBootstrap: bootstrap.getBootstrap,
1017
1181
  doStart: async (startOptions) => {
1018
1182
  if (startOptions.builtinToolFiltering != null) {
1019
1183
  throw unsupported({
@@ -1050,10 +1214,60 @@ function createACPV1({
1050
1214
  compatibility: providerAuthenticationCompatibility
1051
1215
  });
1052
1216
  const sandboxSession = startOptions.sandboxSession;
1217
+ const implementationEnvironment = resolveImplementationEnvironment({
1218
+ implementation,
1219
+ env
1220
+ });
1221
+ let sandboxImplementationEnvironment = implementationEnvironment;
1222
+ let sandboxProviderAuthenticationEnvironment = resolvedProviderAuthentication.env;
1223
+ let sandboxProviderEnvironment;
1224
+ if (settings.credentialBrokering != null && sandboxSession.addRequestTransformations != null) {
1225
+ const providerEnvironment = resolveProviderEnvironment({
1226
+ resolvedProviderAuthentication,
1227
+ clientApp
1228
+ });
1229
+ const credentialEnvironmentVariables = [
1230
+ .../* @__PURE__ */ new Set([
1231
+ ...settings.credentialEnv ?? [],
1232
+ "AI_GATEWAY_API_KEY",
1233
+ "VERCEL_OIDC_TOKEN"
1234
+ ])
1235
+ ];
1236
+ const requestTransformations = settings.credentialBrokering({
1237
+ env: {
1238
+ ...implementationEnvironment,
1239
+ ...providerEnvironment
1240
+ }
1241
+ });
1242
+ if (requestTransformations.length > 0) {
1243
+ await sandboxSession.addRequestTransformations(
1244
+ requestTransformations
1245
+ );
1246
+ }
1247
+ sandboxImplementationEnvironment = maskSandboxCredentials({
1248
+ environment: implementationEnvironment,
1249
+ credentialEnvironmentVariables
1250
+ });
1251
+ sandboxProviderEnvironment = maskSandboxCredentials({
1252
+ environment: resolveProviderEnvironment({
1253
+ resolvedProviderAuthentication,
1254
+ clientApp,
1255
+ gatewayApiKey: "AI_GATEWAY_API_KEY"
1256
+ }),
1257
+ credentialEnvironmentVariables
1258
+ });
1259
+ sandboxProviderAuthenticationEnvironment = Object.fromEntries(
1260
+ Object.entries(resolvedProviderAuthentication.env).filter(
1261
+ ([key]) => key !== "AI_SDK_ACP_GATEWAY_API_KEY" && key !== "AI_SDK_ACP_GATEWAY_BASE_URL"
1262
+ )
1263
+ );
1264
+ } else if (settings.credentialBrokering != null) {
1265
+ warnCredentialBrokeringUnavailable();
1266
+ }
1053
1267
  const sandbox = sandboxSession.restricted();
1054
1268
  const resolvedBridgeDir = posix.resolve(
1055
1269
  sandboxSession.defaultWorkingDirectory,
1056
- BOOTSTRAP_DIR
1270
+ bootstrap.bootstrapDir
1057
1271
  );
1058
1272
  const resolvedImplementationDir = `${resolvedBridgeDir}/implementation`;
1059
1273
  const workDir = startOptions.sessionWorkDir;
@@ -1129,12 +1343,16 @@ function createACPV1({
1129
1343
  }
1130
1344
  if (coords != null) {
1131
1345
  try {
1132
- const attachUrl = await sandboxSession.getPortUrl({
1346
+ const endpoint2 = await sandboxSession.getPortEndpoint({
1133
1347
  port: coords.port,
1134
1348
  protocol: "ws"
1135
- }) + `?agent_bridge_token=${encodeURIComponent(coords.token)}`;
1349
+ });
1350
+ const attachEndpoint = withBridgeToken({
1351
+ endpoint: endpoint2,
1352
+ token: coords.token
1353
+ });
1136
1354
  const attachChannel = new SandboxChannel({
1137
- connect: () => openWebSocket({ url: attachUrl }),
1355
+ connect: () => openWebSocket(attachEndpoint),
1138
1356
  outboundSchema: outboundMessageSchema,
1139
1357
  initialLastSeenEventId: coords.lastSeenEventId,
1140
1358
  onDiagnostic,
@@ -1267,16 +1485,14 @@ function createACPV1({
1267
1485
  const proc = await sandbox.spawn({
1268
1486
  command: `node ${shellQuote(`${resolvedBridgeDir}/bridge.mjs`)} --workdir ${shellQuote(workDir)} --bridge-state-dir ${shellQuote(bridgeStateDir)} --implementation-dir ${shellQuote(resolvedImplementationDir)} --bridge-type ${shellQuote(settings.harnessId)}`,
1269
1487
  env: {
1270
- ...resolveImplementationEnvironment({
1271
- implementation,
1272
- env
1273
- }),
1488
+ ...sandboxImplementationEnvironment,
1274
1489
  ...createACPBridgeEnvironment({
1275
1490
  authentication: settings.authentication,
1276
1491
  providerAuthentication: resolvedProviderAuthentication.providerAuthentication,
1492
+ providerEnvironment: sandboxProviderEnvironment,
1277
1493
  sessionMeta: settings.session?.meta
1278
1494
  }),
1279
- ...resolvedProviderAuthentication.env,
1495
+ ...sandboxProviderAuthenticationEnvironment,
1280
1496
  BRIDGE_CHANNEL_TOKEN: token,
1281
1497
  BRIDGE_WS_PORT: String(port),
1282
1498
  ...respawnStrategy?.mode === "disk-replay" ? {
@@ -1316,12 +1532,13 @@ function createACPV1({
1316
1532
  })
1317
1533
  });
1318
1534
  void drainBridgeProcessStream(proc.stdout);
1319
- const wsUrl = await sandboxSession.getPortUrl({
1535
+ const endpoint = await sandboxSession.getPortEndpoint({
1320
1536
  port: boundPort,
1321
1537
  protocol: "ws"
1322
- }) + `?agent_bridge_token=${encodeURIComponent(token)}`;
1538
+ });
1539
+ const bridgeEndpoint = withBridgeToken({ endpoint, token });
1323
1540
  const channel = new SandboxChannel({
1324
- connect: () => openWebSocket({ url: wsUrl }),
1541
+ connect: () => openWebSocket(bridgeEndpoint),
1325
1542
  outboundSchema: outboundMessageSchema,
1326
1543
  ...respawnStrategy?.mode === "disk-replay" ? { initialLastSeenEventId: respawnStrategy.afterSeq } : {},
1327
1544
  onDiagnostic,
@@ -1410,30 +1627,39 @@ function createACPV1({
1410
1627
  }
1411
1628
  };
1412
1629
  }
1413
- async function readBridgeAsset({ name }) {
1414
- const candidates = resolveBridgeAssetCandidates({
1415
- name,
1416
- moduleUrl: import.meta.url
1417
- });
1418
- let lastError;
1419
- for (const url of candidates) {
1420
- try {
1421
- return await readFile(fileURLToPath(url), "utf8");
1422
- } catch (error) {
1423
- if (error.code !== "ENOENT") throw error;
1424
- lastError = error;
1425
- }
1630
+ function resolveProviderEnvironment({
1631
+ resolvedProviderAuthentication,
1632
+ clientApp,
1633
+ gatewayApiKey
1634
+ }) {
1635
+ if (resolvedProviderAuthentication.providerAuthentication?.type !== "ai-gateway") {
1636
+ return {};
1426
1637
  }
1427
- throw lastError ?? new Error(`ACP bridge asset not found: ${name}`);
1638
+ return resolveACPLaunchEnvironment({
1639
+ providerAuthentication: resolvedProviderAuthentication.providerAuthentication,
1640
+ gateway: {
1641
+ apiKey: gatewayApiKey ?? requireResolvedEnvironmentValue({
1642
+ environment: resolvedProviderAuthentication.env,
1643
+ name: "AI_SDK_ACP_GATEWAY_API_KEY"
1644
+ }),
1645
+ baseUrl: requireResolvedEnvironmentValue({
1646
+ environment: resolvedProviderAuthentication.env,
1647
+ name: "AI_SDK_ACP_GATEWAY_BASE_URL"
1648
+ }),
1649
+ clientAppName: clientApp.name,
1650
+ clientAppVersion: clientApp.version
1651
+ }
1652
+ });
1428
1653
  }
1429
- function resolveBridgeAssetCandidates({
1430
- name,
1431
- moduleUrl
1654
+ function requireResolvedEnvironmentValue({
1655
+ environment,
1656
+ name
1432
1657
  }) {
1433
- return [
1434
- new URL(`./bridge/${name}`, moduleUrl),
1435
- new URL(`../bridge/${name}`, moduleUrl)
1436
- ];
1658
+ const value = environment[name];
1659
+ if (value == null) {
1660
+ throw new Error(`ACP resolved environment value ${name} is unavailable.`);
1661
+ }
1662
+ return value;
1437
1663
  }
1438
1664
  function resolveBridgePort({
1439
1665
  sandboxSession,
@@ -1447,9 +1673,14 @@ function resolveBridgePort({
1447
1673
  message: `The ${harnessId} ACP harness needs a TCP port exposed by the sandbox. Create the sandbox with \`ports: [<port>]\` or configure the harness \`port\`.`
1448
1674
  });
1449
1675
  }
1450
- function openWebSocket({ url }) {
1676
+ function openWebSocket({
1677
+ url,
1678
+ headers
1679
+ }) {
1451
1680
  return new Promise((resolve, reject) => {
1452
- const ws = new WebSocket(url);
1681
+ const ws = new WebSocket(url, {
1682
+ headers: headers == null ? void 0 : { ...headers }
1683
+ });
1453
1684
  const onOpen = () => {
1454
1685
  ws.off("error", onError);
1455
1686
  resolve(ws);
@@ -1462,6 +1693,14 @@ function openWebSocket({ url }) {
1462
1693
  ws.once("error", onError);
1463
1694
  });
1464
1695
  }
1696
+ function withBridgeToken({
1697
+ endpoint,
1698
+ token
1699
+ }) {
1700
+ const bridgeUrl = new URL(endpoint.url);
1701
+ bridgeUrl.searchParams.set("agent_bridge_token", token);
1702
+ return { ...endpoint, url: bridgeUrl.toString() };
1703
+ }
1465
1704
  function restoreColdACPSession({
1466
1705
  channel,
1467
1706
  harnessId,
@@ -1489,7 +1728,7 @@ function restoreColdACPSession({
1489
1728
  subscriptions.push(
1490
1729
  channel.on("raw", (event) => {
1491
1730
  const rawValue = event.rawValue;
1492
- if (isRecord(rawValue) && rawValue.type === "acp-session-restored" && (rawValue.method === "resume" || rawValue.method === "load")) {
1731
+ if (isRecord2(rawValue) && rawValue.type === "acp-session-restored" && (rawValue.method === "resume" || rawValue.method === "load")) {
1493
1732
  restorationMethod = rawValue.method;
1494
1733
  }
1495
1734
  })
@@ -2105,7 +2344,7 @@ function assertRecoveryToolCatalog({
2105
2344
  function fingerprintValue({ value }) {
2106
2345
  return createHash5("sha256").update(JSON.stringify(value)).digest("hex");
2107
2346
  }
2108
- function isRecord(value) {
2347
+ function isRecord2(value) {
2109
2348
  return value != null && typeof value === "object" && !Array.isArray(value);
2110
2349
  }
2111
2350
  function unsupported({
@@ -2130,7 +2369,7 @@ function deserializeBridgeError({
2130
2369
  }
2131
2370
 
2132
2371
  // src/version.ts
2133
- var VERSION = true ? "1.0.7" : "0.0.0-test";
2372
+ var VERSION = true ? "1.0.9" : "0.0.0-test";
2134
2373
 
2135
2374
  // src/acp-harness.ts
2136
2375
  var ACP_CLIENT_APP = {
@@ -2172,6 +2411,11 @@ var acpResumeStateSchema = z4.object({
2172
2411
  skillsFingerprint: z4.string().optional()
2173
2412
  });
2174
2413
  function createACP(settings) {
2414
+ if (settings.credentialEnv == null !== (settings.credentialBrokering == null)) {
2415
+ throw new Error(
2416
+ "ACP credentialEnv and credentialBrokering must be configured together."
2417
+ );
2418
+ }
2175
2419
  if (settings.mcpServers != null && Object.prototype.hasOwnProperty.call(
2176
2420
  settings.mcpServers,
2177
2421
  "ai-sdk-harness-tools"