@ai-sdk/harness-acp 1.0.28 → 1.0.29

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,12 @@
1
1
  # @ai-sdk/harness-acp
2
2
 
3
+ ## 1.0.29
4
+
5
+ ### Patch Changes
6
+
7
+ - 20312d6: fix(harness-acp): place skills in their harnesses' natively supported directory instead of applying a manual workaround
8
+ - @ai-sdk/harness@1.0.91
9
+
3
10
  ## 1.0.28
4
11
 
5
12
  ### 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.29" : "0.0.0-test";
16
16
  function createHostToolMCPServer({
17
17
  tools: tools2,
18
18
  revision = 1,
package/dist/index.d.ts CHANGED
@@ -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.
@@ -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'];
package/dist/index.js CHANGED
@@ -639,28 +639,15 @@ function convertHarnessPromptToACPTextBlocks({
639
639
  }
640
640
  function prependACPInitialGuidance({
641
641
  prompt,
642
- instructions,
643
- skills
642
+ instructions
644
643
  }) {
645
644
  const hasInstructions = instructions != null && instructions.length > 0;
646
- if (!hasInstructions && skills.length === 0) return [...prompt];
645
+ if (!hasInstructions) return [...prompt];
647
646
  const lines = [
648
647
  "<session-guidance>",
649
648
  "This block is operating guidance from the harness, not user-authored content."
650
649
  ];
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
- }
650
+ lines.push("<instructions>", instructions, "</instructions>");
664
651
  lines.push("</session-guidance>");
665
652
  return [{ type: "text", text: lines.join("\n") }, ...prompt];
666
653
  }
@@ -1015,14 +1002,15 @@ function resolveACPInitialGuidanceApplied({
1015
1002
  function shouldMaterializeACPSkills({
1016
1003
  isResume,
1017
1004
  lifecycleState,
1018
- skillsFingerprint
1005
+ skillsFingerprint,
1006
+ skillsDirectory
1019
1007
  }) {
1020
1008
  if (isResume && lifecycleState?.skillsFingerprint != null && lifecycleState.skillsFingerprint !== skillsFingerprint) {
1021
1009
  throw new Error(
1022
1010
  "ACP lifecycle state was created with a different set of skills."
1023
1011
  );
1024
1012
  }
1025
- return !isResume || lifecycleState?.skillsMaterialized !== true;
1013
+ return !isResume || lifecycleState?.skillsMaterialized !== true || lifecycleState.skillsDirectory !== skillsDirectory;
1026
1014
  }
1027
1015
  function validateACPLifecycleCompatibility({
1028
1016
  harnessId,
@@ -1064,6 +1052,7 @@ import { createHash as createHash4 } from "crypto";
1064
1052
  import path from "path";
1065
1053
  import { writeSkills } from "@ai-sdk/harness/utils";
1066
1054
  var ACP_SKILL_NAME_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
1055
+ var DEFAULT_ACP_SKILLS_DIRECTORY = ".agents/skills";
1067
1056
  function createACPSkillsFingerprint({
1068
1057
  skills
1069
1058
  }) {
@@ -1101,24 +1090,14 @@ function resolveACPPrivateSessionDirectory({
1101
1090
  }
1102
1091
  async function materializeACPSkills({
1103
1092
  sandbox,
1104
- sandboxHomeDir,
1093
+ rootDir,
1105
1094
  sessionWorkDir,
1106
- harnessId,
1107
- sessionId,
1108
1095
  skills,
1109
1096
  shouldMaterialize,
1110
1097
  abortSignal
1111
1098
  }) {
1112
1099
  validateACPSkills({ skills });
1113
- const rootDir = path.posix.join(
1114
- resolveACPPrivateSessionDirectory({
1115
- sandboxHomeDir,
1116
- sessionWorkDir,
1117
- harnessId,
1118
- sessionId
1119
- }),
1120
- "skills"
1121
- );
1100
+ assertOutsideSessionWorkDir({ rootDir, sessionWorkDir });
1122
1101
  if (shouldMaterialize) {
1123
1102
  await writeSkills({
1124
1103
  sandbox,
@@ -1132,14 +1111,22 @@ async function materializeACPSkills({
1132
1111
  )}: expected a relative POSIX path without traversal.`
1133
1112
  });
1134
1113
  }
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
- };
1114
+ }
1115
+ function resolveACPSkillsDirectory({
1116
+ implementationHomeDir,
1117
+ skillsDirectory = DEFAULT_ACP_SKILLS_DIRECTORY,
1118
+ sessionWorkDir
1119
+ }) {
1120
+ const containsTraversal = skillsDirectory.split(/[\\/]/).some((segment) => segment === "..");
1121
+ const normalizedDirectory = path.posix.normalize(skillsDirectory);
1122
+ if (skillsDirectory.length === 0 || skillsDirectory.includes("\\") || path.posix.isAbsolute(skillsDirectory) || path.win32.isAbsolute(skillsDirectory) || containsTraversal || normalizedDirectory === "." || normalizedDirectory.startsWith("../") || normalizedDirectory.includes("/../") || normalizedDirectory.endsWith("/..")) {
1123
+ throw new Error(
1124
+ `ACP skillsDirectory ${JSON.stringify(skillsDirectory)} must be a relative POSIX path without traversal.`
1125
+ );
1126
+ }
1127
+ const rootDir = path.posix.join(implementationHomeDir, normalizedDirectory);
1128
+ assertOutsideSessionWorkDir({ rootDir, sessionWorkDir });
1129
+ return rootDir;
1143
1130
  }
1144
1131
  function validateACPSkills({
1145
1132
  skills
@@ -1420,26 +1407,29 @@ function createACPV1({
1420
1407
  harnessId: settings.harnessId,
1421
1408
  sessionId: startOptions.sessionId
1422
1409
  });
1410
+ const skillsDirectory = resolveACPSkillsDirectory({
1411
+ implementationHomeDir: implementation.source.type === "install-command" ? `${resolvedImplementationDir}/home` : sandboxHomeDir,
1412
+ skillsDirectory: settings.skillsDirectory,
1413
+ sessionWorkDir: workDir
1414
+ });
1423
1415
  const bridgeStateDir = `${privateSessionDir}/bridge`;
1424
1416
  const skills = startOptions.skills ?? [];
1425
1417
  const skillsFingerprint = createACPSkillsFingerprint({ skills });
1426
1418
  const shouldMaterializeSkills = shouldMaterializeACPSkills({
1427
1419
  isResume,
1428
1420
  lifecycleState: lifecycleData,
1429
- skillsFingerprint
1421
+ skillsFingerprint,
1422
+ skillsDirectory
1430
1423
  });
1431
- let skillCatalog = [];
1432
1424
  if (skills.length > 0) {
1433
- skillCatalog = (await materializeACPSkills({
1425
+ await materializeACPSkills({
1434
1426
  sandbox: toolSafeSandboxSession,
1435
- sandboxHomeDir,
1427
+ rootDir: skillsDirectory,
1436
1428
  sessionWorkDir: workDir,
1437
- harnessId: settings.harnessId,
1438
- sessionId: startOptions.sessionId,
1439
1429
  skills,
1440
1430
  shouldMaterialize: shouldMaterializeSkills,
1441
1431
  abortSignal: startOptions.abortSignal
1442
- })).catalog;
1432
+ });
1443
1433
  }
1444
1434
  const report = startOptions.observability?.report;
1445
1435
  const onDiagnostic = report ? (frame) => report(
@@ -1506,8 +1496,8 @@ function createACPV1({
1506
1496
  isResume: true,
1507
1497
  lifecycleState: lifecycleData
1508
1498
  }),
1509
- skillCatalog,
1510
1499
  skillsFingerprint,
1500
+ skillsDirectory,
1511
1501
  acpSessionId: lifecycleData.acpSessionId,
1512
1502
  bridgePort: coords.port,
1513
1503
  bridgeToken: coords.token,
@@ -1744,8 +1734,8 @@ function createACPV1({
1744
1734
  isResume,
1745
1735
  lifecycleState: lifecycleData
1746
1736
  }),
1747
- skillCatalog,
1748
1737
  skillsFingerprint,
1738
+ skillsDirectory,
1749
1739
  acpSessionId: lifecycleData?.acpSessionId,
1750
1740
  bridgePort: boundPort,
1751
1741
  bridgeToken: token,
@@ -1983,8 +1973,8 @@ function createSession({
1983
1973
  mcpServers,
1984
1974
  isMcpToolCall,
1985
1975
  initialGuidanceApplied: initialGuidanceAppliedAtStart,
1986
- skillCatalog,
1987
1976
  skillsFingerprint,
1977
+ skillsDirectory,
1988
1978
  acpSessionId: acpSessionIdAtStart,
1989
1979
  bridgePort,
1990
1980
  bridgeToken,
@@ -2245,7 +2235,8 @@ function createSession({
2245
2235
  ...restoration == null ? {} : { restoration },
2246
2236
  initialGuidanceApplied,
2247
2237
  skillsMaterialized: true,
2248
- skillsFingerprint
2238
+ skillsFingerprint,
2239
+ skillsDirectory
2249
2240
  });
2250
2241
  return {
2251
2242
  sessionId,
@@ -2306,8 +2297,7 @@ function createSession({
2306
2297
  type: "start",
2307
2298
  prompt: initialGuidanceApplied ? prompt : prependACPInitialGuidance({
2308
2299
  prompt,
2309
- instructions: instructionMapping == null ? options.instructions : void 0,
2310
- skills: skillCatalog
2300
+ instructions: instructionMapping == null ? options.instructions : void 0
2311
2301
  }),
2312
2302
  ...instructionMapping == null ? {} : {
2313
2303
  instructionMapping,
@@ -2611,7 +2601,7 @@ function deserializeBridgeError({
2611
2601
  }
2612
2602
 
2613
2603
  // src/version.ts
2614
- var VERSION = true ? "1.0.28" : "0.0.0-test";
2604
+ var VERSION = true ? "1.0.29" : "0.0.0-test";
2615
2605
 
2616
2606
  // src/acp-harness.ts
2617
2607
  var ACP_CLIENT_APP = {
@@ -2651,7 +2641,8 @@ var acpResumeStateSchema = z4.object({
2651
2641
  }).optional(),
2652
2642
  initialGuidanceApplied: z4.boolean().optional(),
2653
2643
  skillsMaterialized: z4.boolean().optional(),
2654
- skillsFingerprint: z4.string().optional()
2644
+ skillsFingerprint: z4.string().optional(),
2645
+ skillsDirectory: z4.string().optional()
2655
2646
  });
2656
2647
  function createACP(settings) {
2657
2648
  if (settings.credentialEnv == null !== (settings.credentialBrokering == null)) {