@ai-sdk/harness-acp 1.0.28 → 1.0.30

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 CHANGED
@@ -1,5 +1,20 @@
1
1
  # @ai-sdk/harness-acp
2
2
 
3
+ ## 1.0.30
4
+
5
+ ### Patch Changes
6
+
7
+ - e0d7cfb: feat(harness): allow harness sessions to optionally authenticate from an isolated environment supplied through the `auth` option, and remove support for the formerly deprecated legacy auth options types
8
+ - Updated dependencies [e0d7cfb]
9
+ - @ai-sdk/harness@1.0.92
10
+
11
+ ## 1.0.29
12
+
13
+ ### Patch Changes
14
+
15
+ - 20312d6: fix(harness-acp): place skills in their harnesses' natively supported directory instead of applying a manual workaround
16
+ - @ai-sdk/harness@1.0.91
17
+
3
18
  ## 1.0.28
4
19
 
5
20
  ### Patch Changes
package/README.md CHANGED
@@ -103,3 +103,8 @@ instructions below the ACP session request's `_meta` field. A
103
103
  the implementation starts. Without a mapping, the adapter preserves its
104
104
  backward-compatible behavior and prepends instructions to the first user
105
105
  prompt.
106
+
107
+ Skills are written to `.agents/skills` below the ACP implementation's effective
108
+ `$HOME` and discovered natively by the implementation. Set `skillsDirectory`
109
+ to another relative path, such as `.claude/skills`, when required by the
110
+ implementation.
@@ -12,7 +12,7 @@ import {
12
12
  ListToolsRequestSchema,
13
13
  McpError
14
14
  } from "@modelcontextprotocol/sdk/types.js";
