@ai-sdk/harness-acp 0.0.0 → 1.0.1

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.
Files changed (49) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/LICENSE +13 -0
  3. package/README.md +69 -3
  4. package/dist/bridge/host-tool-mcp.mjs +302 -0
  5. package/dist/bridge/host-tool-mcp.mjs.map +1 -0
  6. package/dist/bridge/index.mjs +3317 -0
  7. package/dist/bridge/index.mjs.map +1 -0
  8. package/dist/bridge/package.json +12 -0
  9. package/dist/bridge/pnpm-lock.yaml +816 -0
  10. package/dist/index.d.ts +104 -0
  11. package/dist/index.js +2089 -0
  12. package/dist/index.js.map +1 -0
  13. package/package.json +64 -10
  14. package/src/acp-auth.ts +254 -0
  15. package/src/acp-bridge-protocol.ts +13 -0
  16. package/src/acp-harness.ts +107 -0
  17. package/src/index.ts +19 -0
  18. package/src/v1/acp-v1-bridge-protocol.ts +151 -0
  19. package/src/v1/acp-v1-harness.ts +1461 -0
  20. package/src/v1/acp-v1-lifecycle.ts +115 -0
  21. package/src/v1/acp-v1-prompt.ts +122 -0
  22. package/src/v1/acp-v1-settings.ts +97 -0
  23. package/src/v1/acp-v1-skills.ts +228 -0
  24. package/src/v1/acp-v1-turn-start-config.ts +93 -0
  25. package/src/v1/bridge/acp-diagnostics.ts +83 -0
  26. package/src/v1/bridge/acp-stream-capture.ts +152 -0
  27. package/src/v1/bridge/acp-v1-bridge-environment.ts +95 -0
  28. package/src/v1/bridge/agent-stderr-monitor.ts +52 -0
  29. package/src/v1/bridge/create-emit-stream-event.ts +97 -0
  30. package/src/v1/bridge/host-tool-correlation.ts +640 -0
  31. package/src/v1/bridge/host-tool-mcp-server.ts +180 -0
  32. package/src/v1/bridge/host-tool-mcp.ts +185 -0
  33. package/src/v1/bridge/host-tool-relay-client.ts +53 -0
  34. package/src/v1/bridge/host-tool-relay.ts +510 -0
  35. package/src/v1/bridge/index.ts +714 -0
  36. package/src/v1/bridge/package.json +12 -0
  37. package/src/v1/bridge/permission-controller.ts +152 -0
  38. package/src/v1/bridge/permission-mode.ts +163 -0
  39. package/src/v1/bridge/pnpm-lock.yaml +816 -0
  40. package/src/v1/bridge/profile-values.ts +97 -0
  41. package/src/v1/bridge/protocol-configuration.ts +146 -0
  42. package/src/v1/bridge/recovered-session.ts +145 -0
  43. package/src/v1/bridge/refresh-host-tool-catalog.ts +33 -0
  44. package/src/v1/bridge/session-lifecycle.ts +83 -0
  45. package/src/v1/bridge/stream-translator.ts +901 -0
  46. package/src/v1/implementation.ts +308 -0
  47. package/src/v1/index.ts +16 -0
  48. package/src/version.ts +6 -0
  49. package/index.js +0 -1
