@adcp/sdk 16.0.0-beta.6 → 16.0.0-beta.8

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 (137) hide show
  1. package/README.md +17 -0
  2. package/dist/lib/conformance/index.d.mts +2 -0
  3. package/dist/lib/conformance/index.d.ts +2 -0
  4. package/dist/lib/conformance/index.js +3 -0
  5. package/dist/lib/conformance/index.mjs +2 -0
  6. package/dist/lib/conformance/property-lists.d.mts +14 -0
  7. package/dist/lib/conformance/property-lists.d.ts +14 -0
  8. package/dist/lib/conformance/property-lists.js +45 -0
  9. package/dist/lib/conformance/property-lists.mjs +21 -0
  10. package/dist/lib/conformance/runConformance.js +20 -1
  11. package/dist/lib/conformance/runConformance.mjs +20 -1
  12. package/dist/lib/conformance/types.d.mts +3 -0
  13. package/dist/lib/conformance/types.d.ts +3 -0
  14. package/dist/lib/core/SingleAgentClient.d.mts +13 -3
  15. package/dist/lib/core/SingleAgentClient.d.ts +13 -3
  16. package/dist/lib/core/SingleAgentClient.js +109 -21
  17. package/dist/lib/core/SingleAgentClient.mjs +112 -22
  18. package/dist/lib/core/TaskExecutor.d.mts +1 -0
  19. package/dist/lib/core/TaskExecutor.d.ts +1 -0
  20. package/dist/lib/core/TaskExecutor.js +48 -34
  21. package/dist/lib/core/TaskExecutor.mjs +49 -35
  22. package/dist/lib/identifiers.d.mts +53 -0
  23. package/dist/lib/identifiers.d.ts +53 -0
  24. package/dist/lib/identifiers.js +212 -0
  25. package/dist/lib/identifiers.mjs +163 -0
  26. package/dist/lib/identity-3.3/revocation.js +5 -4
  27. package/dist/lib/identity-3.3/revocation.mjs +5 -4
  28. package/dist/lib/index.d.mts +3 -1
  29. package/dist/lib/index.d.ts +3 -1
  30. package/dist/lib/index.js +2 -0
  31. package/dist/lib/index.mjs +1 -0
  32. package/dist/lib/media-buy/index.d.mts +1 -1
  33. package/dist/lib/media-buy/index.d.ts +1 -1
  34. package/dist/lib/media-buy/property-policy.d.mts +15 -1
  35. package/dist/lib/media-buy/property-policy.d.ts +15 -1
  36. package/dist/lib/media-buy/property-policy.js +135 -3
  37. package/dist/lib/media-buy/property-policy.mjs +134 -3
  38. package/dist/lib/registry/index.d.mts +12 -0
  39. package/dist/lib/registry/index.d.ts +12 -0
  40. package/dist/lib/registry/index.js +159 -5
  41. package/dist/lib/registry/index.mjs +156 -4
  42. package/dist/lib/registry/types.d.mts +23 -0
  43. package/dist/lib/registry/types.d.ts +23 -0
  44. package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
  45. package/dist/lib/server/auth-signature.js +23 -2
  46. package/dist/lib/server/auth-signature.mjs +26 -2
  47. package/dist/lib/server/auth.d.mts +6 -0
  48. package/dist/lib/server/auth.d.ts +6 -0
  49. package/dist/lib/server/auth.js +6 -0
  50. package/dist/lib/server/auth.mjs +6 -0
  51. package/dist/lib/server/create-adcp-server.js +19 -3
  52. package/dist/lib/server/create-adcp-server.mjs +23 -4
  53. package/dist/lib/server/decisioning/runtime/from-platform.js +6 -2
  54. package/dist/lib/server/decisioning/runtime/from-platform.mjs +6 -2
  55. package/dist/lib/server/decisioning/specialisms/lists.d.mts +4 -4
  56. package/dist/lib/server/decisioning/specialisms/lists.d.ts +4 -4
  57. package/dist/lib/server/index.d.mts +3 -0
  58. package/dist/lib/server/index.d.ts +3 -0
  59. package/dist/lib/server/index.js +8 -1
  60. package/dist/lib/server/index.mjs +4 -0
  61. package/dist/lib/server/property-lists.d.mts +36 -0
  62. package/dist/lib/server/property-lists.d.ts +36 -0
  63. package/dist/lib/server/property-lists.js +154 -0
  64. package/dist/lib/server/property-lists.mjs +129 -0
  65. package/dist/lib/server/serve.js +2 -1
  66. package/dist/lib/server/serve.mjs +2 -1
  67. package/dist/lib/server/targeting-helpers.d.mts +2 -0
  68. package/dist/lib/server/targeting-helpers.d.ts +2 -0
  69. package/dist/lib/server/targeting-helpers.js +28 -1
  70. package/dist/lib/server/targeting-helpers.mjs +18 -1
  71. package/dist/lib/server/webhook-emitter.js +1 -0
  72. package/dist/lib/server/webhook-emitter.mjs +2 -1
  73. package/dist/lib/signing/crypto.d.mts +1 -1
  74. package/dist/lib/signing/crypto.d.ts +1 -1
  75. package/dist/lib/signing/crypto.js +37 -2
  76. package/dist/lib/signing/crypto.mjs +37 -2
  77. package/dist/lib/signing/parser.d.mts +6 -0
  78. package/dist/lib/signing/parser.d.ts +6 -0
  79. package/dist/lib/signing/parser.js +7 -3
  80. package/dist/lib/signing/parser.mjs +6 -3
  81. package/dist/lib/signing/server.d.mts +1 -1
  82. package/dist/lib/signing/server.d.ts +1 -1
  83. package/dist/lib/signing/server.mjs +5 -1
  84. package/dist/lib/signing/web-bot-auth.d.mts +61 -22
  85. package/dist/lib/signing/web-bot-auth.d.ts +61 -22
  86. package/dist/lib/signing/web-bot-auth.js +286 -266
  87. package/dist/lib/signing/web-bot-auth.mjs +288 -277
  88. package/dist/lib/signing/webhook-verifier.d.mts +11 -0
  89. package/dist/lib/signing/webhook-verifier.d.ts +11 -0
  90. package/dist/lib/signing/webhook-verifier.js +11 -3
  91. package/dist/lib/signing/webhook-verifier.mjs +11 -3
  92. package/dist/lib/supply-path/verify.js +12 -2
  93. package/dist/lib/supply-path/verify.mjs +2 -2
  94. package/dist/lib/testing/client.js +3 -5
  95. package/dist/lib/testing/client.mjs +4 -6
  96. package/dist/lib/testing/compliance/comply.js +7 -2
  97. package/dist/lib/testing/compliance/comply.mjs +8 -3
  98. package/dist/lib/testing/index.d.mts +1 -1
  99. package/dist/lib/testing/index.d.ts +1 -1
  100. package/dist/lib/testing/index.js +4 -2
  101. package/dist/lib/testing/index.mjs +8 -2
  102. package/dist/lib/testing/storyboard/runner.js +12 -12
  103. package/dist/lib/testing/storyboard/runner.mjs +14 -13
  104. package/dist/lib/testing/storyboard/validations.d.mts +1 -1
  105. package/dist/lib/testing/storyboard/validations.d.ts +1 -1
  106. package/dist/lib/testing/storyboard/validations.js +23 -1
  107. package/dist/lib/testing/storyboard/validations.mjs +28 -2
  108. package/dist/lib/testing/types.d.mts +39 -0
  109. package/dist/lib/testing/types.d.ts +39 -0
  110. package/dist/lib/v2/projection/augment-response.js +6 -3
  111. package/dist/lib/v2/projection/augment-response.mjs +8 -5
  112. package/dist/lib/v2/projection/creative-delivery.js +29 -17
  113. package/dist/lib/v2/projection/creative-delivery.mjs +31 -18
  114. package/dist/lib/v2/projection/v1-to-v2.d.mts +7 -0
  115. package/dist/lib/v2/projection/v1-to-v2.d.ts +7 -0
  116. package/dist/lib/v2/projection/v1-to-v2.js +70 -2
  117. package/dist/lib/v2/projection/v1-to-v2.mjs +72 -1
  118. package/dist/lib/validation/index.d.mts +1 -1
  119. package/dist/lib/validation/index.d.ts +1 -1
  120. package/dist/lib/validation/index.js +4 -2
  121. package/dist/lib/validation/index.mjs +4 -2
  122. package/dist/lib/validation/schema-loader.d.mts +26 -0
  123. package/dist/lib/validation/schema-loader.d.ts +26 -0
  124. package/dist/lib/validation/schema-loader.js +98 -14
  125. package/dist/lib/validation/schema-loader.mjs +95 -13
  126. package/dist/lib/version.d.mts +3 -3
  127. package/dist/lib/version.d.ts +3 -3
  128. package/dist/lib/version.js +3 -3
  129. package/dist/lib/version.mjs +3 -3
  130. package/docs/TYPE-SUMMARY.md +2 -2
  131. package/docs/guides/PROPERTY-LISTS.md +264 -0
  132. package/docs/guides/SIGNING-GUIDE.md +25 -0
  133. package/docs/guides/VALIDATE-YOUR-AGENT.md +34 -0
  134. package/docs/llms.txt +2 -2
  135. package/docs/migration-14.x-rc-worksheet.md +4 -4
  136. package/docs/migration-agent-identity-3.3.md +60 -23
  137. package/package.json +1 -1