15
- var VERSION = true ? "1.0.28" : "0.0.0-test";
15
+ var VERSION = true ? "1.0.30" : "0.0.0-test";
16
16
  function createHostToolMCPServer({
17
17
  tools: tools2,
18
18
  revision = 1,
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { HarnessV1RequestTransformation, HarnessV1CredentialForwarding, HarnessV1PermissionMode, HarnessV1PortEndpoint, HarnessV1 } from '@ai-sdk/harness';
1
+ import { HarnessV1Authentication, HarnessV1RequestTransformation, HarnessV1CredentialForwarding, HarnessV1PermissionMode, HarnessV1PortEndpoint, HarnessV1 } from '@ai-sdk/harness';
2
2
  import { ToolSet } from '@ai-sdk/provider-utils';
3
3
 
4
4
  type ACPMetadata = Record<string, unknown>;
@@ -139,7 +139,7 @@ type ACPAuthentication = {
139
139
  readonly meta?: Readonly<Record<string, ACPSerializableValue>>;
140
140
  readonly clientCapabilities?: Readonly<Record<string, ACPSerializableValue>>;
141
141
  };
142
- type ACPProviderAuthenticationMode = 'auto' | 'direct' | 'ai-gateway';
142
+ type ACPAuthenticationMode$1 = HarnessV1Authentication;
143
143
  type ACPProviderAuthentication = {
144
144
  readonly gateway: {
145
145
  readonly env: Readonly<Record<string, ACPProfileValue>>;
@@ -180,7 +180,7 @@ type ACPV1Settings = {
180
180
  readonly harnessId: string;
181
181
  readonly mcpServers?: Record<string, unknown>;
182
182
  readonly isMcpToolCall?: (toolCall: ACPToolCall) => boolean;
183
- readonly auth?: ACPProviderAuthenticationMode;
183
+ readonly auth?: ACPAuthenticationMode$1;
184
184
  readonly source: ACPSource;
185
185
  readonly executable: string;
186
186
  readonly args?: ReadonlyArray<string>;
@@ -201,6 +201,11 @@ type ACPV1Settings = {
201
201
  readonly authentication?: ACPAuthentication;
202
202
  readonly providerAuthentication?: ACPProviderAuthentication;
203
203
  readonly modelId?: string;
204
+ /**
205
+ * Native skills directory relative to the ACP implementation's home
206
+ * directory. Defaults to `.agents/skills`.
207
+ */
208
+ readonly skillsDirectory?: string;
204
209
  /**
205
210
  * Routes HarnessAgent instructions to a runtime-native system or developer
206
211
  * prompt. When omitted, instructions are prepended to the first user prompt.
@@ -222,7 +227,7 @@ type ACPV1Settings = {
222
227
  readonly mintBridgeToken?: (sandboxId: string) => string;
223
228
  };
224
229
 
225
- type ACPAuthOptions = ACPProviderAuthenticationMode;
230
+ type ACPAuthenticationMode = ACPAuthenticationMode$1;
226
231
  type ACPClientApp = {
227
232
  readonly name: string;
228
233
  readonly version: string;
@@ -263,6 +268,7 @@ type ACPHarnessSettings<TBuiltinTools extends ToolSet = {}> = {
263
268
  readonly authentication?: ACPV1Settings['authentication'];
264
269
  readonly providerAuthentication?: ACPV1Settings['providerAuthentication'];
265
270
  readonly modelId?: ACPV1Settings['modelId'];
271
+ readonly skillsDirectory?: ACPV1Settings['skillsDirectory'];
266
272
  readonly instructionMapping?: ACPV1Settings['instructionMapping'];
267
273
  readonly outputSchemaMapping?: ACPV1Settings['outputSchemaMapping'];
268
274
  readonly permissionModeMapping?: ACPV1Settings['permissionModeMapping'];
@@ -273,4 +279,4 @@ declare function createACP<TBuiltinTools extends ToolSet = {}>(settings: ACPHarn
273
279
 
274
280
  declare const VERSION: string;
275
281
 
276
- export { type ACPAnnotations, type ACPAudioContent, type ACPAuthOptions, type ACPAuthentication, type ACPBlobResourceContents, type ACPClientApp, type ACPContentBlock, type ACPCredentialBrokering, type ACPEmbeddedResource, type ACPHarnessSettings, type ACPImageContent, type ACPInstallCommandSource, type ACPInstructionMapping, type ACPMetadata, type ACPNpmLockedSource, type ACPNpmSimpleSource, type ACPOutputSchemaMapping, type ACPPermissionModeMapping, type ACPPermissionModeTarget, type ACPProfileValue, type ACPProviderAuthentication, type ACPProviderAuthenticationMode, type ACPResourceLink, type ACPRole, type ACPSerializablePrimitive, type ACPSerializableValue, type ACPSource, type ACPTextContent, type ACPTextResourceContents, type ACPToolCall, type ACPToolCallContent, type ACPToolCallLocation, type ACPToolCallStatus, type ACPToolKind, type ACPV1Settings, type ACPValueSource, VERSION, createACP };
282
+ export { type ACPAnnotations, type ACPAudioContent, type ACPAuthentication, type ACPAuthenticationMode, type ACPBlobResourceContents, type ACPClientApp, type ACPContentBlock, type ACPCredentialBrokering, type ACPEmbeddedResource, type ACPHarnessSettings, type ACPImageContent, type ACPInstallCommandSource, type ACPInstructionMapping, type ACPMetadata, type ACPNpmLockedSource, type ACPNpmSimpleSource, type ACPOutputSchemaMapping, type ACPPermissionModeMapping, type ACPPermissionModeTarget, type ACPProfileValue, type ACPProviderAuthentication, type ACPResourceLink, type ACPRole, type ACPSerializablePrimitive, type ACPSerializableValue, type ACPSource, type ACPTextContent, type ACPTextResourceContents, type ACPToolCall, type ACPToolCallContent, type ACPToolCallLocation, type ACPToolCallStatus, type ACPToolKind, type ACPV1Settings, type ACPValueSource, VERSION, createACP };
package/dist/index.js CHANGED
@@ -33,7 +33,10 @@ import { WebSocket } from "ws";
33
33
 
34
34
  // src/acp-auth.ts
35
35
  import { createHash } from "crypto";
36
- import { getAiGatewayAuthFromEnv } from "@ai-sdk/harness/utils";
36
+ import {
37
+ getAiGatewayAuthFromEnv,
38
+ isHarnessAuthenticationEnvironment
39
+ } from "@ai-sdk/harness/utils";
37
40
  var DEFAULT_AI_GATEWAY_BASE_URL = "https://ai-gateway.vercel.sh";
38
41
  function createACPAuthenticationProfileIdentity({
39
42
  authentication,
@@ -67,6 +70,10 @@ function resolveACPProviderAuthentication({
67
70
  AI_SDK_ACP_CLIENT_APP_NAME: auth.clientApp.name,
68
71
  AI_SDK_ACP_CLIENT_APP_VERSION: auth.clientApp.version
69
72
  };
73
+ const resolvedEnv = resolveACPAuthenticationEnvironment({
74
+ auth: auth.mode,
75
+ env
76
+ });
70
77
  const providerAuthentication = auth.providerAuthentication;
71
78
  if (providerAuthentication == null) {
72
79
  return { providerAuthentication: void 0, env: clientAppEnv };
@@ -77,7 +84,7 @@ function resolveACPProviderAuthentication({
77
84
  env: clientAppEnv
78
85
  };
79
86
  }
80
- const mode = auth.mode ?? "auto";
87
+ const mode = resolveACPProviderAuthenticationMode(auth.mode);
81
88
  if (mode === "direct") {
82
89
  return {
83
90
  providerAuthentication: { type: "direct" },
@@ -86,11 +93,11 @@ function resolveACPProviderAuthentication({
86
93
  }
87
94
  const gateway = compatibility?.type === "ai-gateway" ? {
88
95
  apiKey: resolveGatewayCredential({
89
- env,
96
+ env: resolvedEnv,
90
97
  credentialSource: compatibility.credentialSource
91
98
  }),
92
99
  baseUrl: compatibility.baseUrl
93
- } : getAiGatewayAuthFromEnv({ env });
100
+ } : getAiGatewayAuthFromEnv({ env: resolvedEnv });
94
101
  const apiKey = gateway.apiKey;
95
102
  if (compatibility == null && mode === "auto" && apiKey == null) {
96
103
  return {
@@ -120,14 +127,16 @@ function resolveACPProviderAuthenticationCompatibility({
120
127
  providerAuthentication,
121
128
  env
122
129
  }) {
130
+ const resolvedEnv = resolveACPAuthenticationEnvironment({
131
+ auth,
132
+ env
133
+ });
123
134
  if (providerAuthentication == null) return void 0;
124
- const mode = auth;
135
+ const mode = resolveACPProviderAuthenticationMode(auth);
125
136
  if (mode === "direct") {
126
137
  return { type: "direct", mode };
127
138
  }
128
- const credentialSource = resolveGatewayCredentialSource({
129
- env
130
- });
139
+ const credentialSource = resolveGatewayCredentialSource({ env: resolvedEnv });
131
140
  if (mode === "auto" && credentialSource == null) {
132
141
  return { type: "direct", mode };
133
142
  }
@@ -136,9 +145,18 @@ function resolveACPProviderAuthenticationCompatibility({
136
145
  mode,
137
146
  env: providerAuthentication.gateway.env,
138
147
  credentialSource,
139
- baseUrl: env.AI_GATEWAY_BASE_URL ?? DEFAULT_AI_GATEWAY_BASE_URL
148
+ baseUrl: resolvedEnv.AI_GATEWAY_BASE_URL ?? DEFAULT_AI_GATEWAY_BASE_URL
140
149
  };
141
150
  }
151
+ function resolveACPProviderAuthenticationMode(auth) {
152
+ return typeof auth === "string" || auth == null ? auth ?? "auto" : "auto";
153
+ }
154
+ function resolveACPAuthenticationEnvironment({
155
+ auth,
156
+ env
157
+ }) {
158
+ return isHarnessAuthenticationEnvironment(auth) ? auth : env;
159
+ }
142
160
  function resolveGatewayCredentialSource({
143
161
  env
144
162
  }) {
@@ -366,18 +384,22 @@ ${implementation.source.command}
366
384
  }
367
385
  function resolveImplementationEnvironment({
368
386
  implementation,
369
- env
387
+ env,
388
+ credentialEnv = env
370
389
  }) {
371
390
  const forwardedEnvironment = {};
372
- for (const name of [
373
- ...implementation.forwardEnv ?? [],
374
- ...implementation.credentialEnv ?? []
375
- ]) {
391
+ for (const name of implementation.forwardEnv ?? []) {
376
392
  const value = env[name];
377
393
  if (value != null && value.length > 0) {
378
394
  forwardedEnvironment[name] = value;
379
395
  }
380
396
  }
397
+ for (const name of implementation.credentialEnv ?? []) {
398
+ const value = credentialEnv[name];
399
+ if (value != null && value.length > 0) {
400
+ forwardedEnvironment[name] = value;
401
+ }
402
+ }
381
403
  return {
382
404
  ...forwardedEnvironment,
383
405
  ...implementation.env
@@ -639,28 +661,15 @@ function convertHarnessPromptToACPTextBlocks({
639
661
  }
640
662
  function prependACPInitialGuidance({
641
663
  prompt,
642
- instructions,
643
- skills
664
+ instructions
644
665
  }) {
645
666
  const hasInstructions = instructions != null && instructions.length > 0;
646
- if (!hasInstructions && skills.length === 0) return [...prompt];
667
+ if (!hasInstructions) return [...prompt];
647
668
  const lines = [
648
669
  "<session-guidance>",
649
670
  "This block is operating guidance from the harness, not user-authored content."
650
671
  ];
651
- if (hasInstructions) {
652
- lines.push("<instructions>", instructions, "</instructions>");
653
- }
654
- if (skills.length > 0) {
655
- lines.push(
656
- "<available-skills>",
657
- "Load a skill only when relevant by reading its SKILL.md file at the listed absolute path."
658
- );
659
- for (const skill of skills) {
660
- lines.push(`- ${skill.name}: ${skill.description} (${skill.path})`);
661
- }
662
- lines.push("</available-skills>");
663
- }
672
+ lines.push("<instructions>", instructions, "</instructions>");
664
673
  lines.push("</session-guidance>");
665
674
  return [{ type: "text", text: lines.join("\n") }, ...prompt];
666
675
  }
@@ -1015,14 +1024,15 @@ function resolveACPInitialGuidanceApplied({
1015
1024
  function shouldMaterializeACPSkills({
1016
1025
  isResume,
1017
1026
  lifecycleState,
1018
- skillsFingerprint
1027
+ skillsFingerprint,
1028
+ skillsDirectory
1019
1029
  }) {
1020
1030
  if (isResume && lifecycleState?.skillsFingerprint != null && lifecycleState.skillsFingerprint !== skillsFingerprint) {
1021
1031
  throw new Error(
1022
1032
  "ACP lifecycle state was created with a different set of skills."
1023
1033
  );
1024
1034
  }
1025
- return !isResume || lifecycleState?.skillsMaterialized !== true;
1035
+ return !isResume || lifecycleState?.skillsMaterialized !== true || lifecycleState.skillsDirectory !== skillsDirectory;
1026
1036
  }
1027
1037
  function validateACPLifecycleCompatibility({
1028
1038
  harnessId,
@@ -1064,6 +1074,7 @@ import { createHash as createHash4 } from "crypto";
1064
1074
  import path from "path";
1065
1075
  import { writeSkills } from "@ai-sdk/harness/utils";
1066
1076
  var ACP_SKILL_NAME_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
1077
+ var DEFAULT_ACP_SKILLS_DIRECTORY = ".agents/skills";
1067
1078
  function createACPSkillsFingerprint({
1068
1079
  skills
1069
1080
  }) {
@@ -1101,24 +1112,14 @@ function resolveACPPrivateSessionDirectory({
1101
1112
  }
1102
1113
  async function materializeACPSkills({
1103
1114
  sandbox,
1104
- sandboxHomeDir,
1115
+ rootDir,
1105
1116
  sessionWorkDir,
1106
- harnessId,
1107
- sessionId,
1108
1117
  skills,
1109
1118
  shouldMaterialize,
1110
1119
  abortSignal
1111
1120
  }) {
1112
1121
  validateACPSkills({ skills });
1113
- const rootDir = path.posix.join(
1114
- resolveACPPrivateSessionDirectory({
1115
- sandboxHomeDir,
1116
- sessionWorkDir,
1117
- harnessId,
1118
- sessionId
1119
- }),
1120
- "skills"
1121
- );
1122
+ assertOutsideSessionWorkDir({ rootDir, sessionWorkDir });
1122
1123
  if (shouldMaterialize) {
1123
1124
  await writeSkills({
1124
1125
  sandbox,
@@ -1132,14 +1133,22 @@ async function materializeACPSkills({
1132
1133
  )}: expected a relative POSIX path without traversal.`
1133
1134
  });
1134
1135
  }
1135
- return {
1136
- rootDir,
1137
- catalog: skills.map((skill) => ({
1138
- name: skill.name,
1139
- description: skill.description,
1140
- path: path.posix.join(rootDir, skill.name, "SKILL.md")
1141
- }))
1142
- };
1136
+ }
1137
+ function resolveACPSkillsDirectory({
1138
+ implementationHomeDir,
1139
+ skillsDirectory = DEFAULT_ACP_SKILLS_DIRECTORY,
1140
+ sessionWorkDir
1141
+ }) {
1142
+ const containsTraversal = skillsDirectory.split(/[\\/]/).some((segment) => segment === "..");
1143
+ const normalizedDirectory = path.posix.normalize(skillsDirectory);
1144
+ if (skillsDirectory.length === 0 || skillsDirectory.includes("\\") || path.posix.isAbsolute(skillsDirectory) || path.win32.isAbsolute(skillsDirectory) || containsTraversal || normalizedDirectory === "." || normalizedDirectory.startsWith("../") || normalizedDirectory.includes("/../") || normalizedDirectory.endsWith("/..")) {
1145
+ throw new Error(
1146
+ `ACP skillsDirectory ${JSON.stringify(skillsDirectory)} must be a relative POSIX path without traversal.`
1147
+ );
1148
+ }
1149
+ const rootDir = path.posix.join(implementationHomeDir, normalizedDirectory);
1150
+ assertOutsideSessionWorkDir({ rootDir, sessionWorkDir });
1151
+ return rootDir;
1143
1152
  }
1144
1153
  function validateACPSkills({
1145
1154
  skills
@@ -1273,6 +1282,10 @@ function createACPV1({
1273
1282
  }
1274
1283
  const permissionMode = startOptions.permissionMode ?? "allow-all";
1275
1284
  const env = { ...process.env };
1285
+ const authenticationEnvironment = resolveACPAuthenticationEnvironment({
1286
+ auth: settings.auth,
1287
+ env
1288
+ });
1276
1289
  const providerAuthenticationCompatibility = resolveACPProviderAuthenticationCompatibility({
1277
1290
  auth: settings.auth,
1278
1291
  providerAuthentication: settings.providerAuthentication,
@@ -1335,7 +1348,8 @@ function createACPV1({
1335
1348
  }
1336
1349
  const implementationEnvironment = resolveImplementationEnvironment({
1337
1350
  implementation,
1338
- env
1351
+ env,
1352
+ credentialEnv: authenticationEnvironment
1339
1353
  });
1340
1354
  let sandboxImplementationEnvironment = implementationEnvironment;
1341
1355
  let sandboxProviderAuthenticationEnvironment = resolvedProviderAuthentication.env;
@@ -1420,26 +1434,29 @@ function createACPV1({
1420
1434
  harnessId: settings.harnessId,
1421
1435
  sessionId: startOptions.sessionId
1422
1436
  });
1437
+ const skillsDirectory = resolveACPSkillsDirectory({
1438
+ implementationHomeDir: implementation.source.type === "install-command" ? `${resolvedImplementationDir}/home` : sandboxHomeDir,
1439
+ skillsDirectory: settings.skillsDirectory,
1440
+ sessionWorkDir: workDir
1441
+ });
1423
1442
  const bridgeStateDir = `${privateSessionDir}/bridge`;
1424
1443
  const skills = startOptions.skills ?? [];
1425
1444
  const skillsFingerprint = createACPSkillsFingerprint({ skills });
1426
1445
  const shouldMaterializeSkills = shouldMaterializeACPSkills({
1427
1446
  isResume,
1428
1447
  lifecycleState: lifecycleData,
1429
- skillsFingerprint
1448
+ skillsFingerprint,
1449
+ skillsDirectory
1430
1450
  });
1431
- let skillCatalog = [];
1432
1451
  if (skills.length > 0) {
1433
- skillCatalog = (await materializeACPSkills({
1452
+ await materializeACPSkills({
1434
1453
  sandbox: toolSafeSandboxSession,
1435
- sandboxHomeDir,
1454
+ rootDir: skillsDirectory,
1436
1455
  sessionWorkDir: workDir,
1437
- harnessId: settings.harnessId,
1438
- sessionId: startOptions.sessionId,
1439
1456
  skills,
1440
1457
  shouldMaterialize: shouldMaterializeSkills,
1441
1458
  abortSignal: startOptions.abortSignal
1442
- })).catalog;
1459
+ });
1443
1460
  }
1444
1461
  const report = startOptions.observability?.report;
1445
1462
  const onDiagnostic = report ? (frame) => report(
@@ -1506,8 +1523,8 @@ function createACPV1({
1506
1523
  isResume: true,
1507
1524
  lifecycleState: lifecycleData
1508
1525
  }),
1509
- skillCatalog,
1510
1526
  skillsFingerprint,
1527
+ skillsDirectory,
1511
1528
  acpSessionId: lifecycleData.acpSessionId,
1512
1529
  bridgePort: coords.port,
1513
1530
  bridgeToken: coords.token,
@@ -1744,8 +1761,8 @@ function createACPV1({
1744
1761
  isResume,
1745
1762
  lifecycleState: lifecycleData
1746
1763
  }),
1747
- skillCatalog,
1748
1764
  skillsFingerprint,
1765
+ skillsDirectory,
1749
1766
  acpSessionId: lifecycleData?.acpSessionId,
1750
1767
  bridgePort: boundPort,
1751
1768
  bridgeToken: token,
@@ -1983,8 +2000,8 @@ function createSession({
1983
2000
  mcpServers,
1984
2001
  isMcpToolCall,
1985
2002
  initialGuidanceApplied: initialGuidanceAppliedAtStart,
1986
- skillCatalog,
1987
2003
  skillsFingerprint,
2004
+ skillsDirectory,
1988
2005
  acpSessionId: acpSessionIdAtStart,
1989
2006
  bridgePort,
1990
2007
  bridgeToken,
@@ -2245,7 +2262,8 @@ function createSession({
2245
2262
  ...restoration == null ? {} : { restoration },
2246
2263
  initialGuidanceApplied,
2247
2264
  skillsMaterialized: true,
2248
- skillsFingerprint
2265
+ skillsFingerprint,
2266
+ skillsDirectory
2249
2267
  });
2250
2268
  return {
2251
2269
  sessionId,
@@ -2306,8 +2324,7 @@ function createSession({
2306
2324
  type: "start",
2307
2325
  prompt: initialGuidanceApplied ? prompt : prependACPInitialGuidance({
2308
2326
  prompt,
2309
- instructions: instructionMapping == null ? options.instructions : void 0,
2310
- skills: skillCatalog
2327
+ instructions: instructionMapping == null ? options.instructions : void 0
2311
2328
  }),
2312
2329
  ...instructionMapping == null ? {} : {
2313
2330
  instructionMapping,
@@ -2611,7 +2628,7 @@ function deserializeBridgeError({
2611
2628
  }
2612
2629
 
2613
2630
  // src/version.ts
2614
- var VERSION = true ? "1.0.28" : "0.0.0-test";
2631
+ var VERSION = true ? "1.0.30" : "0.0.0-test";
2615
2632
 
2616
2633
  // src/acp-harness.ts
2617
2634
  var ACP_CLIENT_APP = {
@@ -2651,7 +2668,8 @@ var acpResumeStateSchema = z4.object({
2651
2668
  }).optional(),
2652
2669
  initialGuidanceApplied: z4.boolean().optional(),
2653
2670
  skillsMaterialized: z4.boolean().optional(),
2654
- skillsFingerprint: z4.string().optional()
2671
+ skillsFingerprint: z4.string().optional(),
2672
+ skillsDirectory: z4.string().optional()
2655
2673
  });
2656
2674
  function createACP(settings) {
2657
2675
  if (settings.credentialEnv == null !== (settings.credentialBrokering == null)) {