@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-sdk/harness-acp",
3
- "version": "1.0.44",
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.106",
30
- "@ai-sdk/provider-utils": "5.0.38",
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": {
@@ -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
@@ -21,6 +21,8 @@ export type {
21
21
  } from './acp-tool-call';
22
22
  export type {
23
23
  ACPAskUserQuestionsSettings,
24
+ ACPAuthenticationFile,
25
+ ACPAuthenticationFiles,
24
26
  ACPCredentialBrokering,
25
27
  ACPAuthentication,
26
28
  ACPHostToolMCPTransport,
@@ -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 = resolveACPAuthenticationEnvironment({
201
- auth: settings.auth,
202
- env,
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
package/src/v1/index.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  export { createACPV1 } from './acp-v1-harness';
2
2
  export type {
3
3
  ACPAskUserQuestionsSettings,
4
+ ACPAuthenticationFile,
5
+ ACPAuthenticationFiles,
4
6
  ACPCredentialBrokering,
5
7
  ACPAuthentication,
6
8
  ACPAuthenticationMode,