@ai-sdk/harness-acp 0.0.0 → 1.0.0
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 +13 -0
- package/LICENSE +13 -0
- package/README.md +70 -3
- package/dist/bridge/host-tool-mcp.mjs +302 -0
- package/dist/bridge/host-tool-mcp.mjs.map +1 -0
- package/dist/bridge/index.mjs +3278 -0
- package/dist/bridge/index.mjs.map +1 -0
- package/dist/bridge/package.json +12 -0
- package/dist/bridge/pnpm-lock.yaml +816 -0
- package/dist/index.d.ts +98 -0
- package/dist/index.js +2094 -0
- package/dist/index.js.map +1 -0
- package/package.json +64 -10
- package/src/acp-auth.ts +254 -0
- package/src/acp-bridge-protocol.ts +13 -0
- package/src/acp-harness.ts +160 -0
- package/src/index.ts +19 -0
- package/src/v1/acp-v1-bridge-protocol.ts +151 -0
- package/src/v1/acp-v1-harness.ts +1447 -0
- package/src/v1/acp-v1-lifecycle.ts +115 -0
- package/src/v1/acp-v1-prompt.ts +122 -0
- package/src/v1/acp-v1-settings.ts +98 -0
- package/src/v1/acp-v1-skills.ts +228 -0
- package/src/v1/acp-v1-turn-start-config.ts +93 -0
- package/src/v1/bridge/acp-diagnostics.ts +57 -0
- package/src/v1/bridge/acp-stream-capture.ts +152 -0
- package/src/v1/bridge/acp-v1-bridge-environment.ts +95 -0
- package/src/v1/bridge/create-emit-stream-event.ts +97 -0
- package/src/v1/bridge/host-tool-correlation.ts +640 -0
- package/src/v1/bridge/host-tool-mcp-server.ts +180 -0
- package/src/v1/bridge/host-tool-mcp.ts +185 -0
- package/src/v1/bridge/host-tool-relay-client.ts +53 -0
- package/src/v1/bridge/host-tool-relay.ts +510 -0
- package/src/v1/bridge/index.ts +729 -0
- package/src/v1/bridge/package.json +12 -0
- package/src/v1/bridge/permission-controller.ts +152 -0
- package/src/v1/bridge/permission-mode.ts +163 -0
- package/src/v1/bridge/pnpm-lock.yaml +816 -0
- package/src/v1/bridge/profile-values.ts +97 -0
- package/src/v1/bridge/protocol-configuration.ts +146 -0
- package/src/v1/bridge/recovered-session.ts +145 -0
- package/src/v1/bridge/refresh-host-tool-catalog.ts +33 -0
- package/src/v1/bridge/session-lifecycle.ts +83 -0
- package/src/v1/bridge/stream-translator.ts +901 -0
- package/src/v1/implementation.ts +282 -0
- package/src/v1/index.ts +16 -0
- package/src/version.ts +6 -0
- package/index.js +0 -1
package/src/acp-auth.ts
ADDED
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { getAiGatewayAuthFromEnv } from '@ai-sdk/harness/utils';
|
|
3
|
+
import type {
|
|
4
|
+
ACPAuthentication,
|
|
5
|
+
ACPProviderAuthentication,
|
|
6
|
+
ACPProviderAuthenticationMode,
|
|
7
|
+
} from './v1';
|
|
8
|
+
import type { ACPResolvedProviderAuthentication } from './v1/bridge/acp-v1-bridge-environment';
|
|
9
|
+
|
|
10
|
+
const DEFAULT_AI_GATEWAY_BASE_URL = 'https://ai-gateway.vercel.sh';
|
|
11
|
+
|
|
12
|
+
export type ACPAuthOptions = ACPProviderAuthenticationMode;
|
|
13
|
+
|
|
14
|
+
export type ACPClientApp = {
|
|
15
|
+
readonly name: string;
|
|
16
|
+
readonly version: string;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
type ACPAuthResolutionOptions = {
|
|
20
|
+
readonly mode?: ACPAuthOptions;
|
|
21
|
+
readonly providerAuthentication: ACPProviderAuthentication | undefined;
|
|
22
|
+
readonly clientApp: ACPClientApp;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
type ACPGatewayCredentialSource = 'AI_GATEWAY_API_KEY' | 'VERCEL_OIDC_TOKEN';
|
|
26
|
+
|
|
27
|
+
export type ACPProviderAuthenticationCompatibility =
|
|
28
|
+
| {
|
|
29
|
+
readonly type: 'direct';
|
|
30
|
+
readonly mode: 'auto' | 'direct';
|
|
31
|
+
}
|
|
32
|
+
| {
|
|
33
|
+
readonly type: 'ai-gateway';
|
|
34
|
+
readonly mode: 'auto' | 'ai-gateway';
|
|
35
|
+
readonly env: ACPProviderAuthentication['gateway']['env'];
|
|
36
|
+
readonly credentialSource: ACPGatewayCredentialSource | null;
|
|
37
|
+
readonly baseUrl: string;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export type ACPAuthenticationProfileIdentity = {
|
|
41
|
+
readonly digest: string;
|
|
42
|
+
readonly acpMethodId?: string;
|
|
43
|
+
readonly providerKind: 'direct' | 'ai-gateway';
|
|
44
|
+
readonly providerMode?: 'auto' | 'direct' | 'ai-gateway';
|
|
45
|
+
readonly gatewayCredentialSource?:
|
|
46
|
+
| 'AI_GATEWAY_API_KEY'
|
|
47
|
+
| 'VERCEL_OIDC_TOKEN'
|
|
48
|
+
| null;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export function createACPAuthenticationProfileIdentity({
|
|
52
|
+
authentication,
|
|
53
|
+
providerAuthenticationCompatibility,
|
|
54
|
+
}: {
|
|
55
|
+
authentication: ACPAuthentication | undefined;
|
|
56
|
+
providerAuthenticationCompatibility:
|
|
57
|
+
| ACPProviderAuthenticationCompatibility
|
|
58
|
+
| undefined;
|
|
59
|
+
}): ACPAuthenticationProfileIdentity {
|
|
60
|
+
const providerKind = providerAuthenticationCompatibility?.type ?? 'direct';
|
|
61
|
+
const digest = createHash('sha256')
|
|
62
|
+
.update(
|
|
63
|
+
stableStringify({
|
|
64
|
+
value: {
|
|
65
|
+
authentication: authentication ?? null,
|
|
66
|
+
providerAuthentication: providerAuthenticationCompatibility ?? null,
|
|
67
|
+
},
|
|
68
|
+
}),
|
|
69
|
+
)
|
|
70
|
+
.digest('hex');
|
|
71
|
+
return {
|
|
72
|
+
digest,
|
|
73
|
+
...(authentication == null ? {} : { acpMethodId: authentication.methodId }),
|
|
74
|
+
providerKind,
|
|
75
|
+
...(providerAuthenticationCompatibility == null
|
|
76
|
+
? {}
|
|
77
|
+
: { providerMode: providerAuthenticationCompatibility.mode }),
|
|
78
|
+
...(providerAuthenticationCompatibility?.type === 'ai-gateway'
|
|
79
|
+
? {
|
|
80
|
+
gatewayCredentialSource:
|
|
81
|
+
providerAuthenticationCompatibility.credentialSource,
|
|
82
|
+
}
|
|
83
|
+
: {}),
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function resolveACPProviderAuthentication({
|
|
88
|
+
auth,
|
|
89
|
+
env,
|
|
90
|
+
compatibility,
|
|
91
|
+
}: {
|
|
92
|
+
auth: ACPAuthResolutionOptions;
|
|
93
|
+
env: Record<string, string | undefined>;
|
|
94
|
+
compatibility?: ACPProviderAuthenticationCompatibility;
|
|
95
|
+
}): {
|
|
96
|
+
readonly providerAuthentication:
|
|
97
|
+
| ACPResolvedProviderAuthentication
|
|
98
|
+
| undefined;
|
|
99
|
+
readonly env: Record<string, string>;
|
|
100
|
+
} {
|
|
101
|
+
const clientAppEnv = {
|
|
102
|
+
AI_SDK_ACP_CLIENT_APP_NAME: auth.clientApp.name,
|
|
103
|
+
AI_SDK_ACP_CLIENT_APP_VERSION: auth.clientApp.version,
|
|
104
|
+
};
|
|
105
|
+
const providerAuthentication = auth.providerAuthentication;
|
|
106
|
+
if (providerAuthentication == null) {
|
|
107
|
+
return { providerAuthentication: undefined, env: clientAppEnv };
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (compatibility?.type === 'direct') {
|
|
111
|
+
return {
|
|
112
|
+
providerAuthentication: { type: 'direct' },
|
|
113
|
+
env: clientAppEnv,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const mode = auth.mode ?? 'auto';
|
|
118
|
+
if (mode === 'direct') {
|
|
119
|
+
return {
|
|
120
|
+
providerAuthentication: { type: 'direct' },
|
|
121
|
+
env: clientAppEnv,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const gateway =
|
|
126
|
+
compatibility?.type === 'ai-gateway'
|
|
127
|
+
? {
|
|
128
|
+
apiKey: resolveGatewayCredential({
|
|
129
|
+
env,
|
|
130
|
+
credentialSource: compatibility.credentialSource,
|
|
131
|
+
}),
|
|
132
|
+
baseUrl: compatibility.baseUrl,
|
|
133
|
+
}
|
|
134
|
+
: getAiGatewayAuthFromEnv({ env });
|
|
135
|
+
const apiKey = gateway.apiKey;
|
|
136
|
+
if (compatibility == null && mode === 'auto' && apiKey == null) {
|
|
137
|
+
return {
|
|
138
|
+
providerAuthentication: { type: 'direct' },
|
|
139
|
+
env: clientAppEnv,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
if (apiKey == null) {
|
|
143
|
+
throw new Error(
|
|
144
|
+
'AI Gateway authentication was selected, but neither AI_GATEWAY_API_KEY nor VERCEL_OIDC_TOKEN is set.',
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return {
|
|
149
|
+
providerAuthentication: {
|
|
150
|
+
type: 'ai-gateway',
|
|
151
|
+
env:
|
|
152
|
+
compatibility?.type === 'ai-gateway'
|
|
153
|
+
? compatibility.env
|
|
154
|
+
: providerAuthentication.gateway.env,
|
|
155
|
+
},
|
|
156
|
+
env: {
|
|
157
|
+
AI_SDK_ACP_GATEWAY_API_KEY: apiKey,
|
|
158
|
+
AI_SDK_ACP_GATEWAY_BASE_URL: gateway.baseUrl,
|
|
159
|
+
...clientAppEnv,
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export function resolveACPProviderAuthenticationCompatibility({
|
|
165
|
+
auth = 'auto',
|
|
166
|
+
providerAuthentication,
|
|
167
|
+
env,
|
|
168
|
+
}: {
|
|
169
|
+
auth?: ACPProviderAuthenticationMode;
|
|
170
|
+
providerAuthentication: ACPProviderAuthentication | undefined;
|
|
171
|
+
env: Record<string, string | undefined>;
|
|
172
|
+
}): ACPProviderAuthenticationCompatibility | undefined {
|
|
173
|
+
if (providerAuthentication == null) return undefined;
|
|
174
|
+
|
|
175
|
+
const mode = auth;
|
|
176
|
+
if (mode === 'direct') {
|
|
177
|
+
return { type: 'direct', mode };
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const credentialSource = resolveGatewayCredentialSource({
|
|
181
|
+
env,
|
|
182
|
+
});
|
|
183
|
+
if (mode === 'auto' && credentialSource == null) {
|
|
184
|
+
return { type: 'direct', mode };
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return {
|
|
188
|
+
type: 'ai-gateway',
|
|
189
|
+
mode,
|
|
190
|
+
env: providerAuthentication.gateway.env,
|
|
191
|
+
credentialSource,
|
|
192
|
+
baseUrl: env.AI_GATEWAY_BASE_URL ?? DEFAULT_AI_GATEWAY_BASE_URL,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export function resolveACPEnv({
|
|
197
|
+
auth,
|
|
198
|
+
env,
|
|
199
|
+
}: {
|
|
200
|
+
auth: ACPAuthResolutionOptions;
|
|
201
|
+
env: Record<string, string | undefined>;
|
|
202
|
+
}): Record<string, string> {
|
|
203
|
+
return resolveACPProviderAuthentication({ auth, env }).env;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function resolveGatewayCredentialSource({
|
|
207
|
+
env,
|
|
208
|
+
}: {
|
|
209
|
+
env: Record<string, string | undefined>;
|
|
210
|
+
}): ACPGatewayCredentialSource | null {
|
|
211
|
+
return env.AI_GATEWAY_API_KEY
|
|
212
|
+
? 'AI_GATEWAY_API_KEY'
|
|
213
|
+
: env.VERCEL_OIDC_TOKEN
|
|
214
|
+
? 'VERCEL_OIDC_TOKEN'
|
|
215
|
+
: null;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function resolveGatewayCredential({
|
|
219
|
+
env,
|
|
220
|
+
credentialSource,
|
|
221
|
+
}: {
|
|
222
|
+
env: Record<string, string | undefined>;
|
|
223
|
+
credentialSource: ACPGatewayCredentialSource | null;
|
|
224
|
+
}): string | undefined {
|
|
225
|
+
const gatewayFromEnv = getAiGatewayAuthFromEnv({ env });
|
|
226
|
+
switch (credentialSource) {
|
|
227
|
+
case 'AI_GATEWAY_API_KEY':
|
|
228
|
+
return env.AI_GATEWAY_API_KEY == null ? undefined : gatewayFromEnv.apiKey;
|
|
229
|
+
case 'VERCEL_OIDC_TOKEN':
|
|
230
|
+
return getAiGatewayAuthFromEnv({
|
|
231
|
+
env: { VERCEL_OIDC_TOKEN: env.VERCEL_OIDC_TOKEN },
|
|
232
|
+
}).apiKey;
|
|
233
|
+
case null:
|
|
234
|
+
return undefined;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function stableStringify({ value }: { value: unknown }): string {
|
|
239
|
+
return JSON.stringify(sortIdentityValue({ value }));
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function sortIdentityValue({ value }: { value: unknown }): unknown {
|
|
243
|
+
if (Array.isArray(value)) {
|
|
244
|
+
return value.map(item => sortIdentityValue({ value: item }));
|
|
245
|
+
}
|
|
246
|
+
if (value != null && typeof value === 'object') {
|
|
247
|
+
return Object.fromEntries(
|
|
248
|
+
Object.entries(value)
|
|
249
|
+
.sort(([left], [right]) => (left < right ? -1 : left > right ? 1 : 0))
|
|
250
|
+
.map(([key, entry]) => [key, sortIdentityValue({ value: entry })]),
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
return value;
|
|
254
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import * as acpV1BridgeProtocol from './v1/acp-v1-bridge-protocol';
|
|
2
|
+
|
|
3
|
+
export const outboundMessageSchema = acpV1BridgeProtocol.outboundMessageSchema;
|
|
4
|
+
export type OutboundMessage = acpV1BridgeProtocol.OutboundMessage;
|
|
5
|
+
|
|
6
|
+
export const startMessageSchema = acpV1BridgeProtocol.startMessageSchema;
|
|
7
|
+
export type StartMessage = acpV1BridgeProtocol.StartMessage;
|
|
8
|
+
|
|
9
|
+
export const inboundMessageSchema = acpV1BridgeProtocol.inboundMessageSchema;
|
|
10
|
+
export type InboundMessage = acpV1BridgeProtocol.InboundMessage;
|
|
11
|
+
|
|
12
|
+
export const bridgeReadySchema = acpV1BridgeProtocol.bridgeReadySchema;
|
|
13
|
+
export type BridgeReady = acpV1BridgeProtocol.BridgeReady;
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import type { HarnessV1 } from '@ai-sdk/harness';
|
|
2
|
+
import type { ToolSet } from '@ai-sdk/provider-utils';
|
|
3
|
+
import { z } from 'zod/v4';
|
|
4
|
+
import {
|
|
5
|
+
createACPAuthenticationProfileIdentity,
|
|
6
|
+
resolveACPProviderAuthenticationCompatibility,
|
|
7
|
+
type ACPAuthenticationProfileIdentity,
|
|
8
|
+
type ACPClientApp,
|
|
9
|
+
} from './acp-auth';
|
|
10
|
+
import { createACPV1, type ACPV1Settings } from './v1';
|
|
11
|
+
import { createImplementationIdentity } from './v1/implementation';
|
|
12
|
+
import {
|
|
13
|
+
acpColdSessionStateSchema,
|
|
14
|
+
acpTurnStartConfigSchema,
|
|
15
|
+
type ACPColdSessionState,
|
|
16
|
+
type ACPTurnStartConfig,
|
|
17
|
+
} from './v1/acp-v1-bridge-protocol';
|
|
18
|
+
import { VERSION } from './version';
|
|
19
|
+
|
|
20
|
+
const ACP_CLIENT_APP = {
|
|
21
|
+
name: 'ai-sdk/harness-acp',
|
|
22
|
+
version: VERSION,
|
|
23
|
+
} as const satisfies ACPClientApp;
|
|
24
|
+
|
|
25
|
+
export type ACPHarnessSettings<TBuiltinTools extends ToolSet = {}> = {
|
|
26
|
+
readonly builtinTools?: TBuiltinTools;
|
|
27
|
+
readonly port?: number;
|
|
28
|
+
readonly startupTimeoutMs?: number;
|
|
29
|
+
readonly clientApp?: ACPClientApp;
|
|
30
|
+
readonly version?: ACPV1Settings['version'];
|
|
31
|
+
readonly harnessId: ACPV1Settings['harnessId'];
|
|
32
|
+
readonly auth?: ACPV1Settings['auth'];
|
|
33
|
+
readonly implementation: ACPV1Settings['implementation'];
|
|
34
|
+
readonly authentication?: ACPV1Settings['authentication'];
|
|
35
|
+
readonly providerAuthentication?: ACPV1Settings['providerAuthentication'];
|
|
36
|
+
readonly modelId?: ACPV1Settings['modelId'];
|
|
37
|
+
readonly permissionModeMapping?: ACPV1Settings['permissionModeMapping'];
|
|
38
|
+
readonly session?: ACPV1Settings['session'];
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const ACP_BUILTIN_TOOLS = {} as const satisfies ToolSet;
|
|
42
|
+
|
|
43
|
+
const acpBridgeCoordsSchema = z.object({
|
|
44
|
+
port: z.number(),
|
|
45
|
+
token: z.string(),
|
|
46
|
+
lastSeenEventId: z.number(),
|
|
47
|
+
sandboxId: z.string().optional(),
|
|
48
|
+
stateDir: z.string().optional(),
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const acpResumeStateSchema = z.object({
|
|
52
|
+
implementationIdentity: z.string(),
|
|
53
|
+
authenticationProfile: z
|
|
54
|
+
.object({
|
|
55
|
+
digest: z.string(),
|
|
56
|
+
acpMethodId: z.string().optional(),
|
|
57
|
+
providerKind: z
|
|
58
|
+
.enum(['implementation-default', 'direct', 'ai-gateway'])
|
|
59
|
+
.transform(value =>
|
|
60
|
+
value === 'implementation-default' ? 'direct' : value,
|
|
61
|
+
),
|
|
62
|
+
providerMode: z.enum(['auto', 'direct', 'ai-gateway']).optional(),
|
|
63
|
+
gatewayCredentialSource: z
|
|
64
|
+
.enum(['AI_GATEWAY_API_KEY', 'VERCEL_OIDC_TOKEN'])
|
|
65
|
+
.nullable()
|
|
66
|
+
.optional(),
|
|
67
|
+
})
|
|
68
|
+
.optional(),
|
|
69
|
+
acpSessionId: z.string().optional(),
|
|
70
|
+
bridge: acpBridgeCoordsSchema.optional(),
|
|
71
|
+
coldSession: acpColdSessionStateSchema.optional(),
|
|
72
|
+
turnStartConfig: acpTurnStartConfigSchema.optional(),
|
|
73
|
+
recovery: z
|
|
74
|
+
.object({
|
|
75
|
+
mode: z.enum(['disk-replay', 'lossy-rerun']),
|
|
76
|
+
reason: z.string(),
|
|
77
|
+
})
|
|
78
|
+
.optional(),
|
|
79
|
+
restoration: z
|
|
80
|
+
.object({
|
|
81
|
+
method: z.enum(['resume', 'load']),
|
|
82
|
+
})
|
|
83
|
+
.optional(),
|
|
84
|
+
initialGuidanceApplied: z.boolean().optional(),
|
|
85
|
+
skillsMaterialized: z.boolean().optional(),
|
|
86
|
+
skillsFingerprint: z.string().optional(),
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
type ACPBridgeCoords = z.infer<typeof acpBridgeCoordsSchema>;
|
|
90
|
+
|
|
91
|
+
export function createACP<TBuiltinTools extends ToolSet = {}>(
|
|
92
|
+
settings: ACPHarnessSettings<TBuiltinTools>,
|
|
93
|
+
): HarnessV1<TBuiltinTools> {
|
|
94
|
+
const version = (settings as { readonly version?: string }).version ?? 'v1';
|
|
95
|
+
switch (version) {
|
|
96
|
+
case 'v1': {
|
|
97
|
+
const clientApp = settings.clientApp ?? ACP_CLIENT_APP;
|
|
98
|
+
const providerAuthenticationCompatibility =
|
|
99
|
+
resolveACPProviderAuthenticationCompatibility({
|
|
100
|
+
auth: settings.auth,
|
|
101
|
+
providerAuthentication: settings.providerAuthentication,
|
|
102
|
+
env: process.env,
|
|
103
|
+
});
|
|
104
|
+
const implementationIdentity = createImplementationIdentity({
|
|
105
|
+
harnessId: settings.harnessId,
|
|
106
|
+
acpVersion: version,
|
|
107
|
+
implementation: settings.implementation,
|
|
108
|
+
clientApp,
|
|
109
|
+
providerAuthentication: providerAuthenticationCompatibility,
|
|
110
|
+
permissionModeMapping: settings.permissionModeMapping,
|
|
111
|
+
});
|
|
112
|
+
const authenticationProfile = createACPAuthenticationProfileIdentity({
|
|
113
|
+
authentication: settings.authentication,
|
|
114
|
+
providerAuthenticationCompatibility,
|
|
115
|
+
});
|
|
116
|
+
const lifecycleStateSchema: z.ZodType<{
|
|
117
|
+
readonly implementationIdentity: string;
|
|
118
|
+
readonly authenticationProfile?: ACPAuthenticationProfileIdentity;
|
|
119
|
+
readonly acpSessionId?: string;
|
|
120
|
+
readonly bridge?: ACPBridgeCoords;
|
|
121
|
+
readonly coldSession?: ACPColdSessionState;
|
|
122
|
+
readonly turnStartConfig?: ACPTurnStartConfig;
|
|
123
|
+
readonly recovery?: {
|
|
124
|
+
readonly mode: 'disk-replay' | 'lossy-rerun';
|
|
125
|
+
readonly reason: string;
|
|
126
|
+
};
|
|
127
|
+
readonly restoration?: {
|
|
128
|
+
readonly method: 'resume' | 'load';
|
|
129
|
+
};
|
|
130
|
+
readonly initialGuidanceApplied?: boolean;
|
|
131
|
+
readonly skillsMaterialized?: boolean;
|
|
132
|
+
readonly skillsFingerprint?: string;
|
|
133
|
+
}> = acpResumeStateSchema.extend({
|
|
134
|
+
implementationIdentity: z.literal(implementationIdentity),
|
|
135
|
+
authenticationProfile: acpResumeStateSchema.shape.authenticationProfile
|
|
136
|
+
.unwrap()
|
|
137
|
+
.extend({
|
|
138
|
+
digest: z.literal(authenticationProfile.digest),
|
|
139
|
+
})
|
|
140
|
+
.optional(),
|
|
141
|
+
});
|
|
142
|
+
return createACPV1({
|
|
143
|
+
settings,
|
|
144
|
+
builtinTools:
|
|
145
|
+
settings.builtinTools ?? (ACP_BUILTIN_TOOLS as TBuiltinTools),
|
|
146
|
+
port: settings.port,
|
|
147
|
+
startupTimeoutMs: settings.startupTimeoutMs,
|
|
148
|
+
clientApp,
|
|
149
|
+
implementationIdentity,
|
|
150
|
+
authenticationProfile,
|
|
151
|
+
providerAuthenticationCompatibility,
|
|
152
|
+
lifecycleStateSchema,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
default:
|
|
156
|
+
throw new Error(
|
|
157
|
+
`Unsupported ACP protocol version ${JSON.stringify(version)}. Supported versions: "v1".`,
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export { createACP } from './acp-harness';
|
|
2
|
+
export type { ACPAuthOptions, ACPClientApp } from './acp-auth';
|
|
3
|
+
export type { ACPHarnessSettings } from './acp-harness';
|
|
4
|
+
export type {
|
|
5
|
+
ACPAuthentication,
|
|
6
|
+
ACPLockedNpmImplementation,
|
|
7
|
+
ACPNpmImplementation,
|
|
8
|
+
ACPPermissionModeMapping,
|
|
9
|
+
ACPPermissionModeTarget,
|
|
10
|
+
ACPProfileValue,
|
|
11
|
+
ACPProviderAuthentication,
|
|
12
|
+
ACPProviderAuthenticationMode,
|
|
13
|
+
ACPSerializablePrimitive,
|
|
14
|
+
ACPSerializableValue,
|
|
15
|
+
ACPSimpleNpmImplementation,
|
|
16
|
+
ACPV1Settings,
|
|
17
|
+
ACPValueSource,
|
|
18
|
+
} from './v1';
|
|
19
|
+
export { VERSION } from './version';
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import {
|
|
2
|
+
harnessV1BridgeInboundCommandSchemas,
|
|
3
|
+
harnessV1BridgeOutboundMessageSchema,
|
|
4
|
+
harnessV1BridgeReadySchema,
|
|
5
|
+
harnessV1BridgeStartBaseSchema,
|
|
6
|
+
harnessV1BridgeToolWireSchema,
|
|
7
|
+
type HarnessV1BuiltinTool,
|
|
8
|
+
type HarnessV1DebugConfig,
|
|
9
|
+
type HarnessV1LifecycleState,
|
|
10
|
+
type HarnessV1PermissionMode,
|
|
11
|
+
type HarnessV1ToolSpec,
|
|
12
|
+
} from '@ai-sdk/harness';
|
|
13
|
+
import { z } from 'zod/v4';
|
|
14
|
+
import type { ACPPermissionModeMapping } from './acp-v1-settings';
|
|
15
|
+
import { acpTextContentBlockSchema } from './acp-v1-prompt';
|
|
16
|
+
|
|
17
|
+
export type ACPBuiltinToolMapping = {
|
|
18
|
+
/**
|
|
19
|
+
* Key of the tool in `HarnessV1.builtinTools`, which is also emitted as the
|
|
20
|
+
* Harness V1 `toolName`. It is the common tool name when the tool has a
|
|
21
|
+
* standardized Harness V1 equivalent; otherwise it is the native tool name.
|
|
22
|
+
*/
|
|
23
|
+
readonly toolName: string;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Name used by the underlying agent runtime. It is present when `toolName`
|
|
27
|
+
* is a Harness V1 common tool name that differs from the runtime's name.
|
|
28
|
+
*/
|
|
29
|
+
readonly nativeName?: HarnessV1BuiltinTool['nativeName'];
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* JSON Schema used to identify tool calls when an ACP implementation does
|
|
33
|
+
* not provide a programmatic tool name. It is optional because built-in
|
|
34
|
+
* definitions are allowed to omit their input schema.
|
|
35
|
+
*/
|
|
36
|
+
readonly inputSchema?: HarnessV1LifecycleState['data'];
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const builtinToolSchema: z.ZodType<ACPBuiltinToolMapping> = z.object({
|
|
40
|
+
toolName: z.string(),
|
|
41
|
+
nativeName: z.string().optional(),
|
|
42
|
+
inputSchema: z.json().optional(),
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
type ACPSerializableToolSpec = Omit<HarnessV1ToolSpec, 'inputSchema'> & {
|
|
46
|
+
readonly inputSchema?: NonNullable<HarnessV1ToolSpec['inputSchema']> &
|
|
47
|
+
HarnessV1LifecycleState['data'];
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const jsonValueSchema = z.json();
|
|
51
|
+
|
|
52
|
+
export const acpSerializableToolSpecSchema: z.ZodType<ACPSerializableToolSpec> =
|
|
53
|
+
harnessV1BridgeToolWireSchema.extend({
|
|
54
|
+
inputSchema: z
|
|
55
|
+
.custom<NonNullable<ACPSerializableToolSpec['inputSchema']>>(
|
|
56
|
+
value =>
|
|
57
|
+
value != null &&
|
|
58
|
+
typeof value === 'object' &&
|
|
59
|
+
!Array.isArray(value) &&
|
|
60
|
+
jsonValueSchema.safeParse(value).success,
|
|
61
|
+
)
|
|
62
|
+
.optional(),
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const permissionModeTargetSchema = z.discriminatedUnion('type', [
|
|
66
|
+
z.object({
|
|
67
|
+
type: z.literal('session-mode'),
|
|
68
|
+
modeId: z.string(),
|
|
69
|
+
}),
|
|
70
|
+
z.object({
|
|
71
|
+
type: z.literal('session-config-option'),
|
|
72
|
+
configId: z.string(),
|
|
73
|
+
value: z.union([z.string(), z.boolean()]),
|
|
74
|
+
}),
|
|
75
|
+
]);
|
|
76
|
+
|
|
77
|
+
const permissionModeMappingSchema: z.ZodType<ACPPermissionModeMapping> =
|
|
78
|
+
z.object({
|
|
79
|
+
'allow-reads': permissionModeTargetSchema.nullable(),
|
|
80
|
+
'allow-edits': permissionModeTargetSchema.nullable(),
|
|
81
|
+
'allow-all': permissionModeTargetSchema.nullable(),
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
export type ACPTurnStartConfig = {
|
|
85
|
+
readonly version: 1;
|
|
86
|
+
readonly configurationFingerprint: string;
|
|
87
|
+
readonly prompt: Array<z.infer<typeof acpTextContentBlockSchema>>;
|
|
88
|
+
readonly tools: Array<ACPSerializableToolSpec>;
|
|
89
|
+
readonly builtinTools: Array<ACPBuiltinToolMapping>;
|
|
90
|
+
readonly permissionMode: HarnessV1PermissionMode;
|
|
91
|
+
readonly permissionModeMapping?: ACPPermissionModeMapping;
|
|
92
|
+
readonly debug?: HarnessV1DebugConfig;
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
export const acpTurnStartConfigSchema = z.object({
|
|
96
|
+
version: z.literal(1),
|
|
97
|
+
configurationFingerprint: z.string(),
|
|
98
|
+
prompt: z.array(acpTextContentBlockSchema),
|
|
99
|
+
tools: z.array(acpSerializableToolSpecSchema).default([]),
|
|
100
|
+
builtinTools: z.array(builtinToolSchema).default([]),
|
|
101
|
+
permissionMode: harnessV1BridgeStartBaseSchema.shape.permissionMode.unwrap(),
|
|
102
|
+
permissionModeMapping: permissionModeMappingSchema.optional(),
|
|
103
|
+
debug: harnessV1BridgeStartBaseSchema.shape.debug.optional(),
|
|
104
|
+
}) satisfies z.ZodType<ACPTurnStartConfig>;
|
|
105
|
+
|
|
106
|
+
export type ACPColdSessionState = Omit<
|
|
107
|
+
ACPTurnStartConfig,
|
|
108
|
+
'prompt' | 'debug'
|
|
109
|
+
> & {
|
|
110
|
+
readonly modelId?: string;
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
export const acpColdSessionStateSchema: z.ZodType<ACPColdSessionState> =
|
|
114
|
+
acpTurnStartConfigSchema.omit({ prompt: true, debug: true }).extend({
|
|
115
|
+
modelId: z.string().optional(),
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
const lossyRecoverySchema = z.object({
|
|
119
|
+
type: z.literal('lossy-rerun'),
|
|
120
|
+
acpSessionId: z.string(),
|
|
121
|
+
reason: z.string(),
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
const coldRestoreSchema = z.object({
|
|
125
|
+
type: z.literal('cold-restore'),
|
|
126
|
+
acpSessionId: z.string(),
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
export const outboundMessageSchema = harnessV1BridgeOutboundMessageSchema;
|
|
130
|
+
export type OutboundMessage = z.infer<typeof outboundMessageSchema>;
|
|
131
|
+
|
|
132
|
+
export const startMessageSchema = harnessV1BridgeStartBaseSchema.extend({
|
|
133
|
+
prompt: z.array(acpTextContentBlockSchema),
|
|
134
|
+
tools: z.array(acpSerializableToolSpecSchema).optional(),
|
|
135
|
+
builtinTools: z.array(builtinToolSchema).readonly().default([]),
|
|
136
|
+
permissionModeMapping: permissionModeMappingSchema.optional(),
|
|
137
|
+
turnStartConfig: acpTurnStartConfigSchema,
|
|
138
|
+
recoveryMode: z
|
|
139
|
+
.discriminatedUnion('type', [lossyRecoverySchema, coldRestoreSchema])
|
|
140
|
+
.optional(),
|
|
141
|
+
});
|
|
142
|
+
export type StartMessage = z.infer<typeof startMessageSchema>;
|
|
143
|
+
|
|
144
|
+
export const inboundMessageSchema = z.discriminatedUnion('type', [
|
|
145
|
+
startMessageSchema,
|
|
146
|
+
...harnessV1BridgeInboundCommandSchemas,
|
|
147
|
+
]);
|
|
148
|
+
export type InboundMessage = z.infer<typeof inboundMessageSchema>;
|
|
149
|
+
|
|
150
|
+
export const bridgeReadySchema = harnessV1BridgeReadySchema;
|
|
151
|
+
export type BridgeReady = z.infer<typeof bridgeReadySchema>;
|