@adcp/sdk 14.0.0 → 14.2.0

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 (234) hide show
  1. package/dist/lib/adapters/implicit-account-store.d.mts +12 -7
  2. package/dist/lib/adapters/implicit-account-store.d.ts +12 -7
  3. package/dist/lib/adapters/implicit-account-store.js +69 -15
  4. package/dist/lib/adapters/implicit-account-store.mjs +69 -15
  5. package/dist/lib/core/AgentClient.d.mts +1 -0
  6. package/dist/lib/core/AgentClient.d.ts +1 -0
  7. package/dist/lib/core/AgentClient.js +3 -0
  8. package/dist/lib/core/AgentClient.mjs +3 -0
  9. package/dist/lib/core/SingleAgentClient.d.mts +35 -2
  10. package/dist/lib/core/SingleAgentClient.d.ts +35 -2
  11. package/dist/lib/core/SingleAgentClient.js +377 -36
  12. package/dist/lib/core/SingleAgentClient.mjs +387 -38
  13. package/dist/lib/core/TaskExecutor.d.mts +3 -1
  14. package/dist/lib/core/TaskExecutor.d.ts +3 -1
  15. package/dist/lib/core/TaskExecutor.js +17 -12
  16. package/dist/lib/core/TaskExecutor.mjs +17 -12
  17. package/dist/lib/core/account-key.d.mts +3 -0
  18. package/dist/lib/core/account-key.d.ts +3 -0
  19. package/dist/lib/core/account-key.js +41 -0
  20. package/dist/lib/core/account-key.mjs +17 -0
  21. package/dist/lib/core/account-resolution.d.mts +2 -0
  22. package/dist/lib/core/account-resolution.d.ts +2 -0
  23. package/dist/lib/core/buyer-account-registry.d.mts +93 -0
  24. package/dist/lib/core/buyer-account-registry.d.ts +93 -0
  25. package/dist/lib/core/buyer-account-registry.js +602 -0
  26. package/dist/lib/core/buyer-account-registry.mjs +578 -0
  27. package/dist/lib/core/product-cache.d.mts +18 -0
  28. package/dist/lib/core/product-cache.d.ts +18 -0
  29. package/dist/lib/core/product-cache.js +137 -0
  30. package/dist/lib/core/product-cache.mjs +112 -0
  31. package/dist/lib/errors/index.d.mts +40 -1
  32. package/dist/lib/errors/index.d.ts +40 -1
  33. package/dist/lib/errors/index.js +69 -3
  34. package/dist/lib/errors/index.mjs +64 -3
  35. package/dist/lib/governance/authorization.d.mts +17 -1
  36. package/dist/lib/governance/authorization.d.ts +17 -1
  37. package/dist/lib/governance/authorization.js +55 -7
  38. package/dist/lib/governance/authorization.mjs +59 -7
  39. package/dist/lib/governance/index.d.mts +2 -2
  40. package/dist/lib/governance/index.d.ts +2 -2
  41. package/dist/lib/governance/index.js +2 -0
  42. package/dist/lib/governance/index.mjs +3 -1
  43. package/dist/lib/index.d.mts +7 -4
  44. package/dist/lib/index.d.ts +7 -4
  45. package/dist/lib/index.js +29 -0
  46. package/dist/lib/index.mjs +31 -1
  47. package/dist/lib/net/agent-transport-fetch.d.mts +4 -0
  48. package/dist/lib/net/agent-transport-fetch.d.ts +4 -0
  49. package/dist/lib/net/agent-transport-fetch.js +18 -5
  50. package/dist/lib/net/agent-transport-fetch.mjs +17 -5
  51. package/dist/lib/protocols/a2a.js +9 -1
  52. package/dist/lib/protocols/a2a.mjs +9 -1
  53. package/dist/lib/protocols/index.js +9 -2
  54. package/dist/lib/protocols/index.mjs +9 -2
  55. package/dist/lib/protocols/mcp-modern.js +2 -1
  56. package/dist/lib/protocols/mcp-modern.mjs +2 -1
  57. package/dist/lib/protocols/mcp.js +5 -2
  58. package/dist/lib/protocols/mcp.mjs +5 -2
  59. package/dist/lib/protocols/rawResponseCapture.d.mts +6 -0
  60. package/dist/lib/protocols/rawResponseCapture.d.ts +6 -0
  61. package/dist/lib/protocols/rawResponseCapture.js +41 -29
  62. package/dist/lib/protocols/rawResponseCapture.mjs +40 -29
  63. package/dist/lib/protocols/signedRequestRejection.d.mts +9 -0
  64. package/dist/lib/protocols/signedRequestRejection.d.ts +9 -0
  65. package/dist/lib/protocols/signedRequestRejection.js +209 -0
  66. package/dist/lib/protocols/signedRequestRejection.mjs +189 -0
  67. package/dist/lib/protocols/transportDiagnostics.d.mts +1 -0
  68. package/dist/lib/protocols/transportDiagnostics.d.ts +1 -0
  69. package/dist/lib/protocols/transportDiagnostics.js +2 -0
  70. package/dist/lib/protocols/transportDiagnostics.mjs +1 -0
  71. package/dist/lib/registry/types.generated.d.mts +112 -45
  72. package/dist/lib/registry/types.generated.d.ts +112 -45
  73. package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
  74. package/dist/lib/server/account-provisioning.d.mts +2 -0
  75. package/dist/lib/server/account-provisioning.d.ts +2 -0
  76. package/dist/lib/server/account-provisioning.js +30 -0
  77. package/dist/lib/server/account-provisioning.mjs +6 -0
  78. package/dist/lib/server/account-reference-warnings.d.mts +12 -0
  79. package/dist/lib/server/account-reference-warnings.d.ts +12 -0
  80. package/dist/lib/server/account-reference-warnings.js +48 -0
  81. package/dist/lib/server/account-reference-warnings.mjs +23 -0
  82. package/dist/lib/server/auth-signature.js +1 -0
  83. package/dist/lib/server/auth-signature.mjs +1 -0
  84. package/dist/lib/server/create-adcp-server.d.mts +34 -0
  85. package/dist/lib/server/create-adcp-server.d.ts +34 -0
  86. package/dist/lib/server/create-adcp-server.js +225 -14
  87. package/dist/lib/server/create-adcp-server.mjs +225 -14
  88. package/dist/lib/server/decisioning/account.d.mts +2 -0
  89. package/dist/lib/server/decisioning/account.d.ts +2 -0
  90. package/dist/lib/server/decisioning/runtime/from-platform.js +49 -10
  91. package/dist/lib/server/decisioning/runtime/from-platform.mjs +49 -10
  92. package/dist/lib/server/index.d.mts +2 -2
  93. package/dist/lib/server/index.d.ts +2 -2
  94. package/dist/lib/server/index.js +2 -0
  95. package/dist/lib/server/index.mjs +3 -1
  96. package/dist/lib/signing/agent-resolver/consistency.d.mts +6 -13
  97. package/dist/lib/signing/agent-resolver/consistency.d.ts +6 -13
  98. package/dist/lib/signing/agent-resolver/consistency.js +0 -1
  99. package/dist/lib/signing/agent-resolver/consistency.mjs +0 -1
  100. package/dist/lib/signing/agent-resolver/errors.d.mts +3 -1
  101. package/dist/lib/signing/agent-resolver/errors.d.ts +3 -1
  102. package/dist/lib/signing/agent-resolver/errors.js +6 -0
  103. package/dist/lib/signing/agent-resolver/errors.mjs +6 -0
  104. package/dist/lib/signing/agent-resolver/fetch-helpers.d.mts +11 -0
  105. package/dist/lib/signing/agent-resolver/fetch-helpers.d.ts +11 -0
  106. package/dist/lib/signing/agent-resolver/fetch-helpers.js +37 -2
  107. package/dist/lib/signing/agent-resolver/fetch-helpers.mjs +40 -3
  108. package/dist/lib/signing/agent-resolver/jwks-set.js +24 -3
  109. package/dist/lib/signing/agent-resolver/jwks-set.mjs +24 -3
  110. package/dist/lib/signing/agent-resolver/legacy-brand.d.mts +15 -0
  111. package/dist/lib/signing/agent-resolver/legacy-brand.d.ts +15 -0
  112. package/dist/lib/signing/agent-resolver/legacy-brand.js +60 -0
  113. package/dist/lib/signing/agent-resolver/legacy-brand.mjs +36 -0
  114. package/dist/lib/signing/agent-resolver/operator-authorization.d.mts +13 -0
  115. package/dist/lib/signing/agent-resolver/operator-authorization.d.ts +13 -0
  116. package/dist/lib/signing/agent-resolver/operator-authorization.js +108 -0
  117. package/dist/lib/signing/agent-resolver/operator-authorization.mjs +84 -0
  118. package/dist/lib/signing/agent-resolver/resolve-agent.d.mts +14 -4
  119. package/dist/lib/signing/agent-resolver/resolve-agent.d.ts +14 -4
  120. package/dist/lib/signing/agent-resolver/resolve-agent.js +148 -137
  121. package/dist/lib/signing/agent-resolver/resolve-agent.mjs +157 -139
  122. package/dist/lib/signing/agent-resolver/resolved-agent-jwks.d.mts +7 -1
  123. package/dist/lib/signing/agent-resolver/resolved-agent-jwks.d.ts +7 -1
  124. package/dist/lib/signing/agent-resolver/resolved-agent-jwks.js +50 -17
  125. package/dist/lib/signing/agent-resolver/resolved-agent-jwks.mjs +50 -17
  126. package/dist/lib/signing/agent-resolver/select-agent.d.mts +14 -15
  127. package/dist/lib/signing/agent-resolver/select-agent.d.ts +14 -15
  128. package/dist/lib/signing/agent-resolver/select-agent.js +98 -13
  129. package/dist/lib/signing/agent-resolver/select-agent.mjs +95 -13
  130. package/dist/lib/signing/brand-jwks.d.mts +29 -75
  131. package/dist/lib/signing/brand-jwks.d.ts +29 -75
  132. package/dist/lib/signing/brand-jwks.js +120 -182
  133. package/dist/lib/signing/brand-jwks.mjs +120 -182
  134. package/dist/lib/signing/errors.d.mts +7 -3
  135. package/dist/lib/signing/errors.d.ts +7 -3
  136. package/dist/lib/signing/errors.js +7 -2
  137. package/dist/lib/signing/errors.mjs +7 -2
  138. package/dist/lib/signing/jwks-https.d.mts +7 -0
  139. package/dist/lib/signing/jwks-https.d.ts +7 -0
  140. package/dist/lib/signing/jwks-https.js +31 -8
  141. package/dist/lib/signing/jwks-https.mjs +31 -8
  142. package/dist/lib/signing/jwks.d.mts +8 -0
  143. package/dist/lib/signing/jwks.d.ts +8 -0
  144. package/dist/lib/signing/middleware.js +2 -1
  145. package/dist/lib/signing/middleware.mjs +2 -1
  146. package/dist/lib/signing/publisher-pins.d.mts +11 -0
  147. package/dist/lib/signing/publisher-pins.d.ts +11 -0
  148. package/dist/lib/signing/publisher-pins.js +125 -0
  149. package/dist/lib/signing/publisher-pins.mjs +101 -0
  150. package/dist/lib/signing/server.d.mts +1 -0
  151. package/dist/lib/signing/server.d.ts +1 -0
  152. package/dist/lib/signing/types.d.mts +5 -0
  153. package/dist/lib/signing/types.d.ts +5 -0
  154. package/dist/lib/signing/verifier.js +50 -5
  155. package/dist/lib/signing/verifier.mjs +50 -5
  156. package/dist/lib/signing/webhook-verifier.d.mts +7 -2
  157. package/dist/lib/signing/webhook-verifier.d.ts +7 -2
  158. package/dist/lib/signing/webhook-verifier.js +42 -2
  159. package/dist/lib/signing/webhook-verifier.mjs +42 -2
  160. package/dist/lib/testing/storyboard/account-policy.d.mts +2 -0
  161. package/dist/lib/testing/storyboard/account-policy.d.ts +2 -0
  162. package/dist/lib/testing/storyboard/account-policy.js +35 -0
  163. package/dist/lib/testing/storyboard/account-policy.mjs +11 -0
  164. package/dist/lib/testing/storyboard/context.js +6 -0
  165. package/dist/lib/testing/storyboard/context.mjs +6 -0
  166. package/dist/lib/testing/storyboard/request-builder.js +12 -2
  167. package/dist/lib/testing/storyboard/request-builder.mjs +12 -2
  168. package/dist/lib/testing/storyboard/runner.js +3 -2
  169. package/dist/lib/testing/storyboard/runner.mjs +3 -2
  170. package/dist/lib/testing/storyboard/validations.d.mts +1 -1
  171. package/dist/lib/testing/storyboard/validations.d.ts +1 -1
  172. package/dist/lib/types/accept-proposal.d.ts +19 -1
  173. package/dist/lib/types/buy-products.d.ts +19 -1
  174. package/dist/lib/types/check-governance.d.ts +19 -1
  175. package/dist/lib/types/comply-test-controller.d.ts +19 -1
  176. package/dist/lib/types/control-media-buy.d.ts +19 -1
  177. package/dist/lib/types/core.generated.d.mts +14 -1
  178. package/dist/lib/types/core.generated.d.ts +14 -1
  179. package/dist/lib/types/create-media-buy.d.ts +14 -1
  180. package/dist/lib/types/get-media-buys.d.ts +14 -1
  181. package/dist/lib/types/get-products.d.ts +19 -1
  182. package/dist/lib/types/list-products.d.ts +19 -1
  183. package/dist/lib/types/refine-proposals.d.ts +19 -1
  184. package/dist/lib/types/request-proposals.d.ts +19 -1
  185. package/dist/lib/types/schemas.generated.d.ts +12 -3
  186. package/dist/lib/types/schemas.generated.js +4 -1
  187. package/dist/lib/types/schemas.generated.mjs +4 -1
  188. package/dist/lib/types/tools.generated.d.mts +14 -1
  189. package/dist/lib/types/tools.generated.d.ts +14 -1
  190. package/dist/lib/types/update-media-buy.d.ts +14 -1
  191. package/dist/lib/version.d.mts +3 -3
  192. package/dist/lib/version.d.ts +3 -3
  193. package/dist/lib/version.js +3 -3
  194. package/dist/lib/version.mjs +3 -3
  195. package/dist/lib/webhooks/index.d.mts +24 -0
  196. package/dist/lib/webhooks/index.d.ts +24 -0
  197. package/dist/lib/webhooks/index.js +50 -24
  198. package/dist/lib/webhooks/index.mjs +49 -24
  199. package/dist/lib/wholesale-feed-sync/index.d.mts +2 -0
  200. package/dist/lib/wholesale-feed-sync/index.d.ts +2 -0
  201. package/dist/lib/wholesale-feed-sync/index.js +7 -0
  202. package/dist/lib/wholesale-feed-sync/index.mjs +4 -0
  203. package/dist/lib/wholesale-feed-sync/mirror.d.mts +97 -0
  204. package/dist/lib/wholesale-feed-sync/mirror.d.ts +97 -0
  205. package/dist/lib/wholesale-feed-sync/mirror.js +350 -0
  206. package/dist/lib/wholesale-feed-sync/mirror.mjs +322 -0
  207. package/dist/lib/wholesale-feed-sync/sync.d.mts +13 -29
  208. package/dist/lib/wholesale-feed-sync/sync.d.ts +13 -29
  209. package/dist/lib/wholesale-feed-sync/sync.js +208 -281
  210. package/dist/lib/wholesale-feed-sync/sync.mjs +213 -281
  211. package/dist/lib/wholesale-feed-sync/types.d.mts +4 -3
  212. package/dist/lib/wholesale-feed-sync/types.d.ts +4 -3
  213. package/docs/README.md +6 -0
  214. package/docs/TYPE-SUMMARY.md +2 -2
  215. package/docs/guides/BUILD-AN-AGENT.md +2 -2
  216. package/docs/guides/BUYER-QUICKSTART-3.2.md +2 -0
  217. package/docs/guides/BUYER-STORAGE.md +3 -0
  218. package/docs/guides/FIRST-CALL-TO-A-SELLER.md +106 -0
  219. package/docs/guides/SIGNING-GUIDE.md +16 -7
  220. package/docs/guides/account-resolution.md +132 -10
  221. package/docs/llms.txt +3 -2
  222. package/docs/migration-14.0-to-14.1.md +85 -0
  223. package/docs/migration-14.x-rc-worksheet.md +4 -4
  224. package/docs/migration-4.x-to-5.x.md +1 -0
  225. package/docs/migration-agent-resolution-3.3.md +125 -0
  226. package/docs/recipes/verifying-inbound-webhooks.md +60 -15
  227. package/package.json +3 -2
  228. package/skills/adcp-brand.previous/SKILL.md +0 -200
  229. package/skills/adcp-creative.previous/SKILL.md +0 -305
  230. package/skills/adcp-governance.previous/SKILL.md +0 -566
  231. package/skills/adcp-measurement.previous/SKILL.md +0 -136
  232. package/skills/adcp-media-buy.previous/SKILL.md +0 -556
  233. package/skills/adcp-si.previous/SKILL.md +0 -206
  234. package/skills/adcp-signals.previous/SKILL.md +0 -204