@@ -0,0 +1,308 @@
1
+ import { createHash } from 'node:crypto';
2
+ import type {
3
+ ACPClientApp,
4
+ ACPProviderAuthenticationCompatibility,
5
+ } from '../acp-auth';
6
+ import type {
7
+ ACPNpmSimpleSource,
8
+ ACPPermissionModeMapping,
9
+ ACPSource,
10
+ ACPV1Settings,
11
+ } from './acp-v1-settings';
12
+
13
+ /*
14
+ * Launch descriptor assembled from the launch-related settings fields. The
15
+ * fields are declared here rather than derived from ACPV1Settings so that the
16
+ * settings type stays free to describe the configuration surface while this
17
+ * type describes what the bootstrap, install, and identity logic consume. The
18
+ * two shapes are required to agree, and createACPV1Implementation is the only
19
+ * place that maps between them.
20
+ */
21
+ export type ACPNpmImplementation = {
22
+ readonly source: ACPSource;
23
+ readonly executable: string;
24
+ readonly args?: ReadonlyArray<string>;
25
+ readonly forwardEnv?: ReadonlyArray<string>;
26
+ readonly env?: Readonly<Record<string, string>>;
27
+ };
28
+
29
+ export function createACPV1Implementation({
30
+ settings,
31
+ }: {
32
+ settings: ACPV1Settings;
33
+ }): ACPNpmImplementation {
34
+ return {
35
+ source: settings.source,
36
+ executable: settings.executable,
37
+ args: settings.args,
38
+ forwardEnv: settings.forwardEnv,
39
+ env: settings.env,
40
+ };
41
+ }
42
+
43
+ const EXACT_SEMVER_REGEXP =
44
+ /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
45
+ const PACKAGE_NAME_REGEXP =
46
+ /^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/;
47
+ const EXECUTABLE_NAME_REGEXP = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
48
+ const ENVIRONMENT_VARIABLE_NAME_REGEXP = /^[A-Za-z_][A-Za-z0-9_]*$/;
49
+
50
+ export function validateACPV1Implementation(
51
+ implementation: ACPNpmImplementation,
52
+ ): void {
53
+ const { source } = implementation;
54
+ if (source.type === 'npm-locked') {
55
+ if (source.packageJson.length === 0) {
56
+ throw new Error('ACP source.packageJson must not be empty.');
57
+ }
58
+ if (source.pnpmLockYaml.length === 0) {
59
+ throw new Error('ACP source.pnpmLockYaml must not be empty.');
60
+ }
61
+ } else {
62
+ validateNpmSimpleSource({ source });
63
+ }
64
+ if (!EXECUTABLE_NAME_REGEXP.test(implementation.executable)) {
65
+ throw new Error(
66
+ `ACP executable must be a package bin name without a path; received ${JSON.stringify(implementation.executable)}.`,
67
+ );
68
+ }
69
+
70
+ validateForwardEnvironment({ forwardEnv: implementation.forwardEnv });
71
+ validateEnvironment({ env: implementation.env });
72
+ const forwardedKeys = new Set(implementation.forwardEnv ?? []);
73
+ for (const key of Object.keys(implementation.env ?? {})) {
74
+ if (forwardedKeys.has(key)) {
75
+ throw new Error(
76
+ `ACP runtime environment key ${JSON.stringify(key)} cannot be configured in both forwardEnv and env.`,
77
+ );
78
+ }
79
+ }
80
+ }
81
+
82
+ export function createImplementationManifest({
83
+ implementation,
84
+ }: {
85
+ implementation: ACPNpmImplementation;
86
+ }): string {
87
+ const { source } = implementation;
88
+ if (source.type === 'npm-locked') {
89
+ return source.packageJson;
90
+ }
91
+ return (
92
+ JSON.stringify(
93
+ {
94
+ name: 'harness-acp-implementation',
95
+ version: '0.0.0',
96
+ private: true,
97
+ type: 'module',
98
+ dependencies: {
99
+ [source.packageName]: source.packageVersion ?? 'latest',
100
+ },
101
+ },
102
+ null,
103
+ 2,
104
+ ) + '\n'
105
+ );
106
+ }
107
+
108
+ export function getImplementationLockfile({
109
+ implementation,
110
+ }: {
111
+ implementation: ACPNpmImplementation;
112
+ }): string | undefined {
113
+ const { source } = implementation;
114
+ return source.type === 'npm-locked' ? source.pnpmLockYaml : undefined;
115
+ }
116
+
117
+ export function createImplementationDescriptor({
118
+ implementation,
119
+ }: {
120
+ implementation: ACPNpmImplementation;
121
+ }): string {
122
+ return (
123
+ JSON.stringify(
124
+ {
125
+ executable: implementation.executable,
126
+ args: implementation.args ?? [],
127
+ envKeys: getImplementationEnvironmentKeys({ implementation }),
128
+ },
129
+ null,
130
+ 2,
131
+ ) + '\n'
132
+ );
133
+ }
134
+
135
+ export function createImplementationIdentity({
136
+ harnessId,
137
+ acpVersion,
138
+ implementation,
139
+ clientApp,
140
+ providerAuthentication,
141
+ permissionModeMapping,
142
+ }: {
143
+ harnessId: string;
144
+ acpVersion: 'v1';
145
+ implementation: ACPNpmImplementation;
146
+ clientApp: ACPClientApp;
147
+ providerAuthentication: ACPProviderAuthenticationCompatibility | undefined;
148
+ permissionModeMapping?: ACPPermissionModeMapping;
149
+ }): string {
150
+ const { source } = implementation;
151
+ const sourceIdentity =
152
+ source.type === 'npm-locked'
153
+ ? {
154
+ type: source.type,
155
+ packageJson: source.packageJson,
156
+ pnpmLockYaml: source.pnpmLockYaml,
157
+ }
158
+ : {
159
+ type: source.type,
160
+ packageName: source.packageName,
161
+ ...(source.packageVersion == null
162
+ ? {}
163
+ : { packageVersion: source.packageVersion }),
164
+ };
165
+ const forwardedEnvironment = [
166
+ ...new Set(implementation.forwardEnv ?? []),
167
+ ].sort();
168
+ const literalEnvironment = Object.fromEntries(
169
+ Object.entries(implementation.env ?? {})
170
+ .sort(([left], [right]) => left.localeCompare(right))
171
+ .map(([key, value]) => [key, { value }]),
172
+ );
173
+ const payload = {
174
+ harnessId,
175
+ acpVersion,
176
+ source: sourceIdentity,
177
+ executable: implementation.executable,
178
+ args: implementation.args ?? [],
179
+ clientApp,
180
+ environment: {
181
+ forwarded: forwardedEnvironment,
182
+ literal: literalEnvironment,
183
+ },
184
+ providerAuthentication: providerAuthentication ?? null,
185
+ permissionModeMapping: permissionModeMapping ?? null,
186
+ };
187
+ return createHash('sha256')
188
+ .update(stableStringify({ value: payload }))
189
+ .digest('hex');
190
+ }
191
+
192
+ export function createImplementationInstallCommand({
193
+ implementationDir,
194
+ storeDir,
195
+ implementation,
196
+ }: {
197
+ implementationDir: string;
198
+ storeDir: string;
199
+ implementation: ACPNpmImplementation;
200
+ }): string {
201
+ return (
202
+ `pnpm --dir ${implementationDir} install` +
203
+ (implementation.source.type === 'npm-locked' ? ' --frozen-lockfile' : '') +
204
+ ` --prod --store-dir ${storeDir}`
205
+ );
206
+ }
207
+
208
+ export function resolveImplementationEnvironment({
209
+ implementation,
210
+ env,
211
+ }: {
212
+ implementation: ACPNpmImplementation;
213
+ env: Readonly<Record<string, string | undefined>>;
214
+ }): Record<string, string> {
215
+ const forwardedEnvironment: Record<string, string> = {};
216
+ for (const name of implementation.forwardEnv ?? []) {
217
+ const value = env[name];
218
+ if (value != null && value.length > 0) {
219
+ forwardedEnvironment[name] = value;
220
+ }
221
+ }
222
+ return {
223
+ ...forwardedEnvironment,
224
+ ...implementation.env,
225
+ };
226
+ }
227
+
228
+ function validateNpmSimpleSource({
229
+ source,
230
+ }: {
231
+ source: ACPNpmSimpleSource;
232
+ }): void {
233
+ if (!PACKAGE_NAME_REGEXP.test(source.packageName)) {
234
+ throw new Error(
235
+ `ACP npm package name is invalid: ${JSON.stringify(source.packageName)}.`,
236
+ );
237
+ }
238
+ if (
239
+ source.packageVersion != null &&
240
+ !EXACT_SEMVER_REGEXP.test(source.packageVersion)
241
+ ) {
242
+ throw new Error(
243
+ `ACP npm package version must be an exact semantic version; received ${JSON.stringify(source.packageVersion)}.`,
244
+ );
245
+ }
246
+ }
247
+
248
+ function validateEnvironment({
249
+ env,
250
+ }: {
251
+ env: Readonly<Record<string, string>> | undefined;
252
+ }): void {
253
+ for (const [key, value] of Object.entries(env ?? {})) {
254
+ if (!ENVIRONMENT_VARIABLE_NAME_REGEXP.test(key)) {
255
+ throw new Error(
256
+ `ACP environment variable name is invalid: ${JSON.stringify(key)}.`,
257
+ );
258
+ }
259
+ if (value.includes('\0')) {
260
+ throw new Error(`ACP runtime environment value for ${key} contains NUL.`);
261
+ }
262
+ }
263
+ }
264
+
265
+ function validateForwardEnvironment({
266
+ forwardEnv,
267
+ }: {
268
+ forwardEnv: ReadonlyArray<string> | undefined;
269
+ }): void {
270
+ for (const name of forwardEnv ?? []) {
271
+ if (!ENVIRONMENT_VARIABLE_NAME_REGEXP.test(name)) {
272
+ throw new Error(
273
+ `ACP environment variable name is invalid: ${JSON.stringify(name)}.`,
274
+ );
275
+ }
276
+ }
277
+ }
278
+
279
+ function getImplementationEnvironmentKeys({
280
+ implementation,
281
+ }: {
282
+ implementation: ACPNpmImplementation;
283
+ }): string[] {
284
+ return [
285
+ ...new Set([
286
+ ...(implementation.forwardEnv ?? []),
287
+ ...Object.keys(implementation.env ?? {}),
288
+ ]),
289
+ ].sort();
290
+ }
291
+
292
+ function stableStringify({ value }: { value: unknown }): string {
293
+ return JSON.stringify(sortValue({ value }));
294
+ }
295
+
296
+ function sortValue({ value }: { value: unknown }): unknown {
297
+ if (Array.isArray(value)) {
298
+ return value.map(item => sortValue({ value: item }));
299
+ }
300
+ if (value != null && typeof value === 'object') {
301
+ return Object.fromEntries(
302
+ Object.entries(value)
303
+ .sort(([left], [right]) => left.localeCompare(right))
304
+ .map(([key, item]) => [key, sortValue({ value: item })]),
305
+ );
306
+ }
307
+ return value;
308
+ }
@@ -0,0 +1,16 @@
1
+ export { createACPV1 } from './acp-v1-harness';
2
+ export type {
3
+ ACPAuthentication,
4
+ ACPNpmLockedSource,
5
+ ACPNpmSimpleSource,
6
+ ACPPermissionModeMapping,
7
+ ACPPermissionModeTarget,
8
+ ACPProfileValue,
9
+ ACPProviderAuthentication,
10
+ ACPProviderAuthenticationMode,
11
+ ACPSerializablePrimitive,
12
+ ACPSerializableValue,
13
+ ACPSource,
14
+ ACPV1Settings,
15
+ ACPValueSource,
16
+ } from './acp-v1-settings';
package/src/version.ts ADDED
@@ -0,0 +1,6 @@
1
+ declare const __PACKAGE_VERSION__: string | undefined;
2
+
3
+ export const VERSION: string =
4
+ typeof __PACKAGE_VERSION__ !== 'undefined'
5
+ ? __PACKAGE_VERSION__
6
+ : '0.0.0-test';
package/index.js DELETED
@@ -1 +0,0 @@
1
- export {};