@adcp/sdk 15.9.4 → 15.9.6

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 (71) hide show
  1. package/dist/lib/core/AgentClient.d.mts +4 -2
  2. package/dist/lib/core/AgentClient.d.ts +4 -2
  3. package/dist/lib/core/AgentClient.js +4 -2
  4. package/dist/lib/core/AgentClient.mjs +4 -2
  5. package/dist/lib/core/SingleAgentClient.d.mts +7 -3
  6. package/dist/lib/core/SingleAgentClient.d.ts +7 -3
  7. package/dist/lib/core/SingleAgentClient.js +204 -13
  8. package/dist/lib/core/SingleAgentClient.mjs +209 -13
  9. package/dist/lib/core/TaskExecutor.d.mts +1 -0
  10. package/dist/lib/core/TaskExecutor.d.ts +1 -0
  11. package/dist/lib/core/TaskExecutor.js +48 -34
  12. package/dist/lib/core/TaskExecutor.mjs +49 -35
  13. package/dist/lib/core/product-cache.d.mts +3 -1
  14. package/dist/lib/core/product-cache.d.ts +3 -1
  15. package/dist/lib/core/product-cache.js +2 -1
  16. package/dist/lib/core/product-cache.mjs +2 -1
  17. package/dist/lib/registry/types.d.mts +3 -2
  18. package/dist/lib/registry/types.d.ts +3 -2
  19. package/dist/lib/registry/types.generated.d.mts +21 -9
  20. package/dist/lib/registry/types.generated.d.ts +21 -9
  21. package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
  22. package/dist/lib/server/decisioning/runtime/from-platform.js +6 -2
  23. package/dist/lib/server/decisioning/runtime/from-platform.mjs +6 -2
  24. package/dist/lib/testing/client.js +3 -5
  25. package/dist/lib/testing/client.mjs +4 -6
  26. package/dist/lib/testing/compliance/comply.js +7 -2
  27. package/dist/lib/testing/compliance/comply.mjs +8 -3
  28. package/dist/lib/testing/index.d.mts +1 -1
  29. package/dist/lib/testing/index.d.ts +1 -1
  30. package/dist/lib/testing/index.js +4 -2
  31. package/dist/lib/testing/index.mjs +8 -2
  32. package/dist/lib/testing/storyboard/runner.js +12 -12
  33. package/dist/lib/testing/storyboard/runner.mjs +14 -13
  34. package/dist/lib/testing/storyboard/validations.d.mts +1 -1
  35. package/dist/lib/testing/storyboard/validations.d.ts +1 -1
  36. package/dist/lib/testing/storyboard/validations.js +23 -1
  37. package/dist/lib/testing/storyboard/validations.mjs +28 -2
  38. package/dist/lib/testing/types.d.mts +39 -0
  39. package/dist/lib/testing/types.d.ts +39 -0
  40. package/dist/lib/v2/projection/augment-response.js +6 -3
  41. package/dist/lib/v2/projection/augment-response.mjs +8 -5
  42. package/dist/lib/v2/projection/creative-delivery.d.mts +2 -0
  43. package/dist/lib/v2/projection/creative-delivery.d.ts +2 -0
  44. package/dist/lib/v2/projection/creative-delivery.js +36 -17
  45. package/dist/lib/v2/projection/creative-delivery.mjs +37 -18
  46. package/dist/lib/v2/projection/legacy-metadata.d.mts +12 -0
  47. package/dist/lib/v2/projection/legacy-metadata.d.ts +12 -0
  48. package/dist/lib/v2/projection/legacy-metadata.js +41 -1
  49. package/dist/lib/v2/projection/legacy-metadata.mjs +37 -1
  50. package/dist/lib/v2/projection/v1-to-v2.d.mts +7 -0
  51. package/dist/lib/v2/projection/v1-to-v2.d.ts +7 -0
  52. package/dist/lib/v2/projection/v1-to-v2.js +83 -2
  53. package/dist/lib/v2/projection/v1-to-v2.mjs +86 -1
  54. package/dist/lib/validation/index.d.mts +1 -1
  55. package/dist/lib/validation/index.d.ts +1 -1
  56. package/dist/lib/validation/index.js +4 -2
  57. package/dist/lib/validation/index.mjs +4 -2
  58. package/dist/lib/validation/schema-loader.d.mts +26 -0
  59. package/dist/lib/validation/schema-loader.d.ts +26 -0
  60. package/dist/lib/validation/schema-loader.js +98 -14
  61. package/dist/lib/validation/schema-loader.mjs +95 -13
  62. package/dist/lib/version.d.mts +3 -3
  63. package/dist/lib/version.d.ts +3 -3
  64. package/dist/lib/version.js +3 -3
  65. package/dist/lib/version.mjs +3 -3
  66. package/docs/TYPE-SUMMARY.md +2 -2
  67. package/docs/guides/CREATIVE-DELIVERY.md +45 -6
  68. package/docs/guides/VALIDATE-YOUR-AGENT.md +34 -0
  69. package/docs/llms.txt +2 -2
  70. package/docs/migration-14.x-rc-worksheet.md +4 -4
  71. 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.
