@ai-sdk/harness-acp 1.0.31 → 1.0.33

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.31",
3
+ "version": "1.0.33",
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.93",
30
- "@ai-sdk/provider-utils": "5.0.33",
29
+ "@ai-sdk/harness": "1.0.95",
30
+ "@ai-sdk/provider-utils": "5.0.34",
31
31
  "ws": "^8.21.0"
32
32
  },
33
33
  "peerDependencies": {
@@ -48,11 +48,12 @@ export type ACPHarnessSettings<TBuiltinTools extends ToolSet = {}> = {
48
48
  readonly credentialForwarding?: ACPV1Settings['credentialForwarding'];
49
49
  readonly env?: ACPV1Settings['env'];
50
50
  readonly authentication?: ACPV1Settings['authentication'];
51
+ readonly clientCapabilities?: ACPV1Settings['clientCapabilities'];
51
52
  readonly providerAuthentication?: ACPV1Settings['providerAuthentication'];
52
53
  /**
53
- * Maps a HarnessAgent model identifier to implementation launch settings.
54
+ * Maps the HarnessAgent model identifier to an ACP session operation.
54
55
  */
55
- readonly resolveModel: ACPV1Settings['resolveModel'];
56
+ readonly modelMapping: ACPV1Settings['modelMapping'];
56
57
  /**
57
58
  * @deprecated Use `model` on `HarnessAgent` instead.
58
59
  */
@@ -114,6 +115,8 @@ const acpResumeStateSchema = z.object({
114
115
  skillsDirectory: z.string().optional(),
115
116
  });
116
117
 
118
+ type ACPBridgeCoords = z.infer<typeof acpBridgeCoordsSchema>;
119
+
117
120
  export function createACP<TBuiltinTools extends ToolSet = {}>(
118
121
  settings: ACPHarnessSettings<TBuiltinTools>,
119
122
  ): HarnessV1<TBuiltinTools> {
@@ -148,7 +151,9 @@ export function createACP<TBuiltinTools extends ToolSet = {}>(
148
151
  portEndpoint: settings.portEndpoint,
149
152
  startupTimeoutMs: settings.startupTimeoutMs,
150
153
  clientApp,
151
- lifecycleStateSchema: acpResumeStateSchema,
154
+ lifecycleStateSchema: acpResumeStateSchema satisfies z.ZodType<{
155
+ bridge?: ACPBridgeCoords;
156
+ }>,
152
157
  });
153
158
  }
154
159
  default:
package/src/index.ts CHANGED
@@ -24,8 +24,7 @@ export type {
24
24
  ACPAuthentication,
25
25
  ACPInstallCommandSource,
26
26
  ACPInstructionMapping,
27
- ACPModelResolution,
28
- ACPModelResolver,
27
+ ACPModelMapping,
29
28
  ACPNpmLockedSource,
30
29
  ACPNpmSimpleSource,
31
30
  ACPOutputSchemaMapping,
@@ -120,31 +120,23 @@ export function createACPBootstrap({
120
120
  }
121
121
 
122
122
  async function readBridgeAsset({ name }: { name: string }): Promise<string> {
123
- const candidates = resolveBridgeAssetCandidates({
124
- name,
125
- moduleUrl: import.meta.url,
126
- });
127
- let lastError: unknown;
128
- for (const url of candidates) {
129
- try {
130
- return await readFile(fileURLToPath(url), 'utf8');
131
- } catch (error) {
132
- if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error;
133
- lastError = error;
134
- }
135
- }
136
- throw lastError ?? new Error(`ACP bridge asset not found: ${name}`);
123
+ return readFile(
124
+ fileURLToPath(
125
+ resolveBridgeAssetUrl({
126
+ name,
127
+ moduleUrl: import.meta.url,
128
+ }),
129
+ ),
130
+ 'utf8',
131
+ );
137
132
  }
138
133
 
139
- export function resolveBridgeAssetCandidates({
134
+ export function resolveBridgeAssetUrl({
140
135
  name,
141
136
  moduleUrl,
142
137
  }: {
143
138
  name: string;
144
139
  moduleUrl: string | URL;
145
- }): URL[] {
146
- return [
147
- new URL(`./bridge/${name}`, moduleUrl),
148
- new URL(`../bridge/${name}`, moduleUrl),
149
- ];
140
+ }): URL {
141
+ return new URL(`./bridge/${name}`, moduleUrl);
150
142
  }
@@ -14,6 +14,7 @@ import { z } from 'zod/v4';
14
14
  import type { ACPToolCall } from '../acp-tool-call';
15
15
  import type {
16
16
  ACPInstructionMapping,
17
+ ACPModelMapping,
17
18
  ACPPermissionModeMapping,
18
19
  } from './acp-v1-settings';
19
20
  import { acpTextContentBlockSchema } from './acp-v1-prompt';
@@ -125,6 +126,20 @@ const outputSchemaMappingSchema: z.ZodType<ACPSerializableOutputSchemaMapping> =
125
126
  path: z.array(z.string().min(1)).min(1),
126
127
  });
127
128
 
129
+ const modelMappingSchema: z.ZodType<ACPModelMapping> = z.discriminatedUnion(
130
+ 'type',
131
+ [
132
+ z.object({
133
+ type: z.literal('session-config-option'),
134
+ path: z.string().min(1),
135
+ }),
136
+ z.object({
137
+ type: z.literal('session-model'),
138
+ path: z.string().min(1),
139
+ }),
140
+ ],
141
+ );
142
+
128
143
  export type ACPTurnStartConfig = {
129
144
  readonly version: 1;
130
145
  readonly configurationFingerprint: string;
@@ -133,6 +148,8 @@ export type ACPTurnStartConfig = {
133
148
  readonly builtinTools: Array<ACPBuiltinToolMapping>;
134
149
  readonly permissionMode: HarnessV1PermissionMode;
135
150
  readonly permissionModeMapping?: ACPPermissionModeMapping;
151
+ readonly model?: string;
152
+ readonly modelMapping?: ACPModelMapping;
136
153
  readonly debug?: HarnessV1DebugConfig;
137
154
  readonly responseFormat?: z.infer<
138
155
  typeof harnessV1BridgeStartBaseSchema
@@ -148,6 +165,8 @@ export const acpTurnStartConfigSchema = z.object({
148
165
  builtinTools: z.array(builtinToolSchema).default([]),
149
166
  permissionMode: harnessV1BridgeStartBaseSchema.shape.permissionMode.unwrap(),
150
167
  permissionModeMapping: permissionModeMappingSchema.optional(),
168
+ model: harnessV1BridgeStartBaseSchema.shape.model.optional(),
169
+ modelMapping: modelMappingSchema.optional(),
151
170
  debug: harnessV1BridgeStartBaseSchema.shape.debug.optional(),
152
171
  responseFormat:
153
172
  harnessV1BridgeStartBaseSchema.shape.responseFormat.optional(),
@@ -156,14 +175,15 @@ export const acpTurnStartConfigSchema = z.object({
156
175
 
157
176
  export type ACPColdSessionState = Omit<
158
177
  ACPTurnStartConfig,
159
- 'prompt' | 'debug'
160
- > & {
161
- readonly modelId?: string;
162
- };
178
+ 'prompt' | 'debug' | 'model' | 'modelMapping'
179
+ >;
163
180
 
164
181
  export const acpColdSessionStateSchema: z.ZodType<ACPColdSessionState> =
165
- acpTurnStartConfigSchema.omit({ prompt: true, debug: true }).extend({
166
- modelId: z.string().optional(),
182
+ acpTurnStartConfigSchema.omit({
183
+ prompt: true,
184
+ debug: true,
185
+ model: true,
186
+ modelMapping: true,
167
187
  });
168
188
 
169
189
  const lossyRecoverySchema = z.object({
@@ -203,6 +223,7 @@ export const startMessageSchema = harnessV1BridgeStartBaseSchema.extend({
203
223
  tools: z.array(acpSerializableToolSpecSchema).optional(),
204
224
  builtinTools: z.array(builtinToolSchema).readonly().default([]),
205
225
  permissionModeMapping: permissionModeMappingSchema.optional(),
226
+ modelMapping: modelMappingSchema.optional(),
206
227
  turnStartConfig: acpTurnStartConfigSchema,
207
228
  recoveryMode: z
208
229
  .discriminatedUnion('type', [lossyRecoverySchema, coldRestoreSchema])
@@ -1,4 +1,4 @@
1
- import { createHash, randomBytes } from 'node:crypto';
1
+ import { createHash } from 'node:crypto';
2
2
  import { posix } from 'node:path';
3
3
  import {
4
4
  HarnessCapabilityUnsupportedError,
@@ -16,6 +16,7 @@ import {
16
16
  import { HarnessBridgeCapabilityUnsupportedError } from '@ai-sdk/harness/bridge';
17
17
  import {
18
18
  applyCredentialForwarding,
19
+ createBridgeToken,
19
20
  createBridgeErrorHandler,
20
21
  createBridgeStartupError,
21
22
  createSandboxCredentialEnvironment,
@@ -30,6 +31,7 @@ import {
30
31
  shellQuote,
31
32
  warnCredentialBrokeringUnavailable,
32
33
  waitForBridgeReady,
34
+ withBridgeToken,
33
35
  } from '@ai-sdk/harness/utils';
34
36
  import {
35
37
  asSchema,
@@ -53,7 +55,7 @@ import {
53
55
  resolveImplementationEnvironment,
54
56
  validateACPV1Implementation,
55
57
  } from './implementation';
56
- import { createACPBootstrap } from './acp-bootstrap';
58
+ import { createACPBootstrap } from './acp-v1-bootstrap';
57
59
  import {
58
60
  outboundMessageSchema,
59
61
  type ACPBuiltinToolMapping,
@@ -80,6 +82,7 @@ import {
80
82
  } from './acp-v1-prompt';
81
83
  import type {
82
84
  ACPInstructionMapping,
85
+ ACPModelMapping,
83
86
  ACPOutputSchemaMapping,
84
87
  ACPPermissionModeMapping,
85
88
  ACPPermissionModeTarget,
@@ -158,11 +161,11 @@ export function createACPV1<TBuiltinTools extends ToolSet = {}>({
158
161
  `ACP harnessId must be a stable kebab-case identifier; received ${JSON.stringify(settings.harnessId)}.`,
159
162
  );
160
163
  }
161
- const baseImplementation = createACPV1Implementation({ settings });
162
- validateACPV1Implementation(baseImplementation);
164
+ const implementation = createACPV1Implementation({ settings });
165
+ validateACPV1Implementation(implementation);
163
166
  const bootstrap = createACPBootstrap({
164
167
  harnessId: settings.harnessId,
165
- implementation: baseImplementation,
168
+ implementation,
166
169
  });
167
170
  const permissionModeMapping = isCompletePermissionModeMapping({
168
171
  value: settings.permissionModeMapping,
@@ -186,24 +189,6 @@ export function createACPV1<TBuiltinTools extends ToolSet = {}>({
186
189
  'ACP built-in tool filtering is not available in this initial ACP v1 implementation.',
187
190
  });
188
191
  }
189
- const modelId = startOptions.model ?? settings.modelId;
190
- const modelConfiguration =
191
- modelId == null ? undefined : settings.resolveModel({ model: modelId });
192
- const implementation = {
193
- ...baseImplementation,
194
- ...(modelConfiguration?.args == null
195
- ? {}
196
- : { args: modelConfiguration.args }),
197
- ...(modelConfiguration?.env == null
198
- ? {}
199
- : {
200
- env: {
201
- ...baseImplementation.env,
202
- ...modelConfiguration.env,
203
- },
204
- }),
205
- };
206
- validateACPV1Implementation(implementation);
207
192
  const permissionMode = startOptions.permissionMode ?? 'allow-all';
208
193
  const env = { ...process.env };
209
194
  const authenticationEnvironment = resolveACPAuthenticationEnvironment({
@@ -221,6 +206,8 @@ export function createACPV1<TBuiltinTools extends ToolSet = {}>({
221
206
  acpVersion: 'v1',
222
207
  implementation,
223
208
  clientApp,
209
+ clientCapabilities: settings.clientCapabilities,
210
+ modelMapping: settings.modelMapping,
224
211
  providerAuthentication: providerAuthenticationCompatibility,
225
212
  permissionModeMapping: settings.permissionModeMapping,
226
213
  });
@@ -452,7 +439,8 @@ export function createACPV1<TBuiltinTools extends ToolSet = {}>({
452
439
  harnessId: settings.harnessId,
453
440
  channel: attachChannel,
454
441
  proc: undefined,
455
- modelId,
442
+ modelMapping: settings.modelMapping,
443
+ defaultModelId: settings.modelId,
456
444
  sessionMeta: settings.session?.meta,
457
445
  instructionMapping: settings.instructionMapping,
458
446
  outputSchemaMapping: settings.outputSchemaMapping,
@@ -518,6 +506,7 @@ export function createACPV1<TBuiltinTools extends ToolSet = {}>({
518
506
  sessionMeta: settings.session?.meta,
519
507
  instructionMapping: settings.instructionMapping,
520
508
  outputSchemaMapping: settings.outputSchemaMapping,
509
+ modelMapping: settings.modelMapping,
521
510
  builtinTools: builtinToolCatalog,
522
511
  permissionModeMapping,
523
512
  mcpServers: settings.mcpServers,
@@ -544,12 +533,12 @@ export function createACPV1<TBuiltinTools extends ToolSet = {}>({
544
533
  }
545
534
  const turnStartConfig = validateACPColdSessionConfiguration({
546
535
  coldSession,
547
- modelId,
548
536
  permissionMode,
549
537
  authenticationProfile,
550
538
  sessionMeta: settings.session?.meta,
551
539
  instructionMapping: settings.instructionMapping,
552
540
  outputSchemaMapping: settings.outputSchemaMapping,
541
+ modelMapping: settings.modelMapping,
553
542
  builtinTools: builtinToolCatalog,
554
543
  permissionModeMapping,
555
544
  mcpServers: settings.mcpServers,
@@ -579,7 +568,7 @@ export function createACPV1<TBuiltinTools extends ToolSet = {}>({
579
568
  });
580
569
  const token =
581
570
  settings.mintBridgeToken == null
582
- ? randomBytes(32).toString('hex')
571
+ ? createBridgeToken()
583
572
  : settings.mintBridgeToken(sandboxId!);
584
573
  await toolSafeSandboxSession.run({
585
574
  command: `mkdir -p ${shellQuote(workDir)} ${shellQuote(bridgeStateDir)}`,
@@ -608,8 +597,7 @@ export function createACPV1<TBuiltinTools extends ToolSet = {}>({
608
597
  providerEnvironment:
609
598
  sandboxProviderEnvironment == null ? undefined : {},
610
599
  sessionMeta: settings.session?.meta,
611
- implementationArgs: modelConfiguration?.args,
612
- implementationEnv: modelConfiguration?.env,
600
+ clientCapabilities: settings.clientCapabilities,
613
601
  }),
614
602
  ...sandboxProviderAuthenticationEnvironment,
615
603
  BRIDGE_CHANNEL_TOKEN: token,
@@ -722,7 +710,8 @@ export function createACPV1<TBuiltinTools extends ToolSet = {}>({
722
710
  harnessId: settings.harnessId,
723
711
  channel,
724
712
  proc,
725
- modelId,
713
+ modelMapping: settings.modelMapping,
714
+ defaultModelId: settings.modelId,
726
715
  sessionMeta: settings.session?.meta,
727
716
  instructionMapping: settings.instructionMapping,
728
717
  outputSchemaMapping: settings.outputSchemaMapping,
@@ -945,18 +934,6 @@ function openWebSocket({
945
934
  });
946
935
  }
947
936
 
948
- function withBridgeToken({
949
- endpoint,
950
- token,
951
- }: {
952
- endpoint: HarnessV1PortEndpoint;
953
- token: string;
954
- }): HarnessV1PortEndpoint {
955
- const bridgeUrl = new URL(endpoint.url);
956
- bridgeUrl.searchParams.set('agent_bridge_token', token);
957
- return { ...endpoint, url: bridgeUrl.toString() };
958
- }
959
-
960
937
  function restoreColdACPSession({
961
938
  channel,
962
939
  harnessId,
@@ -1046,7 +1023,8 @@ function createSession({
1046
1023
  harnessId,
1047
1024
  channel,
1048
1025
  proc,
1049
- modelId,
1026
+ modelMapping,
1027
+ defaultModelId,
1050
1028
  sessionMeta,
1051
1029
  instructionMapping,
1052
1030
  outputSchemaMapping,
@@ -1081,7 +1059,8 @@ function createSession({
1081
1059
  harnessId: string;
1082
1060
  channel: ACPChannel;
1083
1061
  proc: Experimental_SandboxProcess | undefined;
1084
- modelId: string | undefined;
1062
+ modelMapping: ACPModelMapping;
1063
+ defaultModelId: string | undefined;
1085
1064
  sessionMeta: Readonly<Record<string, ACPSerializableValue>> | undefined;
1086
1065
  instructionMapping: ACPInstructionMapping | undefined;
1087
1066
  outputSchemaMapping: ACPOutputSchemaMapping | undefined;
@@ -1375,7 +1354,6 @@ function createSession({
1375
1354
  : {
1376
1355
  coldSession: createACPColdSessionState({
1377
1356
  turnStartConfig: latestTurnStartConfig,
1378
- modelId,
1379
1357
  }),
1380
1358
  }),
1381
1359
  ...(!includeTurnStartConfig || latestTurnStartConfig == null
@@ -1407,7 +1385,6 @@ function createSession({
1407
1385
  return {
1408
1386
  sessionId,
1409
1387
  isResume,
1410
- ...(modelId == null ? {} : { modelId }),
1411
1388
  doPromptTurn: async options => {
1412
1389
  await synchronizeSkills({
1413
1390
  skills: options.skills,
@@ -1442,6 +1419,7 @@ function createSession({
1442
1419
  prompt: options.prompt,
1443
1420
  harnessId,
1444
1421
  });
1422
+ const model = options.model ?? defaultModelId;
1445
1423
  const turnStartConfig = createACPTurnStartConfig({
1446
1424
  prompt,
1447
1425
  tools: options.tools ?? [],
@@ -1455,6 +1433,8 @@ function createSession({
1455
1433
  instructionMapping,
1456
1434
  responseFormat: options.responseFormat,
1457
1435
  outputSchemaMapping,
1436
+ model,
1437
+ modelMapping,
1458
1438
  });
1459
1439
  const nextInstructionsFingerprint = fingerprintValue({
1460
1440
  value: options.instructions ?? null,
@@ -1491,6 +1471,7 @@ function createSession({
1491
1471
  builtinTools,
1492
1472
  permissionMode,
1493
1473
  permissionModeMapping,
1474
+ ...(model == null ? {} : { model, modelMapping }),
1494
1475
  ...(options.responseFormat == null
1495
1476
  ? {}
1496
1477
  : { responseFormat: options.responseFormat }),
@@ -1554,6 +1535,12 @@ function createSession({
1554
1535
  builtinTools: turnStartConfig.builtinTools,
1555
1536
  permissionMode: turnStartConfig.permissionMode,
1556
1537
  permissionModeMapping: turnStartConfig.permissionModeMapping,
1538
+ ...(turnStartConfig.model == null
1539
+ ? {}
1540
+ : {
1541
+ model: turnStartConfig.model,
1542
+ modelMapping: turnStartConfig.modelMapping,
1543
+ }),
1557
1544
  ...(turnStartConfig.responseFormat == null
1558
1545
  ? {}
1559
1546
  : { responseFormat: turnStartConfig.responseFormat }),
@@ -1751,6 +1738,7 @@ function validateACPTurnStartConfig({
1751
1738
  sessionMeta,
1752
1739
  instructionMapping,
1753
1740
  outputSchemaMapping,
1741
+ modelMapping,
1754
1742
  builtinTools,
1755
1743
  permissionModeMapping,
1756
1744
  mcpServers,
@@ -1760,6 +1748,7 @@ function validateACPTurnStartConfig({
1760
1748
  sessionMeta: Readonly<Record<string, ACPSerializableValue>> | undefined;
1761
1749
  instructionMapping: ACPInstructionMapping | undefined;
1762
1750
  outputSchemaMapping: ACPOutputSchemaMapping | undefined;
1751
+ modelMapping: ACPModelMapping;
1763
1752
  builtinTools: ReadonlyArray<ACPBuiltinToolMapping>;
1764
1753
  permissionModeMapping: ACPPermissionModeMapping | undefined;
1765
1754
  mcpServers: Record<string, unknown> | undefined;
@@ -1777,6 +1766,8 @@ function validateACPTurnStartConfig({
1777
1766
  instructionMapping,
1778
1767
  responseFormat: turnStartConfig.responseFormat,
1779
1768
  outputSchemaMapping,
1769
+ model: turnStartConfig.model,
1770
+ modelMapping,
1780
1771
  });
1781
1772
  if (
1782
1773
  current.configurationFingerprint !==
@@ -1790,24 +1781,24 @@ function validateACPTurnStartConfig({
1790
1781
 
1791
1782
  function validateACPColdSessionConfiguration({
1792
1783
  coldSession,
1793
- modelId,
1794
1784
  permissionMode,
1795
1785
  authenticationProfile,
1796
1786
  sessionMeta,
1797
1787
  instructionMapping,
1798
1788
  outputSchemaMapping,
1789
+ modelMapping,
1799
1790
  builtinTools,
1800
1791
  permissionModeMapping,
1801
1792
  mcpServers,
1802
1793
  debug,
1803
1794
  }: {
1804
1795
  coldSession: ACPColdSessionState;
1805
- modelId: string | undefined;
1806
1796
  permissionMode: NonNullable<StartMessage['permissionMode']>;
1807
1797
  authenticationProfile: ACPAuthenticationProfileIdentity;
1808
1798
  sessionMeta: Readonly<Record<string, ACPSerializableValue>> | undefined;
1809
1799
  instructionMapping: ACPInstructionMapping | undefined;
1810
1800
  outputSchemaMapping: ACPOutputSchemaMapping | undefined;
1801
+ modelMapping: ACPModelMapping;
1811
1802
  builtinTools: ReadonlyArray<ACPBuiltinToolMapping>;
1812
1803
  permissionModeMapping: ACPPermissionModeMapping | undefined;
1813
1804
  mcpServers: Record<string, unknown> | undefined;
@@ -1826,11 +1817,12 @@ function validateACPColdSessionConfiguration({
1826
1817
  instructionMapping,
1827
1818
  responseFormat: coldSession.responseFormat,
1828
1819
  outputSchemaMapping,
1820
+ model: undefined,
1821
+ modelMapping,
1829
1822
  });
1830
1823
  if (
1831
1824
  current.configurationFingerprint !== coldSession.configurationFingerprint ||
1832
- coldSession.permissionMode !== permissionMode ||
1833
- coldSession.modelId !== modelId
1825
+ coldSession.permissionMode !== permissionMode
1834
1826
  ) {
1835
1827
  throw new Error(
1836
1828
  'ACP cold-session state is incompatible with the current non-secret session configuration.',
@@ -75,20 +75,15 @@ export type ACPProviderAuthentication = {
75
75
  };
76
76
  };
77
77
 
78
- export type ACPModelResolution = {
79
- /**
80
- * Replaces the implementation's static launch arguments.
81
- */
82
- readonly args?: ReadonlyArray<string>;
83
- /**
84
- * Overlays the implementation's static launch environment.
85
- */
86
- readonly env?: Readonly<Record<string, string>>;
87
- };
88
-
89
- export type ACPModelResolver = (options: {
90
- readonly model: string;
91
- }) => ACPModelResolution;
78
+ export type ACPModelMapping =
79
+ | {
80
+ readonly type: 'session-config-option';
81
+ readonly path: string;
82
+ }
83
+ | {
84
+ readonly type: 'session-model';
85
+ readonly path: string;
86
+ };
92
87
 
93
88
  export type ACPCredentialBrokering = ({
94
89
  env,
@@ -158,11 +153,12 @@ export type ACPV1Settings = {
158
153
  */
159
154
  readonly env?: Readonly<Record<string, string>>;
160
155
  readonly authentication?: ACPAuthentication;
156
+ readonly clientCapabilities?: Readonly<Record<string, ACPSerializableValue>>;
161
157
  readonly providerAuthentication?: ACPProviderAuthentication;
162
158
  /**
163
- * Maps a HarnessAgent model identifier to implementation launch settings.
159
+ * Maps the HarnessAgent model identifier to an ACP session operation.
164
160
  */
165
- readonly resolveModel: ACPModelResolver;
161
+ readonly modelMapping: ACPModelMapping;
166
162
  /**
167
163
  * @deprecated Use `model` on `HarnessAgent` instead.
168
164
  */
@@ -11,6 +11,7 @@ import {
11
11
  import type { ACPTextContentBlock } from './acp-v1-prompt';
12
12
  import type {
13
13
  ACPInstructionMapping,
14
+ ACPModelMapping,
14
15
  ACPOutputSchemaMapping,
15
16
  ACPPermissionModeMapping,
16
17
  ACPSerializableValue,
@@ -29,6 +30,8 @@ export function createACPTurnStartConfig({
29
30
  instructionMapping,
30
31
  responseFormat,
31
32
  outputSchemaMapping,
33
+ model,
34
+ modelMapping,
32
35
  }: {
33
36
  prompt: ReadonlyArray<ACPTextContentBlock>;
34
37
  tools: ReadonlyArray<HarnessV1ToolSpec>;
@@ -42,6 +45,8 @@ export function createACPTurnStartConfig({
42
45
  instructionMapping: ACPInstructionMapping | undefined;
43
46
  responseFormat: StartMessage['responseFormat'];
44
47
  outputSchemaMapping: ACPOutputSchemaMapping | undefined;
48
+ model: string | undefined;
49
+ modelMapping: ACPModelMapping;
45
50
  }): ACPTurnStartConfig {
46
51
  return {
47
52
  version: 1,
@@ -52,6 +57,7 @@ export function createACPTurnStartConfig({
52
57
  sessionMeta: sessionMeta ?? null,
53
58
  ...(instructionMapping == null ? {} : { instructionMapping }),
54
59
  ...(outputSchemaMapping == null ? {} : { outputSchemaMapping }),
60
+ modelMapping,
55
61
  builtinTools,
56
62
  permissionModeMapping: permissionModeMapping ?? null,
57
63
  mcpServers: mcpServers ?? null,
@@ -62,6 +68,7 @@ export function createACPTurnStartConfig({
62
68
  tools: tools.map(tool => acpSerializableToolSpecSchema.parse(tool)),
63
69
  builtinTools: [...builtinTools],
64
70
  permissionMode,
71
+ ...(model == null ? {} : { model, modelMapping }),
65
72
  ...(responseFormat == null ? {} : { responseFormat }),
66
73
  ...(outputSchemaMapping == null
67
74
  ? {}
@@ -78,10 +85,8 @@ export function createACPTurnStartConfig({
78
85
 
79
86
  export function createACPColdSessionState({
80
87
  turnStartConfig,
81
- modelId,
82
88
  }: {
83
89
  turnStartConfig: ACPTurnStartConfig;
84
- modelId: string | undefined;
85
90
  }): ACPColdSessionState {
86
91
  return {
87
92
  version: turnStartConfig.version,
@@ -98,7 +103,6 @@ export function createACPColdSessionState({
98
103
  ...(turnStartConfig.permissionModeMapping == null
99
104
  ? {}
100
105
  : { permissionModeMapping: turnStartConfig.permissionModeMapping }),
101
- ...(modelId == null ? {} : { modelId }),
102
106
  };
103
107
  }
104
108
 
@@ -59,8 +59,7 @@ export type ACPBridgeConfiguration = {
59
59
  readonly providerAuthentication?: ACPResolvedProviderAuthentication;
60
60
  readonly providerEnvironment?: Readonly<Record<string, string>>;
61
61
  readonly sessionMeta?: Readonly<Record<string, ACPSerializableValue>>;
62
- readonly implementationArgs?: ReadonlyArray<string>;
63
- readonly implementationEnv?: Readonly<Record<string, string>>;
62
+ readonly clientCapabilities?: Readonly<Record<string, ACPSerializableValue>>;
64
63
  };
65
64
 
66
65
  const bridgeConfigurationSchema: z.ZodType<ACPBridgeConfiguration> = z.object({
@@ -68,8 +67,7 @@ const bridgeConfigurationSchema: z.ZodType<ACPBridgeConfiguration> = z.object({
68
67
  providerAuthentication: providerAuthenticationSchema.optional(),
69
68
  providerEnvironment: z.record(z.string(), z.string()).optional(),
70
69
  sessionMeta: serializableRecordSchema.optional(),
71
- implementationArgs: z.array(z.string()).optional(),
72
- implementationEnv: z.record(z.string(), z.string()).optional(),
70
+ clientCapabilities: serializableRecordSchema.optional(),
73
71
  });
74
72
 
75
73
  export function createACPBridgeEnvironment({
@@ -77,8 +75,7 @@ export function createACPBridgeEnvironment({
77
75
  providerAuthentication,
78
76
  providerEnvironment,
79
77
  sessionMeta,
80
- implementationArgs,
81
- implementationEnv,
78
+ clientCapabilities,
82
79
  }: ACPBridgeConfiguration): Record<string, string> {
83
80
  return {
84
81
  [ACP_BRIDGE_CONFIGURATION_ENV]: JSON.stringify({
@@ -86,8 +83,7 @@ export function createACPBridgeEnvironment({
86
83
  ...(providerAuthentication == null ? {} : { providerAuthentication }),
87
84
  ...(providerEnvironment == null ? {} : { providerEnvironment }),
88
85
  ...(sessionMeta == null ? {} : { sessionMeta }),
89
- ...(implementationArgs == null ? {} : { implementationArgs }),
90
- ...(implementationEnv == null ? {} : { implementationEnv }),
86
+ ...(clientCapabilities == null ? {} : { clientCapabilities }),
91
87
  }),
92
88
  };
93
89
  }
@@ -44,6 +44,7 @@ import {
44
44
  } from './refresh-host-tool-catalog';
45
45
  import { createACPPermissionController } from './permission-controller';
46
46
  import { configureACPPermissionMode } from './permission-mode';
47
+ import { configureACPModel } from './model-mapping';
47
48
  import {
48
49
  assertACPResumeCapability,
49
50
  createACPRecoveredSession,
@@ -214,6 +215,13 @@ async function runTurn(start: StartMessage, turn: BridgeTurn): Promise<void> {
214
215
  return;
215
216
  }
216
217
 
218
+ await configureACPModel({
219
+ agent: connection!.agent,
220
+ sessionId: activeSession.sessionId,
221
+ model: start.model,
222
+ mapping: start.modelMapping,
223
+ });
224
+
217
225
  let rejectCancellationFailure!: (error: unknown) => void;
218
226
  const cancellationFailure = new Promise<never>((_, reject) => {
219
227
  rejectCancellationFailure = reject;
@@ -419,12 +427,11 @@ async function ensureSession({
419
427
  implementationDir,
420
428
  privateHome: implementation.privateHome,
421
429
  }),
422
- ...bridgeConfiguration.implementationEnv,
423
430
  },
424
431
  });
425
432
  child = spawn(
426
433
  `${implementationDir}/${implementation.executablePath}`,
427
- [...(bridgeConfiguration.implementationArgs ?? implementation.args)],
434
+ [...implementation.args],
428
435
  {
429
436
  cwd: workDir,
430
437
  env: instructionConfiguration.environment,
@@ -475,6 +482,7 @@ async function ensureSession({
475
482
  protocolVersion: acp.PROTOCOL_VERSION,
476
483
  clientApp,
477
484
  authentication,
485
+ clientCapabilities: bridgeConfiguration.clientCapabilities,
478
486
  supportsBooleanSessionConfigOptions: Object.values(
479
487
  start.permissionModeMapping ?? {},
480
488
  ).some(