@ai-sdk/harness-acp 1.0.8 → 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/CHANGELOG.md +12 -0
- package/README.md +17 -4
- package/dist/bridge/host-tool-mcp.mjs +1 -1
- package/dist/bridge/index.mjs +4 -3
- package/dist/bridge/index.mjs.map +1 -1
- package/dist/index.d.ts +9 -2
- package/dist/index.js +349 -105
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/acp-harness.ts +10 -0
- package/src/index.ts +1 -0
- package/src/v1/acp-bootstrap.ts +119 -0
- package/src/v1/acp-v1-harness.ts +163 -118
- package/src/v1/acp-v1-settings.ts +12 -1
- package/src/v1/bridge/acp-v1-bridge-environment.ts +4 -0
- package/src/v1/bridge/index.ts +10 -8
- package/src/v1/implementation.ts +25 -1
- package/src/v1/index.ts +1 -0
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
|
|
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
|
|
944
|
-
|
|
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:
|
|
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
|
-
|
|
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
|
|
1346
|
+
const endpoint2 = await sandboxSession.getPortEndpoint({
|
|
1133
1347
|
port: coords.port,
|
|
1134
1348
|
protocol: "ws"
|
|
1135
|
-
})
|
|
1349
|
+
});
|
|
1350
|
+
const attachEndpoint = withBridgeToken({
|
|
1351
|
+
endpoint: endpoint2,
|
|
1352
|
+
token: coords.token
|
|
1353
|
+
});
|
|
1136
1354
|
const attachChannel = new SandboxChannel({
|
|
1137
|
-
connect: () => openWebSocket(
|
|
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
|
-
...
|
|
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
|
-
...
|
|
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
|
|
1535
|
+
const endpoint = await sandboxSession.getPortEndpoint({
|
|
1320
1536
|
port: boundPort,
|
|
1321
1537
|
protocol: "ws"
|
|
1322
|
-
})
|
|
1538
|
+
});
|
|
1539
|
+
const bridgeEndpoint = withBridgeToken({ endpoint, token });
|
|
1323
1540
|
const channel = new SandboxChannel({
|
|
1324
|
-
connect: () => openWebSocket(
|
|
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
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
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
|
-
|
|
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
|
|
1430
|
-
|
|
1431
|
-
|
|
1654
|
+
function requireResolvedEnvironmentValue({
|
|
1655
|
+
environment,
|
|
1656
|
+
name
|
|
1432
1657
|
}) {
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
new
|
|
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({
|
|
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 (
|
|
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
|
|
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.
|
|
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"
|