@@ -0,0 +1,125 @@
1
+ # Agent resolution and publisher pins
2
+
3
+ SDK 14.1 follows the shared AdCP 3.3 agent-resolution algorithm for request,
4
+ webhook and governance signatures. SDK 14.1.0 still uses **AdCP 3.2.1 on the
5
+ wire**: this alignment changes key discovery and verification, not the configured
6
+ wire protocol version. Start with the [14.0-to-14.1 upgrade checklist](./migration-14.0-to-14.1.md)
7
+ for required changes and opt-in features outside signature verification.
8
+
9
+ ## Expected agent URLs
10
+
11
+ Prefer passing the agent URL already recorded by your integration:
12
+
13
+ ```ts
14
+ const jwks = new ResolvedAgentJwksResolver(expectedSellerUrl, 'mcp', {
15
+ legacyWebhookFallback: true, // Webhooks from older 3.x sellers only.
16
+ });
17
+ ```
18
+
19
+ `BrandJsonJwksResolver(operatorUrl, { agentUrl, agentType, agentId })` remains
20
+ available for onboarding mappings. It confirms `operatorUrl` against the agent's
21
+ capabilities on cache refresh. `agentUrl` is optional for existing callers.
22
+ When omitted, the resolver infers one canonical URL from its trusted onboarding
23
+ record using the existing type/id/brand selectors, then confirms that URL's
24
+ capabilities-selected operator record before accepting any key. Ambiguous
25
+ onboarding fails closed; the inferred identity stays pinned for that resolver.
26
+ Moving to a different agent URL requires a new resolver instance. Explicit
27
+ `agentUrl` configurations must use the exact capabilities-published operator
28
+ URL; legacy onboarding may resolve document indirection before confirming it.
29
+ Existing `jwksOptions` types remain accepted; cache age and cooldown settings
30
+ apply within protocol bounds, and verification always fails closed on expiry.
31
+ `agentType` and `agentId` only narrow the final canonical URL match. Deprecated
32
+ `brandId` and `maxRedirects` apply only to legacy onboarding, and do not restrict
33
+ the final operator collections or capability discovery.
34
+ Explicit capability URLs allow no HTTP or document-indirection redirects. The
35
+ webhook-only fallback allows the bounded host/www HTTP policy and one document
36
+ indirection. Request verification rejects keys discovered through that fallback.
37
+ `SingleAgentClient` and `BrandJsonJwksResolver` enable the fallback by default for webhooks;
38
+ set `webhookVerification.resolverOptions.legacyWebhookFallback` to `false` to
39
+ disable it. Set `legacyWebhookFallback: false` on a standalone brand resolver to disable it.
40
+ `ResolvedAgentJwksResolver` requires explicitly enabling the fallback for webhook use.
41
+ Wrappers shared with request verification must forward `resolveWithMetadata`
42
+ so the verifier can refuse webhook-only fallback keys.
43
+
44
+ Capabilities discovery defaults to MCP. A2A integrations using
45
+ `BrandJsonJwksResolver` must set `protocol: 'a2a'`; the onboarding record does
46
+ not determine the transport. Existing cross-domain onboarding records must
47
+ agree with the agent's capabilities-selected operator record. A legacy
48
+ webhook without `brand_json_url` must use the agent-origin well-known record.
49
+
50
+ Expired operator mappings fail closed. Successful operator records have a
51
+ 30-second minimum polling interval, including `no-cache` records, with their
52
+ effective cache lifetime bounded above by the JWKS revocation polling interval
53
+ and the configured local cap. Capabilities are re-confirmed on every refresh.
54
+ A local cap shorter than the discovery cooldown can temporarily reject
55
+ verification rather than reuse an expired mapping. Negative discovery results
56
+ are throttled for at most 60 seconds.
57
+ `maxAgeSeconds` must be positive; zero cannot provide a usable verified mapping
58
+ with the protocol discovery cooldown. Public `forceRefresh()` is an
59
+ operator-triggered cache flush and bypasses normal resolved-key cooldowns;
60
+ failed onboarding attempts retain their 30-second cooldown.
61
+ Standalone `HttpsJwksResolver` also applies its configured cooldown after a
62
+ failed initial fetch or refresh and rejects non-finite or negative cache
63
+ options. Brand resolvers validate their configuration before fetching.
64
+
65
+ Canonical identity normalization preserves path slashes, query order, trailing
66
+ empty queries and scheme distinctions. Update principal indexes that previously
67
+ used non-canonical spellings. Duplicate canonical matches within one collection
68
+ are ambiguous. Shared portfolio declarations count once only when their type
69
+ and JWKS source agree.
70
+
71
+ ## Publisher pin context
72
+
73
+ Pass `publisherPins` to `verifyWebhookSignature` or `createWebhookVerifier`.
74
+ With the high-level client, configure `webhookVerification.publisherPins` to
75
+ look up those pins from the persisted registration and your own media-buy record.
76
+ Include every publisher whose inventory the delivery concerns. Never use payload
77
+ fields to choose publishers.
78
+
79
+ Each pin holds `publisher`, `signingKeys` and an async `refresh` callback that
80
+ bypasses the publisher's adagents.json cache. Reuse the refresh callback across
81
+ deliveries and bind it to the agent, publisher and tenant context it reads.
82
+ The SDK coalesces concurrent refreshes and reuses their confirmed result (or
83
+ failure) for 30 seconds, so a captured rejected delivery cannot repeatedly force
84
+ uncached publisher fetches. Missing `signingKeys` means no pin;
85
+ an empty array accepts no key. `refresh` must throw on failure; `null` means a
86
+ successful fetch confirmed removal of the pin. A pinned key must also appear in
87
+ the agent JWKS and match by RFC 7638 thumbprint. `kid`-only entries match nothing;
88
+ revoked entries cannot authorize delivery. `key_origins` remains enforced.
89
+
90
+ ## Governance
91
+
92
+ Supply `buyerIdentity` for each authenticated request to governance verification
93
+ or enforcement middleware:
94
+
95
+ ```ts
96
+ await enforceGovernance({
97
+ ...governedRequest,
98
+ buyerIdentity: {
99
+ brandJson: request.verifiedSigner.operatorRecord.document,
100
+ brandDomain: governedRequest.payload.brand.domain,
101
+ },
102
+ }, performGovernedAction);
103
+ ```
104
+
105
+ For signed buyers, use the exact `operatorRecord.document` exposed by request
106
+ verification, the authenticator or Express middleware. Do not refetch a different
107
+ brand.json at its host. Other authenticated buyer identity paths may supply their
108
+ own trusted current record. Select the governed brand's collection, with house
109
+ fallback only when it has no agents override; never use a sibling brand's agents.
110
+ The inline brand URL hostname must equal the governed brand domain; `www` and
111
+ the bare domain are distinct here.
112
+
113
+ The legacy `jwks` plus `expectedIssuer` integration remains available for trusted
114
+ onboarding resolvers. With `buyerIdentity`, the SDK selects and caches the matched
115
+ entry's JWKS instead; no extra `jwks` argument is needed. Reuse a configured
116
+ `jwksOptions` object across requests to share its bounded resolver cache.
117
+ Governance replay and
118
+ revocation lookups use canonical issuer URLs. Migrate external indexes and replay
119
+ store keys when they contain non-canonical issuer spellings.
120
+
121
+ Origin binding reads `authorized_operators` only from a House Portfolio and
122
+ matches the exact agent eTLD+1. Brands and countries apply to account authorization,
123
+ separately from key discovery. Existing explicit `requiredOperatorBrand`,
124
+ `requiredOperatorScope` and `requiredOperatorCountry` receiver policies still
125
+ check the delegation tuple and its validity bounds, including at cache acceptance.
@@ -75,12 +75,12 @@ verify before parsing or re-serializing.
75
75
  ## Recommended RFC 9421 Setup
