@adcp/sdk 16.0.0-beta.1 → 16.0.0-beta.3

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 (155) hide show
  1. package/README.md +54 -0
  2. package/dist/lib/core/ConversationTypes.d.mts +8 -3
  3. package/dist/lib/core/ConversationTypes.d.ts +8 -3
  4. package/dist/lib/core/SingleAgentClient.d.mts +15 -4
  5. package/dist/lib/core/SingleAgentClient.d.ts +15 -4
  6. package/dist/lib/core/SingleAgentClient.js +29 -8
  7. package/dist/lib/core/SingleAgentClient.mjs +29 -8
  8. package/dist/lib/core/TaskExecutor.js +5 -1
  9. package/dist/lib/core/TaskExecutor.mjs +5 -1
  10. package/dist/lib/errors/index.d.mts +9 -4
  11. package/dist/lib/errors/index.d.ts +9 -4
  12. package/dist/lib/identity-3.3/generated-compat.d.mts +34 -0
  13. package/dist/lib/identity-3.3/generated-compat.d.ts +34 -0
  14. package/dist/lib/identity-3.3/generated-compat.js +16 -0
  15. package/dist/lib/identity-3.3/generated-compat.mjs +0 -0
  16. package/dist/lib/identity-3.3/index.d.mts +2 -2
  17. package/dist/lib/identity-3.3/index.d.ts +2 -2
  18. package/dist/lib/identity-3.3/index.js +4 -0
  19. package/dist/lib/identity-3.3/index.mjs +5 -1
  20. package/dist/lib/identity-3.3/lifecycle.d.mts +17 -5
  21. package/dist/lib/identity-3.3/lifecycle.d.ts +17 -5
  22. package/dist/lib/identity-3.3/lifecycle.js +32 -5
  23. package/dist/lib/identity-3.3/lifecycle.mjs +38 -6
  24. package/dist/lib/identity-3.3/revocation.d.mts +39 -3
  25. package/dist/lib/identity-3.3/revocation.d.ts +39 -3
  26. package/dist/lib/identity-3.3/revocation.js +88 -18
  27. package/dist/lib/identity-3.3/revocation.mjs +89 -19
  28. package/dist/lib/identity-3.3/server.d.mts +12 -0
  29. package/dist/lib/identity-3.3/server.d.ts +12 -0
  30. package/dist/lib/identity-3.3/server.js +103 -43
  31. package/dist/lib/identity-3.3/server.mjs +111 -45
  32. package/dist/lib/identity-3.3/trust.d.mts +62 -1
  33. package/dist/lib/identity-3.3/trust.d.ts +62 -1
  34. package/dist/lib/identity-3.3/trust.js +126 -21
  35. package/dist/lib/identity-3.3/trust.mjs +124 -21
  36. package/dist/lib/identity-3.3/types.d.mts +39 -11
  37. package/dist/lib/identity-3.3/types.d.ts +39 -11
  38. package/dist/lib/index.d.mts +3 -3
  39. package/dist/lib/index.d.ts +3 -3
  40. package/dist/lib/index.js +8 -0
  41. package/dist/lib/index.mjs +9 -1
  42. package/dist/lib/negotiation/types.d.mts +20 -39
  43. package/dist/lib/negotiation/types.d.ts +20 -39
  44. package/dist/lib/protocols/index.d.mts +61 -25
  45. package/dist/lib/protocols/index.d.ts +61 -25
  46. package/dist/lib/protocols/index.js +11 -6
  47. package/dist/lib/protocols/index.mjs +5 -2
  48. package/dist/lib/protocols/mcp-modern.js +1 -0
  49. package/dist/lib/protocols/mcp-modern.mjs +2 -1
  50. package/dist/lib/protocols/mcp.js +1 -0
  51. package/dist/lib/protocols/mcp.mjs +2 -1
  52. package/dist/lib/protocols/responseSizeLimit.d.mts +91 -5
  53. package/dist/lib/protocols/responseSizeLimit.d.ts +91 -5
  54. package/dist/lib/protocols/responseSizeLimit.js +195 -31
  55. package/dist/lib/protocols/responseSizeLimit.mjs +192 -31
  56. package/dist/lib/reporting/bigquery/index.js +8 -0
  57. package/dist/lib/reporting/bigquery/index.mjs +8 -0
  58. package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
  59. package/dist/lib/server/auth-signature.d.mts +6 -1
  60. package/dist/lib/server/auth-signature.d.ts +6 -1
  61. package/dist/lib/server/auth-signature.js +24 -13
  62. package/dist/lib/server/auth-signature.mjs +29 -13
  63. package/dist/lib/server/auth.d.mts +2 -2
  64. package/dist/lib/server/auth.d.ts +2 -2
  65. package/dist/lib/server/create-adcp-server.js +31 -10
  66. package/dist/lib/server/create-adcp-server.mjs +37 -11
  67. package/dist/lib/signing/crypto.d.mts +15 -0
  68. package/dist/lib/signing/crypto.d.ts +15 -0
  69. package/dist/lib/signing/crypto.js +83 -3
  70. package/dist/lib/signing/crypto.mjs +86 -3
  71. package/dist/lib/signing/fetch-async.js +1 -1
  72. package/dist/lib/signing/fetch-async.mjs +1 -1
  73. package/dist/lib/signing/fetch.js +1 -1
  74. package/dist/lib/signing/fetch.mjs +1 -1
  75. package/dist/lib/signing/middleware.js +9 -0
  76. package/dist/lib/signing/middleware.mjs +14 -0
  77. package/dist/lib/signing/parser.d.mts +8 -5
  78. package/dist/lib/signing/parser.d.ts +8 -5
  79. package/dist/lib/signing/parser.js +77 -61
  80. package/dist/lib/signing/parser.mjs +77 -61
  81. package/dist/lib/signing/revocation.js +3 -0
  82. package/dist/lib/signing/revocation.mjs +3 -0
  83. package/dist/lib/signing/server.d.mts +1 -1
  84. package/dist/lib/signing/server.d.ts +1 -1
  85. package/dist/lib/signing/server.js +2 -0
  86. package/dist/lib/signing/server.mjs +2 -1
  87. package/dist/lib/signing/signer.js +2 -8
  88. package/dist/lib/signing/signer.mjs +3 -9
  89. package/dist/lib/signing/testing.js +2 -6
  90. package/dist/lib/signing/testing.mjs +3 -7
  91. package/dist/lib/signing/verifier.js +3 -3
  92. package/dist/lib/signing/verifier.mjs +4 -4
  93. package/dist/lib/signing/web-bot-auth.d.mts +84 -10
  94. package/dist/lib/signing/web-bot-auth.d.ts +84 -10
  95. package/dist/lib/signing/web-bot-auth.js +278 -88
  96. package/dist/lib/signing/web-bot-auth.mjs +279 -94
  97. package/dist/lib/signing/webhook-verifier.js +3 -3
  98. package/dist/lib/signing/webhook-verifier.mjs +4 -4
  99. package/dist/lib/substitution/compile.js +2 -1
  100. package/dist/lib/substitution/compile.mjs +2 -1
  101. package/dist/lib/substitution/consent-macros.d.mts +4 -0
  102. package/dist/lib/substitution/consent-macros.d.ts +4 -0
  103. package/dist/lib/substitution/consent-macros.js +45 -0
  104. package/dist/lib/substitution/consent-macros.mjs +20 -0
  105. package/dist/lib/substitution/index.d.mts +1 -1
  106. package/dist/lib/substitution/index.d.ts +1 -1
  107. package/dist/lib/substitution/index.js +2 -0
  108. package/dist/lib/substitution/index.mjs +2 -1
  109. package/dist/lib/substitution/translate.d.mts +15 -2
  110. package/dist/lib/substitution/translate.d.ts +15 -2
  111. package/dist/lib/substitution/translate.js +148 -26
  112. package/dist/lib/substitution/translate.mjs +147 -26
  113. package/dist/lib/testing/client.js +2 -1
  114. package/dist/lib/testing/client.mjs +2 -1
  115. package/dist/lib/testing/scenarios/capabilities.js +1 -1
  116. package/dist/lib/testing/scenarios/capabilities.mjs +1 -1
  117. package/dist/lib/testing/storyboard/request-signing/builder.js +2 -5
  118. package/dist/lib/testing/storyboard/request-signing/builder.mjs +3 -6
  119. package/dist/lib/testing/storyboard/signer-grader/grader.js +2 -7
  120. package/dist/lib/testing/storyboard/signer-grader/grader.mjs +2 -7
  121. package/dist/lib/testing/storyboard/validations.d.mts +1 -1
  122. package/dist/lib/testing/storyboard/validations.d.ts +1 -1
  123. package/dist/lib/types/enums.generated.d.mts +3 -0
  124. package/dist/lib/types/enums.generated.d.ts +3 -0
  125. package/dist/lib/types/enums.generated.js +9 -0
  126. package/dist/lib/types/enums.generated.mjs +6 -0
  127. package/dist/lib/v2/projection/catalog-snapshot.js +3 -1
  128. package/dist/lib/v2/projection/catalog-snapshot.mjs +3 -1
  129. package/dist/lib/v2/projection/creative-delivery.d.mts +31 -9
  130. package/dist/lib/v2/projection/creative-delivery.d.ts +31 -9
  131. package/dist/lib/v2/projection/index.d.mts +1 -1
  132. package/dist/lib/v2/projection/index.d.ts +1 -1
  133. package/dist/lib/v2/projection/index.js +2 -0
  134. package/dist/lib/v2/projection/index.mjs +2 -0
  135. package/dist/lib/v2/projection/legacy-routes.d.mts +10 -0
  136. package/dist/lib/v2/projection/legacy-routes.d.ts +10 -0
  137. package/dist/lib/v2/projection/legacy-routes.js +7 -0
  138. package/dist/lib/v2/projection/legacy-routes.mjs +6 -0
  139. package/dist/lib/validation/schema-loader.js +25 -0
  140. package/dist/lib/validation/schema-loader.mjs +24 -0
  141. package/dist/lib/version.d.mts +3 -3
  142. package/dist/lib/version.d.ts +3 -3
  143. package/dist/lib/version.js +3 -3
  144. package/dist/lib/version.mjs +3 -3
  145. package/docs/TYPE-SUMMARY.md +7 -2
  146. package/docs/guides/DISCOVERY-CACHE-AND-SESSION-REUSE.md +3 -2
  147. package/docs/guides/REPORTING-LEDGER.md +15 -5
  148. package/docs/guides/REPORTING-OPERATIONS.md +17 -11
  149. package/docs/guides/UNIVERSAL-MACRO-COMPILATION.md +38 -0
  150. package/docs/guides/VALIDATE-YOUR-AGENT.md +12 -0
  151. package/docs/llms.txt +2 -2
  152. package/docs/migration-12-to-13.md +8 -0
  153. package/docs/migration-14.x-rc-worksheet.md +4 -4
  154. package/docs/migration-agent-identity-3.3.md +150 -11
  155. package/package.json +4 -1
