@ai-sdk/harness-acp 1.0.1 → 1.0.3

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.1",
3
+ "version": "1.0.3",
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.64",
31
- "@ai-sdk/provider-utils": "5.0.25"
30
+ "@ai-sdk/harness": "1.0.66",
31
+ "@ai-sdk/provider-utils": "5.0.26"
32
32
  },
33
33
  "peerDependencies": {
34
34
  "zod": "^3.25.76 || ^4.1.8"
@@ -2,6 +2,7 @@ import type { HarnessV1 } from '@ai-sdk/harness';
2
2
  import type { ToolSet } from '@ai-sdk/provider-utils';
3
3
  import { z } from 'zod/v4';
4
4
  import type { ACPClientApp } from './acp-auth';
5
+ import type { ACPToolCall } from './acp-tool-call';
5
6
  import { createACPV1, type ACPV1Settings } from './v1';
6
7
  import {
7
8
  acpColdSessionStateSchema,
@@ -16,6 +17,12 @@ const ACP_CLIENT_APP = {
16
17
 
17
18
  export type ACPHarnessSettings<TBuiltinTools extends ToolSet = {}> = {
18
19
  readonly builtinTools?: TBuiltinTools;
20
+ /**
21
+ * MCP server definitions keyed by server name. Each definition uses the
22
+ * underlying runtime's native MCP server configuration format.
23
+ */
24
+ readonly mcpServers?: Record<string, unknown>;
25
+ readonly isMcpToolCall?: (toolCall: ACPToolCall) => boolean;
19
26
  readonly port?: number;
20
27
  readonly startupTimeoutMs?: number;
21
28
  readonly clientApp?: ACPClientApp;
@@ -30,8 +37,10 @@ export type ACPHarnessSettings<TBuiltinTools extends ToolSet = {}> = {
30
37
  readonly authentication?: ACPV1Settings['authentication'];
31
38
  readonly providerAuthentication?: ACPV1Settings['providerAuthentication'];
32
39
  readonly modelId?: ACPV1Settings['modelId'];
40
+ readonly instructionMapping?: ACPV1Settings['instructionMapping'];
33
41
  readonly permissionModeMapping?: ACPV1Settings['permissionModeMapping'];
34
42
  readonly session?: ACPV1Settings['session'];
43
+ readonly mintBridgeToken?: ACPV1Settings['mintBridgeToken'];
35
44
  };
36
45
 
37
46
  const ACP_BUILTIN_TOOLS = {} as const satisfies ToolSet;
@@ -85,6 +94,17 @@ const acpResumeStateSchema = z.object({
85
94
  export function createACP<TBuiltinTools extends ToolSet = {}>(
86
95
  settings: ACPHarnessSettings<TBuiltinTools>,
87
96
  ): HarnessV1<TBuiltinTools> {
97
+ if (
98
+ settings.mcpServers != null &&
99
+ Object.prototype.hasOwnProperty.call(
100
+ settings.mcpServers,
101
+ 'ai-sdk-harness-tools',
102
+ )
103
+ ) {
104
+ throw new Error(
105
+ 'ACP MCP server name "ai-sdk-harness-tools" is reserved for HarnessAgent tools.',
106
+ );
107
+ }
88
108
  const version = (settings as { readonly version?: string }).version ?? 'v1';
89
109
  switch (version) {
90
110
  case 'v1': {
@@ -0,0 +1,124 @@
1
+ export type ACPMetadata = Record<string, unknown>;
2
+
3
+ export type ACPRole = 'assistant' | 'user';
4
+
5
+ export type ACPAnnotations = {
6
+ audience?: Array<ACPRole> | null;
7
+ lastModified?: string | null;
8
+ priority?: number | null;
9
+ _meta?: ACPMetadata | null;
10
+ };
11
+
12
+ export type ACPTextContent = {
13
+ annotations?: ACPAnnotations | null;
14
+ text: string;
15
+ _meta?: ACPMetadata | null;
16
+ };
17
+
18
+ export type ACPImageContent = {
19
+ annotations?: ACPAnnotations | null;
20
+ data: string;
21
+ mimeType: string;
22
+ uri?: string | null;
23
+ _meta?: ACPMetadata | null;
24
+ };
25
+
26
+ export type ACPAudioContent = {
27
+ annotations?: ACPAnnotations | null;
28
+ data: string;
29
+ mimeType: string;
30
+ _meta?: ACPMetadata | null;
31
+ };
32
+
33
+ export type ACPResourceLink = {
34
+ annotations?: ACPAnnotations | null;
35
+ description?: string | null;
36
+ mimeType?: string | null;
37
+ name: string;
38
+ size?: number | null;
39
+ title?: string | null;
40
+ uri: string;
41
+ _meta?: ACPMetadata | null;
42
+ };
43
+
44
+ export type ACPTextResourceContents = {
45
+ mimeType?: string | null;
46
+ text: string;
47
+ uri: string;
48
+ _meta?: ACPMetadata | null;
49
+ };
50
+
51
+ export type ACPBlobResourceContents = {
52
+ blob: string;
53
+ mimeType?: string | null;
54
+ uri: string;
55
+ _meta?: ACPMetadata | null;
56
+ };
57
+
58
+ export type ACPEmbeddedResource = {
59
+ annotations?: ACPAnnotations | null;
60
+ resource: ACPTextResourceContents | ACPBlobResourceContents;
61
+ _meta?: ACPMetadata | null;
62
+ };
63
+
64
+ export type ACPContentBlock =
65
+ | (ACPTextContent & { type: 'text' })
66
+ | (ACPImageContent & { type: 'image' })
67
+ | (ACPAudioContent & { type: 'audio' })
68
+ | (ACPResourceLink & { type: 'resource_link' })
69
+ | (ACPEmbeddedResource & { type: 'resource' });
70
+
71
+ export type ACPToolCallContent =
72
+ | {
73
+ type: 'content';
74
+ content: ACPContentBlock;
75
+ _meta?: ACPMetadata | null;
76
+ }
77
+ | {
78
+ type: 'diff';
79
+ path: string;
80
+ oldText?: string | null;
81
+ newText: string;
82
+ _meta?: ACPMetadata | null;
83
+ }
84
+ | {
85
+ type: 'terminal';
86
+ terminalId: string;
87
+ _meta?: ACPMetadata | null;
88
+ };
89
+
90
+ export type ACPToolKind =
91
+ | 'read'
92
+ | 'edit'
93
+ | 'delete'
94
+ | 'move'
95
+ | 'search'
96
+ | 'execute'
97
+ | 'think'
98
+ | 'fetch'
99
+ | 'switch_mode'
100
+ | 'other';
101
+
102
+ export type ACPToolCallStatus =
103
+ | 'pending'
104
+ | 'in_progress'
105
+ | 'completed'
106
+ | 'failed';
107
+
108
+ export type ACPToolCallLocation = {
109
+ path: string;
110
+ line?: number | null;
111
+ _meta?: ACPMetadata | null;
112
+ };
113
+
114
+ export type ACPToolCall = {
115
+ toolCallId: string;
116
+ title: string;
117
+ kind?: ACPToolKind;
118
+ status?: ACPToolCallStatus;
119
+ content?: Array<ACPToolCallContent>;
120
+ locations?: Array<ACPToolCallLocation>;
121
+ rawInput?: unknown;
122
+ rawOutput?: unknown;
123
+ _meta?: ACPMetadata | null;
124
+ };
package/src/index.ts CHANGED
@@ -1,8 +1,27 @@
1
1
  export { createACP } from './acp-harness';
2
2
  export type { ACPAuthOptions, ACPClientApp } from './acp-auth';
3
3
  export type { ACPHarnessSettings } from './acp-harness';
4
+ export type {
5
+ ACPAnnotations,
6
+ ACPAudioContent,
7
+ ACPBlobResourceContents,
8
+ ACPContentBlock,
9
+ ACPEmbeddedResource,
10
+ ACPImageContent,
11
+ ACPMetadata,
12
+ ACPResourceLink,
13
+ ACPRole,
14
+ ACPTextContent,
15
+ ACPTextResourceContents,
16
+ ACPToolCall,
17
+ ACPToolCallContent,
18
+ ACPToolCallLocation,
19
+ ACPToolCallStatus,
20
+ ACPToolKind,
21
+ } from './acp-tool-call';
4
22
  export type {
5
23
  ACPAuthentication,
24
+ ACPInstructionMapping,
6
25
  ACPNpmLockedSource,
7
26
  ACPNpmSimpleSource,
8
27
  ACPPermissionModeMapping,
@@ -11,7 +11,11 @@ import {
11
11
  type HarnessV1ToolSpec,
12
12
  } from '@ai-sdk/harness';
13
13
  import { z } from 'zod/v4';
14
- import type { ACPPermissionModeMapping } from './acp-v1-settings';
14
+ import type { ACPToolCall } from '../acp-tool-call';
15
+ import type {
16
+ ACPInstructionMapping,
17
+ ACPPermissionModeMapping,
18
+ } from './acp-v1-settings';
15
19
  import { acpTextContentBlockSchema } from './acp-v1-prompt';
16
20
 
17
21
  export type ACPBuiltinToolMapping = {
@@ -81,6 +85,21 @@ const permissionModeMappingSchema: z.ZodType<ACPPermissionModeMapping> =
81
85
  'allow-all': permissionModeTargetSchema.nullable(),
82
86
  });
83
87
 
88
+ const instructionMappingPathSchema = z.array(z.string().min(1)).min(1);
89
+
90
+ const instructionMappingSchema: z.ZodType<ACPInstructionMapping> =
91
+ z.discriminatedUnion('type', [
92
+ z.object({
93
+ type: z.literal('session-meta'),
94
+ path: instructionMappingPathSchema,
95
+ }),
96
+ z.object({
97
+ type: z.literal('launch-env-json'),
98
+ variable: z.string().min(1),
99
+ path: instructionMappingPathSchema,
100
+ }),
101
+ ]);
102
+
84
103
  export type ACPTurnStartConfig = {
85
104
  readonly version: 1;
86
105
  readonly configurationFingerprint: string;
@@ -126,11 +145,28 @@ const coldRestoreSchema = z.object({
126
145
  acpSessionId: z.string(),
127
146
  });
128
147
 
129
- export const outboundMessageSchema = harnessV1BridgeOutboundMessageSchema;
148
+ const acpToolCallCandidateSchema = z.object({
149
+ type: z.literal('acp-tool-call-candidate'),
150
+ toolCall: z.custom<ACPToolCall>(
151
+ value =>
152
+ value != null &&
153
+ typeof value === 'object' &&
154
+ typeof (value as { toolCallId?: unknown }).toolCallId === 'string' &&
155
+ typeof (value as { title?: unknown }).title === 'string',
156
+ ),
157
+ });
158
+
159
+ export const outboundMessageSchema = z.union([
160
+ harnessV1BridgeOutboundMessageSchema,
161
+ acpToolCallCandidateSchema,
162
+ ]);
130
163
  export type OutboundMessage = z.infer<typeof outboundMessageSchema>;
131
164
 
132
165
  export const startMessageSchema = harnessV1BridgeStartBaseSchema.extend({
133
166
  prompt: z.array(acpTextContentBlockSchema),
167
+ instructions: z.string().optional(),
168
+ instructionMapping: instructionMappingSchema.optional(),
169
+ mcpServers: z.record(z.string(), z.unknown()).optional(),
134
170
  tools: z.array(acpSerializableToolSpecSchema).optional(),
135
171
  builtinTools: z.array(builtinToolSchema).readonly().default([]),
136
172
  permissionModeMapping: permissionModeMappingSchema.optional(),
@@ -40,6 +40,7 @@ import {
40
40
  type ACPAuthenticationProfileIdentity,
41
41
  type ACPClientApp,
42
42
  } from '../acp-auth';
43
+ import type { ACPToolCall } from '../acp-tool-call';
43
44
  import {
44
45
  createACPV1Implementation,
45
46
  createImplementationDescriptor,
@@ -76,6 +77,7 @@ import {
76
77
  type ACPSkillCatalogEntry,
77
78
  } from './acp-v1-prompt';
78
79
  import type {
80
+ ACPInstructionMapping,
79
81
  ACPPermissionModeMapping,
80
82
  ACPPermissionModeTarget,
81
83
  ACPSerializableValue,
@@ -126,6 +128,17 @@ export function createACPV1<TBuiltinTools extends ToolSet = {}>({
126
128
  HarnessV1<TBuiltinTools>['lifecycleStateSchema']
127
129
  >;
128
130
  }): HarnessV1<TBuiltinTools> {
131
+ if (
132
+ settings.mcpServers != null &&
133
+ Object.prototype.hasOwnProperty.call(
134
+ settings.mcpServers,
135
+ 'ai-sdk-harness-tools',
136
+ )
137
+ ) {
138
+ throw new Error(
139
+ 'ACP MCP server name "ai-sdk-harness-tools" is reserved for HarnessAgent tools.',
140
+ );
141
+ }
129
142
  if (!HARNESS_ID_REGEXP.test(settings.harnessId)) {
130
143
  throw new Error(
131
144
  `ACP harnessId must be a stable kebab-case identifier; received ${JSON.stringify(settings.harnessId)}.`,
@@ -364,12 +377,15 @@ export function createACPV1<TBuiltinTools extends ToolSet = {}>({
364
377
  proc: undefined,
365
378
  modelId: settings.modelId,
366
379
  sessionMeta: settings.session?.meta,
380
+ instructionMapping: settings.instructionMapping,
367
381
  debug: startOptions.observability?.debug,
368
382
  implementationIdentity,
369
383
  authenticationProfile,
370
384
  builtinTools: builtinToolCatalog,
371
385
  permissionMode,
372
386
  permissionModeMapping,
387
+ mcpServers: settings.mcpServers,
388
+ isMcpToolCall: settings.isMcpToolCall,
373
389
  initialGuidanceApplied: resolveACPInitialGuidanceApplied({
374
390
  isResume: true,
375
391
  lifecycleState: lifecycleData,
@@ -419,8 +435,10 @@ export function createACPV1<TBuiltinTools extends ToolSet = {}>({
419
435
  turnStartConfig,
420
436
  authenticationProfile,
421
437
  sessionMeta: settings.session?.meta,
438
+ instructionMapping: settings.instructionMapping,
422
439
  builtinTools: builtinToolCatalog,
423
440
  permissionModeMapping,
441
+ mcpServers: settings.mcpServers,
424
442
  });
425
443
  respawnStrategy = {
426
444
  mode: 'lossy-rerun',
@@ -448,8 +466,10 @@ export function createACPV1<TBuiltinTools extends ToolSet = {}>({
448
466
  permissionMode,
449
467
  authenticationProfile,
450
468
  sessionMeta: settings.session?.meta,
469
+ instructionMapping: settings.instructionMapping,
451
470
  builtinTools: builtinToolCatalog,
452
471
  permissionModeMapping,
472
+ mcpServers: settings.mcpServers,
453
473
  debug: startOptions.observability?.debug,
454
474
  });
455
475
  respawnStrategy = {
@@ -465,7 +485,10 @@ export function createACPV1<TBuiltinTools extends ToolSet = {}>({
465
485
  override: portOverride,
466
486
  harnessId: settings.harnessId,
467
487
  });
468
- const token = randomBytes(32).toString('hex');
488
+ const token =
489
+ settings.mintBridgeToken == null
490
+ ? randomBytes(32).toString('hex')
491
+ : settings.mintBridgeToken(sandboxSession.id);
469
492
  await sandbox.run({
470
493
  command: `mkdir -p ${shellQuote(workDir)} ${shellQuote(bridgeStateDir)}`,
471
494
  abortSignal: startOptions.abortSignal,
@@ -570,6 +593,12 @@ export function createACPV1<TBuiltinTools extends ToolSet = {}>({
570
593
  builtinTools: builtinToolCatalog,
571
594
  permissionMode,
572
595
  permissionModeMapping,
596
+ ...(settings.instructionMapping == null
597
+ ? {}
598
+ : { instructionMapping: settings.instructionMapping }),
599
+ ...(settings.mcpServers == null
600
+ ? {}
601
+ : { mcpServers: settings.mcpServers }),
573
602
  tools: respawnStrategy.turnStartConfig.tools,
574
603
  turnStartConfig: respawnStrategy.turnStartConfig,
575
604
  recoveryMode: {
@@ -600,12 +629,15 @@ export function createACPV1<TBuiltinTools extends ToolSet = {}>({
600
629
  proc,
601
630
  modelId: settings.modelId,
602
631
  sessionMeta: settings.session?.meta,
632
+ instructionMapping: settings.instructionMapping,
603
633
  debug: startOptions.observability?.debug,
604
634
  implementationIdentity,
605
635
  authenticationProfile,
606
636
  builtinTools: builtinToolCatalog,
607
637
  permissionMode,
608
638
  permissionModeMapping,
639
+ mcpServers: settings.mcpServers,
640
+ isMcpToolCall: settings.isMcpToolCall,
609
641
  initialGuidanceApplied: resolveACPInitialGuidanceApplied({
610
642
  isResume,
611
643
  lifecycleState: lifecycleData,
@@ -801,12 +833,15 @@ function createSession({
801
833
  proc,
802
834
  modelId,
803
835
  sessionMeta,
836
+ instructionMapping,
804
837
  debug,
805
838
  implementationIdentity,
806
839
  authenticationProfile,
807
840
  builtinTools,
808
841
  permissionMode,
809
842
  permissionModeMapping,
843
+ mcpServers,
844
+ isMcpToolCall,
810
845
  initialGuidanceApplied: initialGuidanceAppliedAtStart,
811
846
  skillCatalog,
812
847
  skillsFingerprint,
@@ -829,12 +864,15 @@ function createSession({
829
864
  proc: Experimental_SandboxProcess | undefined;
830
865
  modelId: string | undefined;
831
866
  sessionMeta: Readonly<Record<string, ACPSerializableValue>> | undefined;
867
+ instructionMapping: ACPInstructionMapping | undefined;
832
868
  debug: HarnessV1DebugConfig | undefined;
833
869
  implementationIdentity: string;
834
870
  authenticationProfile: ACPAuthenticationProfileIdentity;
835
871
  builtinTools: ReadonlyArray<ACPBuiltinToolMapping>;
836
872
  permissionMode: NonNullable<StartMessage['permissionMode']>;
837
873
  permissionModeMapping: StartMessage['permissionModeMapping'];
874
+ mcpServers: Record<string, unknown> | undefined;
875
+ isMcpToolCall: ((toolCall: ACPToolCall) => boolean) | undefined;
838
876
  initialGuidanceApplied: boolean;
839
877
  skillCatalog: ReadonlyArray<ACPSkillCatalogEntry>;
840
878
  skillsFingerprint: string;
@@ -889,6 +927,8 @@ function createSession({
889
927
  let openBlock:
890
928
  | { readonly type: 'text' | 'reasoning'; readonly id: string }
891
929
  | undefined;
930
+ const dynamicToolCalls = new Map<string, boolean>();
931
+ const toolCallClassificationErrors = new Map<string, unknown>();
892
932
  const subscriptions: Array<() => void> = [];
893
933
  const forward = (event: HarnessV1StreamPart) => {
894
934
  if (event.type === 'text-start' || event.type === 'reasoning-start') {
@@ -923,9 +963,7 @@ function createSession({
923
963
  'reasoning-start',
924
964
  'reasoning-delta',
925
965
  'reasoning-end',
926
- 'tool-call',
927
966
  'tool-approval-request',
928
- 'tool-result',
929
967
  'file-change',
930
968
  'finish-step',
931
969
  'raw',
@@ -954,6 +992,45 @@ function createSession({
954
992
  settle({ error: abortError });
955
993
  }
956
994
  };
995
+ subscriptions.push(
996
+ channel.on('acp-tool-call-candidate', event => {
997
+ try {
998
+ dynamicToolCalls.set(
999
+ event.toolCall.toolCallId,
1000
+ isMcpToolCall?.(event.toolCall) === true,
1001
+ );
1002
+ } catch (error) {
1003
+ toolCallClassificationErrors.set(event.toolCall.toolCallId, error);
1004
+ }
1005
+ }),
1006
+ );
1007
+ subscriptions.push(
1008
+ channel.on('tool-call', event => {
1009
+ if (toolCallClassificationErrors.has(event.toolCallId)) {
1010
+ const error = toolCallClassificationErrors.get(event.toolCallId);
1011
+ closeForwardedBlock();
1012
+ forward({ type: 'error', error });
1013
+ try {
1014
+ channel.send({ type: 'abort' });
1015
+ } catch {}
1016
+ settle({ error });
1017
+ return;
1018
+ }
1019
+ forward(
1020
+ dynamicToolCalls.get(event.toolCallId) === true
1021
+ ? { ...event, dynamic: true }
1022
+ : event,
1023
+ );
1024
+ }),
1025
+ );
1026
+ subscriptions.push(
1027
+ channel.on('tool-result', event => {
1028
+ const dynamic = dynamicToolCalls.get(event.toolCallId) === true;
1029
+ dynamicToolCalls.delete(event.toolCallId);
1030
+ toolCallClassificationErrors.delete(event.toolCallId);
1031
+ forward(dynamic ? { ...event, dynamic: true } : event);
1032
+ }),
1033
+ );
957
1034
  for (const type of eventTypes) {
958
1035
  subscriptions.push(channel.on(type, event => forward(event)));
959
1036
  }
@@ -1110,9 +1187,11 @@ function createSession({
1110
1187
  builtinTools,
1111
1188
  permissionMode,
1112
1189
  permissionModeMapping,
1190
+ mcpServers,
1113
1191
  debug,
1114
1192
  authenticationProfile,
1115
1193
  sessionMeta,
1194
+ instructionMapping,
1116
1195
  });
1117
1196
  const control = wireTurn({
1118
1197
  emit: options.emit,
@@ -1130,13 +1209,25 @@ function createSession({
1130
1209
  ? prompt
1131
1210
  : prependACPInitialGuidance({
1132
1211
  prompt,
1133
- instructions: options.instructions,
1212
+ instructions:
1213
+ instructionMapping == null
1214
+ ? options.instructions
1215
+ : undefined,
1134
1216
  skills: skillCatalog,
1135
1217
  }),
1218
+ ...(instructionMapping == null
1219
+ ? {}
1220
+ : {
1221
+ instructionMapping,
1222
+ ...(options.instructions == null
1223
+ ? {}
1224
+ : { instructions: options.instructions }),
1225
+ }),
1136
1226
  debug,
1137
1227
  builtinTools,
1138
1228
  permissionMode,
1139
1229
  permissionModeMapping,
1230
+ ...(mcpServers == null ? {} : { mcpServers }),
1140
1231
  tools: options.tools == null ? undefined : turnStartConfig.tools,
1141
1232
  turnStartConfig,
1142
1233
  });
@@ -1174,6 +1265,15 @@ function createSession({
1174
1265
  builtinTools: turnStartConfig.builtinTools,
1175
1266
  permissionMode: turnStartConfig.permissionMode,
1176
1267
  permissionModeMapping: turnStartConfig.permissionModeMapping,
1268
+ ...(instructionMapping == null
1269
+ ? {}
1270
+ : {
1271
+ instructionMapping,
1272
+ ...(options.instructions == null
1273
+ ? {}
1274
+ : { instructions: options.instructions }),
1275
+ }),
1276
+ ...(mcpServers == null ? {} : { mcpServers }),
1177
1277
  tools: turnStartConfig.tools,
1178
1278
  turnStartConfig,
1179
1279
  recoveryMode: {
@@ -1336,14 +1436,18 @@ function validateACPTurnStartConfig({
1336
1436
  turnStartConfig,
1337
1437
  authenticationProfile,
1338
1438
  sessionMeta,
1439
+ instructionMapping,
1339
1440
  builtinTools,
1340
1441
  permissionModeMapping,
1442
+ mcpServers,
1341
1443
  }: {
1342
1444
  turnStartConfig: ACPTurnStartConfig;
1343
1445
  authenticationProfile: ACPAuthenticationProfileIdentity;
1344
1446
  sessionMeta: Readonly<Record<string, ACPSerializableValue>> | undefined;
1447
+ instructionMapping: ACPInstructionMapping | undefined;
1345
1448
  builtinTools: ReadonlyArray<ACPBuiltinToolMapping>;
1346
1449
  permissionModeMapping: ACPPermissionModeMapping | undefined;
1450
+ mcpServers: Record<string, unknown> | undefined;
1347
1451
  }): void {
1348
1452
  const current = createACPTurnStartConfig({
1349
1453
  prompt: turnStartConfig.prompt,
@@ -1351,9 +1455,11 @@ function validateACPTurnStartConfig({
1351
1455
  builtinTools,
1352
1456
  permissionMode: turnStartConfig.permissionMode,
1353
1457
  permissionModeMapping,
1458
+ mcpServers,
1354
1459
  debug: turnStartConfig.debug,
1355
1460
  authenticationProfile,
1356
1461
  sessionMeta,
1462
+ instructionMapping,
1357
1463
  });
1358
1464
  if (
1359
1465
  current.configurationFingerprint !==
@@ -1371,8 +1477,10 @@ function validateACPColdSessionConfiguration({
1371
1477
  permissionMode,
1372
1478
  authenticationProfile,
1373
1479
  sessionMeta,
1480
+ instructionMapping,
1374
1481
  builtinTools,
1375
1482
  permissionModeMapping,
1483
+ mcpServers,
1376
1484
  debug,
1377
1485
  }: {
1378
1486
  coldSession: ACPColdSessionState;
@@ -1380,8 +1488,10 @@ function validateACPColdSessionConfiguration({
1380
1488
  permissionMode: NonNullable<StartMessage['permissionMode']>;
1381
1489
  authenticationProfile: ACPAuthenticationProfileIdentity;
1382
1490
  sessionMeta: Readonly<Record<string, ACPSerializableValue>> | undefined;
1491
+ instructionMapping: ACPInstructionMapping | undefined;
1383
1492
  builtinTools: ReadonlyArray<ACPBuiltinToolMapping>;
1384
1493
  permissionModeMapping: ACPPermissionModeMapping | undefined;
1494
+ mcpServers: Record<string, unknown> | undefined;
1385
1495
  debug: HarnessV1DebugConfig | undefined;
1386
1496
  }): ACPTurnStartConfig {
1387
1497
  const current = createACPTurnStartConfig({
@@ -1390,9 +1500,11 @@ function validateACPColdSessionConfiguration({
1390
1500
  builtinTools,
1391
1501
  permissionMode,
1392
1502
  permissionModeMapping,
1503
+ mcpServers,
1393
1504
  debug,
1394
1505
  authenticationProfile,
1395
1506
  sessionMeta,
1507
+ instructionMapping,
1396
1508
  });
1397
1509
  if (
1398
1510
  current.configurationFingerprint !== coldSession.configurationFingerprint ||
@@ -1,4 +1,5 @@
1
1
  import type { HarnessV1PermissionMode } from '@ai-sdk/harness';
2
+ import type { ACPToolCall } from '../acp-tool-call';
2
3
 
3
4
  export type ACPSerializablePrimitive = string | number | boolean | null;
4
5
 
@@ -74,9 +75,27 @@ export type ACPPermissionModeMapping = Readonly<
74
75
  Record<HarnessV1PermissionMode, ACPPermissionModeTarget | null>
75
76
  >;
76
77
 
78
+ /**
79
+ * Describes where an ACP implementation accepts native session instructions.
80
+ * Session metadata paths are relative to `_meta`; launch environment paths are
81
+ * relative to the parsed JSON object stored in `variable`.
82
+ */
83
+ export type ACPInstructionMapping =
84
+ | {
85
+ readonly type: 'session-meta';
86
+ readonly path: ReadonlyArray<string>;
87
+ }
88
+ | {
89
+ readonly type: 'launch-env-json';
90
+ readonly variable: string;
91
+ readonly path: ReadonlyArray<string>;
92
+ };
93
+
77
94
  export type ACPV1Settings = {
78
95
  readonly version?: 'v1';
79
96
  readonly harnessId: string;
97
+ readonly mcpServers?: Record<string, unknown>;
98
+ readonly isMcpToolCall?: (toolCall: ACPToolCall) => boolean;
80
99
  readonly auth?: ACPProviderAuthenticationMode;
81
100
  readonly source: ACPSource;
82
101
  readonly executable: string;
@@ -90,8 +109,18 @@ export type ACPV1Settings = {
90
109
  readonly authentication?: ACPAuthentication;
91
110
  readonly providerAuthentication?: ACPProviderAuthentication;
92
111
  readonly modelId?: string;
112
+ /**
113
+ * Routes HarnessAgent instructions to a runtime-native system or developer
114
+ * prompt. When omitted, instructions are prepended to the first user prompt.
115
+ */
116
+ readonly instructionMapping?: ACPInstructionMapping;
93
117
  readonly permissionModeMapping?: ACPPermissionModeMapping;
94
118
  readonly session?: {
95
119
  readonly meta?: Readonly<Record<string, ACPSerializableValue>>;
96
120
  };
121
+ /**
122
+ * Creates the authentication token used by the sandbox bridge. Defaults to
123
+ * a random 32-byte hexadecimal token.
124
+ */
125
+ readonly mintBridgeToken?: (sandboxId: string) => string;
97
126
  };