76
76
 
77
77
  Create one verifier per expected sending agent. The resolver is bound to that
78
- agent's `brand.json` and selector, so a webhook for one seller cannot be
78
+ agent's canonical URL and protocol, so a webhook for one seller cannot be
79
79
  verified with another seller's keys.
80
80
 
81
81
  ```ts
82
82
  import {
83
- BrandJsonJwksResolver,
83
+ ResolvedAgentJwksResolver,
84
84
  createWebhookVerifier,
85
85
  type BrandAgentType,
86
86
  type RequestLike,
@@ -89,24 +89,26 @@ import {
89
89
  const verifiers = new Map<string, ReturnType<typeof createWebhookVerifier>>();
90
90
 
91
91
  type SenderRecord = {
92
+ operationId: string;
93
+ agentUrl: string;
94
+ protocol: 'mcp' | 'a2a';
92
95
  agentId: string;
96
+ publisherPins?: readonly import('@adcp/sdk/signing/server').PublisherSigningKeyPin[];
93
97
  agentType: BrandAgentType;
94
- brandJsonUrl: string;
95
- brandId?: string;
96
98
  webhookAuth: 'rfc9421' | 'legacy-hmac';
97
99
  legacyHmacSecret?: string;
98
100
  };
99
101
 
100
102
  function verifierFor(sender: SenderRecord) {
101
- const cacheKey = `${sender.brandJsonUrl}#${sender.brandId ?? '-'}#${sender.agentType}:${sender.agentId}`;
103
+ const cacheKey = `${sender.operationId}#${sender.agentUrl}#${sender.agentType}:${sender.agentId}`;
102
104
  let verifier = verifiers.get(cacheKey);
