@ai-sdk/harness-acp 1.0.39 → 1.0.40

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/dist/index.d.ts CHANGED
@@ -152,9 +152,10 @@ type ACPModelMapping = {
152
152
  readonly type: 'session-model';
153
153
  readonly path: string;
154
154
  };
155
- type ACPCredentialBrokering = ({ env, sandboxEnv, }: {
155
+ type ACPCredentialBrokering = ({ env, sandboxEnv, headers, }: {
156
156
  env: Readonly<Record<string, string>>;
157
157
  sandboxEnv?: Readonly<Record<string, string>>;
158
+ headers?: Readonly<Record<string, string>>;
158
159
  }) => ReadonlyArray<HarnessV1RequestTransformation>;
159
160
  type ACPPermissionModeTarget = {
160
161
  readonly type: 'session-mode';
@@ -177,11 +178,19 @@ type ACPInstructionMapping = {
177
178
  readonly type: 'launch-env-json';
178
179
  readonly variable: string;
179
180
  readonly path: ReadonlyArray<string>;
181
+ } | {
182
+ readonly type: 'filesystem';
183
+ readonly path: string;
180
184
  };
181
185
  type ACPOutputSchemaMapping = {
182
186
  readonly type: 'session-prompt-meta';
183
187
  readonly path: ReadonlyArray<string>;
184
188
  };
189
+ /**
190
+ * Transport used for the harness-owned MCP server that exposes host tools to
191
+ * an ACP implementation.
192
+ */
193
+ type ACPHostToolMCPTransport = 'stdio' | 'http';
185
194
  type ACPAskUserQuestionsSettings = {
186
195
  readonly requestMethod: string;
187
196
  readonly isNativeToolCall?: (options: {
@@ -252,6 +261,14 @@ type ACPV1Settings = {
252
261
  * below the ACP session prompt's `_meta` field.
253
262
  */
254
263
  readonly outputSchemaMapping?: ACPOutputSchemaMapping;
264
+ /**
265
+ * Transport used for the harness-owned MCP server that exposes host tools to
266
+ * the ACP implementation. Defaults to `stdio`. Set this to `http` for
267
+ * implementations that only accept HTTP or SSE MCP servers from the client,
268
+ * which requires the implementation to advertise
269
+ * `agentCapabilities.mcpCapabilities.http`.
270
+ */
271
+ readonly hostToolMcpTransport?: ACPHostToolMCPTransport;
255
272
  readonly askUserQuestions?: ACPAskUserQuestionsSettings;
256
273
  readonly permissionModeMapping?: ACPPermissionModeMapping;
257
274
  readonly session?: {
@@ -316,6 +333,14 @@ type ACPHarnessSettings<TBuiltinTools extends ToolSet = {}, TAskUserQuestions ex
316
333
  readonly skillsDirectory?: ACPV1Settings['skillsDirectory'];
317
334
  readonly instructionMapping?: ACPV1Settings['instructionMapping'];
318
335
  readonly outputSchemaMapping?: ACPV1Settings['outputSchemaMapping'];
336
+ /**
337
+ * Transport used for the harness-owned MCP server that exposes host tools to
338
+ * the ACP implementation. Defaults to `stdio`. Set this to `http` for
339
+ * implementations that only accept HTTP or SSE MCP servers from the client,
340
+ * which requires the implementation to advertise
341
+ * `agentCapabilities.mcpCapabilities.http`.
342
+ */
343
+ readonly hostToolMcpTransport?: ACPV1Settings['hostToolMcpTransport'];
319
344
  readonly askUserQuestions?: TAskUserQuestions;
320
345
  readonly permissionModeMapping?: ACPV1Settings['permissionModeMapping'];
321
346
  readonly session?: ACPV1Settings['session'];
@@ -328,4 +353,4 @@ declare function createACP<TBuiltinTools extends ToolSet = {}, TAskUserQuestions
328
353
 
329
354
  declare const VERSION: string;
330
355
 
331
- export { type ACPAnnotations, type ACPAskUserQuestionsSettings, 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 ACPModelMapping, 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 };
356
+ export { type ACPAnnotations, type ACPAskUserQuestionsSettings, type ACPAudioContent, type ACPAuthentication, type ACPAuthenticationMode, type ACPBlobResourceContents, type ACPClientApp, type ACPContentBlock, type ACPCredentialBrokering, type ACPEmbeddedResource, type ACPHarnessSettings, type ACPHostToolMCPTransport, type ACPImageContent, type ACPInstallCommandSource, type ACPInstructionMapping, type ACPMetadata, type ACPModelMapping, 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
@@ -29,7 +29,9 @@ import {
29
29
  shellQuote,
30
30
  warnCredentialBrokeringUnavailable,
31
31
  waitForBridgeReady,
32
- withBridgeToken
32
+ withBridgeToken,
33
+ writeInstructions,
34
+ writeSkills
33
35
  } from "@ai-sdk/harness/utils";
34
36
  import {
35
37
  asSchema
@@ -709,6 +711,10 @@ var instructionMappingSchema = z2.discriminatedUnion("type", [
709
711
  type: z2.literal("launch-env-json"),
710
712
  variable: z2.string().min(1),
711
713
  path: instructionMappingPathSchema
714
+ }),
715
+ z2.object({
716
+ type: z2.literal("filesystem"),
717
+ path: z2.string().min(1)
712
718
  })
713
719
  ]);
714
720
  var outputSchemaMappingSchema = z2.object({
@@ -825,6 +831,10 @@ var profileValueSchema = z3.lazy(
825
831
  z3.record(z3.string(), profileValueSchema)
826
832
  ])
827
833
  );
834
+ var hostToolMcpTransportSchema = z3.enum([
835
+ "stdio",
836
+ "http"
837
+ ]);
828
838
  var serializableRecordSchema = z3.record(z3.string(), serializableValueSchema);
829
839
  var profileRecordSchema = z3.record(z3.string(), profileValueSchema);
830
840
  var authenticationSchema = z3.object({
@@ -845,7 +855,8 @@ var bridgeConfigurationSchema = z3.object({
845
855
  providerEnvironment: z3.record(z3.string(), z3.string()).optional(),
846
856
  sessionMeta: serializableRecordSchema.optional(),
847
857
  clientCapabilities: serializableRecordSchema.optional(),
848
- askUserQuestionsRequestMethod: z3.string().optional()
858
+ askUserQuestionsRequestMethod: z3.string().optional(),
859
+ hostToolMcpTransport: hostToolMcpTransportSchema.optional()
849
860
  });
850
861
  function createACPBridgeEnvironment({
851
862
  authentication,
@@ -853,7 +864,8 @@ function createACPBridgeEnvironment({
853
864
  providerEnvironment,
854
865
  sessionMeta,
855
866
  clientCapabilities,
856
- askUserQuestionsRequestMethod
867
+ askUserQuestionsRequestMethod,
868
+ hostToolMcpTransport
857
869
  }) {
858
870
  return {
859
871
  [ACP_BRIDGE_CONFIGURATION_ENV]: JSON.stringify({
@@ -862,7 +874,8 @@ function createACPBridgeEnvironment({
862
874
  ...providerEnvironment == null ? {} : { providerEnvironment },
863
875
  ...sessionMeta == null ? {} : { sessionMeta },
864
876
  ...clientCapabilities == null ? {} : { clientCapabilities },
865
- ...askUserQuestionsRequestMethod == null ? {} : { askUserQuestionsRequestMethod }
877
+ ...askUserQuestionsRequestMethod == null ? {} : { askUserQuestionsRequestMethod },
878
+ ...hostToolMcpTransport == null ? {} : { hostToolMcpTransport }
866
879
  })
867
880
  };
868
881
  }
@@ -1085,51 +1098,25 @@ function validateACPLifecycleCompatibility({
1085
1098
  // src/v1/acp-v1-skills.ts
1086
1099
  import { createHash as createHash4 } from "crypto";
1087
1100
  import path from "path";
1088
- import { writeSkills } from "@ai-sdk/harness/utils";
1089
1101
  var ACP_SKILL_NAME_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
1090
1102
  var DEFAULT_ACP_SKILLS_DIRECTORY = ".agents/skills";
1091
1103
  function resolveACPPrivateSessionDirectory({
1092
1104
  sandboxHomeDir,
1093
- sessionWorkDir,
1094
1105
  harnessId,
1095
1106
  sessionId
1096
1107
  }) {
1097
1108
  const sessionKey = createHash4("sha256").update(sessionId).digest("hex");
1098
- const rootDir = path.posix.join(
1109
+ return path.posix.join(
1099
1110
  sandboxHomeDir,
1100
1111
  ".ai-sdk",
1101
1112
  "harness-acp",
1102
1113
  harnessId,
1103
1114
  sessionKey
1104
1115
  );
1105
- assertOutsideSessionWorkDir({ rootDir, sessionWorkDir });
1106
- return rootDir;
1107
- }
1108
- async function materializeACPSkills({
1109
- sandbox,
1110
- rootDir,
1111
- sessionWorkDir,
1112
- skills,
1113
- abortSignal
1114
- }) {
1115
- validateACPSkills({ skills });
1116
- assertOutsideSessionWorkDir({ rootDir, sessionWorkDir });
1117
- return writeSkills({
1118
- sandbox,
1119
- rootDir,
1120
- skills,
1121
- abortSignal,
1122
- skillNamePattern: ACP_SKILL_NAME_PATTERN,
1123
- invalidSkillNameMessage: ({ name }) => `Invalid ACP skill name ${JSON.stringify(name)}: expected a kebab-case slug.`,
1124
- invalidSkillFilePathMessage: ({ skillName, filePath }) => `Invalid ACP skill file path ${JSON.stringify(filePath)} for skill ${JSON.stringify(
1125
- skillName
1126
- )}: expected a relative POSIX path without traversal.`
1127
- });
1128
1116
  }
1129
1117
  function resolveACPSkillsDirectory({
1130
1118
  implementationHomeDir,
1131
- skillsDirectory = DEFAULT_ACP_SKILLS_DIRECTORY,
1132
- sessionWorkDir
1119
+ skillsDirectory = DEFAULT_ACP_SKILLS_DIRECTORY
1133
1120
  }) {
1134
1121
  const containsTraversal = skillsDirectory.split(/[\\/]/).some((segment) => segment === "..");
1135
1122
  const normalizedDirectory = path.posix.normalize(skillsDirectory);
@@ -1138,9 +1125,7 @@ function resolveACPSkillsDirectory({
1138
1125
  `ACP skillsDirectory ${JSON.stringify(skillsDirectory)} must be a relative POSIX path without traversal.`
1139
1126
  );
1140
1127
  }
1141
- const rootDir = path.posix.join(implementationHomeDir, normalizedDirectory);
1142
- assertOutsideSessionWorkDir({ rootDir, sessionWorkDir });
1143
- return rootDir;
1128
+ return path.posix.join(implementationHomeDir, normalizedDirectory);
1144
1129
  }
1145
1130
  function validateACPSkills({
1146
1131
  skills
@@ -1198,20 +1183,6 @@ function validateACPAttachedFilePath({
1198
1183
  }
1199
1184
  return normalizedPath;
1200
1185
  }
1201
- function assertOutsideSessionWorkDir({
1202
- rootDir,
1203
- sessionWorkDir
1204
- }) {
1205
- const relative = path.posix.relative(sessionWorkDir, rootDir);
1206
- const isInside = relative === "" || !relative.startsWith("../") && !path.posix.isAbsolute(relative);
1207
- if (isInside) {
1208
- throw new Error(
1209
- `ACP skill directory ${JSON.stringify(rootDir)} must be outside sessionWorkDir ${JSON.stringify(
1210
- sessionWorkDir
1211
- )}.`
1212
- );
1213
- }
1214
- }
1215
1186
 
1216
1187
  // src/v1/acp-v1-harness.ts
1217
1188
  var HARNESS_ID_REGEXP = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
@@ -1378,7 +1349,8 @@ function createACPV1({
1378
1349
  };
1379
1350
  const requestTransformations = settings.credentialBrokering({
1380
1351
  env: brokeringEnvironment,
1381
- sandboxEnv: sandboxImplementationEnvironment
1352
+ sandboxEnv: sandboxImplementationEnvironment,
1353
+ ...startOptions.headers == null ? {} : { headers: startOptions.headers }
1382
1354
  });
1383
1355
  if (requestTransformations.length > 0) {
1384
1356
  await sandboxSession.addRequestTransformations(
@@ -1416,14 +1388,14 @@ function createACPV1({
1416
1388
  });
1417
1389
  const privateSessionDir = resolveACPPrivateSessionDirectory({
1418
1390
  sandboxHomeDir,
1419
- sessionWorkDir: workDir,
1420
1391
  harnessId: settings.harnessId,
1421
1392
  sessionId: startOptions.sessionId
1422
1393
  });
1394
+ const implementationHomeDir = implementation.source.type === "install-command" ? `${resolvedImplementationDir}/home` : sandboxHomeDir;
1395
+ const skillsDir = settings.skillsDirectory ?? DEFAULT_ACP_SKILLS_DIRECTORY;
1423
1396
  const skillsDirectory = resolveACPSkillsDirectory({
1424
- implementationHomeDir: implementation.source.type === "install-command" ? `${resolvedImplementationDir}/home` : sandboxHomeDir,
1425
- skillsDirectory: settings.skillsDirectory,
1426
- sessionWorkDir: workDir
1397
+ implementationHomeDir,
1398
+ skillsDirectory: settings.skillsDirectory
1427
1399
  });
1428
1400
  const bridgeStateDir = `${privateSessionDir}/bridge`;
1429
1401
  const report = startOptions.observability?.report;
@@ -1495,7 +1467,8 @@ function createACPV1({
1495
1467
  }),
1496
1468
  instructionsFingerprint: lifecycleData.instructionsFingerprint,
1497
1469
  sandbox: toolSafeSandboxSession,
1498
- sessionWorkDir: workDir,
1470
+ homePath: implementationHomeDir,
1471
+ skillsDir,
1499
1472
  skillsDirectory,
1500
1473
  acpSessionId: lifecycleData.acpSessionId,
1501
1474
  bridgePort: coords.port,
@@ -1633,7 +1606,8 @@ function createACPV1({
1633
1606
  providerEnvironment: sandboxProviderEnvironment == null ? void 0 : {},
1634
1607
  sessionMeta: settings.session?.meta,
1635
1608
  clientCapabilities: settings.clientCapabilities,
1636
- askUserQuestionsRequestMethod: settings.askUserQuestions?.requestMethod
1609
+ askUserQuestionsRequestMethod: settings.askUserQuestions?.requestMethod,
1610
+ hostToolMcpTransport: settings.hostToolMcpTransport
1637
1611
  }),
1638
1612
  ...sandboxProviderAuthenticationEnvironment,
1639
1613
  BRIDGE_CHANNEL_TOKEN: token,
@@ -1756,7 +1730,8 @@ function createACPV1({
1756
1730
  }),
1757
1731
  instructionsFingerprint: lifecycleData?.instructionsFingerprint,
1758
1732
  sandbox: toolSafeSandboxSession,
1759
- sessionWorkDir: workDir,
1733
+ homePath: implementationHomeDir,
1734
+ skillsDir,
1760
1735
  skillsDirectory,
1761
1736
  acpSessionId: lifecycleData?.acpSessionId,
1762
1737
  bridgePort: boundPort,
@@ -1991,7 +1966,8 @@ function createSession({
1991
1966
  initialGuidanceApplied: initialGuidanceAppliedAtStart,
1992
1967
  instructionsFingerprint: instructionsFingerprintAtStart,
1993
1968
  sandbox,
1994
- sessionWorkDir,
1969
+ homePath,
1970
+ skillsDir,
1995
1971
  skillsDirectory,
1996
1972
  acpSessionId: acpSessionIdAtStart,
1997
1973
  bridgePort,
@@ -2407,14 +2383,34 @@ function createSession({
2407
2383
  skills,
2408
2384
  abortSignal
2409
2385
  }) => {
2410
- await materializeACPSkills({
2386
+ validateACPSkills({ skills });
2387
+ await writeSkills({
2411
2388
  sandbox,
2412
- rootDir: skillsDirectory,
2413
- sessionWorkDir,
2389
+ homePath,
2390
+ skillsDir,
2414
2391
  skills,
2415
- abortSignal
2392
+ abortSignal,
2393
+ skillNamePattern: ACP_SKILL_NAME_PATTERN,
2394
+ invalidSkillNameMessage: ({ name }) => `Invalid ACP skill name ${JSON.stringify(name)}: expected a kebab-case slug.`,
2395
+ invalidSkillFilePathMessage: ({ skillName, filePath }) => `Invalid ACP skill file path ${JSON.stringify(filePath)} for skill ${JSON.stringify(
2396
+ skillName
2397
+ )}: expected a relative POSIX path without traversal.`
2416
2398
  });
2417
2399
  };
2400
+ const synchronizeInstructions = async ({
2401
+ instructions,
2402
+ abortSignal
2403
+ }) => {
2404
+ if (instructionMapping?.type === "filesystem") {
2405
+ await writeInstructions({
2406
+ sandbox,
2407
+ homePath,
2408
+ instructionsFile: instructionMapping.path,
2409
+ instructions,
2410
+ abortSignal
2411
+ });
2412
+ }
2413
+ };
2418
2414
  return {
2419
2415
  sessionId,
2420
2416
  isResume,
@@ -2423,6 +2419,10 @@ function createSession({
2423
2419
  skills: options.skills,
2424
2420
  abortSignal: options.abortSignal
2425
2421
  });
2422
+ await synchronizeInstructions({
2423
+ instructions: options.instructions,
2424
+ abortSignal: options.abortSignal
2425
+ });
2426
2426
  if (options.responseFormat?.type === "json") {
2427
2427
  if (options.responseFormat.schema == null) {
2428
2428
  throw unsupported({
@@ -2481,7 +2481,7 @@ function createSession({
2481
2481
  turnInFlight = true;
2482
2482
  channel.send({
2483
2483
  type: "start",
2484
- prompt: instructionsFingerprint !== nextInstructionsFingerprint && (instructionMapping == null || initialGuidanceApplied) ? prependACPInstructionGuidance({
2484
+ prompt: instructionMapping?.type !== "filesystem" && instructionsFingerprint !== nextInstructionsFingerprint && (instructionMapping == null || initialGuidanceApplied) ? prependACPInstructionGuidance({
2485
2485
  prompt,
2486
2486
  instructions: options.instructions
2487
2487
  }) : prompt,
@@ -2512,6 +2512,10 @@ function createSession({
2512
2512
  skills: options.skills,
2513
2513
  abortSignal: options.abortSignal
2514
2514
  });
2515
+ await synchronizeInstructions({
2516
+ instructions: options.instructions,
2517
+ abortSignal: options.abortSignal
2518
+ });
2515
2519
  if (options.responseFormat?.type === "json") {
2516
2520
  if (options.responseFormat.schema == null) {
2517
2521
  throw unsupported({
@@ -2587,11 +2591,6 @@ function createSession({
2587
2591
  `${harnessId} ACP session ${sessionId} is stopped; cannot suspend.`
2588
2592
  );
2589
2593
  }
2590
- if (!turnInFlight) {
2591
- throw new Error(
2592
- `${harnessId} ACP session ${sessionId} has no in-flight turn to suspend.`
2593
- );
2594
- }
2595
2594
  stopped = true;
2596
2595
  const lastSeenEventId = await channel.suspend();
2597
2596
  return {
@@ -2841,7 +2840,7 @@ function deserializeBridgeError({
2841
2840
  }
2842
2841
 
2843
2842
  // src/version.ts
2844
- var VERSION = true ? "1.0.39" : "0.0.0-test";
2843
+ var VERSION = true ? "1.0.40" : "0.0.0-test";
2845
2844
 
2846
2845
  // src/acp-harness.ts
2847
2846
  var ACP_CLIENT_APP = {