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

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 (75) hide show
  1. package/dist/lib/core/TaskExecutor.d.mts +1 -0
  2. package/dist/lib/core/TaskExecutor.d.ts +1 -0
  3. package/dist/lib/core/TaskExecutor.js +48 -34
  4. package/dist/lib/core/TaskExecutor.mjs +49 -35
  5. package/dist/lib/identity-3.3/revocation.js +5 -4
  6. package/dist/lib/identity-3.3/revocation.mjs +5 -4
  7. package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
  8. package/dist/lib/server/auth-signature.js +23 -2
  9. package/dist/lib/server/auth-signature.mjs +26 -2
  10. package/dist/lib/server/auth.d.mts +6 -0
  11. package/dist/lib/server/auth.d.ts +6 -0
  12. package/dist/lib/server/auth.js +6 -0
  13. package/dist/lib/server/auth.mjs +6 -0
  14. package/dist/lib/server/create-adcp-server.js +19 -3
  15. package/dist/lib/server/create-adcp-server.mjs +23 -4
  16. package/dist/lib/server/decisioning/runtime/from-platform.js +6 -2
  17. package/dist/lib/server/decisioning/runtime/from-platform.mjs +6 -2
  18. package/dist/lib/server/serve.js +2 -1
  19. package/dist/lib/server/serve.mjs +2 -1
  20. package/dist/lib/server/webhook-emitter.js +1 -0
  21. package/dist/lib/server/webhook-emitter.mjs +2 -1
  22. package/dist/lib/signing/crypto.d.mts +1 -1
  23. package/dist/lib/signing/crypto.d.ts +1 -1
  24. package/dist/lib/signing/crypto.js +37 -2
  25. package/dist/lib/signing/crypto.mjs +37 -2
  26. package/dist/lib/signing/parser.d.mts +6 -0
  27. package/dist/lib/signing/parser.d.ts +6 -0
  28. package/dist/lib/signing/parser.js +5 -1
  29. package/dist/lib/signing/parser.mjs +4 -1
  30. package/dist/lib/signing/web-bot-auth.d.mts +61 -22
  31. package/dist/lib/signing/web-bot-auth.d.ts +61 -22
  32. package/dist/lib/signing/web-bot-auth.js +286 -266
  33. package/dist/lib/signing/web-bot-auth.mjs +288 -277
  34. package/dist/lib/testing/client.js +3 -5
  35. package/dist/lib/testing/client.mjs +4 -6
  36. package/dist/lib/testing/compliance/comply.js +7 -2
  37. package/dist/lib/testing/compliance/comply.mjs +8 -3
  38. package/dist/lib/testing/index.d.mts +1 -1
  39. package/dist/lib/testing/index.d.ts +1 -1
  40. package/dist/lib/testing/index.js +4 -2
  41. package/dist/lib/testing/index.mjs +8 -2
  42. package/dist/lib/testing/storyboard/runner.js +12 -12
  43. package/dist/lib/testing/storyboard/runner.mjs +14 -13
  44. package/dist/lib/testing/storyboard/validations.d.mts +1 -1
  45. package/dist/lib/testing/storyboard/validations.d.ts +1 -1
  46. package/dist/lib/testing/storyboard/validations.js +23 -1
  47. package/dist/lib/testing/storyboard/validations.mjs +28 -2
  48. package/dist/lib/testing/types.d.mts +39 -0
  49. package/dist/lib/testing/types.d.ts +39 -0
  50. package/dist/lib/v2/projection/augment-response.js +6 -3
  51. package/dist/lib/v2/projection/augment-response.mjs +8 -5
  52. package/dist/lib/v2/projection/creative-delivery.js +29 -17
  53. package/dist/lib/v2/projection/creative-delivery.mjs +31 -18
  54. package/dist/lib/v2/projection/v1-to-v2.d.mts +7 -0
  55. package/dist/lib/v2/projection/v1-to-v2.d.ts +7 -0
  56. package/dist/lib/v2/projection/v1-to-v2.js +70 -2
  57. package/dist/lib/v2/projection/v1-to-v2.mjs +72 -1
  58. package/dist/lib/validation/index.d.mts +1 -1
  59. package/dist/lib/validation/index.d.ts +1 -1
  60. package/dist/lib/validation/index.js +4 -2
  61. package/dist/lib/validation/index.mjs +4 -2
  62. package/dist/lib/validation/schema-loader.d.mts +26 -0
  63. package/dist/lib/validation/schema-loader.d.ts +26 -0
  64. package/dist/lib/validation/schema-loader.js +98 -14
  65. package/dist/lib/validation/schema-loader.mjs +95 -13
  66. package/dist/lib/version.d.mts +3 -3
  67. package/dist/lib/version.d.ts +3 -3
  68. package/dist/lib/version.js +3 -3
  69. package/dist/lib/version.mjs +3 -3
  70. package/docs/TYPE-SUMMARY.md +2 -2
  71. package/docs/guides/VALIDATE-YOUR-AGENT.md +34 -0
  72. package/docs/llms.txt +2 -2
  73. package/docs/migration-14.x-rc-worksheet.md +4 -4
  74. package/docs/migration-agent-identity-3.3.md +60 -23
  75. package/package.json +1 -1