103
105
  if (!verifier) {
104
- const jwks = new BrandJsonJwksResolver(sender.brandJsonUrl, {
106
+ const jwks = new ResolvedAgentJwksResolver(sender.agentUrl, sender.protocol, {
105
107
  agentType: sender.agentType,
106
108
  agentId: sender.agentId,
107
- ...(sender.brandId ? { brandId: sender.brandId } : {}),
109
+ legacyWebhookFallback: true,
108
110
  });
109
- verifier = createWebhookVerifier({ jwks });
111
+ verifier = createWebhookVerifier({ jwks, publisherPins: sender.publisherPins });
110
112
  verifiers.set(cacheKey, verifier);
111
113
  }
112
114
  return verifier;
@@ -117,13 +119,50 @@ async function verifyRfc9421Webhook(sender: SenderRecord, request: RequestLike)
117
119
  }
118
120
  ```
119
121
 
122
+ Discovery fetches the seller's `get_adcp_capabilities` through the official
123
+ protocol client and uses its `identity.brand_json_url`. Canonical URL matching
124
+ selects the agent; type and id only narrow that match. `key_origins` is checked
125
+ for pinned keys too. Cached mappings are re-confirmed within the brand.json
126
+ cache lifetime. The explicitly enabled 3.x fallback uses the agent host's
127
+ `/.well-known/brand.json`, then its eTLD+1 only when the host serves no record.
128
+ A present `brand_json_url` is always used, including when it is invalid or unreachable.
129
+
130
+ Populate `publisherPins` from your **own stored media-buy inventory**, including
131
+ every applicable publisher. Never choose publishers from the incoming payload.
132
+ A key must be published in the agent JWKS and match every applicable pin by
133
+ RFC 7638 thumbprint; a matching `kid` alone is insufficient. Each pin's `refresh`
134
+ callback must bypass the publisher's adagents.json cache and throw on failure.
135
+ Reuse each callback bound to its seller and tenant context across deliveries;
136
+ concurrent refreshes and retries share a 30-second cooldown.
137
+ Return `null` only when a successful authoritative fetch confirms removal of the
138
+ pin. Cache verifier instances per operation because different buys may have
139
+ different publishers, and remove them when the operation is retired.
140
+
141
+ ```ts
142
+ const publisherPins = mediaBuy.publisherAuthorizations.map(authorization => ({
143
+ publisher: authorization.publisherDomain,
144
+ signingKeys: authorization.signingKeys,
145
+ refresh: async () => {
146
+ const current = await publisherStore.refreshAgentAuthorization(
147
+ authorization.publisherDomain, sender.agentUrl, { bypassCache: true }
148
+ );
149
+ return current.signingKeys ?? null;
150
+ },
151
+ }));
152
+ ```
153
+
154
+ `mediaBuy` and `publisherStore` are application-owned trusted records and a
155
+ publisher-document fetcher. Pin misses force-refresh before final rejection,
156
+ after signature authentication, and fail with `webhook_signature_key_unknown`.
157
+ Use `onKeyResolutionError` to log the specific `request_signature_*` cause locally.
158
+
120
159
  `createWebhookVerifier` defaults replay and revocation stores once at factory
121
160
  creation time. That is safe for a single process. It is not enough behind a
122
161
  load balancer.
123
162
 
124
163
  `SenderRecord`, `lookupSenderForOperation()`, and `processWebhook()` are
125
164
  application-owned. Persist the expected `agentId`, `agentType`,
126
- `brandJsonUrl`, optional `brandId`, and exact `webhookAuth` mode when you
165
+ `agentUrl`, protocol, and exact `webhookAuth` mode when you
127
166
  initiate or register the operation. The webhook receiver should read that
128
167
  state by operation ID before looking at any signature header.
129
168
 
@@ -234,7 +273,7 @@ Postgres:
234
273
  ```ts
235
274
  import { Pool } from 'pg';
236
275
  import {
237
- BrandJsonJwksResolver,
276
+ ResolvedAgentJwksResolver,
238
277
  PostgresReplayStore,
239
278
  createWebhookVerifier,
240
279
  getReplayStoreMigration,
@@ -249,12 +288,13 @@ const replayStore = new PostgresReplayStore(pool);
249
288
 
250
289
  function buildVerifier(sender: SenderRecord) {
251
290
  return createWebhookVerifier({
252
- jwks: new BrandJsonJwksResolver(sender.brandJsonUrl, {
291
+ jwks: new ResolvedAgentJwksResolver(sender.agentUrl, sender.protocol, {
253
292
  agentType: sender.agentType,
254
293
  agentId: sender.agentId,
255
- ...(sender.brandId ? { brandId: sender.brandId } : {}),
294
+ legacyWebhookFallback: true,
256
295
  }),
257
296
  replayStore,
297
+ publisherPins: sender.publisherPins,
258
298
  });
259
299
  }
260
300
  ```
@@ -263,7 +303,7 @@ Redis:
263
303
 
264
304
  ```ts
265
305
  import { createClient } from 'redis';
266
- import { BrandJsonJwksResolver, RedisReplayStore, createWebhookVerifier } from '@adcp/sdk/signing/server';
306
+ import { ResolvedAgentJwksResolver, RedisReplayStore, createWebhookVerifier } from '@adcp/sdk/signing/server';
267
307
 
268
308
  const redis = createClient({ url: process.env.REDIS_URL });
269
309
  await redis.connect();
@@ -275,12 +315,13 @@ const replayStore = new RedisReplayStore(redis, {
275
315
 
276
316
  function buildVerifier(sender: SenderRecord) {
277
317
  return createWebhookVerifier({
278
- jwks: new BrandJsonJwksResolver(sender.brandJsonUrl, {
318
+ jwks: new ResolvedAgentJwksResolver(sender.agentUrl, sender.protocol, {
279
319
  agentType: sender.agentType,
280
320
  agentId: sender.agentId,
281
- ...(sender.brandId ? { brandId: sender.brandId } : {}),
321
+ legacyWebhookFallback: true,
282
322
  }),
283
323
  replayStore,
324
+ publisherPins: sender.publisherPins,
284
325
  });
285
326
  }
286
327
  ```
@@ -346,3 +387,7 @@ debug sink with redaction.
346
387
  - [ ] Multi-replica deployments use Redis or Postgres replay storage.
347
388
  - [ ] Signature failure never falls back to another auth scheme.
348
389
  - [ ] JSON parsing and side effects happen only after verification succeeds.
390
+
391
+ Legacy HMAC receivers can call `preflightWebhookRequest({ headers }, { maxSkewSeconds: 300 })`
392
+ from `@adcp/sdk/webhooks` before secret lookup. Success checks syntax/freshness;
393
+ then authenticate with `verifyWebhookRequest` using the secret and exact raw body.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adcp/sdk",
3
- "version": "14.0.0",
3
+ "version": "14.2.0",
4
4
  "description": "AdCP SDK — client, server, and compliance harnesses for the AdContext Protocol (MCP + A2A)",
5
5
  "workspaces": [
6
6
  ".",
@@ -541,6 +541,7 @@
541
541
  "!dist/lib/schemas-data/*/bundled/**/*",
542
542
  "bin/**/*.js",
543
543
  "skills/**/*",
544
+ "!skills/*.previous/**/*",
544
545
  ".claude-plugin/**/*",
545
546
  "compliance/cache/3.0.25/**/*",
546
547
  "compliance/cache/3.1.24/**/*",
@@ -749,7 +750,7 @@
749
750
  "@a2a-js/sdk": "^1.0.1",
750
751
  "@apidevtools/json-schema-ref-parser": "^15.5.2",
751
752
  "@arethetypeswrong/cli": "^0.18.5",
752
- "@changesets/cli": "^2.31.1",
753
+ "@changesets/cli": "^3.0.3",
753
754
  "@commitlint/cli": "^20.5.3",
754
755
  "@commitlint/config-conventional": "^20.5.3",
755
756
  "@modelcontextprotocol/sdk": "^1.30.0",
@@ -1,200 +0,0 @@
1
- ---
2
- name: adcp-brand
3
- description: Execute AdCP Brand Protocol operations with brand agents - get brand identity data, search for licensable rights, acquire rights for campaigns, and manage existing grants. Use when users want to look up brand identities, find talent or IP for licensing, or manage rights grants.
4
- ---
5
-
6
- # AdCP Brand Protocol
7
-
8
- This skill enables you to execute the AdCP Brand Protocol with brand agents. The Brand Protocol provides access to brand identity, creative guidelines, and licensable rights (talent, IP, content).
9
-
10
- > **Buyer-side basics** — idempotency replay, `oneOf` variants, async `status:'submitted'` polling, error recovery from `adcp_error.issues[]` — live in `skills/call-adcp-agent/SKILL.md`. This skill covers per-task semantics only.
11
-
12
- ## Overview
13
-
14
- The Brand Protocol provides 4 standardized tasks:
15
-
16
- | Task | Purpose | Response Time |
17
- |------|---------|---------------|
18
- | `get_brand_identity` | Get brand identity and guidelines | ~1-3s |
19
- | `get_rights` | Search licensable rights | ~1-5s |
20
- | `acquire_rights` | Acquire rights for a campaign | ~1-10s |
21
- | `update_rights` | Modify an existing grant | ~1-5s |
22
-
23
- ## Typical Workflow
24
-
25
- ### Brand Identity Lookup
26
- 1. **Get identity**: `get_brand_identity` with brand domain and optional field filter
27
- 2. **Use data**: Apply colors, logos, tone, guidelines to creative generation
28
-
29
- ### Rights Licensing
30
- 1. **Search rights**: `get_rights` with natural language query and use types
31
- 2. **Review options**: Evaluate matches by pricing, availability, compatibility
32
- 3. **Acquire**: `acquire_rights` with selected pricing option and campaign details
33
- 4. **Manage**: `update_rights` to extend, adjust caps, or pause/resume
34
-
35
- ---
36
-
37
- ## Task Reference
38
-
39
- ### get_brand_identity
40
-
41
- Get brand identity data from a brand agent.
42
-
43
- **Request:**
44
- ```json
45
- {
46
- "brand_id": "athlete-jane-doe",
47
- "fields": ["description", "logos", "colors", "tone"],
48
- "use_case": "creative_production",
49
- "authorized": true
50
- }
51
- ```
52
-
53
- **Key fields:**
54
- - `brand_id` (string, required): Brand identifier within the agent's roster
55
- - `fields` (array, optional): Sections to include — `description`, `industry`, `keller_type`, `logos`, `colors`, `fonts`, `visual_guidelines`, `tone`, `tagline`, `voice_synthesis`, `assets`, `rights`. Omit for all.
56
- - `use_case` (string, optional): Intended use — `endorsement`, `voice_synthesis`, `likeness`, `creative_production`, `media_planning`
57
- - `authorized` (boolean, optional): Sandbox only — simulate authorized access to see protected fields. Real agents use OAuth. Default false.
58
-
59
- **Response contains:**
60
- - `brand`: Brand identity object with requested fields
61
- - Public fields (always available): `description`, `industry`, `logos` (public subset)
62
- - Protected fields (require authorization): `colors`, `fonts`, `tone`, `voice_synthesis`, `visual_guidelines`, full `assets`
63
-
64
- ---
65
-
66
- ### get_rights
67
-
68
- Search for licensable rights (talent, IP, content) from a brand agent.
69
-
70
- **Request:**
71
- ```json
72
- {
73
- "query": "Dutch athlete for restaurant brand in Amsterdam, budget 400 EUR/month",
74
- "uses": ["likeness", "endorsement"],
75
- "buyer_brand": {
76
- "domain": "restaurant.nl"
77
- },
78
- "countries": ["NL"],
79
- "include_excluded": false
80
- }
81
- ```
82
-
83
- **Key fields:**
84
- - `query` (string, required): Natural language description of desired rights
85
- - `uses` (array, required): Rights uses — `likeness`, `voice`, `name`, `endorsement`
86
- - `buyer_brand` (object, optional): Buyer brand for compatibility filtering — `{ domain, brand_id }`
87
- - `countries` (array, optional): Countries where rights are needed (ISO 3166-1 alpha-2)
88
- - `brand_id` (string, optional): Search within a specific brand only
89
- - `include_excluded` (boolean, optional): Include filtered-out results with reasons. Default false.
90
-
91
- **Response contains:**
92
- - `rights`: Array of matching rights offerings with:
93
- - `rights_id`: Use in `acquire_rights`
94
- - `brand_id`, `name`, `description`: Who/what the rights cover
95
- - `uses`: Available use types
96
- - `pricing_options`: Array with `pricing_option_id`, `price`, `currency`, `period`
97
- - `availability`: Geographic and temporal restrictions
98
- - `exclusions`: Any brand/category conflicts
99
-
100
- ---
101
-
102
- ### acquire_rights
103
-
104
- Acquire rights from a brand agent for a campaign.
105
-
106
- **Request:**
107
- ```json
108
- {
109
- "rights_id": "rights_jane_doe_endorsement",
110
- "pricing_option_id": "monthly_standard",
111
- "buyer": {
112
- "domain": "restaurant.nl"
113
- },
114
- "campaign": {
115
- "description": "Social media campaign featuring athlete endorsement for Amsterdam restaurant launch",
116
- "uses": ["likeness", "endorsement"],
117
- "countries": ["NL"],
118
- "estimated_impressions": 500000,
119
- "start_date": "2025-03-01",
120
- "end_date": "2025-06-30"
121
- }
122
- }
123
- ```
124
-
125
- **Key fields:**
126
- - `rights_id` (string, required): From `get_rights` response
127
- - `pricing_option_id` (string, required): Selected pricing option
128
- - `buyer` (object, required): Buyer brand identity — `{ domain, brand_id }`
129
- - `campaign` (object, required): Campaign details for rights clearance
130
- - `description` (string, required): How the rights will be used
131
- - `uses` (array, required): Rights uses for this campaign
132
- - `countries` (array, optional): Campaign countries
133
- - `estimated_impressions` (integer, optional): Estimated total impressions
134
- - `start_date`, `end_date` (string, optional): Campaign dates (YYYY-MM-DD)
135
-
136
- **Response contains:**
137
- - `status`: `acquired`, `pending_approval`, or `rejected`
138
- - `rights_grant_id`: Grant identifier (if acquired)
139
- - `generation_credentials`: Credentials for AI generation (voice synthesis, likeness, etc.)
140
- - `rejection_reason`: Why the request was rejected (category conflict, exclusivity, etc.)
141
-
142
- ---
143
-
144
- ### update_rights
145
-
146
- Update an existing rights grant — extend dates, adjust impression caps, or pause/resume.
147
-
148
- **Request:**
149
- ```json
150
- {
151
- "rights_id": "grant_abc123",
152
- "end_date": "2025-09-30",
153
- "impression_cap": 1000000,
154
- "paused": false
155
- }
156
- ```
157
-
158
- **Key fields:**
159
- - `rights_id` (string, required): Rights grant identifier from `acquire_rights`
160
- - `end_date` (string, optional): New end date (must be >= current end date)
161
- - `impression_cap` (number, optional): New impression cap (must be >= current)
162
- - `paused` (boolean, optional): Pause or resume the grant
163
-
164
- ---
165
-
166
- ## Key Concepts
167
-
168
- ### Public vs Protected Fields
169
-
170
- Brand agents distinguish between public and protected data:
171
- - **Public**: Available without authorization — basic description, industry, public logos
172
- - **Protected**: Requires OAuth or authorized flag — colors, fonts, tone, voice synthesis credentials, full asset library
173
-
174
- ### Rights Use Types
175
-
176
- - `likeness`: Use of a person's visual likeness (photos, AI-generated images)
177
- - `voice`: Voice synthesis or audio recording rights
178
- - `name`: Use of a person's name in advertising
179
- - `endorsement`: Endorsement/testimonial rights
180
-
181
- ### Rights Clearance
182
-
183
- `acquire_rights` checks:
184
- 1. Brand/category compatibility (no competitor conflicts)
185
- 2. Geographic availability
186
- 3. Temporal availability
187
- 4. Existing exclusivity agreements
188
-
189
- Results: `acquired` (immediate), `pending_approval` (human review), or `rejected` (with reason).
190
-
191
- ---
192
-
193
- ## Error Handling
194
-
195
- Common error codes:
196
-
197
- - `REFERENCE_NOT_FOUND`: Invalid brand_id, rights_id, or pricing_option_id (brand-protocol resources use the universal not-found fallback per `error-handling.mdx`; brands lack a dedicated `*_NOT_FOUND` code)
198
- - `CATEGORY_CONFLICT`: Buyer brand conflicts with existing agreements
199
- - `GEOGRAPHIC_RESTRICTION`: Rights not available in requested countries
200
- - `AUTHORIZATION_REQUIRED`: Protected fields require OAuth