@ai-sdk/harness-acp 1.0.44 → 1.0.46
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 +21 -0
- package/dist/bridge/host-tool-mcp.mjs +1 -1
- package/dist/bridge/index.mjs +1 -1
- package/dist/index.d.ts +28 -1
- package/dist/index.js +50 -5
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/acp-harness.ts +2 -0
- package/src/index.ts +2 -0
- package/src/v1/acp-v1-harness.ts +68 -7
- package/src/v1/acp-v1-settings.ts +27 -0
- package/src/v1/index.ts +2 -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.46",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -26,8 +26,8 @@
|
|
|
26
26
|
}
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@ai-sdk/harness": "1.0.
|
|
30
|
-
"@ai-sdk/provider-utils": "5.0.
|
|
29
|
+
"@ai-sdk/harness": "1.0.108",
|
|
30
|
+
"@ai-sdk/provider-utils": "5.0.40",
|
|
31
31
|
"ws": "^8.21.0"
|
|
32
32
|
},
|
|
33
33
|
"peerDependencies": {
|
package/src/acp-harness.ts
CHANGED
|
@@ -49,12 +49,14 @@ export type ACPHarnessSettings<
|
|
|
49
49
|
readonly version?: ACPV1Settings['version'];
|
|
50
50
|
readonly harnessId: ACPV1Settings['harnessId'];
|
|
51
51
|
readonly auth?: ACPAuthenticationMode;
|
|
52
|
+
readonly resolveAuthenticationEnvironment?: ACPV1Settings['resolveAuthenticationEnvironment'];
|
|
52
53
|
readonly source: ACPV1Settings['source'];
|
|
53
54
|
readonly executable: ACPV1Settings['executable'];
|
|
54
55
|
readonly args?: ACPV1Settings['args'];
|
|
55
56
|
readonly forwardEnv?: ACPV1Settings['forwardEnv'];
|
|
56
57
|
readonly credentialEnv?: ACPV1Settings['credentialEnv'];
|
|
57
58
|
readonly credentialBrokering?: ACPV1Settings['credentialBrokering'];
|
|
59
|
+
readonly authenticationFiles?: ACPV1Settings['authenticationFiles'];
|
|
58
60
|
/**
|
|
59
61
|
* Customizes each credential value before it is forwarded into a sandbox
|
|
60
62
|
* process. This does not restrict which credentials the harness adapter can
|
package/src/index.ts
CHANGED
package/src/v1/acp-v1-harness.ts
CHANGED
|
@@ -197,15 +197,21 @@ export function createACPV1<TBuiltinTools extends ToolSet = {}>({
|
|
|
197
197
|
}
|
|
198
198
|
const permissionMode = startOptions.permissionMode ?? 'allow-all';
|
|
199
199
|
const env = { ...process.env };
|
|
200
|
-
const authenticationEnvironment =
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
200
|
+
const authenticationEnvironment =
|
|
201
|
+
settings.resolveAuthenticationEnvironment == null
|
|
202
|
+
? resolveACPAuthenticationEnvironment({
|
|
203
|
+
auth: settings.auth,
|
|
204
|
+
env,
|
|
205
|
+
})
|
|
206
|
+
: await settings.resolveAuthenticationEnvironment({
|
|
207
|
+
auth: settings.auth,
|
|
208
|
+
env,
|
|
209
|
+
});
|
|
204
210
|
const providerAuthenticationCompatibility =
|
|
205
211
|
resolveACPProviderAuthenticationCompatibility({
|
|
206
212
|
auth: settings.auth,
|
|
207
213
|
providerAuthentication: settings.providerAuthentication,
|
|
208
|
-
env,
|
|
214
|
+
env: authenticationEnvironment,
|
|
209
215
|
});
|
|
210
216
|
const implementationIdentity = createImplementationIdentity({
|
|
211
217
|
harnessId: settings.harnessId,
|
|
@@ -228,7 +234,7 @@ export function createACPV1<TBuiltinTools extends ToolSet = {}>({
|
|
|
228
234
|
providerAuthentication: settings.providerAuthentication,
|
|
229
235
|
clientApp,
|
|
230
236
|
},
|
|
231
|
-
env,
|
|
237
|
+
env: authenticationEnvironment,
|
|
232
238
|
compatibility: providerAuthenticationCompatibility,
|
|
233
239
|
});
|
|
234
240
|
const sandboxSession = startOptions.sandboxSession;
|
|
@@ -273,7 +279,7 @@ export function createACPV1<TBuiltinTools extends ToolSet = {}>({
|
|
|
273
279
|
}
|
|
274
280
|
const implementationEnvironment = resolveImplementationEnvironment({
|
|
275
281
|
implementation,
|
|
276
|
-
env,
|
|
282
|
+
env: { ...env, ...authenticationEnvironment },
|
|
277
283
|
credentialEnv: authenticationEnvironment,
|
|
278
284
|
});
|
|
279
285
|
let sandboxImplementationEnvironment = implementationEnvironment;
|
|
@@ -589,6 +595,17 @@ export function createACPV1<TBuiltinTools extends ToolSet = {}>({
|
|
|
589
595
|
],
|
|
590
596
|
});
|
|
591
597
|
}
|
|
598
|
+
await writeACPAuthenticationFiles({
|
|
599
|
+
sandbox: toolSafeSandboxSession,
|
|
600
|
+
homePath: implementationHomeDir,
|
|
601
|
+
files:
|
|
602
|
+
settings.authenticationFiles?.({
|
|
603
|
+
env: implementationEnvironment,
|
|
604
|
+
sandboxEnv: forwardedImplementationEnvironment,
|
|
605
|
+
credentialBrokeringAvailable: sandboxCredentialEnvironment != null,
|
|
606
|
+
}) ?? [],
|
|
607
|
+
abortSignal: startOptions.abortSignal,
|
|
608
|
+
});
|
|
592
609
|
const port = resolveBridgePort({
|
|
593
610
|
sandboxSession,
|
|
594
611
|
override: portOverride,
|
|
@@ -797,6 +814,50 @@ export function createACPV1<TBuiltinTools extends ToolSet = {}>({
|
|
|
797
814
|
};
|
|
798
815
|
}
|
|
799
816
|
|
|
817
|
+
async function writeACPAuthenticationFiles({
|
|
818
|
+
sandbox,
|
|
819
|
+
homePath,
|
|
820
|
+
files,
|
|
821
|
+
abortSignal,
|
|
822
|
+
}: {
|
|
823
|
+
sandbox: SandboxSession;
|
|
824
|
+
homePath: string;
|
|
825
|
+
files: ReadonlyArray<{ readonly path: string; readonly content: string }>;
|
|
826
|
+
abortSignal?: AbortSignal;
|
|
827
|
+
}): Promise<void> {
|
|
828
|
+
const targetPaths = files.map(file => {
|
|
829
|
+
if (
|
|
830
|
+
file.path.length === 0 ||
|
|
831
|
+
posix.isAbsolute(file.path) ||
|
|
832
|
+
file.path.split('/').some(segment => segment === '..')
|
|
833
|
+
) {
|
|
834
|
+
throw new Error(
|
|
835
|
+
`ACP authentication file path must be relative without traversal: ${JSON.stringify(file.path)}.`,
|
|
836
|
+
);
|
|
837
|
+
}
|
|
838
|
+
return posix.join(homePath, file.path);
|
|
839
|
+
});
|
|
840
|
+
|
|
841
|
+
for (let index = 0; index < files.length; index++) {
|
|
842
|
+
await sandbox.writeTextFile({
|
|
843
|
+
path: targetPaths[index],
|
|
844
|
+
content: files[index].content,
|
|
845
|
+
abortSignal,
|
|
846
|
+
});
|
|
847
|
+
}
|
|
848
|
+
if (targetPaths.length === 0) return;
|
|
849
|
+
|
|
850
|
+
const result = await sandbox.run({
|
|
851
|
+
command: `chmod 600 -- ${targetPaths.map(shellQuote).join(' ')}`,
|
|
852
|
+
abortSignal,
|
|
853
|
+
});
|
|
854
|
+
if (result.exitCode !== 0) {
|
|
855
|
+
throw new Error(
|
|
856
|
+
`Failed to secure ACP authentication files (exit ${result.exitCode})${result.stderr ? `: ${result.stderr}` : ''}`,
|
|
857
|
+
);
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
|
|
800
861
|
function resolveProviderEnvironment({
|
|
801
862
|
resolvedProviderAuthentication,
|
|
802
863
|
clientApp,
|
|
@@ -98,6 +98,20 @@ export type ACPCredentialBrokering = ({
|
|
|
98
98
|
headers?: Readonly<Record<string, string>>;
|
|
99
99
|
}) => ReadonlyArray<HarnessV1RequestTransformation>;
|
|
100
100
|
|
|
101
|
+
export type ACPAuthenticationFile = {
|
|
102
|
+
/** Path relative to the ACP implementation's private home directory. */
|
|
103
|
+
readonly path: string;
|
|
104
|
+
readonly content: string;
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
export type ACPAuthenticationFiles = (options: {
|
|
108
|
+
/** Host values resolved for the implementation. */
|
|
109
|
+
readonly env: Readonly<Record<string, string>>;
|
|
110
|
+
/** Values exposed to the sandbox process after credential brokering. */
|
|
111
|
+
readonly sandboxEnv: Readonly<Record<string, string>>;
|
|
112
|
+
readonly credentialBrokeringAvailable: boolean;
|
|
113
|
+
}) => ReadonlyArray<ACPAuthenticationFile>;
|
|
114
|
+
|
|
101
115
|
export type ACPPermissionModeTarget =
|
|
102
116
|
| {
|
|
103
117
|
readonly type: 'session-mode';
|
|
@@ -169,12 +183,25 @@ export type ACPV1Settings = {
|
|
|
169
183
|
readonly mcpServers?: Record<string, unknown>;
|
|
170
184
|
readonly isMcpToolCall?: (toolCall: ACPToolCall) => boolean;
|
|
171
185
|
readonly auth?: ACPAuthenticationMode;
|
|
186
|
+
/**
|
|
187
|
+
* Resolves adapter-owned authentication on the host before any environment
|
|
188
|
+
* values are forwarded to the ACP implementation.
|
|
189
|
+
*/
|
|
190
|
+
readonly resolveAuthenticationEnvironment?: (options: {
|
|
191
|
+
readonly auth: ACPAuthenticationMode | undefined;
|
|
192
|
+
readonly env: Readonly<Record<string, string | undefined>>;
|
|
193
|
+
}) => Promise<Readonly<Record<string, string | undefined>>>;
|
|
172
194
|
readonly source: ACPSource;
|
|
173
195
|
readonly executable: string;
|
|
174
196
|
readonly args?: ReadonlyArray<string>;
|
|
175
197
|
readonly forwardEnv?: ReadonlyArray<string>;
|
|
176
198
|
readonly credentialEnv?: ReadonlyArray<string>;
|
|
177
199
|
readonly credentialBrokering?: ACPCredentialBrokering;
|
|
200
|
+
/**
|
|
201
|
+
* Materializes private authentication files below the ACP implementation's
|
|
202
|
+
* home directory before the implementation starts.
|
|
203
|
+
*/
|
|
204
|
+
readonly authenticationFiles?: ACPAuthenticationFiles;
|
|
178
205
|
/**
|
|
179
206
|
* Customizes each credential value before it is forwarded into a sandbox
|
|
180
207
|
* process. This does not restrict which credentials the harness adapter can
|