@@ -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++) {
@@ -230,6 +230,8 @@ export interface CreativeFormatSelectorContainer {
230
230
  /** Canonical package/product selectors accepted by the primary sync API. */
231
231
  export interface CanonicalCreativeFormatSelectorContainer {
232
232
  package_id?: string;
233
+ /** Target product, used to recover seller declarations after serializing selectors. */
234
+ product_id?: string;
233
235
  format_options?: ReadonlyArray<CanonicalFormatDeclaration>;
234
236
  format_option_refs?: ReadonlyArray<FormatOptionReference>;
235
237
  format_kind?: CanonicalFormatKind;
@@ -230,6 +230,8 @@ export interface CreativeFormatSelectorContainer {
230
230
  /** Canonical package/product selectors accepted by the primary sync API. */
231
231
  export interface CanonicalCreativeFormatSelectorContainer {
232
232
  package_id?: string;
233
+ /** Target product, used to recover seller declarations after serializing selectors. */
234
+ product_id?: string;
233
235
  format_options?: ReadonlyArray<CanonicalFormatDeclaration>;
234
236
  format_option_refs?: ReadonlyArray<FormatOptionReference>;
235
237
  format_kind?: CanonicalFormatKind;
@@ -20,6 +20,7 @@ var creative_delivery_exports = {};
20
20
  __export(creative_delivery_exports, {
21
21
  CreativeFormatCapabilityError: () => CreativeFormatCapabilityError,
22
22
  CreativeFormatProjectionError: () => CreativeFormatProjectionError,
23
+ canUseProductOptionsForCreativeWire: () => canUseProductOptionsForCreativeWire,
23
24
  projectCreativeForDelivery: () => projectCreativeForDelivery,
24
25
  projectMediaBuyCreativesForDelivery: () => projectMediaBuyCreativesForDelivery,
25
26
  projectSyncCreativesForDelivery: () => projectSyncCreativesForDelivery,
@@ -378,26 +379,15 @@ function candidatesFromContainer(container, canonicalFormatLegacyResolver, opera
378
379
  );
379
380
  }
380
381
  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) {
382
+ const nativeOptions = Array.isArray(container.format_options) ? container.format_options : [];
383
+ const formatOptions = [...nativeOptions, ...(0, import_legacy_metadata.selectedFormatOptions)(container)];
384
+ const resolvedOptions = formatOptions.map((optionValue) => {
396
385
  const option = record(optionValue);
397
- if (!option) continue;
386
+ if (!option) return optionValue;
398
387
  const formatKind = typeof option.format_kind === "string" ? option.format_kind : void 0;
399
388
  const formatOptionRef = optionReference(option);
400
389
  let refs = [...(0, import_legacy_metadata.legacyFormatRefsForDeclaration)(option)];
390
+ if (refs.length > 0) return optionValue;
401
391
  if (refs.length === 0 && option.canonical_formats_only !== true && formatKind) {
402
392
  try {
403
393
  refs = (0, import_v2_to_v1.resolveCanonicalFormatLegacyRefs)(canonicalFormatLegacyResolver, {
@@ -415,10 +405,33 @@ function candidatesFromContainer(container, canonicalFormatLegacyResolver, opera
415
405
  );
416
406
  }
417
407
  }
418
- for (const ref of refs) {
408
+ return refs.length > 0 ? { ...option, v1_format_ref: refs } : optionValue;
409
+ });
410
+ const pairedOptions = [
411
+ ...(0, import_v1_to_v2.withDualEmissionLegacyRefs)(resolvedOptions.slice(0, nativeOptions.length), container.format_ids),
412
+ ...resolvedOptions.slice(nativeOptions.length)
413
+ ];
414
+ for (const optionValue of pairedOptions) {
415
+ const option = record(optionValue);
416
+ if (!option) continue;
417
+ const formatKind = typeof option.format_kind === "string" ? option.format_kind : void 0;
418
+ const formatOptionRef = optionReference(option);
419
+ for (const ref of (0, import_legacy_metadata.legacyFormatRefsForDeclaration)(option)) {
419
420
  candidates.push({ ref, formatKind, formatOptionRef, selectorParams: option.params ?? container.params });
420
421
  }
421
422
  }
423
+ const linkedRefs = new Set(candidates.map((candidate) => candidateKey({ ref: candidate.ref })));
424
+ const containerKind = typeof container.format_kind === "string" ? container.format_kind : void 0;
425
+ for (const key of ["format_ids", "formats"]) {
426
+ for (const ref of refsFromValue(container[key])) {
427
+ if (linkedRefs.has(candidateKey({ ref }))) continue;
428
+ candidates.push({
429
+ ref,
430
+ formatKind: (0, import_v1_to_v2.resolveCanonicalFormatKind)(ref.id) ?? containerKind,
431
+ selectorParams: container.params
432
+ });
433
+ }
434
+ }
422
435
  return uniqueCandidates(candidates);
423
436
  }
424
437
  function containerOptsOutOfLegacy(container, formatKind, formatOptionRef) {
@@ -1106,10 +1119,16 @@ function resolveCreativeFormatWireMode(capabilities, negotiatedVersion) {
1106
1119
  if (explicitCanonical === false) return "legacy";
1107
1120
  return "unknown";
1108
1121
  }
1122
+ function canUseProductOptionsForCreativeWire(capabilities, requestedVersion) {
1123
+ const caps = record(capabilities);
1124
+ const negotiated = mutuallyServedRelease(caps, record(caps?._raw), requestedVersion);
1125
+ return negotiated?.major === 3 && negotiated.minor === 1;
1126
+ }
1109
1127
  // Annotate the CommonJS export names for ESM import in node:
1110
1128
  0 && (module.exports = {
1111
1129
  CreativeFormatCapabilityError,
1112
1130
  CreativeFormatProjectionError,
1131
+ canUseProductOptionsForCreativeWire,
1113
1132
  projectCreativeForDelivery,
1114
1133
  projectMediaBuyCreativesForDelivery,
1115
1134
  projectSyncCreativesForDelivery,
@@ -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) {
@@ -1085,9 +1098,15 @@ function resolveCreativeFormatWireMode(capabilities, negotiatedVersion) {
1085
1098
  if (explicitCanonical === false) return "legacy";
1086
1099
  return "unknown";
1087
1100
  }
1101
+ function canUseProductOptionsForCreativeWire(capabilities, requestedVersion) {
1102
+ const caps = record(capabilities);
1103
+ const negotiated = mutuallyServedRelease(caps, record(caps?._raw), requestedVersion);
1104
+ return negotiated?.major === 3 && negotiated.minor === 1;
1105
+ }
1088
1106
  export {
1089
1107
  CreativeFormatCapabilityError,
1090
1108
  CreativeFormatProjectionError,
1109
+ canUseProductOptionsForCreativeWire,
1091
1110
  projectCreativeForDelivery,
1092
1111
  projectMediaBuyCreativesForDelivery,
1093
1112
  projectSyncCreativesForDelivery,
@@ -12,9 +12,21 @@ type DeclarationWithMetadata = CanonicalFormatDeclaration;
12
12
  export type PackageSelectionMetadata = {
13
13
  format_option_refs?: readonly unknown[];
14
14
  };
15
+ export interface SellerFormatEvidence {
16
+ scopeKey: string;
17
+ accountScope: string;
18
+ productId: string;
19
+ expiresAt: number;
20
+ }
21
+ /** Positive evidence is client/account-bound and cannot survive a JSON round trip on its own. */
22
+ export declare function rememberSellerFormatEvidence(declaration: object, evidence: SellerFormatEvidence): void;
23
+ export declare function sellerFormatEvidence(declaration: object): SellerFormatEvidence | undefined;
15
24
  /** Keep seller-origin evidence private while canonical products pass through the cache. */
16
25
  export declare function rememberSellerDeclaredFormatOption(declaration: object, declared: boolean): void;
17
26
  export declare function sellerDeclaredFormatOption(declaration: object): boolean | undefined;
27
+ /** A pure legacy projection cannot establish what the original seller advertised. */
28
+ export declare function rememberSynthesizedFormatOption(declaration: object): void;
29
+ export declare function isSynthesizedFormatOption(declaration: object): boolean;
18
30
  export declare function concealLegacyFormatRefs(declaration: ProductFormatDeclaration | V2ProductFormatDeclaration | CanonicalFormatDeclaration): DeclarationWithMetadata;
19
31
  export declare function legacyFormatRefsForDeclaration(value: unknown): readonly V1FormatId[];
20
32
  export declare function selectedFormatOptions(value: unknown): readonly DeclarationWithMetadata[];
@@ -12,9 +12,21 @@ type DeclarationWithMetadata = CanonicalFormatDeclaration;
12
12
  export type PackageSelectionMetadata = {
13
13
  format_option_refs?: readonly unknown[];
14
14
  };
15
+ export interface SellerFormatEvidence {
16
+ scopeKey: string;
17
+ accountScope: string;
18
+ productId: string;
19
+ expiresAt: number;
20
+ }
21
+ /** Positive evidence is client/account-bound and cannot survive a JSON round trip on its own. */
22
+ export declare function rememberSellerFormatEvidence(declaration: object, evidence: SellerFormatEvidence): void;
23
+ export declare function sellerFormatEvidence(declaration: object): SellerFormatEvidence | undefined;
15
24
  /** Keep seller-origin evidence private while canonical products pass through the cache. */
16
25
  export declare function rememberSellerDeclaredFormatOption(declaration: object, declared: boolean): void;
17
26
  export declare function sellerDeclaredFormatOption(declaration: object): boolean | undefined;
27
+ /** A pure legacy projection cannot establish what the original seller advertised. */
28
+ export declare function rememberSynthesizedFormatOption(declaration: object): void;
29
+ export declare function isSynthesizedFormatOption(declaration: object): boolean;
18
30
  export declare function concealLegacyFormatRefs(declaration: ProductFormatDeclaration | V2ProductFormatDeclaration | CanonicalFormatDeclaration): DeclarationWithMetadata;
19
31
  export declare function legacyFormatRefsForDeclaration(value: unknown): readonly V1FormatId[];
20
32
  export declare function selectedFormatOptions(value: unknown): readonly DeclarationWithMetadata[];
@@ -20,22 +20,52 @@ var legacy_metadata_exports = {};
20
20
  __export(legacy_metadata_exports, {
21
21
  concealLegacyFormatRefs: () => concealLegacyFormatRefs,
22
22
  concealSelectedFormatOptions: () => concealSelectedFormatOptions,
23
+ isSynthesizedFormatOption: () => isSynthesizedFormatOption,
23
24
  legacyFormatRefsForDeclaration: () => legacyFormatRefsForDeclaration,
24
25
  rememberSellerDeclaredFormatOption: () => rememberSellerDeclaredFormatOption,
26
+ rememberSellerFormatEvidence: () => rememberSellerFormatEvidence,
27
+ rememberSynthesizedFormatOption: () => rememberSynthesizedFormatOption,
25
28
  selectedFormatOptions: () => selectedFormatOptions,
26
29
  sellerDeclaredFormatOption: () => sellerDeclaredFormatOption,
30
+ sellerFormatEvidence: () => sellerFormatEvidence,
27
31
  structuredCloneWithLegacyCreativeMetadata: () => structuredCloneWithLegacyCreativeMetadata,
28
32
  transferLegacyCreativeMetadata: () => transferLegacyCreativeMetadata
29
33
  });
30
34
  module.exports = __toCommonJS(legacy_metadata_exports);
35
+ var import_jcs = require('../../utils/jcs.js');
31
36
  const legacyRefsByDeclaration = /* @__PURE__ */ new WeakMap();
32
37
  const selectedOptionsByRefs = /* @__PURE__ */ new WeakMap();
33
38
  const sellerDeclaredByDeclaration = /* @__PURE__ */ new WeakMap();
39
+ const sellerEvidenceByDeclaration = /* @__PURE__ */ new WeakMap();
40
+ function rememberSellerFormatEvidence(declaration, evidence) {
41
+ try {
42
+ sellerEvidenceByDeclaration.set(
43
+ declaration,
44
+ Object.freeze({ ...evidence, fingerprint: (0, import_jcs.canonicalize)(declaration) })
45
+ );
46
+ } catch {
47
+ }
48
+ }
49
+ function sellerFormatEvidence(declaration) {
50
+ const evidence = sellerEvidenceByDeclaration.get(declaration);
51
+ try {
52
+ return evidence?.fingerprint === (0, import_jcs.canonicalize)(declaration) ? evidence : void 0;
53
+ } catch {
54
+ return void 0;
55
+ }
56
+ }
34
57
  function rememberSellerDeclaredFormatOption(declaration, declared) {
35
58
  sellerDeclaredByDeclaration.set(declaration, declared);
36
59
  }
37
60
  function sellerDeclaredFormatOption(declaration) {
38
- return sellerDeclaredByDeclaration.get(declaration);
61
+ const origin = sellerDeclaredByDeclaration.get(declaration);
62
+ return typeof origin === "boolean" ? origin : void 0;
63
+ }
64
+ function rememberSynthesizedFormatOption(declaration) {
65
+ sellerDeclaredByDeclaration.set(declaration, "synthesized");
66
+ }
67
+ function isSynthesizedFormatOption(declaration) {
68
+ return sellerDeclaredByDeclaration.get(declaration) === "synthesized";
39
69
  }
40
70
  function concealLegacyFormatRefs(declaration) {
41
71
  const { v1_format_ref: refs, ...canonical } = declaration;
@@ -43,6 +73,8 @@ function concealLegacyFormatRefs(declaration) {
43
73
  const refsToConceal = Array.isArray(existingHidden) && existingHidden.length > 0 ? existingHidden : refs;
44
74
  const declared = sellerDeclaredByDeclaration.get(declaration);
45
75
  if (declared !== void 0) sellerDeclaredByDeclaration.set(canonical, declared);
76
+ const evidence = sellerEvidenceByDeclaration.get(declaration);
77
+ if (evidence) sellerEvidenceByDeclaration.set(canonical, evidence);
46
78
  if (Array.isArray(refsToConceal) && refsToConceal.length > 0) {
47
79
  legacyRefsByDeclaration.set(
48
80
  canonical,
@@ -75,6 +107,8 @@ function transferLegacyCreativeMetadata(source, target) {
75
107
  if (selected) selectedOptionsByRefs.set(target, selected);
76
108
  const declared = sellerDeclaredByDeclaration.get(source);
77
109
  if (declared !== void 0) sellerDeclaredByDeclaration.set(target, declared);
110
+ const evidence = sellerEvidenceByDeclaration.get(source);
111
+ if (evidence) sellerEvidenceByDeclaration.set(target, evidence);
78
112
  }
79
113
  function structuredCloneWithLegacyCreativeMetadata(value) {
80
114
  const clone = structuredClone(value);
@@ -82,6 +116,8 @@ function structuredCloneWithLegacyCreativeMetadata(value) {
82
116
  const snapshotMetadata = (source, target) => {
83
117
  const declared = sellerDeclaredByDeclaration.get(source);
84
118
  if (declared !== void 0) sellerDeclaredByDeclaration.set(target, declared);
119
+ const evidence = sellerEvidenceByDeclaration.get(source);
120
+ if (evidence) sellerEvidenceByDeclaration.set(target, evidence);
85
121
  const refs = legacyRefsByDeclaration.get(source);
86
122
  if (refs) legacyRefsByDeclaration.set(target, structuredClone(refs));
87
123
  const selected = selectedOptionsByRefs.get(source);
@@ -119,10 +155,14 @@ function structuredCloneWithLegacyCreativeMetadata(value) {
119
155
  0 && (module.exports = {
120
156
  concealLegacyFormatRefs,
121
157
  concealSelectedFormatOptions,
158
+ isSynthesizedFormatOption,
122
159
  legacyFormatRefsForDeclaration,
123
160
  rememberSellerDeclaredFormatOption,
161
+ rememberSellerFormatEvidence,
162
+ rememberSynthesizedFormatOption,
124
163
  selectedFormatOptions,
125
164
  sellerDeclaredFormatOption,
165
+ sellerFormatEvidence,
126
166
  structuredCloneWithLegacyCreativeMetadata,
127
167
  transferLegacyCreativeMetadata
128
168
  });
@@ -1,11 +1,37 @@
1
+ import { canonicalize } from "../../utils/jcs.mjs";
1
2
  const legacyRefsByDeclaration = /* @__PURE__ */ new WeakMap();
2
3
  const selectedOptionsByRefs = /* @__PURE__ */ new WeakMap();
3
4
  const sellerDeclaredByDeclaration = /* @__PURE__ */ new WeakMap();
5
+ const sellerEvidenceByDeclaration = /* @__PURE__ */ new WeakMap();
6
+ function rememberSellerFormatEvidence(declaration, evidence) {
7
+ try {
8
+ sellerEvidenceByDeclaration.set(
9
+ declaration,
10
+ Object.freeze({ ...evidence, fingerprint: canonicalize(declaration) })
11
+ );
12
+ } catch {
13
+ }
14
+ }
15
+ function sellerFormatEvidence(declaration) {
16
+ const evidence = sellerEvidenceByDeclaration.get(declaration);
17
+ try {
18
+ return evidence?.fingerprint === canonicalize(declaration) ? evidence : void 0;
19
+ } catch {
20
+ return void 0;
21
+ }
22
+ }
4
23
  function rememberSellerDeclaredFormatOption(declaration, declared) {
5
24
  sellerDeclaredByDeclaration.set(declaration, declared);
6
25
  }
7
26
  function sellerDeclaredFormatOption(declaration) {
8
- return sellerDeclaredByDeclaration.get(declaration);
27
+ const origin = sellerDeclaredByDeclaration.get(declaration);
28
+ return typeof origin === "boolean" ? origin : void 0;
29
+ }
30
+ function rememberSynthesizedFormatOption(declaration) {
31
+ sellerDeclaredByDeclaration.set(declaration, "synthesized");
32
+ }
33
+ function isSynthesizedFormatOption(declaration) {
34
+ return sellerDeclaredByDeclaration.get(declaration) === "synthesized";
9
35
  }
10
36
  function concealLegacyFormatRefs(declaration) {
11
37
  const { v1_format_ref: refs, ...canonical } = declaration;
@@ -13,6 +39,8 @@ function concealLegacyFormatRefs(declaration) {
13
39
  const refsToConceal = Array.isArray(existingHidden) && existingHidden.length > 0 ? existingHidden : refs;
14
40
  const declared = sellerDeclaredByDeclaration.get(declaration);
15
41
  if (declared !== void 0) sellerDeclaredByDeclaration.set(canonical, declared);
42
+ const evidence = sellerEvidenceByDeclaration.get(declaration);
43
+ if (evidence) sellerEvidenceByDeclaration.set(canonical, evidence);
16
44
  if (Array.isArray(refsToConceal) && refsToConceal.length > 0) {
17
45
  legacyRefsByDeclaration.set(
18
46
  canonical,
@@ -45,6 +73,8 @@ function transferLegacyCreativeMetadata(source, target) {
45
73
  if (selected) selectedOptionsByRefs.set(target, selected);
46
74
  const declared = sellerDeclaredByDeclaration.get(source);
47
75
  if (declared !== void 0) sellerDeclaredByDeclaration.set(target, declared);
76
+ const evidence = sellerEvidenceByDeclaration.get(source);
77
+ if (evidence) sellerEvidenceByDeclaration.set(target, evidence);
48
78
  }
49
79
  function structuredCloneWithLegacyCreativeMetadata(value) {
50
80
  const clone = structuredClone(value);
@@ -52,6 +82,8 @@ function structuredCloneWithLegacyCreativeMetadata(value) {
52
82
  const snapshotMetadata = (source, target) => {
53
83
  const declared = sellerDeclaredByDeclaration.get(source);
54
84
  if (declared !== void 0) sellerDeclaredByDeclaration.set(target, declared);
85
+ const evidence = sellerEvidenceByDeclaration.get(source);
86
+ if (evidence) sellerEvidenceByDeclaration.set(target, evidence);
55
87
  const refs = legacyRefsByDeclaration.get(source);
56
88
  if (refs) legacyRefsByDeclaration.set(target, structuredClone(refs));
57
89
  const selected = selectedOptionsByRefs.get(source);
@@ -88,10 +120,14 @@ function structuredCloneWithLegacyCreativeMetadata(value) {
88
120
  export {
89
121
  concealLegacyFormatRefs,
90
122
  concealSelectedFormatOptions,
123
+ isSynthesizedFormatOption,
91
124
  legacyFormatRefsForDeclaration,
92
125
  rememberSellerDeclaredFormatOption,
126
+ rememberSellerFormatEvidence,
127
+ rememberSynthesizedFormatOption,
93
128
  selectedFormatOptions,
94
129
  sellerDeclaredFormatOption,
130
+ sellerFormatEvidence,
95
131
  structuredCloneWithLegacyCreativeMetadata,
96
132
  transferLegacyCreativeMetadata
97
133
  };
@@ -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[];