@ai-sdk/harness-acp 1.0.22 → 1.0.24
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/host-tool-mcp.mjs +1 -1
- package/dist/bridge/index.mjs +24 -6
- package/dist/bridge/index.mjs.map +1 -1
- package/dist/index.d.ts +6 -2
- package/dist/index.js +51 -9
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/index.ts +1 -0
- package/src/v1/acp-bootstrap.ts +25 -6
- package/src/v1/acp-v1-settings.ts +9 -1
- package/src/v1/bridge/index.ts +31 -6
- package/src/v1/implementation.ts +57 -21
- package/src/v1/index.ts +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-sdk/harness-acp",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.24",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -27,8 +27,8 @@
|
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"ws": "^8.21.0",
|
|
30
|
-
"@ai-sdk/harness": "1.0.
|
|
31
|
-
"@ai-sdk/provider-utils": "5.0.
|
|
30
|
+
"@ai-sdk/harness": "1.0.86",
|
|
31
|
+
"@ai-sdk/provider-utils": "5.0.29"
|
|
32
32
|
},
|
|
33
33
|
"peerDependencies": {
|
|
34
34
|
"zod": "^3.25.76 || ^4.1.8"
|
package/src/index.ts
CHANGED
package/src/v1/acp-bootstrap.ts
CHANGED
|
@@ -5,8 +5,9 @@ import {
|
|
|
5
5
|
createImplementationDescriptor,
|
|
6
6
|
createImplementationInstallCommand,
|
|
7
7
|
createImplementationManifest,
|
|
8
|
+
getImplementationInstallScript,
|
|
8
9
|
getImplementationLockfile,
|
|
9
|
-
type
|
|
10
|
+
type ACPImplementation,
|
|
10
11
|
} from './implementation';
|
|
11
12
|
|
|
12
13
|
export function createACPBootstrap({
|
|
@@ -14,7 +15,7 @@ export function createACPBootstrap({
|
|
|
14
15
|
implementation,
|
|
15
16
|
}: {
|
|
16
17
|
readonly harnessId: string;
|
|
17
|
-
readonly implementation:
|
|
18
|
+
readonly implementation: ACPImplementation;
|
|
18
19
|
}): {
|
|
19
20
|
readonly bootstrapDir: string;
|
|
20
21
|
readonly getBootstrap: () => Promise<HarnessV1Bootstrap>;
|
|
@@ -33,9 +34,15 @@ export function createACPBootstrap({
|
|
|
33
34
|
readBridgeAsset({ name: 'index.mjs' }),
|
|
34
35
|
readBridgeAsset({ name: 'host-tool-mcp.mjs' }),
|
|
35
36
|
]);
|
|
37
|
+
const implementationManifest = createImplementationManifest({
|
|
38
|
+
implementation,
|
|
39
|
+
});
|
|
36
40
|
const implementationLock = getImplementationLockfile({
|
|
37
41
|
implementation,
|
|
38
42
|
});
|
|
43
|
+
const implementationInstallScript = getImplementationInstallScript({
|
|
44
|
+
implementation,
|
|
45
|
+
});
|
|
39
46
|
cachedBootstrap = {
|
|
40
47
|
harnessId,
|
|
41
48
|
bootstrapDir,
|
|
@@ -53,14 +60,18 @@ export function createACPBootstrap({
|
|
|
53
60
|
path: `${bootstrapDir}/host-tool-mcp.mjs`,
|
|
54
61
|
content: hostToolMCP,
|
|
55
62
|
},
|
|
56
|
-
{
|
|
57
|
-
path: `${bootstrapDir}/implementation/package.json`,
|
|
58
|
-
content: createImplementationManifest({ implementation }),
|
|
59
|
-
},
|
|
60
63
|
{
|
|
61
64
|
path: `${bootstrapDir}/implementation/implementation.json`,
|
|
62
65
|
content: createImplementationDescriptor({ implementation }),
|
|
63
66
|
},
|
|
67
|
+
...(implementationManifest == null
|
|
68
|
+
? []
|
|
69
|
+
: [
|
|
70
|
+
{
|
|
71
|
+
path: `${bootstrapDir}/implementation/package.json`,
|
|
72
|
+
content: implementationManifest,
|
|
73
|
+
},
|
|
74
|
+
]),
|
|
64
75
|
...(implementationLock == null
|
|
65
76
|
? []
|
|
66
77
|
: [
|
|
@@ -69,6 +80,14 @@ export function createACPBootstrap({
|
|
|
69
80
|
content: implementationLock,
|
|
70
81
|
},
|
|
71
82
|
]),
|
|
83
|
+
...(implementationInstallScript == null
|
|
84
|
+
? []
|
|
85
|
+
: [
|
|
86
|
+
{
|
|
87
|
+
path: `${bootstrapDir}/implementation/install.sh`,
|
|
88
|
+
content: implementationInstallScript,
|
|
89
|
+
},
|
|
90
|
+
]),
|
|
72
91
|
],
|
|
73
92
|
commands: [
|
|
74
93
|
{
|
|
@@ -47,7 +47,15 @@ export type ACPNpmLockedSource = {
|
|
|
47
47
|
readonly pnpmLockYaml: string;
|
|
48
48
|
};
|
|
49
49
|
|
|
50
|
-
export type
|
|
50
|
+
export type ACPInstallCommandSource = {
|
|
51
|
+
readonly type: 'install-command';
|
|
52
|
+
readonly command: string;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export type ACPSource =
|
|
56
|
+
| ACPNpmSimpleSource
|
|
57
|
+
| ACPNpmLockedSource
|
|
58
|
+
| ACPInstallCommandSource;
|
|
51
59
|
|
|
52
60
|
export type ACPAuthentication = {
|
|
53
61
|
readonly methodId: string;
|
package/src/v1/bridge/index.ts
CHANGED
|
@@ -53,7 +53,8 @@ import {
|
|
|
53
53
|
} from './session-lifecycle';
|
|
54
54
|
|
|
55
55
|
type ImplementationDescriptor = {
|
|
56
|
-
readonly
|
|
56
|
+
readonly executablePath: string;
|
|
57
|
+
readonly privateHome: boolean;
|
|
57
58
|
readonly args: ReadonlyArray<string>;
|
|
58
59
|
readonly envKeys: ReadonlyArray<string>;
|
|
59
60
|
};
|
|
@@ -381,10 +382,14 @@ async function ensureSession({
|
|
|
381
382
|
instructions: start.instructions,
|
|
382
383
|
instructionMapping: start.instructionMapping,
|
|
383
384
|
sessionMeta: bridgeConfiguration.sessionMeta,
|
|
384
|
-
environment: createChildEnvironment({
|
|
385
|
+
environment: createChildEnvironment({
|
|
386
|
+
launchEnv,
|
|
387
|
+
implementationDir,
|
|
388
|
+
privateHome: implementation.privateHome,
|
|
389
|
+
}),
|
|
385
390
|
});
|
|
386
391
|
child = spawn(
|
|
387
|
-
`${implementationDir}
|
|
392
|
+
`${implementationDir}/${implementation.executablePath}`,
|
|
388
393
|
[...implementation.args],
|
|
389
394
|
{
|
|
390
395
|
cwd: workDir,
|
|
@@ -686,8 +691,12 @@ function resolveGatewayValues({
|
|
|
686
691
|
|
|
687
692
|
function createChildEnvironment({
|
|
688
693
|
launchEnv,
|
|
694
|
+
implementationDir,
|
|
695
|
+
privateHome,
|
|
689
696
|
}: {
|
|
690
697
|
launchEnv: Readonly<Record<string, string>>;
|
|
698
|
+
implementationDir: string;
|
|
699
|
+
privateHome: boolean;
|
|
691
700
|
}): NodeJS.ProcessEnv {
|
|
692
701
|
const blocked = new Set([
|
|
693
702
|
'BRIDGE_CHANNEL_TOKEN',
|
|
@@ -698,7 +707,7 @@ function createChildEnvironment({
|
|
|
698
707
|
'AI_SDK_ACP_CLIENT_APP_VERSION',
|
|
699
708
|
ACP_BRIDGE_CONFIGURATION_ENV,
|
|
700
709
|
]);
|
|
701
|
-
|
|
710
|
+
const environment = {
|
|
702
711
|
...Object.fromEntries(
|
|
703
712
|
Object.entries(processEnv).filter(
|
|
704
713
|
([key, value]) => !blocked.has(key) && value != null,
|
|
@@ -706,6 +715,20 @@ function createChildEnvironment({
|
|
|
706
715
|
),
|
|
707
716
|
...launchEnv,
|
|
708
717
|
};
|
|
718
|
+
if (!privateHome) return environment;
|
|
719
|
+
|
|
720
|
+
const home = `${implementationDir}/home`;
|
|
721
|
+
const inheritedPath = environment.PATH;
|
|
722
|
+
return {
|
|
723
|
+
...environment,
|
|
724
|
+
HOME: home,
|
|
725
|
+
PATH: [
|
|
726
|
+
`${home}/.local/bin`,
|
|
727
|
+
...(inheritedPath == null || inheritedPath.length === 0
|
|
728
|
+
? []
|
|
729
|
+
: [inheritedPath]),
|
|
730
|
+
].join(':'),
|
|
731
|
+
};
|
|
709
732
|
}
|
|
710
733
|
|
|
711
734
|
function isRecord(value: unknown): value is Readonly<Record<string, unknown>> {
|
|
@@ -798,7 +821,8 @@ async function readImplementationDescriptor({
|
|
|
798
821
|
}).json();
|
|
799
822
|
if (
|
|
800
823
|
!isRecord(value) ||
|
|
801
|
-
typeof value.
|
|
824
|
+
typeof value.executablePath !== 'string' ||
|
|
825
|
+
typeof value.privateHome !== 'boolean' ||
|
|
802
826
|
!Array.isArray(value.args) ||
|
|
803
827
|
!value.args.every(item => typeof item === 'string') ||
|
|
804
828
|
!Array.isArray(value.envKeys) ||
|
|
@@ -807,7 +831,8 @@ async function readImplementationDescriptor({
|
|
|
807
831
|
throw new Error('Invalid ACP implementation descriptor.');
|
|
808
832
|
}
|
|
809
833
|
return {
|
|
810
|
-
|
|
834
|
+
executablePath: value.executablePath,
|
|
835
|
+
privateHome: value.privateHome,
|
|
811
836
|
args: value.args as string[],
|
|
812
837
|
envKeys: value.envKeys as string[],
|
|
813
838
|
};
|
package/src/v1/implementation.ts
CHANGED
|
@@ -18,7 +18,7 @@ import type {
|
|
|
18
18
|
* two shapes are required to agree, and createACPV1Implementation is the only
|
|
19
19
|
* place that maps between them.
|
|
20
20
|
*/
|
|
21
|
-
export type
|
|
21
|
+
export type ACPImplementation = {
|
|
22
22
|
readonly source: ACPSource;
|
|
23
23
|
readonly executable: string;
|
|
24
24
|
readonly args?: ReadonlyArray<string>;
|
|
@@ -31,7 +31,7 @@ export function createACPV1Implementation({
|
|
|
31
31
|
settings,
|
|
32
32
|
}: {
|
|
33
33
|
settings: ACPV1Settings;
|
|
34
|
-
}):
|
|
34
|
+
}): ACPImplementation {
|
|
35
35
|
return {
|
|
36
36
|
source: settings.source,
|
|
37
37
|
executable: settings.executable,
|
|
@@ -50,7 +50,7 @@ const EXECUTABLE_NAME_REGEXP = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
|
50
50
|
const ENVIRONMENT_VARIABLE_NAME_REGEXP = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
51
51
|
|
|
52
52
|
export function validateACPV1Implementation(
|
|
53
|
-
implementation:
|
|
53
|
+
implementation: ACPImplementation,
|
|
54
54
|
): void {
|
|
55
55
|
const { source } = implementation;
|
|
56
56
|
if (source.type === 'npm-locked') {
|
|
@@ -60,12 +60,14 @@ export function validateACPV1Implementation(
|
|
|
60
60
|
if (source.pnpmLockYaml.length === 0) {
|
|
61
61
|
throw new Error('ACP source.pnpmLockYaml must not be empty.');
|
|
62
62
|
}
|
|
63
|
-
} else {
|
|
63
|
+
} else if (source.type === 'npm-simple') {
|
|
64
64
|
validateNpmSimpleSource({ source });
|
|
65
|
+
} else if (source.command.trim().length === 0) {
|
|
66
|
+
throw new Error('ACP source.command must not be empty.');
|
|
65
67
|
}
|
|
66
68
|
if (!EXECUTABLE_NAME_REGEXP.test(implementation.executable)) {
|
|
67
69
|
throw new Error(
|
|
68
|
-
`ACP executable must be a
|
|
70
|
+
`ACP executable must be a bare command name without a path; received ${JSON.stringify(implementation.executable)}.`,
|
|
69
71
|
);
|
|
70
72
|
}
|
|
71
73
|
|
|
@@ -98,9 +100,10 @@ export function validateACPV1Implementation(
|
|
|
98
100
|
export function createImplementationManifest({
|
|
99
101
|
implementation,
|
|
100
102
|
}: {
|
|
101
|
-
implementation:
|
|
102
|
-
}): string {
|
|
103
|
+
implementation: ACPImplementation;
|
|
104
|
+
}): string | undefined {
|
|
103
105
|
const { source } = implementation;
|
|
106
|
+
if (source.type === 'install-command') return undefined;
|
|
104
107
|
if (source.type === 'npm-locked') {
|
|
105
108
|
return source.packageJson;
|
|
106
109
|
}
|
|
@@ -124,7 +127,7 @@ export function createImplementationManifest({
|
|
|
124
127
|
export function getImplementationLockfile({
|
|
125
128
|
implementation,
|
|
126
129
|
}: {
|
|
127
|
-
implementation:
|
|
130
|
+
implementation: ACPImplementation;
|
|
128
131
|
}): string | undefined {
|
|
129
132
|
const { source } = implementation;
|
|
130
133
|
return source.type === 'npm-locked' ? source.pnpmLockYaml : undefined;
|
|
@@ -133,12 +136,16 @@ export function getImplementationLockfile({
|
|
|
133
136
|
export function createImplementationDescriptor({
|
|
134
137
|
implementation,
|
|
135
138
|
}: {
|
|
136
|
-
implementation:
|
|
139
|
+
implementation: ACPImplementation;
|
|
137
140
|
}): string {
|
|
138
141
|
return (
|
|
139
142
|
JSON.stringify(
|
|
140
143
|
{
|
|
141
|
-
|
|
144
|
+
executablePath:
|
|
145
|
+
implementation.source.type === 'install-command'
|
|
146
|
+
? `home/.local/bin/${implementation.executable}`
|
|
147
|
+
: `node_modules/.bin/${implementation.executable}`,
|
|
148
|
+
privateHome: implementation.source.type === 'install-command',
|
|
142
149
|
args: implementation.args ?? [],
|
|
143
150
|
envKeys: getImplementationEnvironmentKeys({ implementation }),
|
|
144
151
|
},
|
|
@@ -158,7 +165,7 @@ export function createImplementationIdentity({
|
|
|
158
165
|
}: {
|
|
159
166
|
harnessId: string;
|
|
160
167
|
acpVersion: 'v1';
|
|
161
|
-
implementation:
|
|
168
|
+
implementation: ACPImplementation;
|
|
162
169
|
clientApp: ACPClientApp;
|
|
163
170
|
providerAuthentication: ACPProviderAuthenticationCompatibility | undefined;
|
|
164
171
|
permissionModeMapping?: ACPPermissionModeMapping;
|
|
@@ -171,13 +178,18 @@ export function createImplementationIdentity({
|
|
|
171
178
|
packageJson: source.packageJson,
|
|
172
179
|
pnpmLockYaml: source.pnpmLockYaml,
|
|
173
180
|
}
|
|
174
|
-
:
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
+
: source.type === 'npm-simple'
|
|
182
|
+
? {
|
|
183
|
+
type: source.type,
|
|
184
|
+
packageName: source.packageName,
|
|
185
|
+
...(source.packageVersion == null
|
|
186
|
+
? {}
|
|
187
|
+
: { packageVersion: source.packageVersion }),
|
|
188
|
+
}
|
|
189
|
+
: {
|
|
190
|
+
type: source.type,
|
|
191
|
+
command: source.command,
|
|
192
|
+
};
|
|
181
193
|
const forwardedEnvironment = [
|
|
182
194
|
...new Set(implementation.forwardEnv ?? []),
|
|
183
195
|
].sort();
|
|
@@ -216,8 +228,11 @@ export function createImplementationInstallCommand({
|
|
|
216
228
|
}: {
|
|
217
229
|
implementationDir: string;
|
|
218
230
|
storeDir: string;
|
|
219
|
-
implementation:
|
|
231
|
+
implementation: ACPImplementation;
|
|
220
232
|
}): string {
|
|
233
|
+
if (implementation.source.type === 'install-command') {
|
|
234
|
+
return 'bash implementation/install.sh';
|
|
235
|
+
}
|
|
221
236
|
return (
|
|
222
237
|
`pnpm --dir ${implementationDir} install` +
|
|
223
238
|
(implementation.source.type === 'npm-locked' ? ' --frozen-lockfile' : '') +
|
|
@@ -225,11 +240,32 @@ export function createImplementationInstallCommand({
|
|
|
225
240
|
);
|
|
226
241
|
}
|
|
227
242
|
|
|
243
|
+
export function getImplementationInstallScript({
|
|
244
|
+
implementation,
|
|
245
|
+
}: {
|
|
246
|
+
implementation: ACPImplementation;
|
|
247
|
+
}): string | undefined {
|
|
248
|
+
if (implementation.source.type !== 'install-command') return undefined;
|
|
249
|
+
return `#!/usr/bin/env bash
|
|
250
|
+
set -euo pipefail
|
|
251
|
+
|
|
252
|
+
ACP_IMPLEMENTATION_DIR="$(cd "$(dirname "\${BASH_SOURCE[0]}")" && pwd)"
|
|
253
|
+
ACP_INSTALL_HOME="$ACP_IMPLEMENTATION_DIR/home"
|
|
254
|
+
export HOME="$ACP_INSTALL_HOME"
|
|
255
|
+
export PATH="$ACP_INSTALL_HOME/.local/bin:$PATH"
|
|
256
|
+
|
|
257
|
+
mkdir -p "$ACP_INSTALL_HOME/.local/bin"
|
|
258
|
+
cd "$ACP_IMPLEMENTATION_DIR"
|
|
259
|
+
|
|
260
|
+
${implementation.source.command}
|
|
261
|
+
`;
|
|
262
|
+
}
|
|
263
|
+
|
|
228
264
|
export function resolveImplementationEnvironment({
|
|
229
265
|
implementation,
|
|
230
266
|
env,
|
|
231
267
|
}: {
|
|
232
|
-
implementation:
|
|
268
|
+
implementation: ACPImplementation;
|
|
233
269
|
env: Readonly<Record<string, string | undefined>>;
|
|
234
270
|
}): Record<string, string> {
|
|
235
271
|
const forwardedEnvironment: Record<string, string> = {};
|
|
@@ -302,7 +338,7 @@ function validateForwardEnvironment({
|
|
|
302
338
|
function getImplementationEnvironmentKeys({
|
|
303
339
|
implementation,
|
|
304
340
|
}: {
|
|
305
|
-
implementation:
|
|
341
|
+
implementation: ACPImplementation;
|
|
306
342
|
}): string[] {
|
|
307
343
|
return [
|
|
308
344
|
...new Set([
|