@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
@@ -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.
@@ -224,7 +224,10 @@ function toCanonicalFormatOptionsWithRoutes(productId, formatOptions) {
224
224
  }
225
225
  function toAdditiveCanonicalProduct(product, options) {
226
226
  const { product: augmented, diagnostics } = augmentProductWithFormatOptions(product, options);
227
- const projected = toCanonicalFormatOptionsWithRoutes(product.product_id, augmented.format_options);
227
+ const projected = toCanonicalFormatOptionsWithRoutes(
228
+ product.product_id,
229
+ options?._inferDualEmissionLegacyRefs === false ? augmented.format_options : (0, import_v1_to_v2.withDualEmissionLegacyRefs)(augmented.format_options, product.format_ids)
230
+ );
228
231
  return {
229
232
  product: {
230
233
  ...augmented,
@@ -299,13 +302,13 @@ function formatRefCoverageKey(ref) {
299
302
  function toCanonicalOnlyProduct(product, options) {
300
303
  const existing = product.format_options;
301
304
  if (Array.isArray(existing)) {
302
- const formatOptions = existing;
305
+ const formatOptions = options?._inferDualEmissionLegacyRefs === false ? existing : (0, import_v1_to_v2.withDualEmissionLegacyRefs)(existing, product.format_ids);
303
306
  const diagnostics2 = [];
304
307
  const inputIds = Array.isArray(product.format_ids) ? product.format_ids : [];
305
308
  if (inputIds.length > 0) {
306
309
  const covered = /* @__PURE__ */ new Set();
307
310
  for (const opt of formatOptions) {
308
- const refs = Array.isArray(opt?.v1_format_ref) ? opt.v1_format_ref : [];
311
+ const refs = (0, import_legacy_metadata.legacyFormatRefsForDeclaration)(opt);
309
312
  for (const ref of refs) covered.add(formatRefCoverageKey(ref));
310
313
  }
311
314
  for (let k = 0; k < inputIds.length; k++) {
@@ -1,7 +1,7 @@
1
- import { projectV1ProductToV2 } from "./v1-to-v2.mjs";
1
+ import { projectV1ProductToV2, withDualEmissionLegacyRefs } from "./v1-to-v2.mjs";
2
2
  import { LIBRARY_VERSION } from "../../version.mjs";
3
3
  import { canonicalize as canonicalizeJson } from "../../utils/jcs.mjs";
4
- import { concealLegacyFormatRefs } from "./legacy-metadata.mjs";
4
+ import { concealLegacyFormatRefs, legacyFormatRefsForDeclaration } from "./legacy-metadata.mjs";
5
5
  import { legacyRoutesForProduct } from "./legacy-routes.mjs";
6
6
  const SDK_ID = `@adcp/sdk@${LIBRARY_VERSION}`;
7
7
  const DIRECT_LEGACY_FORMAT_IDENTITY_KEYS = /* @__PURE__ */ new Set(["agenturl", "formatid", "formatids", "v1formatref"]);
@@ -194,7 +194,10 @@ function toCanonicalFormatOptionsWithRoutes(productId, formatOptions) {
194
194
  }
195
195
  function toAdditiveCanonicalProduct(product, options) {
196
196
  const { product: augmented, diagnostics } = augmentProductWithFormatOptions(product, options);
197
- const projected = toCanonicalFormatOptionsWithRoutes(product.product_id, augmented.format_options);
197
+ const projected = toCanonicalFormatOptionsWithRoutes(
198
+ product.product_id,
199
+ options?._inferDualEmissionLegacyRefs === false ? augmented.format_options : withDualEmissionLegacyRefs(augmented.format_options, product.format_ids)
200
+ );
198
201
  return {
199
202
  product: {
200
203
  ...augmented,
@@ -269,13 +272,13 @@ function formatRefCoverageKey(ref) {
269
272
  function toCanonicalOnlyProduct(product, options) {
270
273
  const existing = product.format_options;
271
274
  if (Array.isArray(existing)) {
272
- const formatOptions = existing;
275
+ const formatOptions = options?._inferDualEmissionLegacyRefs === false ? existing : withDualEmissionLegacyRefs(existing, product.format_ids);
273
276
  const diagnostics2 = [];
274
277
  const inputIds = Array.isArray(product.format_ids) ? product.format_ids : [];
275
278
  if (inputIds.length > 0) {
276
279
  const covered = /* @__PURE__ */ new Set();
277
280
  for (const opt of formatOptions) {
278
- const refs = Array.isArray(opt?.v1_format_ref) ? opt.v1_format_ref : [];
281
+ const refs = legacyFormatRefsForDeclaration(opt);
279
282
  for (const ref of refs) covered.add(formatRefCoverageKey(ref));
280
283
  }
281
284
  for (let k = 0; k < inputIds.length; k++) {
@@ -378,26 +378,15 @@ function candidatesFromContainer(container, canonicalFormatLegacyResolver, opera
378
378
  );
379
379
  }
380
380
  const candidates = [];
381
- const containerKind = typeof container.format_kind === "string" ? container.format_kind : void 0;
382
- for (const key of ["format_ids", "formats"]) {
383
- for (const ref of refsFromValue(container[key])) {
384
- candidates.push({
385
- ref,
386
- formatKind: (0, import_v1_to_v2.resolveCanonicalFormatKind)(ref.id) ?? containerKind,
387
- selectorParams: container.params
388
- });
389
- }
390
- }
391
- const formatOptions = [
392
- ...Array.isArray(container.format_options) ? container.format_options : [],
393
- ...(0, import_legacy_metadata.selectedFormatOptions)(container)
394
- ];
395
- for (const optionValue of formatOptions) {
381
+ const nativeOptions = Array.isArray(container.format_options) ? container.format_options : [];
382
+ const formatOptions = [...nativeOptions, ...(0, import_legacy_metadata.selectedFormatOptions)(container)];
383
+ const resolvedOptions = formatOptions.map((optionValue) => {
396
384
  const option = record(optionValue);
397
- if (!option) continue;
385
+ if (!option) return optionValue;
398
386
  const formatKind = typeof option.format_kind === "string" ? option.format_kind : void 0;
399
387
  const formatOptionRef = optionReference(option);
400
388
  let refs = [...(0, import_legacy_metadata.legacyFormatRefsForDeclaration)(option)];
389
+ if (refs.length > 0) return optionValue;
401
390
  if (refs.length === 0 && option.canonical_formats_only !== true && formatKind) {
402
391
  try {
403
392
  refs = (0, import_v2_to_v1.resolveCanonicalFormatLegacyRefs)(canonicalFormatLegacyResolver, {
@@ -415,10 +404,33 @@ function candidatesFromContainer(container, canonicalFormatLegacyResolver, opera
415
404
  );
416
405
  }
417
406
  }
418
- for (const ref of refs) {
407
+ return refs.length > 0 ? { ...option, v1_format_ref: refs } : optionValue;
408
+ });
409
+ const pairedOptions = [
410
+ ...(0, import_v1_to_v2.withDualEmissionLegacyRefs)(resolvedOptions.slice(0, nativeOptions.length), container.format_ids),
411
+ ...resolvedOptions.slice(nativeOptions.length)
412
+ ];
413
+ for (const optionValue of pairedOptions) {
414
+ const option = record(optionValue);
415
+ if (!option) continue;
416
+ const formatKind = typeof option.format_kind === "string" ? option.format_kind : void 0;
417
+ const formatOptionRef = optionReference(option);
418
+ for (const ref of (0, import_legacy_metadata.legacyFormatRefsForDeclaration)(option)) {
419
419
  candidates.push({ ref, formatKind, formatOptionRef, selectorParams: option.params ?? container.params });
420
420
  }
421
421
  }
422
+ const linkedRefs = new Set(candidates.map((candidate) => candidateKey({ ref: candidate.ref })));
423
+ const containerKind = typeof container.format_kind === "string" ? container.format_kind : void 0;
424
+ for (const key of ["format_ids", "formats"]) {
425
+ for (const ref of refsFromValue(container[key])) {
426
+ if (linkedRefs.has(candidateKey({ ref }))) continue;
427
+ candidates.push({
428
+ ref,
429
+ formatKind: (0, import_v1_to_v2.resolveCanonicalFormatKind)(ref.id) ?? containerKind,
430
+ selectorParams: container.params
431
+ });
432
+ }
433
+ }
422
434
  return uniqueCandidates(candidates);
423
435
  }
424
436
  function containerOptsOutOfLegacy(container, formatKind, formatOptionRef) {
@@ -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
  /**