@@ -22,6 +22,7 @@
22
22
  * so failures can point at a specific spec clause. Numbers are 1-based to
23
23
  * match the security doc.
24
24
  */
25
+ import { type SfBinaryEncoding } from './content-digest.mjs';
25
26
  import type { JwksResolver } from './jwks.mjs';
26
27
  import { type PublisherSigningKeyPin } from './publisher-pins.mjs';
27
28
  import { type ReplayStore } from './replay.mjs';
@@ -43,6 +44,16 @@ export interface WebhookRequestLike {
43
44
  export declare const WEBHOOK_MANDATORY_COMPONENTS: ReadonlyArray<string>;
44
45
  export interface VerifyWebhookOptions {
45
46
  jwks: JwksResolver;
47
+ /**
48
+ * Trusted receiver configuration for the Signature byte sequence.
49
+ * Defaults to `legacy-base64url`, as the AdCP webhook-v1 profile requires
50
+ * throughout 3.x. Set `rfc8941-base64` only for an explicitly configured
51
+ * peer emitting standard padded Base64 (for example, a generic RFC 9421
52
+ * request signer with the webhook tag). There is no encoding fallback.
53
+ * URL canonicalization remains AdCP 3.2 in either mode; Signature encoding
54
+ * does not identify a webhook's AdCP version.
55
+ */
56
+ signatureEncoding?: SfBinaryEncoding;
46
57
  /** Applicable publishers from the verifier's own media-buy record. */
47
58
  publisherPins?: readonly PublisherSigningKeyPin[];
48
59
  /** Log the underlying request_signature_* discovery cause locally. */
@@ -22,6 +22,7 @@
22
22
  * so failures can point at a specific spec clause. Numbers are 1-based to
23
23
  * match the security doc.
24
24
  */
25
+ import { type SfBinaryEncoding } from './content-digest';
25
26
  import type { JwksResolver } from './jwks';
26
27
  import { type PublisherSigningKeyPin } from './publisher-pins';
27
28
  import { type ReplayStore } from './replay';
@@ -43,6 +44,16 @@ export interface WebhookRequestLike {
43
44
  export declare const WEBHOOK_MANDATORY_COMPONENTS: ReadonlyArray<string>;
44
45
  export interface VerifyWebhookOptions {
45
46
  jwks: JwksResolver;
47
+ /**
48
+ * Trusted receiver configuration for the Signature byte sequence.
49
+ * Defaults to `legacy-base64url`, as the AdCP webhook-v1 profile requires
50
+ * throughout 3.x. Set `rfc8941-base64` only for an explicitly configured
51
+ * peer emitting standard padded Base64 (for example, a generic RFC 9421
52
+ * request signer with the webhook tag). There is no encoding fallback.
53
+ * URL canonicalization remains AdCP 3.2 in either mode; Signature encoding
54
+ * does not identify a webhook's AdCP version.
55
+ */
56
+ signatureEncoding?: SfBinaryEncoding;
46
57
  /** Applicable publishers from the verifier's own media-buy record. */
47
58
  publisherPins?: readonly PublisherSigningKeyPin[];
48
59
  /** Log the underlying request_signature_* discovery cause locally. */
@@ -50,6 +50,7 @@ const WEBHOOK_MANDATORY_COMPONENTS = [
50
50
  "content-digest"
51
51
  ];
52
52
  async function verifyWebhookSignature(request, options) {
53
+ const signatureEncoding = resolveSignatureEncoding(options.signatureEncoding);
53
54
  const currentTime = options.now ?? (() => Date.now() / 1e3);
54
55
  const now = currentTime();
55
56
  const requiredTag = options.requiredTag ?? WEBHOOK_SIGNING_TAG;
@@ -66,12 +67,12 @@ async function verifyWebhookSignature(request, options) {
66
67
  let parsedSig;
67
68
  try {
68
69
  parsedInput = (0, import_parser.parseSignatureInput)(sigInputHeader);
69
- parsedSig = (0, import_parser.parseSignature)(sigHeader, parsedInput.label);
70
+ parsedSig = (0, import_parser.parseSignature)(sigHeader, parsedInput.label, signatureEncoding);
70
71
  } catch (err) {
71
72
  throw new import_errors.WebhookSignatureError(
72
73
  "webhook_signature_header_malformed",
73
74
  1,
74
- err instanceof Error ? err.message : String(err)
75
+ `${err instanceof Error ? err.message : String(err)} (webhook signatureEncoding="${signatureEncoding}").`
75
76
  );
76
77
  }
77
78
  requireParams(parsedInput);
@@ -239,6 +240,12 @@ async function verifyWebhookSignature(request, options) {
239
240
  verified_at: Math.floor(now)
240
241
  };
241
242
  }
243
+ function resolveSignatureEncoding(encoding = "legacy-base64url") {
244
+ if (encoding !== "legacy-base64url" && encoding !== "rfc8941-base64") {
245
+ throw new TypeError('signatureEncoding must be "legacy-base64url" or "rfc8941-base64".');
246
+ }
247
+ return encoding;
248
+ }
242
249
  function assertDelegatedOperatorAuthorizationActive(validUntil, now) {
243
250
  if (validUntil !== void 0 && (!Number.isFinite(validUntil) || now >= validUntil)) {
244
251
  throw new import_errors.WebhookSignatureError(
@@ -345,9 +352,10 @@ function isWebhookLoopbackHost(hostname) {
345
352
  return LOOPBACK_IPV4.test(normalized);
346
353
  }
347
354
  function createWebhookVerifier(options) {
355
+ const signatureEncoding = resolveSignatureEncoding(options.signatureEncoding);
348
356
  const replayStore = options.replayStore ?? new import_replay.InMemoryReplayStore();
349
357
  const revocationStore = options.revocationStore ?? new import_revocation.InMemoryRevocationStore();
350
- return (request) => verifyWebhookSignature(request, { ...options, replayStore, revocationStore });
358
+ return (request) => verifyWebhookSignature(request, { ...options, signatureEncoding, replayStore, revocationStore });
351
359
  }
352
360
  // Annotate the CommonJS export names for ESM import in node:
353
361
  0 && (module.exports = {
@@ -23,6 +23,7 @@ const WEBHOOK_MANDATORY_COMPONENTS = [
23
23
  "content-digest"
24
24
  ];
25
25
  async function verifyWebhookSignature(request, options) {
26
+ const signatureEncoding = resolveSignatureEncoding(options.signatureEncoding);
26
27
  const currentTime = options.now ?? (() => Date.now() / 1e3);
27
28
  const now = currentTime();
28
29
  const requiredTag = options.requiredTag ?? WEBHOOK_SIGNING_TAG;
@@ -39,12 +40,12 @@ async function verifyWebhookSignature(request, options) {
39
40
  let parsedSig;
40
41
  try {
41
42
  parsedInput = parseSignatureInput(sigInputHeader);
42
- parsedSig = parseSignature(sigHeader, parsedInput.label);
43
+ parsedSig = parseSignature(sigHeader, parsedInput.label, signatureEncoding);
43
44
  } catch (err) {
44
45
  throw new WebhookSignatureError(
45
46
  "webhook_signature_header_malformed",
46
47
  1,
47
- err instanceof Error ? err.message : String(err)
48
+ `${err instanceof Error ? err.message : String(err)} (webhook signatureEncoding="${signatureEncoding}").`
48
49
  );
49
50
  }
50
51
  requireParams(parsedInput);
@@ -212,6 +213,12 @@ async function verifyWebhookSignature(request, options) {
212
213
  verified_at: Math.floor(now)
213
214
  };
214
215
  }
216
+ function resolveSignatureEncoding(encoding = "legacy-base64url") {
217
+ if (encoding !== "legacy-base64url" && encoding !== "rfc8941-base64") {
218
+ throw new TypeError('signatureEncoding must be "legacy-base64url" or "rfc8941-base64".');
219
+ }
220
+ return encoding;
221
+ }
215
222
  function assertDelegatedOperatorAuthorizationActive(validUntil, now) {
216
223
  if (validUntil !== void 0 && (!Number.isFinite(validUntil) || now >= validUntil)) {
217
224
  throw new WebhookSignatureError(
@@ -318,9 +325,10 @@ function isWebhookLoopbackHost(hostname) {
318
325
  return LOOPBACK_IPV4.test(normalized);
319
326
  }
320
327
  function createWebhookVerifier(options) {
328
+ const signatureEncoding = resolveSignatureEncoding(options.signatureEncoding);
321
329
  const replayStore = options.replayStore ?? new InMemoryReplayStore();
322
330
  const revocationStore = options.revocationStore ?? new InMemoryRevocationStore();
323
- return (request) => verifyWebhookSignature(request, { ...options, replayStore, revocationStore });
331
+ return (request) => verifyWebhookSignature(request, { ...options, signatureEncoding, replayStore, revocationStore });
324
332
  }
325
333
  export {
326
334
  WEBHOOK_MANDATORY_COMPONENTS,
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
  var verify_exports = {};
20
30
  __export(verify_exports, {
@@ -25,7 +35,6 @@ module.exports = __toCommonJS(verify_exports);
25
35
  var import_abort = require('../protocols/abort.js');
26
36
  var import_publisher_property_selector = require('../discovery/publisher-property-selector.js');
27
37
  var import_validation = require('./validation.js');
28
- var import_registry = require('../registry/index.js');
29
38
  var import_fetch_evidence = require('./fetch-evidence.js');
30
39
  var import_evaluate = require('./evaluate.js');
31
40
  var import_validation2 = require('./validation.js');
@@ -34,7 +43,8 @@ const MAX_PROPERTY_SELECTOR_EXPANSIONS = 4096;
34
43
  async function verifySupplyPath(request, options = { source: "authoritative" }) {
35
44
  const normalized = (0, import_validation2.validateSupplyPathRequest)(request);
36
45
  if (options.source === "registry") {
37
- const result = await (options.registry ?? new import_registry.RegistryClient()).verifySupplyPath(normalized);
46
+ const registry = options.registry ?? new (await import("../registry/index.js")).RegistryClient();
47
+ const result = await registry.verifySupplyPath(normalized);
38
48
  (0, import_validation2.assertRegistrySupplyPathResult)(result, normalized);
39
49
  return result;
40
50
  }
@@ -4,7 +4,6 @@ import {
4
4
  expandPublisherPropertySelector
5
5
  } from "../discovery/publisher-property-selector.mjs";
6
6
  import { domain, records, strings } from "./validation.mjs";
7
- import { RegistryClient } from "../registry/index.mjs";
8
7
  import { SupplyPathEvidenceSession } from "./fetch-evidence.mjs";
9
8
  import {
10
9
  isUnqualifiedPropertySelector,
@@ -20,7 +19,8 @@ const MAX_PROPERTY_SELECTOR_EXPANSIONS = 4096;
20
19
  async function verifySupplyPath(request, options = { source: "authoritative" }) {
21
20
  const normalized = validateSupplyPathRequest(request);
22
21
  if (options.source === "registry") {
23
- const result = await (options.registry ?? new RegistryClient()).verifySupplyPath(normalized);
22
+ const registry = options.registry ?? new (await import("../registry/index.mjs")).RegistryClient();
23
+ const result = await registry.verifySupplyPath(normalized);
24
24
  assertRegistrySupplyPathResult(result, normalized);
25
25
  return result;
26
26
  }
@@ -530,12 +530,10 @@ async function discoverAgentProfile(client, signal, schemaAdcpVersion) {
530
530
  step.error = versionUnsupported;
531
531
  } else if (caps?.data) {
532
532
  profile.raw_capabilities = caps.data;
533
- const validation = (0, import_client_hooks.validateIncomingResponse)(
534
- "get_adcp_capabilities",
533
+ const validation = (0, import_schema_loader.withServedValidationVersion)(
535
534
  caps.data,
536
- "strict",
537
- void 0,
538
- (0, import_schema_loader.resolveServedValidationVersion)(caps.data, schemaAdcpVersion ?? client.getAdcpVersion())
535
+ schemaAdcpVersion ?? client.getAdcpVersion(),
536
+ (validationVersion) => (0, import_client_hooks.validateIncomingResponse)("get_adcp_capabilities", caps.data, "strict", void 0, validationVersion)
539
537
  );
540
538
  if (!validation.valid) {
541
539
  profile.capabilities_schema_issues = validation.issues.map((issue) => ({
@@ -12,7 +12,7 @@ import { normalizeTransportOptions } from "../protocols/index.mjs";
12
12
  import { createHmac, hkdfSync, randomBytes } from "node:crypto";
13
13
  import { AsyncLocalStorage } from "node:async_hooks";
14
14
  import { validateIncomingResponse } from "../validation/client-hooks.mjs";
15
- import { resolveServedValidationVersion } from "../validation/schema-loader.mjs";
15
+ import { withServedValidationVersion } from "../validation/schema-loader.mjs";
16
16
  import { extractVersionUnsupportedDetails } from "../utils/error-extraction.mjs";
17
17
  import { buildAgentSigningContext } from "../signing/client.mjs";
18
18
  import { bindAgentStorage } from "../auth/oauth/storage-registry.mjs";
@@ -487,12 +487,10 @@ async function discoverAgentProfile(client, signal, schemaAdcpVersion) {
487
487
  step.error = versionUnsupported;
488
488
  } else if (caps?.data) {
489
489
  profile.raw_capabilities = caps.data;
490
- const validation = validateIncomingResponse(
491
- "get_adcp_capabilities",
490
+ const validation = withServedValidationVersion(
492
491
  caps.data,
493
- "strict",
494
- void 0,
495
- resolveServedValidationVersion(caps.data, schemaAdcpVersion ?? client.getAdcpVersion())
492
+ schemaAdcpVersion ?? client.getAdcpVersion(),
493
+ (validationVersion) => validateIncomingResponse("get_adcp_capabilities", caps.data, "strict", void 0, validationVersion)
496
494
  );
497
495
  if (!validation.valid) {
498
496
  profile.capabilities_schema_issues = validation.issues.map((issue) => ({
@@ -1358,7 +1358,12 @@ async function complyImpl(agentUrl, options) {
1358
1358
  }
1359
1359
  (0, import_test_kit.validateTestKit)(testOptions.test_kit);
1360
1360
  const signal = externalSignal;
1361
- return await (0, import_schema_loader.withExternalSchemaRoot)(complianceIndex.adcp_version, scopedSchemaRoot, async () => {
1361
+ return await (0, import_schema_loader.withExternalSchemaRoot)(
1362
+ complianceIndex.adcp_version,
1363
+ scopedSchemaRoot,
1364
+ () => (0, import_schema_loader.withServedReleaseSchemaRoots)(testOptions.servedReleaseSchemaRoots, () => complyInSchemaScope())
1365
+ );
1366
+ async function complyInSchemaScope() {
1362
1367
  let effectiveOptions = (0, import_runner.applyAdcpVersionRunOptions)(complianceIndex.adcp_version, {
1363
1368
  ...testOptions,
1364
1369
  // `comply()` has always defaulted an unset transport to MCP when it built
@@ -1667,7 +1672,7 @@ async function complyImpl(agentUrl, options) {
1667
1672
  total_duration_ms: Date.now() - start,
1668
1673
  notices: noticesDedup
1669
1674
  };
1670
- });
1675
+ }
1671
1676
  }
1672
1677
  function hasComplyTimeoutBudgetExpired(start, timeout_ms, now = Date.now()) {
1673
1678
  return timeout_ms !== void 0 && now - start >= timeout_ms;
@@ -35,7 +35,7 @@ import { closeScopedConnections, withMCPConnectionScope } from "../../protocols/
35
35
  import { detectController, hasTestController } from "../test-controller.mjs";
36
36
  import { randomBytes } from "crypto";
37
37
  import { isPre31AdcpVersion } from "../../utils/adcp-version-config.mjs";
38
- import { withExternalSchemaRoot } from "../../validation/schema-loader.mjs";
38
+ import { withExternalSchemaRoot, withServedReleaseSchemaRoots } from "../../validation/schema-loader.mjs";
39
39
  import { redactOAuthUrlForOutput, redactOAuthUrlsInText } from "../storyboard/oauth-metadata-graph/index.mjs";
40
40
  import { LIBRARY_VERSION } from "../../version.mjs";
41
41
  import { validationFailsStep } from "../storyboard/validations.mjs";
@@ -1348,7 +1348,12 @@ async function complyImpl(agentUrl, options) {
1348
1348
  }
1349
1349
  validateTestKit(testOptions.test_kit);
1350
1350
  const signal = externalSignal;
1351
- return await withExternalSchemaRoot(complianceIndex.adcp_version, scopedSchemaRoot, async () => {
1351
+ return await withExternalSchemaRoot(
1352
+ complianceIndex.adcp_version,
1353
+ scopedSchemaRoot,
1354
+ () => withServedReleaseSchemaRoots(testOptions.servedReleaseSchemaRoots, () => complyInSchemaScope())
1355
+ );
1356
+ async function complyInSchemaScope() {
1352
1357
  let effectiveOptions = applyAdcpVersionRunOptions(complianceIndex.adcp_version, {
1353
1358
  ...testOptions,
1354
1359
  // `comply()` has always defaulted an unset transport to MCP when it built
@@ -1657,7 +1662,7 @@ async function complyImpl(agentUrl, options) {
1657
1662
  total_duration_ms: Date.now() - start,
1658
1663
  notices: noticesDedup
1659
1664
  };
1660
- });
1665
+ }
1661
1666
  }
1662
1667
  function hasComplyTimeoutBudgetExpired(start, timeout_ms, now = Date.now()) {
1663
1668
  return timeout_ms !== void 0 && now - start >= timeout_ms;
@@ -16,5 +16,5 @@ export { isSandboxRequest, mergeSeededProductsIntoResponse, filterValidSeededPro
16
16
  export type { TestControllerBridge, TestControllerBridgeContext, BridgeFromSessionStoreOptions, SeededCreative, SeededMediaBuy, SeededAccountFinancials, } from '../server/test-controller-bridge.mjs';
17
17
  export { runAgainstLocalAgent } from './local-agent-runner.mjs';
18
18
  export type { LocalAgentRunResult, PerStoryboardOverride, RunAgainstLocalAgentOptions } from './local-agent-runner.mjs';
19
- export { registerExternalSchemaRoot, unregisterExternalSchemaRoot, withExternalSchemaRoot } from '../validation/index.mjs';
19
+ export { registerExternalSchemaRoot, unregisterExternalSchemaRoot, withExternalSchemaRoot, withServedReleaseSchemaRoots, } from '../validation/index.mjs';
20
20
  export { runStoryboard, runStoryboardStep, applyAdcpVersionRunOptions, applyStoryboardVersionOptions, getFirstStepPreview, parseStoryboard, loadStoryboardFile, normalizeValidationOnlyTasks, VALIDATION_ONLY_TASK, applyFixtureBindingsToRequest, buildFixtureResolutionSpecs, FixtureBindingRegistry, matchesFixtureRequirements, normalizeFixtureMatchExpression, validateFixtureResolutionDeclarations, getComplianceCacheDir, loadComplianceIndex, listBundles, loadBundleStoryboards, listAllComplianceStoryboards, getComplianceStoryboardById, findBundleById, resolveBundleOrStoryboard, resolveStoryboardsForCapabilities, isComplianceVersionSupported, CapabilityResolutionError, TASK_TO_METHOD, executeStoryboardTask, expandCreativeAssetDirectives, findUnresolvedCreativeAssetDirectives, extractContext, injectContext, runValidations, resolvePath, RateLimitTripObserver, RATE_LIMIT_TRIP_CONTRACT, RATE_LIMIT_TRIP_DEFAULT_REPLAY_MAX_WAIT_SECONDS, RATE_LIMIT_TRIP_MAX_ATTEMPTS_MAX, RATE_LIMIT_TRIP_MAX_ATTEMPTS_MIN, validateRateLimitTripSpec, getSandboxEntities, getSandboxBrands, getSandboxBrand, isSandboxDomain, clearSandboxCache, registerAssertion, getAssertion, listAssertions, listDefaultAssertions, clearAssertionRegistry, resolveAssertions, type AssertionSpec, type AssertionContext, type AssertionResult, type RegisterAssertionOptions, type Storyboard, type StoryboardFixtureResolution, type FixtureResolutionStrategy, type FixtureMatchOperator, type FixtureMatchClause, type FixtureResolutionDeclaration, type ProductFixtureResolutionDeclaration, type PricingOptionFixtureResolutionDeclaration, type FixtureResolutionStatus, type FixtureResolutionRecord, type FixtureResolutionAttempt, type FixtureResolutionEvidence, type FixtureResolutionCoverageGap, type StoryboardInvariants, type StoryboardInvariantsObject, type StepInvariantsObject, type StoryboardPhase, type StoryboardStep, type RateLimitTripSpec, type StoryboardValidation, type RateLimitTripClient, type RateLimitTripFailureCode, type RateLimitTripObservation, type RateLimitTripObserverOptions, type RateLimitTripResponseSnapshot, type RateLimitTripStructuredResult, type RateLimitTripTaskOptions, type StoryboardContext, type StoryboardRunOptions, type TrustedMatchContextProviderEndpoint, type TrustedMatchContextRegistrationContext, type TrustedMatchContextRouterRunnerOptions, type TrustedMatchPublisherAuthOperation, type TrustedMatchPublisherCredentialState, type TrustedMatchPublisherAuthTls, type TrustedMatchPublisherAuthProbeConfiguration, type TrustedMatchPublisherAuthRunner, type ValidationResult, type StoryboardStepPreview, type StoryboardStepResult, type StoryboardStepHint, type StoryboardStepHintBase, type ContextValueRejectedHint, type ShapeDriftHint, type MissingRequiredFieldHint, type FormatMismatchHint, type MonotonicViolationHint, type ContextProvenanceEntry, type StoryboardPhaseResult, type StoryboardResult, type RunnerNotice, type NoticeCode, type NoticeSeverity, type AgentCapabilities, type BundleKind, type BundleRef, type CapabilityResolutionCode, type ComplianceIndex, type ComplianceIndexProtocol, type ComplianceIndexSpecialism, type ResolveOptions, type ResolvedBundle, type ResolvedStoryboards, type SandboxBrand, type SandboxAgent, type SandboxEntities, type BrandJson, type AdagentsJson, gradeOAuthMetadataGraph, gradeOAuthMetadataGraphVector, loadOAuthMetadataGraphVectors, normalizeOAuthResourceForComparison, redactOAuthUrlForOutput, type GradeOAuthMetadataGraphOptions, type OAuthMetadataGraphErrorCode, type OAuthMetadataGraphFinding, type OAuthMetadataGraphGrade, type OAuthMetadataGraphObservation, type OAuthMetadataGraphVector, type OAuthMetadataGraphVectorCorpus, BrandJsonSchema, AdagentsJsonSchema, MCP_SESSION_PROBE_TASK, } from './storyboard/index.mjs';
@@ -16,5 +16,5 @@ export { isSandboxRequest, mergeSeededProductsIntoResponse, filterValidSeededPro
16
16
  export type { TestControllerBridge, TestControllerBridgeContext, BridgeFromSessionStoreOptions, SeededCreative, SeededMediaBuy, SeededAccountFinancials, } from '../server/test-controller-bridge';
17
17
  export { runAgainstLocalAgent } from './local-agent-runner';
18
18
  export type { LocalAgentRunResult, PerStoryboardOverride, RunAgainstLocalAgentOptions } from './local-agent-runner';
19
- export { registerExternalSchemaRoot, unregisterExternalSchemaRoot, withExternalSchemaRoot } from '../validation';
19
+ export { registerExternalSchemaRoot, unregisterExternalSchemaRoot, withExternalSchemaRoot, withServedReleaseSchemaRoots, } from '../validation';
20
20
  export { runStoryboard, runStoryboardStep, applyAdcpVersionRunOptions, applyStoryboardVersionOptions, getFirstStepPreview, parseStoryboard, loadStoryboardFile, normalizeValidationOnlyTasks, VALIDATION_ONLY_TASK, applyFixtureBindingsToRequest, buildFixtureResolutionSpecs, FixtureBindingRegistry, matchesFixtureRequirements, normalizeFixtureMatchExpression, validateFixtureResolutionDeclarations, getComplianceCacheDir, loadComplianceIndex, listBundles, loadBundleStoryboards, listAllComplianceStoryboards, getComplianceStoryboardById, findBundleById, resolveBundleOrStoryboard, resolveStoryboardsForCapabilities, isComplianceVersionSupported, CapabilityResolutionError, TASK_TO_METHOD, executeStoryboardTask, expandCreativeAssetDirectives, findUnresolvedCreativeAssetDirectives, extractContext, injectContext, runValidations, resolvePath, RateLimitTripObserver, RATE_LIMIT_TRIP_CONTRACT, RATE_LIMIT_TRIP_DEFAULT_REPLAY_MAX_WAIT_SECONDS, RATE_LIMIT_TRIP_MAX_ATTEMPTS_MAX, RATE_LIMIT_TRIP_MAX_ATTEMPTS_MIN, validateRateLimitTripSpec, getSandboxEntities, getSandboxBrands, getSandboxBrand, isSandboxDomain, clearSandboxCache, registerAssertion, getAssertion, listAssertions, listDefaultAssertions, clearAssertionRegistry, resolveAssertions, type AssertionSpec, type AssertionContext, type AssertionResult, type RegisterAssertionOptions, type Storyboard, type StoryboardFixtureResolution, type FixtureResolutionStrategy, type FixtureMatchOperator, type FixtureMatchClause, type FixtureResolutionDeclaration, type ProductFixtureResolutionDeclaration, type PricingOptionFixtureResolutionDeclaration, type FixtureResolutionStatus, type FixtureResolutionRecord, type FixtureResolutionAttempt, type FixtureResolutionEvidence, type FixtureResolutionCoverageGap, type StoryboardInvariants, type StoryboardInvariantsObject, type StepInvariantsObject, type StoryboardPhase, type StoryboardStep, type RateLimitTripSpec, type StoryboardValidation, type RateLimitTripClient, type RateLimitTripFailureCode, type RateLimitTripObservation, type RateLimitTripObserverOptions, type RateLimitTripResponseSnapshot, type RateLimitTripStructuredResult, type RateLimitTripTaskOptions, type StoryboardContext, type StoryboardRunOptions, type TrustedMatchContextProviderEndpoint, type TrustedMatchContextRegistrationContext, type TrustedMatchContextRouterRunnerOptions, type TrustedMatchPublisherAuthOperation, type TrustedMatchPublisherCredentialState, type TrustedMatchPublisherAuthTls, type TrustedMatchPublisherAuthProbeConfiguration, type TrustedMatchPublisherAuthRunner, type ValidationResult, type StoryboardStepPreview, type StoryboardStepResult, type StoryboardStepHint, type StoryboardStepHintBase, type ContextValueRejectedHint, type ShapeDriftHint, type MissingRequiredFieldHint, type FormatMismatchHint, type MonotonicViolationHint, type ContextProvenanceEntry, type StoryboardPhaseResult, type StoryboardResult, type RunnerNotice, type NoticeCode, type NoticeSeverity, type AgentCapabilities, type BundleKind, type BundleRef, type CapabilityResolutionCode, type ComplianceIndex, type ComplianceIndexProtocol, type ComplianceIndexSpecialism, type ResolveOptions, type ResolvedBundle, type ResolvedStoryboards, type SandboxBrand, type SandboxAgent, type SandboxEntities, type BrandJson, type AdagentsJson, gradeOAuthMetadataGraph, gradeOAuthMetadataGraphVector, loadOAuthMetadataGraphVectors, normalizeOAuthResourceForComparison, redactOAuthUrlForOutput, type GradeOAuthMetadataGraphOptions, type OAuthMetadataGraphErrorCode, type OAuthMetadataGraphFinding, type OAuthMetadataGraphGrade, type OAuthMetadataGraphObservation, type OAuthMetadataGraphVector, type OAuthMetadataGraphVectorCorpus, BrandJsonSchema, AdagentsJsonSchema, MCP_SESSION_PROBE_TASK, } from './storyboard';
@@ -192,7 +192,8 @@ __export(testing_exports, {
192
192
  unregisterExternalSchemaRoot: () => import_validation.unregisterExternalSchemaRoot,
193
193
  validateFixtureResolutionDeclarations: () => import_storyboard.validateFixtureResolutionDeclarations,
194
194
  validateRateLimitTripSpec: () => import_storyboard.validateRateLimitTripSpec,
195
- withExternalSchemaRoot: () => import_validation.withExternalSchemaRoot
195
+ withExternalSchemaRoot: () => import_validation.withExternalSchemaRoot,
196
+ withServedReleaseSchemaRoots: () => import_validation.withServedReleaseSchemaRoots
196
197
  });
197
198
  module.exports = __toCommonJS(testing_exports);
198
199
  var import_pending_web_flow_store = require('./pending-web-flow-store.js');
@@ -384,5 +385,6 @@ var import_storyboard = require('./storyboard/index.js');
384
385
  unregisterExternalSchemaRoot,
385
386
  validateFixtureResolutionDeclarations,
386
387
  validateRateLimitTripSpec,
387
- withExternalSchemaRoot
388
+ withExternalSchemaRoot,
389
+ withServedReleaseSchemaRoots
388
390
  });
@@ -122,7 +122,12 @@ import {
122
122
  bridgeFromSessionStore
123
123
  } from "../server/test-controller-bridge.mjs";
124
124
  import { runAgainstLocalAgent } from "./local-agent-runner.mjs";
125
- import { registerExternalSchemaRoot, unregisterExternalSchemaRoot, withExternalSchemaRoot } from "../validation/index.mjs";
125
+ import {
126
+ registerExternalSchemaRoot,
127
+ unregisterExternalSchemaRoot,
128
+ withExternalSchemaRoot,
129
+ withServedReleaseSchemaRoots
130
+ } from "../validation/index.mjs";
126
131
  import {
127
132
  runStoryboard,
128
133
  runStoryboardStep,
@@ -359,5 +364,6 @@ export {
359
364
  unregisterExternalSchemaRoot,
360
365
  validateFixtureResolutionDeclarations,
361
366
  validateRateLimitTripSpec,
362
- withExternalSchemaRoot
367
+ withExternalSchemaRoot,
368
+ withServedReleaseSchemaRoots
363
369
  };
@@ -783,14 +783,14 @@ async function runStoryboard(agentUrlOrUrls, storyboard, options = {}) {
783
783
  };
784
784
  }
785
785
  const schemaRoot = getRunSchemaRoot(options);
786
+ const runBody = () => (0, import_schema_loader.withServedReleaseSchemaRoots)(
787
+ options.servedReleaseSchemaRoots,
788
+ () => runStoryboardBody(agentUrlOrUrls, storyboard, options)
789
+ );
786
790
  if (schemaRoot) {
787
- return await (0, import_schema_loader.withExternalSchemaRoot)(
788
- schemaRoot.adcpVersion,
789
- schemaRoot.schemaRoot,
790
- () => runStoryboardBody(agentUrlOrUrls, storyboard, options)
791
- );
791
+ return await (0, import_schema_loader.withExternalSchemaRoot)(schemaRoot.adcpVersion, schemaRoot.schemaRoot, runBody);
792
792
  }
793
- return await runStoryboardBody(agentUrlOrUrls, storyboard, options);
793
+ return await runBody();
794
794
  },
795
795
  { isolate: !(0, import_client.hasTestClientScope)() }
796
796
  );
@@ -3117,14 +3117,14 @@ async function runStoryboardStep(agentUrl, storyboard, stepId, options = {}) {
3117
3117
  options = (0, import_test_kit.resolveDeclaredTestKit)(storyboard, options);
3118
3118
  options = (0, import_native_a2a_compliance.applyNativeA2AComplianceTransportOptions)(options);
3119
3119
  const schemaRoot = getRunSchemaRoot(options);
3120
+ const runBody = () => (0, import_schema_loader.withServedReleaseSchemaRoots)(
3121
+ options.servedReleaseSchemaRoots,
3122
+ () => runStoryboardStepBody(agentUrl, storyboard, stepId, options)
3123
+ );
3120
3124
  if (schemaRoot) {
3121
- return await (0, import_schema_loader.withExternalSchemaRoot)(
3122
- schemaRoot.adcpVersion,
3123
- schemaRoot.schemaRoot,
3124
- () => runStoryboardStepBody(agentUrl, storyboard, stepId, options)
3125
- );
3125
+ return await (0, import_schema_loader.withExternalSchemaRoot)(schemaRoot.adcpVersion, schemaRoot.schemaRoot, runBody);
3126
3126
  }
3127
- return await runStoryboardStepBody(agentUrl, storyboard, stepId, options);
3127
+ return await runBody();
3128
3128
  },
3129
3129
  { isolate: !(0, import_client.hasTestClientScope)() }
3130
3130
  );
@@ -61,7 +61,8 @@ import {
61
61
  getSchemaValidatorByRef,
62
62
  resolveBundleKey,
63
63
  schemaAllowsTopLevelField,
64
- withExternalSchemaRoot
64
+ withExternalSchemaRoot,
65
+ withServedReleaseSchemaRoots
65
66
  } from "../../validation/schema-loader.mjs";
66
67
  import { ADCP_VERSION, parseAdcpMajorVersion } from "../../version.mjs";
67
68
  import {
@@ -806,14 +807,14 @@ async function runStoryboard(agentUrlOrUrls, storyboard, options = {}) {
806
807
  };
807
808
  }
808
809
  const schemaRoot = getRunSchemaRoot(options);
810
+ const runBody = () => withServedReleaseSchemaRoots(
811
+ options.servedReleaseSchemaRoots,
812
+ () => runStoryboardBody(agentUrlOrUrls, storyboard, options)
813
+ );
809
814
  if (schemaRoot) {
810
- return await withExternalSchemaRoot(
811
- schemaRoot.adcpVersion,
812
- schemaRoot.schemaRoot,
813
- () => runStoryboardBody(agentUrlOrUrls, storyboard, options)
814
- );
815
+ return await withExternalSchemaRoot(schemaRoot.adcpVersion, schemaRoot.schemaRoot, runBody);
815
816
  }
816
- return await runStoryboardBody(agentUrlOrUrls, storyboard, options);
817
+ return await runBody();
817
818
  },
818
819
  { isolate: !hasTestClientScope() }
819
820
  );
@@ -3140,14 +3141,14 @@ async function runStoryboardStep(agentUrl, storyboard, stepId, options = {}) {
3140
3141
  options = resolveDeclaredTestKit(storyboard, options);
3141
3142
  options = applyNativeA2AComplianceTransportOptions(options);
3142
3143
  const schemaRoot = getRunSchemaRoot(options);
3144
+ const runBody = () => withServedReleaseSchemaRoots(
3145
+ options.servedReleaseSchemaRoots,
3146
+ () => runStoryboardStepBody(agentUrl, storyboard, stepId, options)
3147
+ );
3143
3148
  if (schemaRoot) {
3144
- return await withExternalSchemaRoot(
3145
- schemaRoot.adcpVersion,
3146
- schemaRoot.schemaRoot,
3147
- () => runStoryboardStepBody(agentUrl, storyboard, stepId, options)
3148
- );
3149
+ return await withExternalSchemaRoot(schemaRoot.adcpVersion, schemaRoot.schemaRoot, runBody);
3149
3150
  }
3150
- return await runStoryboardStepBody(agentUrl, storyboard, stepId, options);
3151
+ return await runBody();
3151
3152
  },
3152
3153
  { isolate: !hasTestClientScope() }
3153
3154
  );
@@ -197,7 +197,7 @@ export interface UpstreamTrafficQueryResult {
197
197
  */
198
198
  export declare function runValidations(validations: StoryboardValidation[], context: ValidationContext): ValidationResult[];
199
199
  /** Capability semver emitted in run summaries and used for advisory expiry. */
200
- export declare const RUNNER_CAPABILITY_VERSION = "16.0.0-beta.6";
200
+ export declare const RUNNER_CAPABILITY_VERSION = "16.0.0-beta.8";
201
201
  /** True when a failed validation contributes to the owning step's grade. */
202
202
  export declare function validationFailsStep(result: ValidationResult): boolean;
203
203
  /**
@@ -197,7 +197,7 @@ export interface UpstreamTrafficQueryResult {
197
197
  */
198
198
  export declare function runValidations(validations: StoryboardValidation[], context: ValidationContext): ValidationResult[];
199
199
  /** Capability semver emitted in run summaries and used for advisory expiry. */
200
- export declare const RUNNER_CAPABILITY_VERSION = "16.0.0-beta.6";
200
+ export declare const RUNNER_CAPABILITY_VERSION = "16.0.0-beta.8";
201
201
  /** True when a failed validation contributes to the owning step's grade. */
202
202
  export declare function validationFailsStep(result: ValidationResult): boolean;
203
203
  /**
@@ -259,6 +259,21 @@ function mapZodIssueToSchemaKeyword(issue) {
259
259
  }
260
260
  }
261
261
  function validateResponseSchema(validation, ctx, taskResult) {
262
+ const pinnedVersion = ctx.adcpVersion;
263
+ if (ctx.taskName === "get_adcp_capabilities" && pinnedVersion !== void 0) {
264
+ return (0, import_schema_loader.withServedValidationVersion)(
265
+ taskResult.data,
266
+ pinnedVersion,
267
+ (validationVersion) => validateResponseSchemaForVersion(
268
+ validation,
269
+ validationVersion === pinnedVersion ? ctx : { ...ctx, adcpVersion: validationVersion, responseAdcpVersion: validationVersion },
270
+ taskResult
271
+ )
272
+ );
273
+ }
274
+ return validateResponseSchemaForVersion(validation, ctx, taskResult);
275
+ }
276
+ function validateResponseSchemaForVersion(validation, ctx, taskResult) {
262
277
  const taskName = ctx.taskName;
263
278
  const schemaIdentityVersion = ctx.adcpVersion ?? ctx.responseAdcpVersion ?? import_version.ADCP_VERSION;
264
279
  const { schema_id, schema_url } = resolveSchemaIdentity(ctx.responseSchemaRef, schemaIdentityVersion);
@@ -356,7 +371,14 @@ function validateResponseSchema(validation, ctx, taskResult) {
356
371
  }
357
372
  function isExternalResponseSchemaAuthoritative(ctx) {
358
373
  const validationVersion = responseSchemaValidationVersion(ctx.adcpVersion, ctx.responseAdcpVersion);
359
- return validationVersion !== void 0 && (0, import_schema_loader.isExternalSchemaRootActive)(validationVersion);
374
+ if (validationVersion !== void 0 && (0, import_schema_loader.isExternalSchemaRootActive)(validationVersion)) return true;
375
+ const pinnedVersion = ctx.adcpVersion;
376
+ if (ctx.taskName !== "get_adcp_capabilities" || pinnedVersion === void 0 || !ctx.taskResult) return false;
377
+ return (0, import_schema_loader.withServedValidationVersion)(
378
+ ctx.taskResult.data,
379
+ pinnedVersion,
380
+ (version) => version !== pinnedVersion && (0, import_schema_loader.isExternalSchemaRootActive)(version)
381
+ );
360
382
  }
361
383
  function responseSchemaValidationVersion(adcpVersion, responseAdcpVersion) {
362
384
  if (adcpVersion && (0, import_schema_loader.isExternalSchemaRootActive)(adcpVersion)) return adcpVersion;
@@ -2,7 +2,11 @@ import { prepareResponseForSchemaValidation, TOOL_RESPONSE_SCHEMAS } from "../..
2
2
  import { injectLegacyEnvelopeStatus } from "../../utils/envelope-status-compat.mjs";
3
3
  import { TRANSPORT_SUFFIX_REGEX } from "../../utils/a2a-discovery.mjs";
4
4
  import { validateResponse } from "../../validation/schema-validator.mjs";
5
- import { hasSchemaBundle, isExternalSchemaRootActive } from "../../validation/schema-loader.mjs";
5
+ import {
6
+ hasSchemaBundle,
7
+ isExternalSchemaRootActive,
8
+ withServedValidationVersion
9
+ } from "../../validation/schema-loader.mjs";
6
10
  import { ADCP_VERSION, LIBRARY_VERSION } from "../../version.mjs";
7
11
  import { isPre31AdcpVersion } from "../../utils/adcp-version-config.mjs";
8
12
  import { gte as semverGte, valid as validSemver } from "semver";
@@ -234,6 +238,21 @@ function mapZodIssueToSchemaKeyword(issue) {
234
238
  }
235
239
  }
236
240
  function validateResponseSchema(validation, ctx, taskResult) {
241
+ const pinnedVersion = ctx.adcpVersion;
242
+ if (ctx.taskName === "get_adcp_capabilities" && pinnedVersion !== void 0) {
243
+ return withServedValidationVersion(
244
+ taskResult.data,
245
+ pinnedVersion,
246
+ (validationVersion) => validateResponseSchemaForVersion(
247
+ validation,
248
+ validationVersion === pinnedVersion ? ctx : { ...ctx, adcpVersion: validationVersion, responseAdcpVersion: validationVersion },
249
+ taskResult
250
+ )
251
+ );
252
+ }
253
+ return validateResponseSchemaForVersion(validation, ctx, taskResult);
254
+ }
255
+ function validateResponseSchemaForVersion(validation, ctx, taskResult) {
237
256
  const taskName = ctx.taskName;
238
257
  const schemaIdentityVersion = ctx.adcpVersion ?? ctx.responseAdcpVersion ?? ADCP_VERSION;
239
258
  const { schema_id, schema_url } = resolveSchemaIdentity(ctx.responseSchemaRef, schemaIdentityVersion);
@@ -331,7 +350,14 @@ function validateResponseSchema(validation, ctx, taskResult) {
331
350
  }
332
351
  function isExternalResponseSchemaAuthoritative(ctx) {
333
352
  const validationVersion = responseSchemaValidationVersion(ctx.adcpVersion, ctx.responseAdcpVersion);
334
- return validationVersion !== void 0 && isExternalSchemaRootActive(validationVersion);
353
+ if (validationVersion !== void 0 && isExternalSchemaRootActive(validationVersion)) return true;
354
+ const pinnedVersion = ctx.adcpVersion;
355
+ if (ctx.taskName !== "get_adcp_capabilities" || pinnedVersion === void 0 || !ctx.taskResult) return false;
356
+ return withServedValidationVersion(
357
+ ctx.taskResult.data,
358
+ pinnedVersion,
359
+ (version) => version !== pinnedVersion && isExternalSchemaRootActive(version)
360
+ );
335
361
  }
336
362
  function responseSchemaValidationVersion(adcpVersion, responseAdcpVersion) {
337
363
  if (adcpVersion && isExternalSchemaRootActive(adcpVersion)) return adcpVersion;
@@ -58,6 +58,45 @@ export interface TestOptions {
58
58
  * authoritative over the SDK's generated Zod snapshot for the run.
59
59
  */
60
60
  schemaRoot?: string;
61
+ /**
62
+ * Schema roots for releases the agent may echo in its
63
+ * `get_adcp_capabilities` response, keyed by AdCP version (`'3.2'`,
64
+ * `'3.2.5'`, `'3.3.0-beta.1'`). Values are schema-data roots in the same
65
+ * shape as `schemaRoot`.
66
+ *
67
+ * A 3.0 pin sends only `adcp_major_version`, so an agent may serve and echo
68
+ * a newer release in the same major (versioning spec, "Server echoes").
69
+ * The SDK validates that capabilities response against the echoed release
70
+ * when the echo is the pin's major, is listed in the response's
71
+ * `adcp.supported_versions`, and has a schema root: an entry here, an
72
+ * external root registered for it, or the SDK's bundled schemas. That
73
+ * applies to discovery's capability notices, `getAdcpCapabilities()` and
74
+ * storyboard `response_schema` checks on `get_adcp_capabilities`, including
75
+ * when `schemaRoot` is set for the pin. Otherwise validation stays on the
76
+ * pin.
77
+ *
78
+ * Entries are used for that capabilities validation only. Every other
79
+ * task's response, storyboard selection and grading stay on the pin. Each
80
+ * root is checked against its key once per process; a malformed map throws
81
+ * before the run starts. Honored by `comply()`, `runStoryboard()` and
82
+ * `runStoryboardStep()`; other callers can use `withServedReleaseSchemaRoots`.
83
+ * The echo is compared with the run's pin, so storyboard checks apply it
84
+ * only when the run has one (`adcpVersion`, or the storyboard or compliance
85
+ * bundle's `adcp_version`); `comply()` always does.
86
+ *
87
+ * @example
88
+ * ```ts
89
+ * await comply(agentUrl, {
90
+ * complianceDir: hosted30.complianceDir,
91
+ * schemaRoot: hosted30.schemaRoot,
92
+ * servedReleaseSchemaRoots: {
93
+ * '3.1.29': hosted31.schemaRoot,
94
+ * '3.2.5': hosted32.schemaRoot,
95
+ * },
96
+ * });
97
+ * ```
98
+ */
99
+ servedReleaseSchemaRoots?: Readonly<Record<string, string>>;
61
100
  /**
62
101
  * Whether strict AJV response-schema failures contribute to storyboard
63
102
  * grading. Defaults to `true`, matching the hosted compliance grader.