@@ -2,7 +2,8 @@ import {
2
2
  canonicalDeclarationFromBareId,
3
3
  migratedFormatOptionId,
4
4
  projectV1ProductToV2,
5
- resolveCanonicalFormatKind
5
+ resolveCanonicalFormatKind,
6
+ withDualEmissionLegacyRefs
6
7
  } from "./v1-to-v2.mjs";
7
8
  import { CanonicalFormatLegacyResolutionError, resolveCanonicalFormatLegacyRefs } from "./v2-to-v1.mjs";
8
9
  import {
@@ -357,26 +358,15 @@ function candidatesFromContainer(container, canonicalFormatLegacyResolver, opera
357
358
  );
358
359
  }
359
360
  const candidates = [];
360
- const containerKind = typeof container.format_kind === "string" ? container.format_kind : void 0;
361
- for (const key of ["format_ids", "formats"]) {
362
- for (const ref of refsFromValue(container[key])) {
363
- candidates.push({
364
- ref,
365
- formatKind: resolveCanonicalFormatKind(ref.id) ?? containerKind,
366
- selectorParams: container.params
367
- });
368
- }
369
- }
370
- const formatOptions = [
371
- ...Array.isArray(container.format_options) ? container.format_options : [],
372
- ...selectedFormatOptions(container)
373
- ];
374
- for (const optionValue of formatOptions) {
361
+ const nativeOptions = Array.isArray(container.format_options) ? container.format_options : [];
362
+ const formatOptions = [...nativeOptions, ...selectedFormatOptions(container)];
363
+ const resolvedOptions = formatOptions.map((optionValue) => {
375
364
  const option = record(optionValue);
376
- if (!option) continue;
365
+ if (!option) return optionValue;
377
366
  const formatKind = typeof option.format_kind === "string" ? option.format_kind : void 0;
378
367
  const formatOptionRef = optionReference(option);
379
368
  let refs = [...legacyFormatRefsForDeclaration(option)];
369
+ if (refs.length > 0) return optionValue;
380
370
  if (refs.length === 0 && option.canonical_formats_only !== true && formatKind) {
381
371
  try {
382
372
  refs = resolveCanonicalFormatLegacyRefs(canonicalFormatLegacyResolver, {
@@ -394,10 +384,33 @@ function candidatesFromContainer(container, canonicalFormatLegacyResolver, opera
394
384
  );
395
385
  }
396
386
  }
397
- for (const ref of refs) {
387
+ return refs.length > 0 ? { ...option, v1_format_ref: refs } : optionValue;
388
+ });
389
+ const pairedOptions = [
390
+ ...withDualEmissionLegacyRefs(resolvedOptions.slice(0, nativeOptions.length), container.format_ids),
391
+ ...resolvedOptions.slice(nativeOptions.length)
392
+ ];
393
+ for (const optionValue of pairedOptions) {
394
+ const option = record(optionValue);
395
+ if (!option) continue;
396
+ const formatKind = typeof option.format_kind === "string" ? option.format_kind : void 0;
397
+ const formatOptionRef = optionReference(option);
398
+ for (const ref of legacyFormatRefsForDeclaration(option)) {
398
399
  candidates.push({ ref, formatKind, formatOptionRef, selectorParams: option.params ?? container.params });
399
400
  }
400
401
  }
402
+ const linkedRefs = new Set(candidates.map((candidate) => candidateKey({ ref: candidate.ref })));
403
+ const containerKind = typeof container.format_kind === "string" ? container.format_kind : void 0;
404
+ for (const key of ["format_ids", "formats"]) {
405
+ for (const ref of refsFromValue(container[key])) {
406
+ if (linkedRefs.has(candidateKey({ ref }))) continue;
407
+ candidates.push({
408
+ ref,
409
+ formatKind: resolveCanonicalFormatKind(ref.id) ?? containerKind,
410
+ selectorParams: container.params
411
+ });
412
+ }
413
+ }
401
414
  return uniqueCandidates(candidates);
402
415
  }
403
416
  function containerOptsOutOfLegacy(container, formatKind, formatOptionRef) {
@@ -211,3 +211,10 @@ export declare function canonicalDeclarationFromBareId(id: string, options?: Bar
211
211
  * the `agentUrl` default, and the `assetType` disambiguator.
212
212
  */
213
213
  export declare function resolveCanonicalFormatKind(id: string, options?: BareFormatIdResolveOptions): CanonicalFormatKind | null;
214
+ /**
215
+ * Preserve index pairing only when the whole seller-authored alignment is credible.
216
+ * AdCP 3.1.24 core/product.json defines this correspondence: "the format_options[i]
217
+ * narrows the canonical that the named format in format_ids[i] resolves to via the
218
+ * registry / explicit canonical field". Explicit routes remain authoritative.
219
+ */
220
+ export declare function withDualEmissionLegacyRefs<T>(formatOptions: T[], formatIds: unknown): T[];
@@ -211,3 +211,10 @@ export declare function canonicalDeclarationFromBareId(id: string, options?: Bar
211
211
  * the `agentUrl` default, and the `assetType` disambiguator.
212
212
  */
213
213
  export declare function resolveCanonicalFormatKind(id: string, options?: BareFormatIdResolveOptions): CanonicalFormatKind | null;
214
+ /**
215
+ * Preserve index pairing only when the whole seller-authored alignment is credible.
216
+ * AdCP 3.1.24 core/product.json defines this correspondence: "the format_options[i]
217
+ * narrows the canonical that the named format in format_ids[i] resolves to via the
218
+ * registry / explicit canonical field". Explicit routes remain authoritative.
219
+ */
220
+ export declare function withDualEmissionLegacyRefs<T>(formatOptions: T[], formatIds: unknown): T[];
@@ -21,7 +21,8 @@ __export(v1_to_v2_exports, {
21
21
  canonicalDeclarationFromBareId: () => canonicalDeclarationFromBareId,
22
22
  migratedFormatOptionId: () => migratedFormatOptionId,
23
23
  projectV1ProductToV2: () => projectV1ProductToV2,
24
- resolveCanonicalFormatKind: () => resolveCanonicalFormatKind
24
+ resolveCanonicalFormatKind: () => resolveCanonicalFormatKind,
25
+ withDualEmissionLegacyRefs: () => withDualEmissionLegacyRefs
25
26
  });
26
27
  module.exports = __toCommonJS(v1_to_v2_exports);
27
28
  var import_registry = require('./registry.js');
@@ -33,6 +34,7 @@ var import_resolve_agent_properties = require('../../discovery/resolve-agent-pro
33
34
  var import_address_guards = require('../../net/address-guards.js');
34
35
  var import_crypto = require("crypto");
35
36
  var import_legacy_routes = require('./legacy-routes.js');
37
+ var import_legacy_metadata = require('./legacy-metadata.js');
36
38
  const SDK_ID = `@adcp/sdk@${import_version.LIBRARY_VERSION}`;
37
39
  class CatalogRequirementConflict extends Error {
38
40
  }
@@ -367,10 +369,76 @@ function normalizeAssetTypeSuffix(hint) {
367
369
  function resolveCanonicalFormatKind(id, options) {
368
370
  return canonicalDeclarationFromBareId(id, options)?.format_kind ?? null;
369
371
  }
372
+ function record(value) {
373
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
374
+ }
375
+ function sameIdentity(left, right) {
376
+ const url = (value) => {
377
+ try {
378
+ return new URL(value).href;
379
+ } catch {
380
+ return value;
381
+ }
382
+ };
383
+ return url(left.agent_url) === url(right.agent_url) && left.id === right.id && ["width", "height", "duration_ms"].every((key) => {
384
+ const field = key;
385
+ return left[field] === void 0 || right[field] === void 0 || left[field] === right[field];
386
+ });
387
+ }
388
+ function compatible(option, ref) {
389
+ const known = canonicalDeclarationFromBareId(ref.id, { agentUrl: ref.agent_url });
390
+ if (known && known.format_kind !== option.format_kind) return false;
391
+ const params = record(option.params) ?? {};
392
+ const knownParams = record(known?.params) ?? {};
393
+ const constraints = {
394
+ width: ref.width ?? knownParams.width,
395
+ height: ref.height ?? knownParams.height,
396
+ duration_ms_exact: ref.duration_ms ?? knownParams.duration_ms_exact
397
+ };
398
+ if (Object.entries(constraints).some(
399
+ ([key, value]) => typeof value === "number" && typeof params[key] === "number" && value !== params[key]
400
+ )) {
401
+ return false;
402
+ }
403
+ if (Array.isArray(params.sizes) && params.sizes.length > 0) {
404
+ return params.sizes.some((value) => {
405
+ const size = record(value);
406
+ return size && ["width", "height"].every((key) => {
407
+ const actual = constraints[key];
408
+ return typeof actual !== "number" || typeof size[key] !== "number" || actual === size[key];
409
+ });
410
+ });
411
+ }
412
+ return true;
413
+ }
414
+ function withDualEmissionLegacyRefs(formatOptions, formatIds) {
415
+ if (!Array.isArray(formatIds) || formatIds.length !== formatOptions.length) return formatOptions;
416
+ const aligned = formatOptions.every((value, index) => {
417
+ const option = record(value);
418
+ const ref = record(formatIds[index]);
419
+ if (!option || typeof option.format_kind !== "string" || option.canonical_formats_only === true || !ref || typeof ref.agent_url !== "string" || ref.agent_url.length === 0 || typeof ref.id !== "string" || ref.id.length === 0) {
420
+ return false;
421
+ }
422
+ const typedRef = ref;
423
+ const explicit = (0, import_legacy_metadata.legacyFormatRefsForDeclaration)(option);
424
+ return explicit.length > 0 ? explicit.some((existing) => sameIdentity(existing, typedRef)) : compatible(option, typedRef);
425
+ });
426
+ if (!aligned) return formatOptions;
427
+ return formatOptions.map((value, index) => {
428
+ if ((0, import_legacy_metadata.legacyFormatRefsForDeclaration)(value).length > 0) return value;
429
+ const paired = (0, import_legacy_metadata.concealLegacyFormatRefs)({
430
+ ...value,
431
+ v1_format_ref: [{ ...formatIds[index] }]
432
+ });
433
+ (0, import_legacy_metadata.transferLegacyCreativeMetadata)(value, paired);
434
+ return paired;
435
+ });
436
+ }
370
437
  // Annotate the CommonJS export names for ESM import in node:
371
438
  0 && (module.exports = {
372
439
  canonicalDeclarationFromBareId,
373
440
  migratedFormatOptionId,
374
441
  projectV1ProductToV2,
375
- resolveCanonicalFormatKind
442
+ resolveCanonicalFormatKind,
443
+ withDualEmissionLegacyRefs
376
444
  });
@@ -12,6 +12,11 @@ import { canonicalizeAgentUrl } from "../../discovery/resolve-agent-properties.m
12
12
  import { isLikelyPrivateUrl } from "../../net/address-guards.mjs";
13
13
  import { createHmac } from "crypto";
14
14
  import { legacyRoutesForProduct } from "./legacy-routes.mjs";
15
+ import {
16
+ concealLegacyFormatRefs,
17
+ legacyFormatRefsForDeclaration,
18
+ transferLegacyCreativeMetadata
19
+ } from "./legacy-metadata.mjs";
15
20
  const SDK_ID = `@adcp/sdk@${LIBRARY_VERSION}`;
16
21
  class CatalogRequirementConflict extends Error {
17
22
  }
@@ -346,9 +351,75 @@ function normalizeAssetTypeSuffix(hint) {
346
351
  function resolveCanonicalFormatKind(id, options) {
347
352
  return canonicalDeclarationFromBareId(id, options)?.format_kind ?? null;
348
353
  }
354
+ function record(value) {
355
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
356
+ }
357
+ function sameIdentity(left, right) {
358
+ const url = (value) => {
359
+ try {
360
+ return new URL(value).href;
361
+ } catch {
362
+ return value;
363
+ }
364
+ };
365
+ return url(left.agent_url) === url(right.agent_url) && left.id === right.id && ["width", "height", "duration_ms"].every((key) => {
366
+ const field = key;
367
+ return left[field] === void 0 || right[field] === void 0 || left[field] === right[field];
368
+ });
369
+ }
370
+ function compatible(option, ref) {
371
+ const known = canonicalDeclarationFromBareId(ref.id, { agentUrl: ref.agent_url });
372
+ if (known && known.format_kind !== option.format_kind) return false;
373
+ const params = record(option.params) ?? {};
374
+ const knownParams = record(known?.params) ?? {};
375
+ const constraints = {
376
+ width: ref.width ?? knownParams.width,
377
+ height: ref.height ?? knownParams.height,
378
+ duration_ms_exact: ref.duration_ms ?? knownParams.duration_ms_exact
379
+ };
380
+ if (Object.entries(constraints).some(
381
+ ([key, value]) => typeof value === "number" && typeof params[key] === "number" && value !== params[key]
382
+ )) {
383
+ return false;
384
+ }
385
+ if (Array.isArray(params.sizes) && params.sizes.length > 0) {
386
+ return params.sizes.some((value) => {
387
+ const size = record(value);
388
+ return size && ["width", "height"].every((key) => {
389
+ const actual = constraints[key];
390
+ return typeof actual !== "number" || typeof size[key] !== "number" || actual === size[key];
391
+ });
392
+ });
393
+ }
394
+ return true;
395
+ }
396
+ function withDualEmissionLegacyRefs(formatOptions, formatIds) {
397
+ if (!Array.isArray(formatIds) || formatIds.length !== formatOptions.length) return formatOptions;
398
+ const aligned = formatOptions.every((value, index) => {
399
+ const option = record(value);
400
+ const ref = record(formatIds[index]);
401
+ if (!option || typeof option.format_kind !== "string" || option.canonical_formats_only === true || !ref || typeof ref.agent_url !== "string" || ref.agent_url.length === 0 || typeof ref.id !== "string" || ref.id.length === 0) {
402
+ return false;
403
+ }
404
+ const typedRef = ref;
405
+ const explicit = legacyFormatRefsForDeclaration(option);
406
+ return explicit.length > 0 ? explicit.some((existing) => sameIdentity(existing, typedRef)) : compatible(option, typedRef);
407
+ });
408
+ if (!aligned) return formatOptions;
409
+ return formatOptions.map((value, index) => {
410
+ if (legacyFormatRefsForDeclaration(value).length > 0) return value;
411
+ const paired = concealLegacyFormatRefs({
412
+ ...value,
413
+ v1_format_ref: [{ ...formatIds[index] }]
414
+ });
415
+ transferLegacyCreativeMetadata(value, paired);
416
+ return paired;
417
+ });
418
+ }
349
419
  export {
350
420
  canonicalDeclarationFromBareId,
351
421
  migratedFormatOptionId,
352
422
  projectV1ProductToV2,
353
- resolveCanonicalFormatKind
423
+ resolveCanonicalFormatKind,
424
+ withDualEmissionLegacyRefs
354
425
  };
@@ -6,7 +6,7 @@ export { validateRequest, validateResponse, formatIssues } from './schema-valida
6
6
  export type { ValidationIssue, ValidationOutcome } from './schema-validator.mjs';
7
7
  export { buildValidationError, buildAdcpValidationErrorPayload } from './schema-errors.mjs';
8
8
  export type { ValidationErrorDetails, AdcpValidationErrorDetails } from './schema-errors.mjs';
9
- export { getValidator, listValidatorKeys, resolveBundleKey, hasSchemaBundle, registerExternalSchemaRoot, unregisterExternalSchemaRoot, withExternalSchemaRoot, } from './schema-loader.mjs';
9
+ export { getValidator, listValidatorKeys, resolveBundleKey, hasSchemaBundle, registerExternalSchemaRoot, unregisterExternalSchemaRoot, withExternalSchemaRoot, withServedReleaseSchemaRoots, } from './schema-loader.mjs';
10
10
  export type { Direction, ResponseVariant } from './schema-loader.mjs';
11
11
  export { validateOutgoingRequest, validateIncomingResponse, resolveValidationModes } from './client-hooks.mjs';
12
12
  export type { ValidationMode, ValidationHookConfig } from './client-hooks.mjs';
@@ -6,7 +6,7 @@ export { validateRequest, validateResponse, formatIssues } from './schema-valida
6
6
  export type { ValidationIssue, ValidationOutcome } from './schema-validator';
7
7
  export { buildValidationError, buildAdcpValidationErrorPayload } from './schema-errors';
8
8
  export type { ValidationErrorDetails, AdcpValidationErrorDetails } from './schema-errors';
9
- export { getValidator, listValidatorKeys, resolveBundleKey, hasSchemaBundle, registerExternalSchemaRoot, unregisterExternalSchemaRoot, withExternalSchemaRoot, } from './schema-loader';
9
+ export { getValidator, listValidatorKeys, resolveBundleKey, hasSchemaBundle, registerExternalSchemaRoot, unregisterExternalSchemaRoot, withExternalSchemaRoot, withServedReleaseSchemaRoots, } from './schema-loader';
10
10
  export type { Direction, ResponseVariant } from './schema-loader';
11
11
  export { validateOutgoingRequest, validateIncomingResponse, resolveValidationModes } from './client-hooks';
12
12
  export type { ValidationMode, ValidationHookConfig } from './client-hooks';
@@ -36,7 +36,8 @@ __export(validation_exports, {
36
36
  validateOutgoingRequest: () => import_client_hooks.validateOutgoingRequest,
37
37
  validateRequest: () => import_schema_validator.validateRequest,
38
38
  validateResponse: () => import_schema_validator.validateResponse,
39
- withExternalSchemaRoot: () => import_schema_loader.withExternalSchemaRoot
39
+ withExternalSchemaRoot: () => import_schema_loader.withExternalSchemaRoot,
40
+ withServedReleaseSchemaRoots: () => import_schema_loader.withServedReleaseSchemaRoots
40
41
  });
41
42
  module.exports = __toCommonJS(validation_exports);
42
43
  var import_probe_policy = require('../utils/probe-policy.js');
@@ -201,5 +202,6 @@ async function handleAdCPResponse(response, expectedSchema, agentName) {
201
202
  validateOutgoingRequest,
202
203
  validateRequest,
203
204
  validateResponse,
204
- withExternalSchemaRoot
205
+ withExternalSchemaRoot,
206
+ withServedReleaseSchemaRoots
205
207
  });
@@ -8,7 +8,8 @@ import {
8
8
  hasSchemaBundle,
9
9
  registerExternalSchemaRoot,
10
10
  unregisterExternalSchemaRoot,
11
- withExternalSchemaRoot
11
+ withExternalSchemaRoot,
12
+ withServedReleaseSchemaRoots
12
13
  } from "./schema-loader.mjs";
13
14
  import { validateOutgoingRequest, validateIncomingResponse, resolveValidationModes } from "./client-hooks.mjs";
14
15
  function getExpectedSchema(toolName) {
@@ -167,5 +168,6 @@ export {
167
168
  validateOutgoingRequest,
168
169
  validateRequest,
169
170
  validateResponse,
170
- withExternalSchemaRoot
171
+ withExternalSchemaRoot,
172
+ withServedReleaseSchemaRoots
171
173
  };
@@ -170,6 +170,32 @@ export { SCHEMA_FILENAME_SUFFIX };
170
170
  * we don't have schemas for.
171
171
  */
172
172
  export declare function hasSchemaBundle(version: string): boolean;
173
+ /**
174
+ * Run `fn` with schema roots for the releases an agent may echo in its
175
+ * `get_adcp_capabilities` response.
176
+ *
177
+ * Keys are AdCP versions in any form the SDK accepts for a pin (`'3.2'`,
178
+ * `'3.2.5'`, `'3.3.0-beta.1'`); values are schema-data roots in the same
179
+ * shape as `schemaRoot` (a directory holding `bundled/`, e.g. a hosted
180
+ * `dist/schemas/3.2.5`). Each root is checked against its key the same way
181
+ * `withExternalSchemaRoot` checks one, and a malformed map throws before `fn`
182
+ * runs.
183
+ *
184
+ * The roots are consulted only when validating a `get_adcp_capabilities`
185
+ * response against the release the agent echoed (see
186
+ * {@link resolveServedValidationVersion}): discovery's capability notices,
187
+ * `getAdcpCapabilities()` response validation and storyboard
188
+ * `response_schema` checks on that task. They are not activated for any other
189
+ * task, so every other response, storyboard selection and grading stay on the
190
+ * pin. A supplied root takes precedence over an external root registered for
191
+ * the same release and over the SDK's bundled schemas for it.
192
+ *
193
+ * Scoped to the current async execution chain; nested calls add to the
194
+ * enclosing scope, with the innermost entry for a release winning.
195
+ * `comply()` and `runStoryboard()` accept the same map as
196
+ * `servedReleaseSchemaRoots` and call this for you.
197
+ */
198
+ export declare function withServedReleaseSchemaRoots<T>(roots: Readonly<Record<string, string>> | undefined, fn: () => T): T;
173
199
  /** Load the exact self-contained request projection from an MCP role profile. */
174
200
  export declare function getMcpProfileInputSchema(toolName: string, profile: string, version?: string): Readonly<Record<string, unknown>> | undefined;
175
201
  /**
@@ -170,6 +170,32 @@ export { SCHEMA_FILENAME_SUFFIX };
170
170
  * we don't have schemas for.
171
171
  */
172
172
  export declare function hasSchemaBundle(version: string): boolean;
173
+ /**
174
+ * Run `fn` with schema roots for the releases an agent may echo in its
175
+ * `get_adcp_capabilities` response.
176
+ *
177
+ * Keys are AdCP versions in any form the SDK accepts for a pin (`'3.2'`,
178
+ * `'3.2.5'`, `'3.3.0-beta.1'`); values are schema-data roots in the same
179
+ * shape as `schemaRoot` (a directory holding `bundled/`, e.g. a hosted
180
+ * `dist/schemas/3.2.5`). Each root is checked against its key the same way
181
+ * `withExternalSchemaRoot` checks one, and a malformed map throws before `fn`
182
+ * runs.
183
+ *
184
+ * The roots are consulted only when validating a `get_adcp_capabilities`
185
+ * response against the release the agent echoed (see
186
+ * {@link resolveServedValidationVersion}): discovery's capability notices,
187
+ * `getAdcpCapabilities()` response validation and storyboard
188
+ * `response_schema` checks on that task. They are not activated for any other
189
+ * task, so every other response, storyboard selection and grading stay on the
190
+ * pin. A supplied root takes precedence over an external root registered for
191
+ * the same release and over the SDK's bundled schemas for it.
192
+ *
193
+ * Scoped to the current async execution chain; nested calls add to the
194
+ * enclosing scope, with the innermost entry for a release winning.
195
+ * `comply()` and `runStoryboard()` accept the same map as
196
+ * `servedReleaseSchemaRoots` and call this for you.
197
+ */
198
+ export declare function withServedReleaseSchemaRoots<T>(roots: Readonly<Record<string, string>> | undefined, fn: () => T): T;
173
199
  /** Load the exact self-contained request projection from an MCP role profile. */
174
200
  export declare function getMcpProfileInputSchema(toolName: string, profile: string, version?: string): Readonly<Record<string, unknown>> | undefined;
175
201
  /**
@@ -54,7 +54,9 @@ __export(schema_loader_exports, {
54
54
  unregisterExternalSchemaRoot: () => unregisterExternalSchemaRoot,
55
55
  validateAdcpVersionWire: () => validateAdcpVersionWire,
56
56
  withExternalSchemaRoot: () => withExternalSchemaRoot,
57
- withIdentity33SchemaScope: () => withIdentity33SchemaScope
57
+ withIdentity33SchemaScope: () => withIdentity33SchemaScope,
58
+ withServedReleaseSchemaRoots: () => withServedReleaseSchemaRoots,
59
+ withServedValidationVersion: () => withServedValidationVersion
58
60
  });
59
61
  module.exports = __toCommonJS(schema_loader_exports);
60
62
  var import_ajv = __toESM(require("ajv"));
@@ -755,28 +757,108 @@ function hasSchemaBundle(version) {
755
757
  return false;
756
758
  }
757
759
  }
758
- const SERVED_RELEASE_MAJOR = /^(\d+)\.\d+(?:-[a-zA-Z0-9.-]+)?$/;
759
- const NUMERIC_PIN_MAJOR = /^(\d+)\.\d+/;
760
- function resolveServedValidationVersion(response, pinnedVersion) {
761
- const pinMajor = NUMERIC_PIN_MAJOR.exec(pinnedVersion)?.[1];
762
- if (pinMajor === void 0) return pinnedVersion;
760
+ const servedReleaseSchemaRootScope = new import_async_hooks.AsyncLocalStorage();
761
+ const servedReleaseRootAliases = /* @__PURE__ */ new Map();
762
+ function withServedReleaseSchemaRoots(roots, fn) {
763
+ if (roots === void 0) return fn();
764
+ if (roots === null || typeof roots !== "object" || Array.isArray(roots)) {
765
+ throw new TypeError("servedReleaseSchemaRoots must be an object mapping AdCP versions to schema roots");
766
+ }
767
+ const entries = [];
768
+ for (const [version, root] of Object.entries(roots)) {
769
+ resolveBundleKey(version);
770
+ if (typeof root !== "string" || root.length === 0) {
771
+ throw new TypeError(`servedReleaseSchemaRoots["${version}"] must be a non-empty schema root path`);
772
+ }
773
+ const memoKey = `${version}\0${root}`;
774
+ let aliases = servedReleaseRootAliases.get(memoKey);
775
+ if (!aliases) {
776
+ if (!hasSchemaRootShape(root)) {
777
+ throw new Error(`Served-release AdCP schema root for version "${version}" not found or empty at ${root}`);
778
+ }
779
+ aliases = assertSchemaRootMatchesVersion(version, root);
780
+ servedReleaseRootAliases.set(memoKey, aliases);
781
+ }
782
+ const conflict = entries.find((entry) => entry.root !== root && [...aliases].some((alias) => entry.aliases.has(alias)));
783
+ if (conflict) {
784
+ throw new Error(
785
+ `servedReleaseSchemaRoots["${version}"] and servedReleaseSchemaRoots["${conflict.version}"] name the same release with different schema roots (${root}, ${conflict.root})`
786
+ );
787
+ }
788
+ entries.push({ version, root, aliases });
789
+ }
790
+ if (entries.length === 0) return fn();
791
+ const parent = servedReleaseSchemaRootScope.getStore() ?? [];
792
+ return servedReleaseSchemaRootScope.run([...entries, ...parent], fn);
793
+ }
794
+ function findServedReleaseSchemaRoot(release) {
795
+ const entries = servedReleaseSchemaRootScope.getStore();
796
+ if (!entries?.length) return void 0;
797
+ let key;
763
798
  try {
764
- if (isExternalSchemaRootActive(pinnedVersion)) return pinnedVersion;
799
+ key = resolveBundleKey(release);
765
800
  } catch {
766
- return pinnedVersion;
801
+ return void 0;
767
802
  }
768
- const echoed = response !== null && typeof response === "object" ? response.adcp_version : void 0;
769
- if (typeof echoed !== "string") return pinnedVersion;
803
+ return entries.find((entry) => entry.aliases.has(key) || entry.aliases.has(release));
804
+ }
805
+ const SERVED_RELEASE_MAJOR = /^(\d+)\.\d+(?:-[a-zA-Z0-9.-]+)?$/;
806
+ const NUMERIC_PIN_MAJOR = /^(\d+)\.\d+/;
807
+ function listsSupportedRelease(response, release) {
808
+ const adcp = response.adcp;
809
+ if (adcp === null || typeof adcp !== "object") return false;
810
+ const supported = adcp.supported_versions;
811
+ if (!Array.isArray(supported)) return false;
812
+ return supported.some((entry) => {
813
+ if (typeof entry !== "string") return false;
814
+ if (entry === release) return true;
815
+ try {
816
+ return toReleasePrecisionWire(entry) === release;
817
+ } catch {
818
+ return false;
819
+ }
820
+ });
821
+ }
822
+ function selectServedValidationVersion(response, pinnedVersion) {
823
+ const pin = { version: pinnedVersion };
824
+ const pinMajor = NUMERIC_PIN_MAJOR.exec(pinnedVersion)?.[1];
825
+ if (pinMajor === void 0) return pin;
826
+ if (response === null || typeof response !== "object") return pin;
827
+ const echoed = response.adcp_version;
828
+ if (typeof echoed !== "string") return pin;
770
829
  const echoMajor = SERVED_RELEASE_MAJOR.exec(echoed)?.[1];
771
- if (echoMajor === void 0 || echoMajor !== pinMajor) return pinnedVersion;
830
+ if (echoMajor === void 0 || echoMajor !== pinMajor) return pin;
772
831
  let pinnedRelease;
773
832
  try {
774
833
  pinnedRelease = toReleasePrecisionWire(pinnedVersion);
834
+ } catch {
835
+ return pin;
836
+ }
837
+ if (echoed === pinnedRelease) return pin;
838
+ if (!listsSupportedRelease(response, echoed)) return pin;
839
+ const served = findServedReleaseSchemaRoot(echoed);
840
+ if (served) return { version: echoed, served };
841
+ return hasSchemaBundle(echoed) ? { version: echoed } : pin;
842
+ }
843
+ function resolveServedValidationVersion(response, pinnedVersion) {
844
+ try {
845
+ return selectServedValidationVersion(response, pinnedVersion).version;
775
846
  } catch {
776
847
  return pinnedVersion;
777
848
  }
778
- if (echoed === pinnedRelease) return pinnedVersion;
779
- return hasSchemaBundle(echoed) ? echoed : pinnedVersion;
849
+ }
850
+ function withServedValidationVersion(response, pinnedVersion, fn) {
851
+ let selection;
852
+ try {
853
+ selection = selectServedValidationVersion(response, pinnedVersion);
854
+ } catch {
855
+ selection = { version: pinnedVersion };
856
+ }
857
+ const { version, served } = selection;
858
+ if (!served) return fn(version);
859
+ const next = new Map(scopedExternalSchemaRoots.getStore());
860
+ for (const alias of served.aliases) next.set(alias, served.root);
861
+ return scopedExternalSchemaRoots.run(next, () => fn(version));
780
862
  }
781
863
  const mcpToolSchemaCache = /* @__PURE__ */ new Map();
782
864
  const mcpToolSummaryCache = /* @__PURE__ */ new Map();
@@ -1049,5 +1131,7 @@ function cloneJsonValue(value) {
1049
1131
  unregisterExternalSchemaRoot,
1050
1132
  validateAdcpVersionWire,
1051
1133
  withExternalSchemaRoot,
1052
- withIdentity33SchemaScope
1134
+ withIdentity33SchemaScope,
1135
+ withServedReleaseSchemaRoots,
1136
+ withServedValidationVersion
1053
1137
  });