@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/CHANGELOG.md +19 -0
- package/README.md +13 -0
- package/dist/bridge/host-tool-mcp.mjs +1 -1
- package/dist/bridge/index.mjs +176 -18
- package/dist/bridge/index.mjs.map +1 -1
- package/dist/index.d.ts +132 -1
- package/dist/index.js +130 -13
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/acp-harness.ts +20 -0
- package/src/acp-tool-call.ts +124 -0
- package/src/index.ts +19 -0
- package/src/v1/acp-v1-bridge-protocol.ts +38 -2
- package/src/v1/acp-v1-harness.ts +116 -4
- package/src/v1/acp-v1-settings.ts +29 -0
- package/src/v1/acp-v1-turn-start-config.ts +7 -0
- package/src/v1/bridge/create-emit-stream-event.ts +8 -1
- package/src/v1/bridge/index.ts +61 -6
- package/src/v1/bridge/instruction-mapping.ts +132 -0
- package/src/v1/bridge/stream-translator.ts +62 -5
- 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.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.
|
|
31
|
-
"@ai-sdk/provider-utils": "5.0.
|
|
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"
|
package/src/acp-harness.ts
CHANGED
|
@@ -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 {
|
|
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
|
-
|
|
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(),
|
package/src/v1/acp-v1-harness.ts
CHANGED
|
@@ -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 =
|
|
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:
|
|
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
|
};
|