@@ -78,16 +78,38 @@ export type CanonicalCreateMediaBuyRequest = Omit<CanonicalCreativeResponse<Crea
78
78
  packages?: CanonicalPackageRequest[];
79
79
  };
80
80
  type CanonicalReportingWebhook = NonNullable<CanonicalCreateMediaBuyRequest['reporting_webhook']>;
81
- type ReportingWebhookPreferences = Omit<CanonicalReportingWebhook, 'url' | 'authentication' | 'reporting_frequency'> & {
82
- reporting_frequency?: CanonicalReportingWebhook['reporting_frequency'];
81
+ /**
82
+ * Client-facing form of a wire reporting webhook `W`. Every field is optional:
83
+ * the SDK can supply the callback `url`, `reporting_frequency` and the
84
+ * `authentication` block from client configuration.
85
+ *
86
+ * `authentication` is optional even with a caller-supplied `url`. AdCP 3.3
87
+ * makes it optional on the wire when the seller signs webhooks under its
88
+ * profile (`webhook_signing.supported`), and the experimental 3.3 client sends
89
+ * the registration without it once it has negotiated that. Below 3.3 the wire
90
+ * still requires it: with `webhookUrlTemplate` configured the client throws
91
+ * rather than send a caller URL without credentials (it never pairs its own
92
+ * `webhookSecret` with a caller-controlled host), and request validation
93
+ * reports the missing field otherwise.
94
+ *
95
+ * Derived from `W` rather than restating its fields, so it accepts every
96
+ * reporting webhook the pinned schemas generate, whether they declare
97
+ * `authentication` required (3.2) or optional (3.3).
98
+ *
99
+ * Exported for the type tests only; not re-exported from the package root.
100
+ */
101
+ export type ClientReportingWebhookInputFor<W extends {
102
+ url: string;
103
+ reporting_frequency: unknown;
104
+ }> = Omit<W, 'url' | 'authentication' | 'reporting_frequency'> & {
105
+ url?: W['url'];
106
+ reporting_frequency?: W['reporting_frequency'];
107
+ /** Atomic: `schemes` and `credentials` always come from the same source. */
108
+ authentication?: W extends {
109
+ authentication?: infer A;
110
+ } ? (unknown extends A ? never : A) : never;
83
111
  };
84
- type ClientReportingWebhookInput = (ReportingWebhookPreferences & {
85
- url?: never;
86
- authentication?: CanonicalReportingWebhook['authentication'];
87
- }) | (ReportingWebhookPreferences & {
88
- url: CanonicalReportingWebhook['url'];
89
- authentication: CanonicalReportingWebhook['authentication'];
90
- });
112
+ type ClientReportingWebhookInput = ClientReportingWebhookInputFor<CanonicalReportingWebhook>;
91
113
  /**
92
114
  * Client-facing create request. The SDK may supply the reporting callback URL
93
115
  * and its complete authentication block from client configuration; the wire
@@ -43,7 +43,7 @@ export { projectV1ProductToV2, canonicalDeclarationFromBareId, resolveCanonicalF
43
43
  export type { V1ToV2Result, V1ToV2ProjectionOptions, BareFormatIdResolveOptions, LegacyFormatConversionContext, LegacyFormatConverter, LegacyFormatResolutionContext, LegacyFormatResolver, } from './v1-to-v2.mjs';
44
44
  export { projectV2ProductToV1 } from './v2-to-v1.mjs';
45
45
  export type { V2ToV1Result, V2ToV1ProjectionOptions, CanonicalFormatLegacyResolver, CanonicalFormatLegacyResolutionContext, CanonicalProductFormatLegacyResolutionContext, CanonicalCreativeFormatLegacyResolutionContext, CanonicalSelectorFormatLegacyResolutionContext, } from './v2-to-v1.mjs';
46
- export { canonicalFormatLegacyResolverFromRoutes, legacyRoutesForProduct, type CanonicalFormatLegacyRoute, type LegacyRoutesForProductOptions, } from './legacy-routes.mjs';
46
+ export { canonicalFormatLegacyResolverFromRoutes, carryFormatOptionOrigins, legacyRoutesForProduct, type CanonicalFormatLegacyRoute, type LegacyRoutesForProductOptions, } from './legacy-routes.mjs';
47
47
  export { augmentProductWithFormatOptions, withFormatOptions, toCanonicalFormatOptionsWithRoutes, toAdditiveCanonicalProduct, withAdditiveCanonicalFormatOptions, toCanonicalOnlyProduct, toCanonicalOnlyResponse, type V2AugmentedProduct, type AdditiveCanonicalProduct, type CanonicalOnlyProduct, } from './augment-response.mjs';
48
48
  export { CanonicalFormat, audioDaastFormatDeclaration, audioHostedFormatDeclaration, audioVastFormatDeclaration, agentPlacementFormatDeclaration, canonicalFormatDeclaration, customFormatDeclaration, displayTagFormatDeclaration, legacyFormatRef, legacyFormatRefs, html5FormatDeclaration, imageCarouselFormatDeclaration, imageFormatDeclaration, nativeInFeedFormatDeclaration, productCard, productCardDetailed, responsiveCreativeFormatDeclaration, sellerRenderedStatefulDisplayFormatDeclaration, sponsoredPlacementFormatDeclaration, coordinatedPlacementsFormatDeclaration, videoHostedFormatDeclaration, videoVastFormatDeclaration, type CanonicalFormatDeclaration, type CanonicalFormatDeclarationFields, type CanonicalFormatKind, type CanonicalFormatParams, type FormatReferenceInput, type ProductCardDetailedFields, type ProductCardFields, } from './builders.mjs';
49
49
  export { packageRefsForFormatOptions, packageRefsForCapabilities, legacyFormatIdsFromOptions, tryLegacyFormatIdsFromOptions, legacyFormatIdsForFormatOption, legacyFormatIdsForCapability, lintPackageFormatSelectorDimensions, FormatOptionRefsLookupError, CapabilityIdsLookupError, type PackageFormatRefs, type PackageCapabilityRefs, type FormatOptionRef, type FormatOptionSelector, type FormatOptionRefsLookupErrorCode, type CapabilityIdsLookupErrorCode, type PackageFormatSelectorInput, type PackageFormatSelectorDimensionOptions, type FixedSizeDimensions, type PackageFormatSelectorDimensionDiagnosticCode, type PackageFormatSelectorDimensionDiagnostic, } from './write-side.mjs';
@@ -43,7 +43,7 @@ export { projectV1ProductToV2, canonicalDeclarationFromBareId, resolveCanonicalF
43
43
  export type { V1ToV2Result, V1ToV2ProjectionOptions, BareFormatIdResolveOptions, LegacyFormatConversionContext, LegacyFormatConverter, LegacyFormatResolutionContext, LegacyFormatResolver, } from './v1-to-v2';
44
44
  export { projectV2ProductToV1 } from './v2-to-v1';
45
45
  export type { V2ToV1Result, V2ToV1ProjectionOptions, CanonicalFormatLegacyResolver, CanonicalFormatLegacyResolutionContext, CanonicalProductFormatLegacyResolutionContext, CanonicalCreativeFormatLegacyResolutionContext, CanonicalSelectorFormatLegacyResolutionContext, } from './v2-to-v1';
46
- export { canonicalFormatLegacyResolverFromRoutes, legacyRoutesForProduct, type CanonicalFormatLegacyRoute, type LegacyRoutesForProductOptions, } from './legacy-routes';
46
+ export { canonicalFormatLegacyResolverFromRoutes, carryFormatOptionOrigins, legacyRoutesForProduct, type CanonicalFormatLegacyRoute, type LegacyRoutesForProductOptions, } from './legacy-routes';
47
47
  export { augmentProductWithFormatOptions, withFormatOptions, toCanonicalFormatOptionsWithRoutes, toAdditiveCanonicalProduct, withAdditiveCanonicalFormatOptions, toCanonicalOnlyProduct, toCanonicalOnlyResponse, type V2AugmentedProduct, type AdditiveCanonicalProduct, type CanonicalOnlyProduct, } from './augment-response';
48
48
  export { CanonicalFormat, audioDaastFormatDeclaration, audioHostedFormatDeclaration, audioVastFormatDeclaration, agentPlacementFormatDeclaration, canonicalFormatDeclaration, customFormatDeclaration, displayTagFormatDeclaration, legacyFormatRef, legacyFormatRefs, html5FormatDeclaration, imageCarouselFormatDeclaration, imageFormatDeclaration, nativeInFeedFormatDeclaration, productCard, productCardDetailed, responsiveCreativeFormatDeclaration, sellerRenderedStatefulDisplayFormatDeclaration, sponsoredPlacementFormatDeclaration, coordinatedPlacementsFormatDeclaration, videoHostedFormatDeclaration, videoVastFormatDeclaration, type CanonicalFormatDeclaration, type CanonicalFormatDeclarationFields, type CanonicalFormatKind, type CanonicalFormatParams, type FormatReferenceInput, type ProductCardDetailedFields, type ProductCardFields, } from './builders';
49
49
  export { packageRefsForFormatOptions, packageRefsForCapabilities, legacyFormatIdsFromOptions, tryLegacyFormatIdsFromOptions, legacyFormatIdsForFormatOption, legacyFormatIdsForCapability, lintPackageFormatSelectorDimensions, FormatOptionRefsLookupError, CapabilityIdsLookupError, type PackageFormatRefs, type PackageCapabilityRefs, type FormatOptionRef, type FormatOptionSelector, type FormatOptionRefsLookupErrorCode, type CapabilityIdsLookupErrorCode, type PackageFormatSelectorInput, type PackageFormatSelectorDimensionOptions, type FixedSizeDimensions, type PackageFormatSelectorDimensionDiagnosticCode, type PackageFormatSelectorDimensionDiagnostic, } from './write-side';
@@ -33,6 +33,7 @@ __export(projection_exports, {
33
33
  canonicalFormatDeclaration: () => import_builders.canonicalFormatDeclaration,
34
34
  canonicalFormatLegacyResolverFromCatalogSnapshots: () => import_catalog_snapshot.canonicalFormatLegacyResolverFromCatalogSnapshots,
35
35
  canonicalFormatLegacyResolverFromRoutes: () => import_legacy_routes.canonicalFormatLegacyResolverFromRoutes,
36
+ carryFormatOptionOrigins: () => import_legacy_routes.carryFormatOptionOrigins,
36
37
  coordinatedPlacementsFormatDeclaration: () => import_builders.coordinatedPlacementsFormatDeclaration,
37
38
  customFormatDeclaration: () => import_builders.customFormatDeclaration,
38
39
  displayTagFormatDeclaration: () => import_builders.displayTagFormatDeclaration,
@@ -111,6 +112,7 @@ var import_creative_delivery = require('./creative-delivery.js');
111
112
  canonicalFormatDeclaration,
112
113
  canonicalFormatLegacyResolverFromCatalogSnapshots,
113
114
  canonicalFormatLegacyResolverFromRoutes,
115
+ carryFormatOptionOrigins,
114
116
  coordinatedPlacementsFormatDeclaration,
115
117
  customFormatDeclaration,
116
118
  displayTagFormatDeclaration,
@@ -2,6 +2,7 @@ import { projectV1ProductToV2, canonicalDeclarationFromBareId, resolveCanonicalF
2
2
  import { projectV2ProductToV1 } from "./v2-to-v1.mjs";
3
3
  import {
4
4
  canonicalFormatLegacyResolverFromRoutes,
5
+ carryFormatOptionOrigins,
5
6
  legacyRoutesForProduct
6
7
  } from "./legacy-routes.mjs";
7
8
  import {
@@ -88,6 +89,7 @@ export {
88
89
  canonicalFormatDeclaration,
89
90
  canonicalFormatLegacyResolverFromCatalogSnapshots,
90
91
  canonicalFormatLegacyResolverFromRoutes,
92
+ carryFormatOptionOrigins,
91
93
  coordinatedPlacementsFormatDeclaration,
92
94
  customFormatDeclaration,
93
95
  displayTagFormatDeclaration,
@@ -81,4 +81,14 @@ export declare function legacyRoutesForProduct(productId: string, declarations:
81
81
  * must agree that the seller did not declare it.
82
82
  */
83
83
  export declare function formatOptionOriginFromRoutes(resolver: CanonicalFormatLegacyResolver | undefined, ref: FormatOptionReference, productId: string | undefined): FormatOptionOrigin | undefined;
84
+ /**
85
+ * Make `resolver` report the format-option origins recorded by `sources`.
86
+ *
87
+ * A resolver built by {@link canonicalFormatLegacyResolverFromRoutes} carries
88
+ * each route's `seller_declared` origin, which `AgentClient` reads to rewrite
89
+ * options the seller never declared on the canonical wire. A resolver that
90
+ * wraps or composes it is a new function and would otherwise carry none.
91
+ * Returns `resolver` itself, so the call can wrap a composition inline.
92
+ */
93
+ export declare function carryFormatOptionOrigins(resolver: CanonicalFormatLegacyResolver, ...sources: ReadonlyArray<CanonicalFormatLegacyResolver | undefined>): CanonicalFormatLegacyResolver;
84
94
  export declare function canonicalFormatLegacyResolverFromRoutes(routes: readonly CanonicalFormatLegacyRoute[]): CanonicalFormatLegacyResolver;
@@ -81,4 +81,14 @@ export declare function legacyRoutesForProduct(productId: string, declarations:
81
81
  * must agree that the seller did not declare it.
82
82
  */
83
83
  export declare function formatOptionOriginFromRoutes(resolver: CanonicalFormatLegacyResolver | undefined, ref: FormatOptionReference, productId: string | undefined): FormatOptionOrigin | undefined;
84
+ /**
85
+ * Make `resolver` report the format-option origins recorded by `sources`.
86
+ *
87
+ * A resolver built by {@link canonicalFormatLegacyResolverFromRoutes} carries
88
+ * each route's `seller_declared` origin, which `AgentClient` reads to rewrite
89
+ * options the seller never declared on the canonical wire. A resolver that
90
+ * wraps or composes it is a new function and would otherwise carry none.
91
+ * Returns `resolver` itself, so the call can wrap a composition inline.
92
+ */
93
+ export declare function carryFormatOptionOrigins(resolver: CanonicalFormatLegacyResolver, ...sources: ReadonlyArray<CanonicalFormatLegacyResolver | undefined>): CanonicalFormatLegacyResolver;
84
94
  export declare function canonicalFormatLegacyResolverFromRoutes(routes: readonly CanonicalFormatLegacyRoute[]): CanonicalFormatLegacyResolver;
@@ -19,6 +19,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
19
19
  var legacy_routes_exports = {};
20
20
  __export(legacy_routes_exports, {
21
21
  canonicalFormatLegacyResolverFromRoutes: () => canonicalFormatLegacyResolverFromRoutes,
22
+ carryFormatOptionOrigins: () => carryFormatOptionOrigins,
22
23
  formatOptionOriginFromRoutes: () => formatOptionOriginFromRoutes,
23
24
  legacyRoutesForProduct: () => legacyRoutesForProduct,
24
25
  sameFormatOptionRef: () => sameFormatOptionRef,
@@ -204,6 +205,11 @@ function formatOptionOriginFromRoutes(resolver, ref, productId) {
204
205
  if (matching.some((entry) => entry.origin === void 0)) return void 0;
205
206
  return matching[0].origin;
206
207
  }
208
+ function carryFormatOptionOrigins(resolver, ...sources) {
209
+ const carried = [resolver, ...sources].flatMap((source) => source ? originsByResolver.get(source) ?? [] : []);
210
+ if (carried.length > 0) originsByResolver.set(resolver, Object.freeze(carried));
211
+ return resolver;
212
+ }
207
213
  function canonicalFormatLegacyResolverFromRoutes(routes) {
208
214
  const index = /* @__PURE__ */ new Map();
209
215
  const origins = [];
@@ -237,6 +243,7 @@ function canonicalFormatLegacyResolverFromRoutes(routes) {
237
243
  // Annotate the CommonJS export names for ESM import in node:
238
244
  0 && (module.exports = {
239
245
  canonicalFormatLegacyResolverFromRoutes,
246
+ carryFormatOptionOrigins,
240
247
  formatOptionOriginFromRoutes,
241
248
  legacyRoutesForProduct,
242
249
  sameFormatOptionRef,
@@ -176,6 +176,11 @@ function formatOptionOriginFromRoutes(resolver, ref, productId) {
176
176
  if (matching.some((entry) => entry.origin === void 0)) return void 0;
177
177
  return matching[0].origin;
178
178
  }
179
+ function carryFormatOptionOrigins(resolver, ...sources) {
180
+ const carried = [resolver, ...sources].flatMap((source) => source ? originsByResolver.get(source) ?? [] : []);
181
+ if (carried.length > 0) originsByResolver.set(resolver, Object.freeze(carried));
182
+ return resolver;
183
+ }
179
184
  function canonicalFormatLegacyResolverFromRoutes(routes) {
180
185
  const index = /* @__PURE__ */ new Map();
181
186
  const origins = [];
@@ -208,6 +213,7 @@ function canonicalFormatLegacyResolverFromRoutes(routes) {
208
213
  }
209
214
  export {
210
215
  canonicalFormatLegacyResolverFromRoutes,
216
+ carryFormatOptionOrigins,
211
217
  formatOptionOriginFromRoutes,
212
218
  legacyRoutesForProduct,
213
219
  sameFormatOptionRef,
@@ -48,6 +48,7 @@ __export(schema_loader_exports, {
48
48
  listValidatorKeys: () => listValidatorKeys,
49
49
  registerExternalSchemaRoot: () => registerExternalSchemaRoot,
50
50
  resolveBundleKey: () => resolveBundleKey,
51
+ resolveServedValidationVersion: () => resolveServedValidationVersion,
51
52
  schemaAllowsTopLevelField: () => schemaAllowsTopLevelField,
52
53
  toReleasePrecisionWire: () => toReleasePrecisionWire,
53
54
  unregisterExternalSchemaRoot: () => unregisterExternalSchemaRoot,
@@ -754,6 +755,29 @@ function hasSchemaBundle(version) {
754
755
  return false;
755
756
  }
756
757
  }
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;
763
+ try {
764
+ if (isExternalSchemaRootActive(pinnedVersion)) return pinnedVersion;
765
+ } catch {
766
+ return pinnedVersion;
767
+ }
768
+ const echoed = response !== null && typeof response === "object" ? response.adcp_version : void 0;
769
+ if (typeof echoed !== "string") return pinnedVersion;
770
+ const echoMajor = SERVED_RELEASE_MAJOR.exec(echoed)?.[1];
771
+ if (echoMajor === void 0 || echoMajor !== pinMajor) return pinnedVersion;
772
+ let pinnedRelease;
773
+ try {
774
+ pinnedRelease = toReleasePrecisionWire(pinnedVersion);
775
+ } catch {
776
+ return pinnedVersion;
777
+ }
778
+ if (echoed === pinnedRelease) return pinnedVersion;
779
+ return hasSchemaBundle(echoed) ? echoed : pinnedVersion;
780
+ }
757
781
  const mcpToolSchemaCache = /* @__PURE__ */ new Map();
758
782
  const mcpToolSummaryCache = /* @__PURE__ */ new Map();
759
783
  function boundedMcpToolSummary(value) {
@@ -1019,6 +1043,7 @@ function cloneJsonValue(value) {
1019
1043
  listValidatorKeys,
1020
1044
  registerExternalSchemaRoot,
1021
1045
  resolveBundleKey,
1046
+ resolveServedValidationVersion,
1022
1047
  schemaAllowsTopLevelField,
1023
1048
  toReleasePrecisionWire,
1024
1049
  unregisterExternalSchemaRoot,
@@ -701,6 +701,29 @@ function hasSchemaBundle(version) {
701
701
  return false;
702
702
  }
703
703
  }
704
+ const SERVED_RELEASE_MAJOR = /^(\d+)\.\d+(?:-[a-zA-Z0-9.-]+)?$/;
705
+ const NUMERIC_PIN_MAJOR = /^(\d+)\.\d+/;
706
+ function resolveServedValidationVersion(response, pinnedVersion) {
707
+ const pinMajor = NUMERIC_PIN_MAJOR.exec(pinnedVersion)?.[1];
708
+ if (pinMajor === void 0) return pinnedVersion;
709
+ try {
710
+ if (isExternalSchemaRootActive(pinnedVersion)) return pinnedVersion;
711
+ } catch {
712
+ return pinnedVersion;
713
+ }
714
+ const echoed = response !== null && typeof response === "object" ? response.adcp_version : void 0;
715
+ if (typeof echoed !== "string") return pinnedVersion;
716
+ const echoMajor = SERVED_RELEASE_MAJOR.exec(echoed)?.[1];
717
+ if (echoMajor === void 0 || echoMajor !== pinMajor) return pinnedVersion;
718
+ let pinnedRelease;
719
+ try {
720
+ pinnedRelease = toReleasePrecisionWire(pinnedVersion);
721
+ } catch {
722
+ return pinnedVersion;
723
+ }
724
+ if (echoed === pinnedRelease) return pinnedVersion;
725
+ return hasSchemaBundle(echoed) ? echoed : pinnedVersion;
726
+ }
704
727
  const mcpToolSchemaCache = /* @__PURE__ */ new Map();
705
728
  const mcpToolSummaryCache = /* @__PURE__ */ new Map();
706
729
  function boundedMcpToolSummary(value) {
@@ -965,6 +988,7 @@ export {
965
988
  listValidatorKeys,
966
989
  registerExternalSchemaRoot,
967
990
  resolveBundleKey,
991
+ resolveServedValidationVersion,
968
992
  schemaAllowsTopLevelField,
969
993
  toReleasePrecisionWire,
970
994
  unregisterExternalSchemaRoot,
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * AdCP SDK library version
3
3
  */
4
- export declare const LIBRARY_VERSION = "16.0.0-beta.1";
4
+ export declare const LIBRARY_VERSION = "16.0.0-beta.3";
5
5
  /**
6
6
  * AdCP specification version this library is built for
7
7
  */
@@ -33,10 +33,10 @@ export type AdcpVersion = (typeof COMPATIBLE_ADCP_VERSIONS)[number];
33
33
  * Full version information
34
34
  */
35
35
  export declare const VERSION_INFO: {
36
- readonly library: "16.0.0-beta.1";
36
+ readonly library: "16.0.0-beta.3";
37
37
  readonly adcp: "3.2.4";
38
38
  readonly compatibleVersions: readonly ["v2.5", "v2.6", "v3", "3.0.0", "3.0", "3.0.1", "3.0.2", "3.0.3", "3.0.4", "3.0.5", "3.0.6", "3.0.7", "3.0.8", "3.0.9", "3.0.10", "3.0.11", "3.0.12", "3.0.13", "3.0.14", "3.0.15", "3.0.16", "3.0.17", "3.0.18", "3.0.19", "3.0.20", "3.0.21", "3.0.22", "3.0.23", "3.0.24", "3.0.25", "3.1.0", "3.1", "3.1.1", "3.1.2", "3.1.3", "3.1.4", "3.1.5", "3.1.6", "3.1.7", "3.1.8", "3.1.9", "3.1.10", "3.1.11", "3.1.12", "3.1.13", "3.1.14", "3.1.15", "3.1.16", "3.1.17", "3.1.18", "3.1.19", "3.1.20", "3.1.21", "3.1.22", "3.1.23", "3.1.24", "3.2.1", "3.2", "3.2.2", "3.2.3", "3.2.4"];
39
- readonly generatedAt: "2026-10-14T03:59:15.421Z";
39
+ readonly generatedAt: "2026-10-15T18:57:32.995Z";
40
40
  };
41
41
  /**
42
42
  * Get the AdCP specification version this library is built for
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * AdCP SDK library version
3
3
  */
4
- export declare const LIBRARY_VERSION = "16.0.0-beta.1";
4
+ export declare const LIBRARY_VERSION = "16.0.0-beta.3";
5
5
  /**
6
6
  * AdCP specification version this library is built for
7
7
  */
@@ -33,10 +33,10 @@ export type AdcpVersion = (typeof COMPATIBLE_ADCP_VERSIONS)[number];
33
33
  * Full version information
34
34
  */
35
35
  export declare const VERSION_INFO: {
36
- readonly library: "16.0.0-beta.1";
36
+ readonly library: "16.0.0-beta.3";
37
37
  readonly adcp: "3.2.4";
38
38
  readonly compatibleVersions: readonly ["v2.5", "v2.6", "v3", "3.0.0", "3.0", "3.0.1", "3.0.2", "3.0.3", "3.0.4", "3.0.5", "3.0.6", "3.0.7", "3.0.8", "3.0.9", "3.0.10", "3.0.11", "3.0.12", "3.0.13", "3.0.14", "3.0.15", "3.0.16", "3.0.17", "3.0.18", "3.0.19", "3.0.20", "3.0.21", "3.0.22", "3.0.23", "3.0.24", "3.0.25", "3.1.0", "3.1", "3.1.1", "3.1.2", "3.1.3", "3.1.4", "3.1.5", "3.1.6", "3.1.7", "3.1.8", "3.1.9", "3.1.10", "3.1.11", "3.1.12", "3.1.13", "3.1.14", "3.1.15", "3.1.16", "3.1.17", "3.1.18", "3.1.19", "3.1.20", "3.1.21", "3.1.22", "3.1.23", "3.1.24", "3.2.1", "3.2", "3.2.2", "3.2.3", "3.2.4"];
39
- readonly generatedAt: "2026-10-14T03:59:15.421Z";
39
+ readonly generatedAt: "2026-10-15T18:57:32.995Z";
40
40
  };
41
41
  /**
42
42
  * Get the AdCP specification version this library is built for
@@ -31,7 +31,7 @@ __export(version_exports, {
31
31
  toReleasePrecisionVersion: () => toReleasePrecisionVersion
32
32
  });
33
33
  module.exports = __toCommonJS(version_exports);
34
- const LIBRARY_VERSION = "16.0.0-beta.1";
34
+ const LIBRARY_VERSION = "16.0.0-beta.3";
35
35
  const ADCP_VERSION = "3.2.4";
36
36
  const ADCP_MAJOR_VERSION = 3;
37
37
  const COMPATIBLE_ADCP_VERSIONS = [
@@ -98,10 +98,10 @@ const COMPATIBLE_ADCP_VERSIONS = [
98
98
  "3.2.4"
99
99
  ];
100
100
  const VERSION_INFO = {
101
- library: "16.0.0-beta.1",
101
+ library: "16.0.0-beta.3",
102
102
  adcp: "3.2.4",
103
103
  compatibleVersions: COMPATIBLE_ADCP_VERSIONS,
104
- generatedAt: "2026-10-14T03:59:15.421Z"
104
+ generatedAt: "2026-10-15T18:57:32.995Z"
105
105
  };
106
106
  function getAdcpVersion() {
107
107
  return ADCP_VERSION;
@@ -1,4 +1,4 @@
1
- const LIBRARY_VERSION = "16.0.0-beta.1";
1
+ const LIBRARY_VERSION = "16.0.0-beta.3";
2
2
  const ADCP_VERSION = "3.2.4";
3
3
  const ADCP_MAJOR_VERSION = 3;
4
4
  const COMPATIBLE_ADCP_VERSIONS = [
@@ -65,10 +65,10 @@ const COMPATIBLE_ADCP_VERSIONS = [
65
65
  "3.2.4"
66
66
  ];
67
67
  const VERSION_INFO = {
68
- library: "16.0.0-beta.1",
68
+ library: "16.0.0-beta.3",
69
69
  adcp: "3.2.4",
70
70
  compatibleVersions: COMPATIBLE_ADCP_VERSIONS,
71
- generatedAt: "2026-10-14T03:59:15.421Z"
71
+ generatedAt: "2026-10-15T18:57:32.995Z"
72
72
  };
73
73
  function getAdcpVersion() {
74
74
  return ADCP_VERSION;
@@ -1,7 +1,7 @@
1
1
  # AdCP Type Summary
2
2
 
3
- > Generated at: 2026-10-14
4
- > @adcp/sdk v16.0.0-beta.1
3
+ > Generated at: 2026-10-15
4
+ > @adcp/sdk v16.0.0-beta.3
5
5
 
6
6
  Curated reference of the types that matter for using the AdCP client. For full generated types see `src/lib/types/tools.generated.ts` and `src/lib/types/core.generated.ts`.
7
7
 
@@ -49,6 +49,11 @@ interface A2ALegacyCompatOptions {
49
49
  }
50
50
 
51
51
  interface TransportOptions {
52
+ // Defaults to 4 MiB (DEFAULT_MAX_RESPONSE_BYTES); only 0 opts out
53
+ // (Infinity / negatives fall back to the default).
54
+ // SSE is bounded per event plus `maxResponseBytes * 2` cumulative per call.
55
+ // Over-cap responses throw ResponseTooLargeError; nothing is truncated.
56
+ // A per-call `transport` replaces this object; it does not merge.
52
57
  maxResponseBytes?: number;
53
58
  trustedFetchFn?: typeof fetch;
54
59
  allowPrivateIp?: boolean;
@@ -55,7 +55,7 @@ The key is a SHA-256 over:
55
55
  | configured `adcpVersion` / `wireAdcpVersion` | negotiated version |
56
56
  | every non-correlation, non-credential header | unknown `x-*` tenant / routing / policy headers can select a different upstream view, so they fail closed into the key. This includes `baggage`, **ambient OpenTelemetry baggage included** |
57
57
  | request-signing identity and policy | `kind`, `kid`, `alg`, agent URL and signing policy; never the private key |
58
- | effective `allowPrivateIp`, `maxResponseBytes` | the transport policy the evidence was gathered under (per-call `transport` overrides included) |
58
+ | configured `allowPrivateIp`, `maxResponseBytes` | the transport policy the evidence was gathered under (per-call `transport` overrides included). These are the values the caller *configured*, not the effective ones: an unset `maxResponseBytes` keys as "unset" rather than as the 4 MiB default, so entries written before the default landed stay reusable. The MCP connection cache keys the *resolved* cap instead — see [Reusable MCP connections](#reusable-mcp-connections) |
59
59
 
60
60
  Not part of the key: correlation headers (`x-request-id`, `x-correlation-id`, `traceparent`, ...), and credential header values (`Authorization`, `x-adcp-auth`) — OAuth and `auth_token` credentials are also owned by `authIdentity`; a token rotation for the same authorization scope keeps the entry. **Choose `authIdentity` at least as narrow as the credential's authorization scope.** Change the identity or invalidate the cache when permissions change or access is revoked; use a distinct identity for anonymous access. Two principals must never share an identity. Keys are hashes and never contain the identity, token or header values.
61
61
 
@@ -101,7 +101,7 @@ Unlike [`primeCapabilities`](../TYPE-SUMMARY.md) — which stays client-bound an
101
101
 
102
102
  This behavior applies by default, including calls that pass a signal or timeout outside a connection scope. The process-wide legacy/OAuth session cache retains at most 20 connections per cache with LRU eviction and has no idle expiry. For request workflows with many distinct credentials, use a scope to bound session lifetime. At process shutdown, `closeConnections()` from `@adcp/sdk/advanced` closes the process-wide connections.
103
103
 
104
- A reusable session is keyed only by what changes the _connection_: endpoint, credential, tenant / routing headers, signing identity, `allowPrivateIp`, a caller-injected fetch, and the workflow scope. These no longer choose or split a session:
104
+ A reusable session is keyed only by what changes the _connection_: endpoint, credential, tenant / routing headers, signing identity, `allowPrivateIp`, `maxResponseBytes`, a caller-injected fetch, and the workflow scope. These no longer choose or split a session:
105
105
 
106
106
  - **Correlation headers**: `traceparent`, `tracestate`, `x-request-id`, `x-correlation-id`, `x-trace-id`, `x-span-id`, `x-debug-id`, `x-client-request-id`, `x-b3-*`, `x-amzn-trace-id`, `x-cloud-trace-context`, `x-datadog-*`, `sentry-trace`, and vendor-prefixed `x-<vendor>-request-id` / `-correlation-id` / `-trace-id` / `-span-id` / `-debug-id`. Names suggesting credentials or tenant/routing identity (including `auth`, `token`, `tenant`, `account`, `workspace`, `seat`, `advertiser`, `publisher`, `buyer` and `seller`) are never treated as correlation. **`baggage` stays in the key**: it can carry tenant or routing members. Any header outside the list above also stays in the key (fail closed).
107
107
  - **`AbortSignal`** and **`requestTimeoutMs`**: they bound a call.
@@ -114,6 +114,7 @@ Isolation properties:
114
114
  - A caller that joins another caller's in-flight connect waits under its own signal and timeout. If the creator cancels the connect, the joiner retries for itself, with at most two retries after foreign aborts.
115
115
  - A caller-injected `fetchFn` (a per-call network trust boundary) keeps its own one-shot session outside a connection scope; inside a scope its identity is part of the key.
116
116
  - Connections are never shared across credentials, tenant headers, signing identities or `allowPrivateIp` settings.
117
+ - Connections are never shared across different `maxResponseBytes` values either. The response cap is decided once per request, and a session's standalone `GET` event-stream listener issues exactly one request — at connect time — so it carries whatever policy opened the connection for the life of that connection. Sharing across policies would let one `maxResponseBytes: 0` call hand an uncapped listener to every later caller. The key uses the *resolved* cap, so an explicit `4 * 1024 * 1024` and an unset value are the same policy and share a session; `0` and any other number each get their own. Varying the cap per call therefore costs a session per distinct value against the same agent, against a 20-entry LRU — prefer a single client-level value where you can, or pin the session with `withMCPConnectionScope` where continuity matters.
117
118
 
118
119
  ### Public connection scope
119
120
 
@@ -1017,9 +1017,9 @@ setInterval(() => {
1017
1017
  BigQuery's server-side `jobTimeoutMs` is not used: for a local-file load the
1018
1018
  official client builds `configuration.load` from the load options and cannot
1019
1019
  set job-level fields, and BigQuery silently drops `labels` and `jobTimeoutMs`
1020
- found inside `configuration.load`. (Staging the file in GCS and submitting the load with
1021
- `bigquery.createJob` can set both; the sink does not stage files.) For the
1022
- same reason the `labels` option has no effect (it warns once per process and
1020
+ found inside `configuration.load`. (Staging the file in GCS and submitting
1021
+ the load with `bigquery.createJob` can set both; the sink does not stage
1022
+ files.) For the same reason the `labels` option has no effect (it warns once per process and
1023
1023
  is deprecated). To attribute load cost, query
1024
1024
  `` `region-<location>`.INFORMATION_SCHEMA.JOBS_BY_PROJECT `` (needs
1025
1025
  `bigquery.jobs.listAll`; about 180 days of history), filtering on
@@ -1067,6 +1067,16 @@ setInterval(() => {
1067
1067
  rejects after clearing the sink (it could not unregister the consumer), call
1068
1068
  it again before restarting workers. Re-run `REPORTING_WAREHOUSE_SINK_MIGRATION`
1069
1069
  after upgrading: it adds the `reset_generation` column.
1070
+ - Upgrading from 15.9.0: stop every 15.9.0 sink worker before starting newer
1071
+ ones, then re-apply `REPORTING_WAREHOUSE_SINK_MIGRATION`. A 15.9.0 worker
1072
+ cancels a load at its deadline, so one running beside a newer worker on the
1073
+ same sink can cancel a job the newer worker is waiting on. The job then ends
1074
+ `DONE` with an error and the attempt advances. Rows do not duplicate, but
1075
+ convergence slows. Also set `location` to the dataset's location: without it
1076
+ a replay lookup of a load job returns 404 for datasets outside the US and EU
1077
+ multi-regions, and the sink uploads again, gets a 409, and fails with a
1078
+ "check projectId and location" error. The sink warns once per process when
1079
+ `location` is unset.
1070
1080
  - Stop old sink workers before relying on these overlap guarantees. If an
1071
1081
  earlier overlapping run left a plan whose starting cursor differs from the
1072
1082
  committed cursor, the sink rejects it before loading. Reconcile the retained
@@ -1230,7 +1240,7 @@ Managed-only changes are lifecycle candidates in their own right. A settlement,
1230
1240
 
1231
1241
  `MAX_MATERIALIZATIONS_PER_ACCOUNT` and `MAX_RECEIPTS_PER_CONSUMER` are lifetime counts by default. Because managed evidence is immutable, a long-lived account eventually reaches them and then stops planning materializations and refuses every receipt with no way back. Construct the store with `new PostgresReportingManagedDeliveryStore(pool, { evidenceRetentionDays })` to make the caps active-scope: only evidence recorded inside that window counts against them, and `pruneExpiredEvidence({ account_id, limit })` deletes what has fallen outside it — never anything still inside the window, never a `pending` or leased materialization, and never a receipt batch inside its own replay retention. Retention has a floor: at least the 30-day receipt replay retention, and at least the strongest durable status or resource-retention horizon registered for the database. Adoption and binding installation exclusively lock the migration-created policy sentinel before account/binding locks; settlement, revocation claim, and pruning hold shared locks on it through their writes. A stronger promise therefore cannot land between a weaker check and its write, while unrelated hot-path readers remain concurrent. Pruning genuinely does its selection before taking the account lock, and asks which receipts a live replay row still names by expanding those rows once rather than posing a containment question per candidate — a shape no index could serve, which held the lock long enough to fail concurrent Core writes with 55P03. Because that selection is unlocked it is a proposal rather than a verdict: every victim is revalidated under the account lock against current live replay rows, surviving receipts and unexpired resources before it is deleted, so a replay that commits in the gap keeps the receipt it promised to reproduce. The revalidation is narrowed to the candidates' own consumers and keys, so the lock still holds no per-candidate scan. The registry lock is held to commit inside the transaction that acts on it, and a precondition such as the installed-window check runs inside that same transaction so a refusal rolls the write back rather than leaving a durable promise nobody validated, so an install cannot read an empty registry while a narrower promise is being registered, and a prune cannot approve a horizon another replica is about to widen. Pruning deletes and tombstones in one statement against one frozen cutoff, so a moving boundary can never leave a deleted row without its permanent identity. Successive prunes demote the tombstones they supersede, so a subject has exactly one tombstone still claiming to be its leaf and a chain pruned twice resolves the same way every time. Re-presenting a pruned receipt byte-identically answers `unchanged` with the instant the tombstone kept, resolves that receipt's own tombstone before any rule about its subject — so an exact re-presentation is not refused as new content for a settled subject — replays the same answer under the same idempotency key by rehydrating from the tombstone when the body is gone, counts as already-resolved for the batch's duplicate-subject rule, and never recreates the row: recreating it restarted the retention clock, undid the storage the prune reclaimed, and moved the published `received_at` to a moment the consumer never filed anything at. A pruned receipt ID can never bind different content, a subject whose accepted leaf expired can never reopen — the read projection consults tombstones, not just live rows — and materialization attempt history survives as a compact terminal record, so a revision that exhausted its attempts or already succeeded does not restart at attempt 1 once its rows age out. Both conclusions — the acceptance and the fact a delivery succeeded — are folded into the live, filtered and lifecycle projections alike. Counters describe exactly the records the response emits, never more and never less, so a buyer recomputing the association cannot see `ASSOCIATED_HISTORY_INCOMPLETE`; retention therefore refuses to prune an acceptance while the resource it accepts is still readable, which is what keeps a `complete` period from having nothing to show for itself. An acceptance carries the consumer that gave it, so one consumer's pruned acceptance never settles the obligation on another's behalf — including the anonymous fail-safe consumer, which owns no acceptances at all. Pruning also removes the expired replay row before the receipts it names, keeps any receipt a surviving replay row still references — a later batch can name an earlier receipt — and keeps any materialization whose resource is still readable or that a retained receipt names as its evidence. An adjustment receipt names no materialization — it names the revision it corrects — so the readable-resource hold matches on that revision as well, or a period whose revision resource was still readable lost the adjustment acceptance behind its own `complete`. Run it from the same scheduler that runs the worker. Lifecycle receipt reads are driven from the obligation's own subjects into a `(subject_id, receipt_kind, recorded_at)` index rather than asking the global receipt table which of its rows belong to an obligation — a question that supplies neither account nor consumer, so every due obligation re-scanned the whole receipt history. The managed state digest, the obligated-consumer roster read and the permanent delivery tombstones do the same — consumer statuses carry an `obligation_id` index and materialization tombstones an `(obligation_id, reached_success, reached_success_at)` one — because that digest is read inside the apply transaction while it holds the account lock: a scan there is a scan with the lock held, which pushed concurrent Core writes past their lock timeout and failed them with 55P03.
1232
1242
 
1233
- The managed tables are additive and do not alter the Core tables. This is the schema boundary coordinated with #2943: that work owns transactional reporting notification/activity intent and the existing webhook delivery/credential plane. Managed Delivery does not create a second webhook sender, outbox, credential store, or subscriber model. Apply both feature migrations after the Core migration in either order; each owns separate tables and both reuse the Core authority.
1243
+ The managed tables are additive and do not alter the Core tables. This is the schema boundary coordinated with the reporting notification work: that work owns transactional reporting notification/activity intent and the existing webhook delivery/credential plane. Managed Delivery does not create a second webhook sender, outbox, credential store, or subscriber model. Apply both feature migrations after the Core migration in either order; each owns separate tables and both reuse the Core authority.
1234
1244
 
1235
1245
  ## Transactional reporting notifications and account activity
1236
1246
 
@@ -1749,7 +1759,7 @@ reconciliation and fails closed if both are supplied, preventing double fire;
1749
1759
  the port's pending rows, rather than `listPendingTransitions()`, own retry.
1750
1760
 
1751
1761
  The activity table is deliberately separate from Core revision and obligation
1752
- rows. Managed Delivery/Reconciled Billing work in #2944 can add immutable
1762
+ rows. Managed Delivery/Reconciled Billing work can add immutable
1753
1763
  materialization and receipt tables without changing this transition identity,
1754
1764
  queue schema, or migration ordering.
1755
1765
 
@@ -121,7 +121,14 @@ service runs for you. See
121
121
  cannot hold the table indefinitely.
122
122
  5. **Warehouse sink.** Apply `REPORTING_WAREHOUSE_SINK_MIGRATION`, create the
123
123
  tables, and the `current_rows` view, then schedule `runOnce`. Point analysts
124
- at `current_rows`.
124
+ at `current_rows`. Set the sink's `location` to the dataset's location: replay
125
+ lookups of load jobs need it outside the US and EU multi-regions, and the sink
126
+ warns once per process when it is unset. When upgrading from 15.9.0, stop
127
+ every 15.9.0 sink worker before starting newer ones, then re-apply
128
+ `REPORTING_WAREHOUSE_SINK_MIGRATION`. A 15.9.0 worker cancels a load at its
129
+ deadline, so one running beside a newer worker on the same sink can cancel a
130
+ job the newer worker is waiting on; the job ends `DONE` with an error and the
131
+ attempt advances. Rows do not duplicate, but convergence slows.
125
132
  6. **Retention.** Enable `retention` last, after confirming the advertised
126
133
  `status_retention_days` is the window you intend to keep. Retirement deletes
127
134
  rows and ledger records for whole periods and cannot be undone. Keep backups
@@ -344,7 +351,7 @@ What it checks:
344
351
  and job ID unchanged and that table's persisted deadline grown, the job must
345
352
  end `DONE` without an error (the sink never cancels it), and later runs under
346
353
  the same sink name must converge with no duplicate rows, the same job ID and
347
- no second upload. A #3234 scenario runs a sink whose upload of a 50,000-row
354
+ no second upload. A slow-upload scenario runs a sink whose upload of a 50,000-row
348
355
  batch outlasts its 1-second deadline and asserts that it converges within 8
349
356
  runs, loads every row exactly once, never changes the job ID, and never
350
357
  starts an upload after the job exists (a link fast enough to beat the
@@ -356,11 +363,10 @@ What it checks:
356
363
  it; in practice it accepts the cancel and lets the load finish, which is why
357
364
  the sink never cancels a load and finds it by job ID instead.
358
365
  - **Overlapping runs** (`INFO`, never a failure). Two concurrent `runOnce()`
359
- calls over one pending batch, and a deterministic reproduction of #3196: one
366
+ calls over one pending batch, and a deterministic reproduction of the stale-plan race: one
360
367
  run reads its sink state, another completes the batch and a new revision
361
368
  commits, then the first resumes. Without the fence the first run reloads
362
- revisions the second already loaded (duplicate rows); with the fence from
363
- PR #3197 it rejects as stale and nothing duplicates. Read the verdict in the
369
+ revisions the second already loaded (duplicate rows); with the plan fence it rejects as stale and nothing duplicates. Read the verdict in the
364
370
  report; the exit status does not depend on it.
365
371
  - **Cross-project.** Set `GCP_CLIENT_PROJECT` to a project other than
366
372
  `GCP_BQ_PROJECT`. Load jobs are then created in the destination project, and
@@ -446,15 +452,15 @@ export GCS_BUCKET=my-test-bucket GCS_PREFIX=adcp-reporting-validation
446
452
  npm run validate:reporting-gcp -- --yes
447
453
  ```
448
454
 
449
- Against a pull request branch (for example #3197), keep running the script from
455
+ Against a pull request branch (replace `<n>` with its number), keep running the script from
450
456
  `main` and point it at a second built checkout, so a branch cut before this
451
457
  script existed still works:
452
458
 
453
459
  ```bash
454
- git fetch origin pull/3197/head:pr-3197
455
- git worktree add ../adcp-client-pr-3197 pr-3197
456
- (cd ../adcp-client-pr-3197 && npm ci && npm run build)
457
- ADCP_SDK_ROOT=../adcp-client-pr-3197 npm run validate:reporting-gcp -- --yes
460
+ git fetch origin pull/<n>/head:pr-<n>
461
+ git worktree add ../adcp-client-pr-<n> pr-<n>
462
+ (cd ../adcp-client-pr-<n> && npm ci && npm run build)
463
+ ADCP_SDK_ROOT=../adcp-client-pr-<n> npm run validate:reporting-gcp -- --yes
458
464
  ```
459
465
 
460
466
  The same environment applies. The run ends with a `PASS`, `FAIL` and `INFO`
@@ -462,7 +468,7 @@ report and a markdown block (project and bucket names masked) to paste into
462
468
  the PR. The exit status is 0 with no `FAIL`, 1 with any `FAIL`, and 2 for a
463
469
  configuration problem or a missing `--yes`. Results of the overlap probes are
464
470
  `INFO` on purpose: on `main` expect the stale-run probe to report duplicates,
465
- and on PR #3197 expect it to report none.
471
+ and on a branch with the plan fence expect it to report none.
466
472
 
467
473
  `test/lib/reporting-gcp-validation-pg.test.js` drives the script's core against
468
474
  in-memory BigQuery and Storage doubles and a real PostgreSQL, so the logic,
@@ -96,3 +96,41 @@ Do not create a global mapping based only on a token's spelling. A token such as
96
96
  cookie for another, and an account identifier for a third. Scope every mapping
97
97
  to its source dialect and keep the supporting vendor documentation with the
98
98
  registry entry.
99
+
100
+ Vendor-scoped TCF mappings retain the complete receiving vendor ID: map
101
+ `${GDPR_CONSENT_755}` to `{GDPR_CONSENT_755}`, never to unscoped
102
+ `{GDPR_CONSENT}` or another vendor's token. The suffix must be a positive
103
+ decimal integer without leading zeros. `{ADDTL_CONSENT}` is also supported,
104
+ with the same source-dialect and documentation requirements. Its CMP-provided
105
+ value is opaque; the helper does not generate consent or modify provider lists.
106
+
107
+ For the return translation, supply exact native mappings:
108
+
109
+ ```typescript
110
+ import { translateUniversalMacros } from '@adcp/sdk/substitution';
111
+
112
+ const translated = translateUniversalMacros(
113
+ 'https://pixel.vendor.example/i?gdpr={GDPR}&c={GDPR_CONSENT_755}&a={ADDTL_CONSENT}',
114
+ {
115
+ '{GDPR}': { native: '${GDPR}' },
116
+ '{GDPR_CONSENT_755}': { native: '${GDPR_CONSENT_755}' },
117
+ '{ADDTL_CONSENT}': { native: '${ADDTL_CONSENT}' },
118
+ }
119
+ );
120
+ ```
121
+
122
+ Existing native tokens pass through unchanged. The emitted URL may contain at
123
+ most one scoped TCF token, including pre-existing tokens and native aliases
124
+ identified by the supplied mappings. After dropping unsupported parameters,
125
+ multiple occurrences throw `MultipleVendorConsentMacrosError` with code
126
+ `multiple_vendor_consent_macros` and the second token in canonical AdCP form.
127
+ Additional Consent does not count toward that limit. Concrete `value` mappings
128
+ are encoded and reported in `frozen_consent_macros`; missing consent mappings
129
+ appear in `dropped_consent_macros`. When an unscoped target requires an unknown
130
+ vendor ID, use `'{GDPR_CONSENT}': { value: '' }` and translate `{GDPR}`
131
+ independently rather than guessing an ID.
132
+
133
+ These helpers implement the AdCP 3.3 translation contract. Generated protocol
134
+ types and declaration/capability validation still follow the SDK's pinned schema
135
+ release. Helper support alone does not establish a resolver's declared support
136
+ for a scoped token or Additional Consent.
@@ -668,6 +668,18 @@ try {
668
668
 
669
669
  These policies follow the language-neutral fixture ratified in [AdCP #6674](https://github.com/adcontextprotocol/adcp/issues/6674).
670
670
 
671
+ The AdCP 3.3 fixture also covers vendor-scoped `{GDPR_CONSENT_<gvl_id>}` and
672
+ `{ADDTL_CONSENT}`. Preserve the receiving vendor ID and treat Additional Consent
673
+ as opaque CMP data. Both participate in dropped/frozen consent diagnostics.
674
+ After unsupported parameters are dropped, a URL with multiple scoped TCF
675
+ occurrences throws `MultipleVendorConsentMacrosError` (exported from both
676
+ `@adcp/sdk` and `@adcp/sdk/substitution`), with code
677
+ `multiple_vendor_consent_macros` and the canonical second token in `macro`.
678
+ Existing native tokens and verified native aliases count; encoded concrete
679
+ values and Additional Consent do not. See the
680
+ [compilation guide](UNIVERSAL-MACRO-COMPILATION.md) for mapping and schema-version
681
+ boundaries.
682
+
671
683
  ---
672
684
 
673
685
  ## The fork-matrix: canonical adapter → compliance
package/docs/llms.txt CHANGED
@@ -1,7 +1,7 @@
1
1
  # Ad Context Protocol (AdCP)
2
2
 
3
- > Generated at: 2026-10-14
4
- > Library: @adcp/sdk v16.0.0-beta.1
3
+ > Generated at: 2026-10-15
4
+ > Library: @adcp/sdk v16.0.0-beta.3
5
5
  > AdCP major version: 3
6
6
  > Canonical URL: https://adcontextprotocol.github.io/adcp-client/llms.txt
7
7
  > Note: the `Library` stamp reflects the package.json version at doc-generation time. The narrative below describes the surface that lands on the next-published minor — including any 6.7 helpers documented here ahead of the release tag.