@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
@@ -1,8 +1,12 @@
1
1
  import { parse as parseTld } from "tldts";
2
+ import { isIP } from "node:net";
2
3
  import { createA2AClient, createMCPClient } from "../../protocols/index.mjs";
3
4
  import { isDevelopmentBrandDomain } from "../../brand/domain.mjs";
4
5
  import { readBrandJsonUrl, readIdentityPosture } from "./capabilities-types.mjs";
5
- import { canonicalizeOrigin } from "./canonicalize.mjs";
6
+ import { isAlwaysBlocked, isPrivateIp } from "../../net/index.mjs";
7
+ import { AgentTransportPolicyError, createAgentTransportFetch } from "../../net/agent-transport-fetch.mjs";
8
+ import { checkDelegatedOperatorAuthorization } from "./operator-authorization.mjs";
9
+ import { fetchLegacyBrandJson } from "./legacy-brand.mjs";
6
10
  import {
7
11
  checkOriginConsistency,
8
12
  checkRequiredOrigins,
@@ -10,9 +14,16 @@ import {
10
14
  } from "./consistency.mjs";
11
15
  import { AgentResolverError } from "./errors.mjs";
12
16
  import { eTldPlusOne, sameEtldPlusOne } from "./etld.mjs";
13
- import { MAX_BRAND_JSON_BYTES, MAX_JWKS_BYTES, safeFetchJson, SafeFetchError } from "./fetch-helpers.mjs";
17
+ import {
18
+ MAX_BRAND_JSON_BYTES,
19
+ MAX_JWKS_BYTES,
20
+ safeFetchJson,
21
+ SafeFetchError,
22
+ classifyDiscoveryFailure,
23
+ isPermanentDiscoveryFailure
24
+ } from "./fetch-helpers.mjs";
14
25
  import { unwrapProtocolResponse } from "../protocol-response.mjs";
15
- import { selectAgentByUrl, AgentSelectorError } from "./select-agent.mjs";
26
+ import { selectAgentByUrl, agentJwksUri, AgentSelectorError, canonicalAgentUrl } from "./select-agent.mjs";
16
27
  const DEFAULT_TIMEOUT_MS = 1e4;
17
28
  async function resolveAgent(agentUrl, options = {}) {
18
29
  const trace = [];
@@ -22,7 +33,7 @@ async function resolveAgent(agentUrl, options = {}) {
22
33
  const caps = options.bodyCaps ?? {};
23
34
  const brandJsonCap = caps.brandJsonBytes ?? MAX_BRAND_JSON_BYTES;
24
35
  const jwksCap = caps.jwksBytes ?? MAX_JWKS_BYTES;
25
- const fetchCapabilities = options.fetchCapabilities ?? defaultFetchCapabilities(agentUrl, options.protocol ?? "mcp");
36
+ const fetchCapabilities = options.fetchCapabilities ?? defaultFetchCapabilities(agentUrl, options.protocol ?? "mcp", allowPrivateIp, timeoutMs);
26
37
  let capabilitiesPayload;
27
38
  let capabilitiesFetchedAt;
28
39
  try {
@@ -39,7 +50,7 @@ async function resolveAgent(agentUrl, options = {}) {
39
50
  } catch (err) {
40
51
  const detail = {
41
52
  agent_url: agentUrl,
42
- dns_error: err instanceof SafeFetchError ? err.transport : "fetch_failed",
53
+ ...classifyDiscoveryFailure(err),
43
54
  last_attempt_at: now()
44
55
  };
45
56
  pushTrace(trace, { step: 1, name: "fetch_capabilities", ok: false, url: agentUrl, detail });
@@ -50,7 +61,10 @@ async function resolveAgent(agentUrl, options = {}) {
50
61
  ["agent_url"]
51
62
  );
52
63
  }
53
- const brandJsonUrl = readBrandJsonUrl(capabilitiesPayload);
64
+ let brandJsonUrl = readBrandJsonUrl(capabilitiesPayload);
65
+ const identity = capabilitiesPayload?.identity;
66
+ const legacyFallback = options.legacyWebhookFallback === true && identity?.brand_json_url === void 0 && permitsLegacyWebhookDiscovery(capabilitiesPayload);
67
+ if (legacyFallback) brandJsonUrl = `${new URL(agentUrl).origin}/.well-known/brand.json`;
54
68
  const identityPosture = readIdentityPosture(capabilitiesPayload);
55
69
  const acceptsScheme = (url) => {
56
70
  if (!url) return false;
@@ -68,24 +82,43 @@ async function resolveAgent(agentUrl, options = {}) {
68
82
  ["agent_url"]
69
83
  );
70
84
  }
85
+ if (!legacyFallback && options.expectedBrandJsonUrl !== void 0 && canonicalAgentUrl(brandJsonUrl) !== canonicalAgentUrl(options.expectedBrandJsonUrl)) {
86
+ throw new AgentResolverError(
87
+ "request_signature_brand_origin_mismatch",
88
+ "Cached operator mapping disagrees with identity.brand_json_url",
89
+ { agent_url: agentUrl },
90
+ ["agent_url"]
91
+ );
92
+ }
71
93
  pushTrace(trace, { step: 2, name: "read_brand_json_url", ok: true, detail: { brand_json_url: brandJsonUrl } });
72
- let agentEtld1;
73
- let brandEtld1;
74
- let sameOrigin;
75
- try {
76
- agentEtld1 = eTldPlusOne(agentUrl);
77
- brandEtld1 = eTldPlusOne(brandJsonUrl);
78
- sameOrigin = sameEtldPlusOne(agentUrl, brandJsonUrl);
79
- } catch {
80
- if (allowPrivateIp) {
81
- const agentHost = new URL(agentUrl).hostname;
82
- const brandHost = new URL(brandJsonUrl).hostname;
83
- const agentIsExplicitDevelopmentHost = agentHost === "localhost" || isDevelopmentBrandDomain(agentHost) || parseTld(agentHost, { extractHostname: false }).isIp;
84
- const brandIsExplicitDevelopmentHost = brandHost === "localhost" || isDevelopmentBrandDomain(brandHost) || parseTld(brandHost, { extractHostname: false }).isIp;
85
- if (agentIsExplicitDevelopmentHost && brandIsExplicitDevelopmentHost) {
86
- agentEtld1 = agentHost;
87
- brandEtld1 = brandHost;
88
- sameOrigin = agentHost === brandHost;
94
+ let agentEtld1 = "";
95
+ let brandEtld1 = "";
96
+ let sameOrigin = true;
97
+ if (!legacyFallback) {
98
+ try {
99
+ agentEtld1 = eTldPlusOne(agentUrl);
100
+ brandEtld1 = eTldPlusOne(brandJsonUrl);
101
+ sameOrigin = sameEtldPlusOne(agentUrl, brandJsonUrl);
102
+ } catch {
103
+ if (allowPrivateIp) {
104
+ const agentHost = new URL(agentUrl).hostname;
105
+ const brandHost = new URL(brandJsonUrl).hostname;
106
+ const agentIsExplicitDevelopmentHost = agentHost === "localhost" || isDevelopmentBrandDomain(agentHost) || parseTld(agentHost, { extractHostname: false }).isIp;
107
+ const brandIsExplicitDevelopmentHost = brandHost === "localhost" || isDevelopmentBrandDomain(brandHost) || parseTld(brandHost, { extractHostname: false }).isIp;
108
+ if (agentIsExplicitDevelopmentHost && brandIsExplicitDevelopmentHost) {
109
+ agentEtld1 = agentHost;
110
+ brandEtld1 = brandHost;
111
+ sameOrigin = agentHost === brandHost;
112
+ } else {
113
+ const detail = { agent_url: agentUrl, brand_json_url: brandJsonUrl };
114
+ pushTrace(trace, { step: 3, name: "etld1_binding", ok: false, detail });
115
+ throw new AgentResolverError(
116
+ "request_signature_brand_origin_mismatch",
117
+ `Cannot compute eTLD+1 for agent or brand.json host`,
118
+ detail,
119
+ ["agent_url", "brand_json_url"]
120
+ );
121
+ }
89
122
  } else {
90
123
  const detail = { agent_url: agentUrl, brand_json_url: brandJsonUrl };
91
124
  pushTrace(trace, { step: 3, name: "etld1_binding", ok: false, detail });
@@ -96,25 +129,24 @@ async function resolveAgent(agentUrl, options = {}) {
96
129
  ["agent_url", "brand_json_url"]
97
130
  );
98
131
  }
99
- } else {
100
- const detail = { agent_url: agentUrl, brand_json_url: brandJsonUrl };
101
- pushTrace(trace, { step: 3, name: "etld1_binding", ok: false, detail });
102
- throw new AgentResolverError(
103
- "request_signature_brand_origin_mismatch",
104
- `Cannot compute eTLD+1 for agent or brand.json host`,
105
- detail,
106
- ["agent_url", "brand_json_url"]
107
- );
108
132
  }
109
133
  }
110
134
  let brandJson;
111
135
  let brandJsonFetchedAt;
136
+ let brandJsonCacheControl;
112
137
  try {
113
- const fetched = await safeFetchJson(brandJsonUrl, "brand.json", {
114
- allowPrivateIp,
115
- timeoutMs,
116
- maxBodyBytes: brandJsonCap
117
- });
138
+ const fetchOptions = { allowPrivateIp, timeoutMs, maxBodyBytes: brandJsonCap, lookup: options.lookup };
139
+ const fetched = legacyFallback ? await fetchLegacyBrandJson(agentUrl, fetchOptions) : await safeFetchJson(brandJsonUrl, "brand.json", fetchOptions);
140
+ if (legacyFallback && "url" in fetched) brandJsonUrl = fetched.url;
141
+ brandJsonCacheControl = fetched.headers["cache-control"];
142
+ if (legacyFallback && options.expectedBrandJsonUrl !== void 0 && canonicalAgentUrl(brandJsonUrl) !== canonicalAgentUrl(options.expectedBrandJsonUrl) && canonicalAgentUrl(`${new URL(agentUrl).origin}/.well-known/brand.json`) !== canonicalAgentUrl(options.expectedBrandJsonUrl)) {
143
+ throw new AgentResolverError(
144
+ "request_signature_brand_origin_mismatch",
145
+ "Cached mapping disagrees with legacy discovery",
146
+ { agent_url: agentUrl },
147
+ ["agent_url"]
148
+ );
149
+ }
118
150
  brandJson = fetched.body;
119
151
  brandJsonFetchedAt = fetched.fetchedAt;
120
152
  pushTrace(trace, {
@@ -125,6 +157,7 @@ async function resolveAgent(agentUrl, options = {}) {
125
157
  url: brandJsonUrl
126
158
  });
127
159
  } catch (err) {
160
+ if (err instanceof AgentResolverError) throw err;
128
161
  if (err instanceof SafeFetchError && /strict-JSON parse/.test(err.message)) {
129
162
  const detail2 = {
130
163
  brand_json_url: brandJsonUrl,
@@ -168,7 +201,17 @@ async function resolveAgent(agentUrl, options = {}) {
168
201
  ["agent_url"]
169
202
  );
170
203
  }
171
- operatorAuthorizationValidUntil = delegation.validUntil;
204
+ if (options.requiredOperatorBrand !== void 0 || options.requiredOperatorScope !== void 0 || options.requiredOperatorCountry !== void 0) {
205
+ const authorization = checkDelegatedOperatorAuthorization(brandJson, agentEtld1, now(), options);
206
+ if (!authorization)
207
+ throw new AgentResolverError(
208
+ "request_signature_brand_origin_mismatch",
209
+ "Delegated operator does not satisfy the receiver account authorization policy",
210
+ { agent_url: agentUrl },
211
+ ["agent_url"]
212
+ );
213
+ operatorAuthorizationValidUntil = authorization.validUntil;
214
+ }
172
215
  pushTrace(trace, {
173
216
  step: 3,
174
217
  name: "etld1_binding",
@@ -185,7 +228,7 @@ async function resolveAgent(agentUrl, options = {}) {
185
228
  }
186
229
  let agentEntry;
187
230
  try {
188
- agentEntry = selectAgentByUrl(brandJson, agentUrl);
231
+ agentEntry = selectAgentByUrl(brandJson, agentUrl, options);
189
232
  pushTrace(trace, { step: 5, name: "select_agent", ok: true, detail: { url: agentEntry.url } });
190
233
  } catch (err) {
191
234
  if (err instanceof AgentSelectorError) {
@@ -208,17 +251,19 @@ async function resolveAgent(agentUrl, options = {}) {
208
251
  pushTrace(trace, { step: 5, name: "select_agent", ok: false, detail });
209
252
  throw new AgentResolverError(code, err.message, detail, attacker);
210
253
  }
211
- throw err;
254
+ throw new AgentResolverError(
255
+ "request_signature_brand_json_malformed",
256
+ "Matched agent has an invalid URL or JWKS source",
257
+ { brand_json_url: brandJsonUrl },
258
+ ["brand_json_url"]
259
+ );
212
260
  }
213
- const declaredJwksUri = agentEntry.jwks_uri;
214
- const jwksUriFromEntry = typeof declaredJwksUri === "string" && acceptsScheme(declaredJwksUri) ? declaredJwksUri : void 0;
215
- const jwksUri = jwksUriFromEntry ?? `${originOf(agentUrl)}/.well-known/jwks.json`;
261
+ const jwksUri = agentJwksUri(agentEntry);
216
262
  pushTrace(trace, { step: 6, name: "resolve_jwks_uri", ok: true, detail: { jwks_uri: jwksUri } });
217
- const consistencyResults = runConsistencyChecks({
263
+ const consistencyResults = legacyFallback ? [] : runConsistencyChecks({
218
264
  capabilitiesPayload,
219
265
  identityPosture,
220
266
  jwksUri,
221
- publisherPinnedWebhookSigning: options.publisherPinned?.webhook_signing === true,
222
267
  extraDeclaredPurposes: options.declaredPurposes ?? []
223
268
  });
224
269
  const failedConsistency = consistencyResults.filter((r) => r.ok === false);
@@ -251,7 +296,8 @@ async function resolveAgent(agentUrl, options = {}) {
251
296
  const fetched = await safeFetchJson(jwksUri, "jwks", {
252
297
  allowPrivateIp,
253
298
  timeoutMs,
254
- maxBodyBytes: jwksCap
299
+ maxBodyBytes: jwksCap,
300
+ lookup: options.lookup
255
301
  });
256
302
  if (!fetched.body || typeof fetched.body !== "object" || !Array.isArray(fetched.body.keys)) {
257
303
  throw new SafeFetchError("jwks", "fetch_failed", "JWKS document has no keys[] array");
@@ -288,8 +334,11 @@ async function resolveAgent(agentUrl, options = {}) {
288
334
  );
289
335
  }
290
336
  return {
291
- agentUrl,
337
+ agentUrl: canonicalAgentUrl(agentEntry.url),
292
338
  brandJsonUrl,
339
+ brandJson,
340
+ ...legacyFallback && { legacyWebhookFallback: true },
341
+ ...brandJsonCacheControl !== void 0 && { brandJsonCacheControl },
293
342
  agentEntry,
294
343
  jwksUri,
295
344
  jwks,
@@ -310,101 +359,59 @@ function checkAllowPrivateIp(requested) {
310
359
  "resolveAgent: allowPrivateIp=true refused outside {NODE_ENV=test, NODE_ENV=development}. Set NODE_ENV appropriately for dev/test, or set ADCP_RESOLVER_ALLOW_PRIVATE_IP=1 as an explicit ops acknowledgment if you genuinely need private-IP discovery in this process."
311
360
  );
312
361
  }
313
- function defaultFetchCapabilities(agentUrl, protocol) {
362
+ function defaultFetchCapabilities(agentUrl, protocol, allowPrivateIp, timeoutMs) {
314
363
  return async () => {
315
- const client = protocol === "a2a" ? createA2AClient(agentUrl) : createMCPClient(agentUrl);
316
- return client.callTool("get_adcp_capabilities", {});
317
- };
318
- }
319
- const AUTHORIZED_OPERATOR_SCOPES = /* @__PURE__ */ new Set([
320
- "all",
321
- "media_buying",
322
- "creative_generation",
323
- "rights_clearance",
324
- "governance",
325
- "measurement",
326
- "agent_operations"
327
- ]);
328
- const AUTHORIZED_OPERATOR_DOMAIN = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/;
329
- function findAuthorizedOperator(brandJson, agentEtld1, now, options) {
330
- if (!brandJson || typeof brandJson !== "object") return void 0;
331
- const operators = brandJson.authorized_operators;
332
- if (!Array.isArray(operators)) return void 0;
333
- let latestValidUntil;
334
- let matched = false;
335
- for (const op of operators) {
336
- if (!op || typeof op !== "object" || Array.isArray(op)) continue;
337
- const candidate = op;
338
- const domain = candidate.domain;
339
- const brands = candidate.brands;
340
- if (typeof domain !== "string" || !AUTHORIZED_OPERATOR_DOMAIN.test(domain) || !isValidBrandGrant(brands)) {
341
- continue;
342
- }
343
- try {
344
- if (eTldPlusOne(domain) !== agentEtld1) continue;
345
- } catch {
346
- if (!(options.allowPrivateIp && isDevelopmentBrandDomain(domain) && domain === agentEtld1)) {
347
- continue;
364
+ const url = new URL(agentUrl);
365
+ const hostname = url.hostname.replace(/^\[|\]$/g, "");
366
+ if (!allowPrivateIp && url.protocol !== "https:" || isIP(hostname) && (isAlwaysBlocked(hostname) || !allowPrivateIp && isPrivateIp(hostname)))
367
+ throw new AgentTransportPolicyError("Signing discovery URL refused by address or HTTPS policy.");
368
+ let failure;
369
+ const upstream = createAgentTransportFetch(agentUrl, {
370
+ allowPrivateIp,
371
+ allowPrivateInitialOrigin: false,
372
+ requireHttps: true
373
+ });
374
+ let postSucceeded = false;
375
+ const trustedFetchFn = async (input, init) => {
376
+ const method = init?.method ?? (input instanceof Request ? input.method : "GET");
377
+ try {
378
+ const response = await upstream(input, init);
379
+ const isGet = method.toUpperCase() === "GET";
380
+ const observed = response.status >= 400 ? { dns_error: "fetch_failed", http_status: response.status } : void 0;
381
+ if (protocol === "mcp" && method.toUpperCase() === "POST" && response.ok) postSucceeded = true;
382
+ if (!(protocol === "mcp" && isGet && (postSucceeded || response.status === 405)) && !(isGet && observed && failure && !isPermanentDiscoveryFailure(failure) && isPermanentDiscoveryFailure(observed)))
383
+ failure = observed;
384
+ return response;
385
+ } catch (error) {
386
+ if (!(protocol === "mcp" && method.toUpperCase() === "GET" && postSucceeded))
387
+ failure = classifyDiscoveryFailure(error);
388
+ throw error;
348
389
  }
390
+ };
391
+ const transport = { trustedFetchFn, requestTimeoutMs: timeoutMs };
392
+ const client = protocol === "a2a" ? createA2AClient(agentUrl, void 0, void 0, void 0, void 0, transport) : createMCPClient(agentUrl, void 0, void 0, void 0, void 0, transport);
393
+ try {
394
+ return await client.callTool("get_adcp_capabilities", {});
395
+ } catch (error) {
396
+ if (failure)
397
+ throw new SafeFetchError(
398
+ "capabilities",
399
+ failure.dns_error,
400
+ "Capabilities fetch failed",
401
+ failure.http_status
402
+ );
403
+ throw error;
349
404
  }
350
- const scopes = candidate.scopes;
351
- if (scopes !== void 0 && !isValidScopeGrant(scopes)) continue;
352
- const countries = candidate.countries;
353
- if (countries !== void 0 && !isValidCountryGrant(countries)) continue;
354
- const validFrom = parseOptionalRfc3339(candidate.valid_from);
355
- const validUntil = parseOptionalRfc3339(candidate.valid_until);
356
- if (validFrom === null || validUntil === null) continue;
357
- if (validFrom !== void 0 && validUntil !== void 0 && validFrom >= validUntil) continue;
358
- if (validFrom !== void 0 && now < validFrom) continue;
359
- if (validUntil !== void 0 && now >= validUntil) continue;
360
- if (!matchesGrant(brands, options.requiredOperatorBrand, "*")) continue;
361
- if (scopes !== void 0 && !matchesGrant(scopes, options.requiredOperatorScope, "all")) continue;
362
- if (countries !== void 0 && !matchesGrant(countries, options.requiredOperatorCountry)) continue;
363
- matched = true;
364
- if (validUntil === void 0) return {};
365
- latestValidUntil = Math.max(latestValidUntil ?? Number.NEGATIVE_INFINITY, validUntil);
366
- }
367
- return matched ? { validUntil: latestValidUntil } : void 0;
368
- }
369
- function isValidBrandGrant(value) {
370
- return Array.isArray(value) && value.length > 0 && value.every((item) => typeof item === "string" && (item === "*" || /^[a-z0-9_]+$/.test(item)));
371
- }
372
- function isValidScopeGrant(value) {
373
- return Array.isArray(value) && value.length > 0 && new Set(value).size === value.length && value.every((item) => typeof item === "string" && AUTHORIZED_OPERATOR_SCOPES.has(item));
374
- }
375
- function isValidCountryGrant(value) {
376
- return Array.isArray(value) && value.every((item) => typeof item === "string" && /^[A-Z]{2}$/.test(item));
377
- }
378
- function matchesGrant(grants, required, wildcard) {
379
- if (wildcard !== void 0 && grants.includes(wildcard)) return true;
380
- return required !== void 0 && grants.includes(required);
405
+ };
381
406
  }
382
- function parseOptionalRfc3339(value) {
383
- if (value === void 0) return void 0;
384
- if (typeof value !== "string") return null;
385
- const match = /^(\d{4})-(\d{2})-(\d{2})[Tt](\d{2}):(\d{2}):(\d{2})(?:\.(\d+))?([Zz]|([+-])(\d{2}):(\d{2}))$/.exec(
386
- value
387
- );
388
- if (!match) return null;
389
- const year = Number(match[1]);
390
- const month = Number(match[2]);
391
- const day = Number(match[3]);
392
- const hour = Number(match[4]);
393
- const minute = Number(match[5]);
394
- const second = Number(match[6]);
395
- const offsetHour = match[10] === void 0 ? 0 : Number(match[10]);
396
- const offsetMinute = match[11] === void 0 ? 0 : Number(match[11]);
397
- if (month < 1 || month > 12 || day < 1 || day > daysInMonth(year, month) || hour > 23 || minute > 59 || second > 59 || offsetHour > 23 || offsetMinute > 59) {
398
- return null;
407
+ function findAuthorizedOperator(brandJson, agentEtld1, _now, _options) {
408
+ if (!brandJson || typeof brandJson !== "object") return void 0;
409
+ const obj = brandJson;
410
+ if (!obj.house || typeof obj.house !== "object" || Array.isArray(obj.house) || !Array.isArray(obj.brands)) {
411
+ return void 0;
399
412
  }
400
- const parsed = Date.parse(value) / 1e3;
401
- return Number.isFinite(parsed) ? parsed : null;
402
- }
403
- function daysInMonth(year, month) {
404
- return new Date(Date.UTC(year, month, 0)).getUTCDate();
405
- }
406
- function originOf(url) {
407
- return canonicalizeOrigin(url);
413
+ if (!Array.isArray(obj.authorized_operators)) return void 0;
414
+ return obj.authorized_operators.some((op) => op && typeof op === "object" && op.domain === agentEtld1) ? {} : void 0;
408
415
  }
409
416
  function pushTrace(trace, step) {
410
417
  trace.push(step);
@@ -426,14 +433,25 @@ function runConsistencyChecks(args) {
426
433
  const result = checkOriginConsistency({
427
434
  purpose,
428
435
  declaredOrigin,
429
- resolvedJwksUri: args.jwksUri,
430
- publisherPinned: purpose === "webhook_signing" && args.publisherPinnedWebhookSigning
436
+ resolvedJwksUri: args.jwksUri
431
437
  });
432
438
  if (result.ok === false) results.push(result);
433
439
  }
434
440
  }
435
441
  return results;
436
442
  }
443
+ function permitsLegacyWebhookDiscovery(payload) {
444
+ if (!payload || typeof payload !== "object") return true;
445
+ const caps = payload;
446
+ const major = (value) => typeof value === "string" ? Number(/^(\d+)\./.exec(value)?.[1]) : value;
447
+ if (Number(major(caps.adcp_version)) >= 4 || Array.isArray(caps.adcp?.supported_versions) && caps.adcp.supported_versions.some((v) => Number(major(v)) >= 4) || Array.isArray(caps.adcp?.major_versions) && caps.adcp.major_versions.some((v) => typeof v === "number" && v >= 4))
448
+ return false;
449
+ if (caps.adcp_version !== void 0) return typeof caps.adcp_version === "string" && /^3\./.test(caps.adcp_version);
450
+ if (Array.isArray(caps.adcp?.supported_versions))
451
+ return caps.adcp.supported_versions.some((v) => typeof v === "string" && /^3\./.test(v));
452
+ if (Array.isArray(caps.adcp?.major_versions)) return caps.adcp.major_versions.includes(3);
453
+ return true;
454
+ }
437
455
  export {
438
456
  resolveAgent
439
457
  };
@@ -1,7 +1,7 @@
1
1
  import type { AdcpJsonWebKey } from '../types.mjs';
2
2
  import type { JwksResolution, JwksResolver } from '../jwks.mjs';
3
3
  import { type AgentProtocol, type AgentResolution, type ResolveAgentOptions } from './resolve-agent.mjs';
4
- export interface ResolvedAgentJwksResolverOptions extends Pick<ResolveAgentOptions, 'fetchCapabilities' | 'allowPrivateIp' | 'bodyCaps' | 'timeoutMs' | 'now' | 'requiredOperatorBrand' | 'requiredOperatorScope' | 'requiredOperatorCountry'> {
4
+ export interface ResolvedAgentJwksResolverOptions extends Pick<ResolveAgentOptions, 'fetchCapabilities' | 'agentType' | 'agentId' | 'expectedBrandJsonUrl' | 'legacyWebhookFallback' | 'lookup' | 'allowPrivateIp' | 'bodyCaps' | 'timeoutMs' | 'now' | 'requiredOperatorBrand' | 'requiredOperatorScope' | 'requiredOperatorCountry'> {
5
5
  /** Positive cache lifetime in seconds. Defaults to 300. */
6
6
  cacheTtlSeconds?: number;
7
7
  /** Cooldown before an unknown kid can force another discovery. Defaults to 30. */
@@ -18,14 +18,20 @@ export declare class ResolvedAgentJwksResolver implements JwksResolver {
18
18
  private readonly cacheTtlSeconds;
19
19
  private readonly unknownKidCooldownSeconds;
20
20
  private readonly resolveAgentFn;
21
+ private canonicalAgentUrl?;
22
+ private legacyWebhookFallback?;
23
+ private operatorRecord?;
24
+ get resolvedAgentUrl(): string | undefined;
21
25
  private keys;
22
26
  private expiresAt;
23
27
  private operatorAuthorizationValidUntil?;
24
28
  private lastUnknownKidRefresh;
29
+ private lastDiscoveryAttemptAt;
25
30
  private inFlight?;
26
31
  constructor(agentUrl: string, protocol: AgentProtocol, options?: ResolvedAgentJwksResolverOptions);
27
32
  resolve(keyid: string): Promise<AdcpJsonWebKey | null>;
28
33
  resolveWithMetadata(keyid: string): Promise<JwksResolution>;
34
+ forceRefresh(): Promise<void>;
29
35
  private resolution;
30
36
  private refresh;
31
37
  }
@@ -1,7 +1,7 @@
1
1
  import type { AdcpJsonWebKey } from '../types';
2
2
  import type { JwksResolution, JwksResolver } from '../jwks';
3
3
  import { type AgentProtocol, type AgentResolution, type ResolveAgentOptions } from './resolve-agent';
4
- export interface ResolvedAgentJwksResolverOptions extends Pick<ResolveAgentOptions, 'fetchCapabilities' | 'allowPrivateIp' | 'bodyCaps' | 'timeoutMs' | 'now' | 'requiredOperatorBrand' | 'requiredOperatorScope' | 'requiredOperatorCountry'> {
4
+ export interface ResolvedAgentJwksResolverOptions extends Pick<ResolveAgentOptions, 'fetchCapabilities' | 'agentType' | 'agentId' | 'expectedBrandJsonUrl' | 'legacyWebhookFallback' | 'lookup' | 'allowPrivateIp' | 'bodyCaps' | 'timeoutMs' | 'now' | 'requiredOperatorBrand' | 'requiredOperatorScope' | 'requiredOperatorCountry'> {
5
5
  /** Positive cache lifetime in seconds. Defaults to 300. */
6
6
  cacheTtlSeconds?: number;
7
7
  /** Cooldown before an unknown kid can force another discovery. Defaults to 30. */
@@ -18,14 +18,20 @@ export declare class ResolvedAgentJwksResolver implements JwksResolver {
18
18
  private readonly cacheTtlSeconds;
19
19
  private readonly unknownKidCooldownSeconds;
20
20
  private readonly resolveAgentFn;
21
+ private canonicalAgentUrl?;
22
+ private legacyWebhookFallback?;
23
+ private operatorRecord?;
24
+ get resolvedAgentUrl(): string | undefined;
21
25
  private keys;
22
26
  private expiresAt;
23
27
  private operatorAuthorizationValidUntil?;
24
28
  private lastUnknownKidRefresh;
29
+ private lastDiscoveryAttemptAt;
25
30
  private inFlight?;
26
31
  constructor(agentUrl: string, protocol: AgentProtocol, options?: ResolvedAgentJwksResolverOptions);
27
32
  resolve(keyid: string): Promise<AdcpJsonWebKey | null>;
28
33
  resolveWithMetadata(keyid: string): Promise<JwksResolution>;
34
+ forceRefresh(): Promise<void>;
29
35
  private resolution;
30
36
  private refresh;
31
37
  }
@@ -46,10 +46,17 @@ class ResolvedAgentJwksResolver {
46
46
  cacheTtlSeconds;
47
47
  unknownKidCooldownSeconds;
48
48
  resolveAgentFn;
49
+ canonicalAgentUrl;
50
+ legacyWebhookFallback;
51
+ operatorRecord;
52
+ get resolvedAgentUrl() {
53
+ return this.canonicalAgentUrl;
54
+ }
49
55
  keys = /* @__PURE__ */ new Map();
50
56
  expiresAt = 0;
51
57
  operatorAuthorizationValidUntil;
52
58
  lastUnknownKidRefresh = Number.NEGATIVE_INFINITY;
59
+ lastDiscoveryAttemptAt = Number.NEGATIVE_INFINITY;
53
60
  inFlight;
54
61
  async resolve(keyid) {
55
62
  return (await this.resolveWithMetadata(keyid)).jwk;
@@ -58,6 +65,15 @@ class ResolvedAgentJwksResolver {
58
65
  const now = this.now();
59
66
  let refreshed = false;
60
67
  if (now >= this.expiresAt) {
68
+ if (!this.inFlight && now - this.lastDiscoveryAttemptAt < 30) {
69
+ const expiredAuthorization = this.operatorAuthorizationValidUntil !== void 0 && now >= this.operatorAuthorizationValidUntil;
70
+ throw new import_errors.AgentResolverError(
71
+ expiredAuthorization ? "request_signature_brand_origin_mismatch" : "request_signature_brand_json_unreachable",
72
+ "Expired operator mapping cannot be used during the discovery cooldown",
73
+ { agent_url: this.agentUrl },
74
+ ["agent_url"]
75
+ );
76
+ }
61
77
  await this.refresh();
62
78
  refreshed = true;
63
79
  }
@@ -67,14 +83,22 @@ class ResolvedAgentJwksResolver {
67
83
  this.lastUnknownKidRefresh = now;
68
84
  return this.resolution(null);
69
85
  }
70
- if (now - this.lastUnknownKidRefresh < this.unknownKidCooldownSeconds) return this.resolution(null);
86
+ if (now - Math.max(this.lastUnknownKidRefresh, this.lastDiscoveryAttemptAt) < Math.max(30, this.unknownKidCooldownSeconds))
87
+ return this.resolution(null);
71
88
  await this.refresh();
72
89
  this.lastUnknownKidRefresh = now;
73
90
  return this.resolution(this.keys.get(keyid) ?? null);
74
91
  }
92
+ async forceRefresh() {
93
+ this.expiresAt = 0;
94
+ await this.refresh();
95
+ }
75
96
  resolution(jwk) {
76
97
  return {
77
98
  jwk,
99
+ ...this.operatorRecord !== void 0 && { operatorRecord: this.operatorRecord },
100
+ ...this.legacyWebhookFallback && { legacyWebhookFallback: true },
101
+ ...this.resolvedAgentUrl !== void 0 && { agentUrl: this.resolvedAgentUrl },
78
102
  ...this.operatorAuthorizationValidUntil !== void 0 && {
79
103
  operatorAuthorizationValidUntil: this.operatorAuthorizationValidUntil
80
104
  }
@@ -82,23 +106,11 @@ class ResolvedAgentJwksResolver {
82
106
  }
83
107
  async refresh() {
84
108
  if (this.inFlight) return this.inFlight;
109
+ this.lastDiscoveryAttemptAt = this.now();
85
110
  this.inFlight = (async () => {
86
111
  const resolution = await this.resolveAgentFn(this.agentUrl, {
87
- protocol: this.protocol,
88
- ...this.options.fetchCapabilities && { fetchCapabilities: this.options.fetchCapabilities },
89
- ...this.options.allowPrivateIp !== void 0 && { allowPrivateIp: this.options.allowPrivateIp },
90
- ...this.options.bodyCaps && { bodyCaps: this.options.bodyCaps },
91
- ...this.options.timeoutMs !== void 0 && { timeoutMs: this.options.timeoutMs },
92
- ...this.options.now && { now: this.options.now },
93
- ...this.options.requiredOperatorBrand !== void 0 && {
94
- requiredOperatorBrand: this.options.requiredOperatorBrand
95
- },
96
- ...this.options.requiredOperatorScope !== void 0 && {
97
- requiredOperatorScope: this.options.requiredOperatorScope
98
- },
99
- ...this.options.requiredOperatorCountry !== void 0 && {
100
- requiredOperatorCountry: this.options.requiredOperatorCountry
101
- }
112
+ ...this.options,
113
+ protocol: this.protocol
102
114
  });
103
115
  const refreshedAt = this.now();
104
116
  if (resolution.operatorAuthorizationValidUntil !== void 0 && refreshedAt >= resolution.operatorAuthorizationValidUntil) {
@@ -112,13 +124,28 @@ class ResolvedAgentJwksResolver {
112
124
  const next = /* @__PURE__ */ new Map();
113
125
  for (const candidate of resolution.jwks.keys) {
114
126
  if (candidate && typeof candidate === "object" && typeof candidate.kid === "string") {
127
+ if (next.has(candidate.kid))
128
+ throw new import_errors.AgentResolverError(
129
+ "request_signature_key_unknown",
130
+ "JWKS has duplicate key identifiers",
131
+ { agent_url: this.agentUrl },
132
+ ["agent_url"]
133
+ );
115
134
  next.set(candidate.kid, candidate);
116
135
  }
117
136
  }
118
137
  this.keys = next;
138
+ this.canonicalAgentUrl = resolution.agentUrl;
139
+ this.operatorRecord = resolution.brandJson === void 0 ? void 0 : { url: resolution.brandJsonUrl, document: resolution.brandJson };
140
+ this.legacyWebhookFallback = resolution.legacyWebhookFallback;
119
141
  this.operatorAuthorizationValidUntil = resolution.operatorAuthorizationValidUntil;
120
142
  this.expiresAt = Math.min(
121
- refreshedAt + this.cacheTtlSeconds,
143
+ refreshedAt + Math.min(
144
+ this.cacheTtlSeconds,
145
+ 1800,
146
+ Math.max(30, cacheLifetime(resolution.brandJsonCacheControl)),
147
+ Math.max(60, cacheLifetime(resolution.jwksCacheControl))
148
+ ),
122
149
  resolution.operatorAuthorizationValidUntil ?? Number.POSITIVE_INFINITY
123
150
  );
124
151
  })().finally(() => {
@@ -127,6 +154,12 @@ class ResolvedAgentJwksResolver {
127
154
  return this.inFlight;
128
155
  }
129
156
  }
157
+ function cacheLifetime(cacheControl) {
158
+ if (!cacheControl) return Infinity;
159
+ if (/\bno-cache\b|\bno-store\b/i.test(cacheControl)) return 0;
160
+ const match = /(?:^|,)\s*max-age\s*=\s*"?(\d+)/i.exec(cacheControl);
161
+ return match ? Number(match[1]) : Infinity;
162
+ }
130
163
  // Annotate the CommonJS export names for ESM import in node:
131
164
  0 && (module.exports = {
132
165
  ResolvedAgentJwksResolver