@adcp/sdk 14.0.0 → 14.1.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 (185) 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 +31 -1
  10. package/dist/lib/core/SingleAgentClient.d.ts +31 -1
  11. package/dist/lib/core/SingleAgentClient.js +355 -35
  12. package/dist/lib/core/SingleAgentClient.mjs +365 -37
  13. package/dist/lib/core/TaskExecutor.js +2 -1
  14. package/dist/lib/core/TaskExecutor.mjs +2 -1
  15. package/dist/lib/core/account-key.d.mts +3 -0
  16. package/dist/lib/core/account-key.d.ts +3 -0
  17. package/dist/lib/core/account-key.js +41 -0
  18. package/dist/lib/core/account-key.mjs +17 -0
  19. package/dist/lib/core/account-resolution.d.mts +2 -0
  20. package/dist/lib/core/account-resolution.d.ts +2 -0
  21. package/dist/lib/core/buyer-account-registry.d.mts +65 -0
  22. package/dist/lib/core/buyer-account-registry.d.ts +65 -0
  23. package/dist/lib/core/buyer-account-registry.js +518 -0
  24. package/dist/lib/core/buyer-account-registry.mjs +494 -0
  25. package/dist/lib/core/product-cache.d.mts +18 -0
  26. package/dist/lib/core/product-cache.d.ts +18 -0
  27. package/dist/lib/core/product-cache.js +137 -0
  28. package/dist/lib/core/product-cache.mjs +112 -0
  29. package/dist/lib/errors/index.d.mts +40 -1
  30. package/dist/lib/errors/index.d.ts +40 -1
  31. package/dist/lib/errors/index.js +69 -3
  32. package/dist/lib/errors/index.mjs +64 -3
  33. package/dist/lib/governance/authorization.d.mts +17 -1
  34. package/dist/lib/governance/authorization.d.ts +17 -1
  35. package/dist/lib/governance/authorization.js +55 -7
  36. package/dist/lib/governance/authorization.mjs +59 -7
  37. package/dist/lib/governance/index.d.mts +2 -2
  38. package/dist/lib/governance/index.d.ts +2 -2
  39. package/dist/lib/governance/index.js +2 -0
  40. package/dist/lib/governance/index.mjs +3 -1
  41. package/dist/lib/index.d.mts +5 -3
  42. package/dist/lib/index.d.ts +5 -3
  43. package/dist/lib/index.js +20 -0
  44. package/dist/lib/index.mjs +19 -0
  45. package/dist/lib/protocols/a2a.js +9 -1
  46. package/dist/lib/protocols/a2a.mjs +9 -1
  47. package/dist/lib/protocols/index.js +9 -2
  48. package/dist/lib/protocols/index.mjs +9 -2
  49. package/dist/lib/protocols/mcp-modern.js +2 -1
  50. package/dist/lib/protocols/mcp-modern.mjs +2 -1
  51. package/dist/lib/protocols/mcp.js +5 -2
  52. package/dist/lib/protocols/mcp.mjs +5 -2
  53. package/dist/lib/protocols/rawResponseCapture.d.mts +6 -0
  54. package/dist/lib/protocols/rawResponseCapture.d.ts +6 -0
  55. package/dist/lib/protocols/rawResponseCapture.js +41 -29
  56. package/dist/lib/protocols/rawResponseCapture.mjs +40 -29
  57. package/dist/lib/protocols/signedRequestRejection.d.mts +9 -0
  58. package/dist/lib/protocols/signedRequestRejection.d.ts +9 -0
  59. package/dist/lib/protocols/signedRequestRejection.js +209 -0
  60. package/dist/lib/protocols/signedRequestRejection.mjs +189 -0
  61. package/dist/lib/protocols/transportDiagnostics.d.mts +1 -0
  62. package/dist/lib/protocols/transportDiagnostics.d.ts +1 -0
  63. package/dist/lib/protocols/transportDiagnostics.js +2 -0
  64. package/dist/lib/protocols/transportDiagnostics.mjs +1 -0
  65. package/dist/lib/registry/types.generated.d.mts +112 -45
  66. package/dist/lib/registry/types.generated.d.ts +112 -45
  67. package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
  68. package/dist/lib/server/account-provisioning.d.mts +2 -0
  69. package/dist/lib/server/account-provisioning.d.ts +2 -0
  70. package/dist/lib/server/account-provisioning.js +30 -0
  71. package/dist/lib/server/account-provisioning.mjs +6 -0
  72. package/dist/lib/server/account-reference-warnings.d.mts +12 -0
  73. package/dist/lib/server/account-reference-warnings.d.ts +12 -0
  74. package/dist/lib/server/account-reference-warnings.js +48 -0
  75. package/dist/lib/server/account-reference-warnings.mjs +23 -0
  76. package/dist/lib/server/auth-signature.js +1 -0
  77. package/dist/lib/server/auth-signature.mjs +1 -0
  78. package/dist/lib/server/create-adcp-server.d.mts +34 -0
  79. package/dist/lib/server/create-adcp-server.d.ts +34 -0
  80. package/dist/lib/server/create-adcp-server.js +225 -14
  81. package/dist/lib/server/create-adcp-server.mjs +225 -14
  82. package/dist/lib/server/decisioning/account.d.mts +2 -0
  83. package/dist/lib/server/decisioning/account.d.ts +2 -0
  84. package/dist/lib/server/decisioning/runtime/from-platform.js +49 -10
  85. package/dist/lib/server/decisioning/runtime/from-platform.mjs +49 -10
  86. package/dist/lib/server/index.d.mts +2 -2
  87. package/dist/lib/server/index.d.ts +2 -2
  88. package/dist/lib/server/index.js +2 -0
  89. package/dist/lib/server/index.mjs +3 -1
  90. package/dist/lib/signing/agent-resolver/consistency.d.mts +6 -13
  91. package/dist/lib/signing/agent-resolver/consistency.d.ts +6 -13
  92. package/dist/lib/signing/agent-resolver/consistency.js +0 -1
  93. package/dist/lib/signing/agent-resolver/consistency.mjs +0 -1
  94. package/dist/lib/signing/agent-resolver/errors.d.mts +1 -1
  95. package/dist/lib/signing/agent-resolver/errors.d.ts +1 -1
  96. package/dist/lib/signing/agent-resolver/fetch-helpers.d.mts +2 -0
  97. package/dist/lib/signing/agent-resolver/fetch-helpers.d.ts +2 -0
  98. package/dist/lib/signing/agent-resolver/fetch-helpers.js +2 -1
  99. package/dist/lib/signing/agent-resolver/fetch-helpers.mjs +2 -1
  100. package/dist/lib/signing/agent-resolver/jwks-set.js +24 -3
  101. package/dist/lib/signing/agent-resolver/jwks-set.mjs +24 -3
  102. package/dist/lib/signing/agent-resolver/legacy-brand.d.mts +15 -0
  103. package/dist/lib/signing/agent-resolver/legacy-brand.d.ts +15 -0
  104. package/dist/lib/signing/agent-resolver/legacy-brand.js +60 -0
  105. package/dist/lib/signing/agent-resolver/legacy-brand.mjs +36 -0
  106. package/dist/lib/signing/agent-resolver/operator-authorization.d.mts +13 -0
  107. package/dist/lib/signing/agent-resolver/operator-authorization.d.ts +13 -0
  108. package/dist/lib/signing/agent-resolver/operator-authorization.js +108 -0
  109. package/dist/lib/signing/agent-resolver/operator-authorization.mjs +84 -0
  110. package/dist/lib/signing/agent-resolver/resolve-agent.d.mts +14 -4
  111. package/dist/lib/signing/agent-resolver/resolve-agent.d.ts +14 -4
  112. package/dist/lib/signing/agent-resolver/resolve-agent.js +101 -132
  113. package/dist/lib/signing/agent-resolver/resolve-agent.mjs +102 -133
  114. package/dist/lib/signing/agent-resolver/resolved-agent-jwks.d.mts +7 -1
  115. package/dist/lib/signing/agent-resolver/resolved-agent-jwks.d.ts +7 -1
  116. package/dist/lib/signing/agent-resolver/resolved-agent-jwks.js +50 -17
  117. package/dist/lib/signing/agent-resolver/resolved-agent-jwks.mjs +50 -17
  118. package/dist/lib/signing/agent-resolver/select-agent.d.mts +14 -15
  119. package/dist/lib/signing/agent-resolver/select-agent.d.ts +14 -15
  120. package/dist/lib/signing/agent-resolver/select-agent.js +98 -13
  121. package/dist/lib/signing/agent-resolver/select-agent.mjs +95 -13
  122. package/dist/lib/signing/brand-jwks.d.mts +27 -75
  123. package/dist/lib/signing/brand-jwks.d.ts +27 -75
  124. package/dist/lib/signing/brand-jwks.js +112 -182
  125. package/dist/lib/signing/brand-jwks.mjs +112 -182
  126. package/dist/lib/signing/errors.d.mts +3 -1
  127. package/dist/lib/signing/errors.d.ts +3 -1
  128. package/dist/lib/signing/errors.js +4 -1
  129. package/dist/lib/signing/errors.mjs +4 -1
  130. package/dist/lib/signing/jwks-https.d.mts +7 -0
  131. package/dist/lib/signing/jwks-https.d.ts +7 -0
  132. package/dist/lib/signing/jwks-https.js +31 -8
  133. package/dist/lib/signing/jwks-https.mjs +31 -8
  134. package/dist/lib/signing/jwks.d.mts +8 -0
  135. package/dist/lib/signing/jwks.d.ts +8 -0
  136. package/dist/lib/signing/middleware.js +2 -1
  137. package/dist/lib/signing/middleware.mjs +2 -1
  138. package/dist/lib/signing/publisher-pins.d.mts +11 -0
  139. package/dist/lib/signing/publisher-pins.d.ts +11 -0
  140. package/dist/lib/signing/publisher-pins.js +125 -0
  141. package/dist/lib/signing/publisher-pins.mjs +101 -0
  142. package/dist/lib/signing/server.d.mts +1 -0
  143. package/dist/lib/signing/server.d.ts +1 -0
  144. package/dist/lib/signing/types.d.mts +5 -0
  145. package/dist/lib/signing/types.d.ts +5 -0
  146. package/dist/lib/signing/verifier.js +49 -4
  147. package/dist/lib/signing/verifier.mjs +49 -4
  148. package/dist/lib/signing/webhook-verifier.d.mts +7 -2
  149. package/dist/lib/signing/webhook-verifier.d.ts +7 -2
  150. package/dist/lib/signing/webhook-verifier.js +42 -2
  151. package/dist/lib/signing/webhook-verifier.mjs +43 -3
  152. package/dist/lib/testing/storyboard/account-policy.d.mts +2 -0
  153. package/dist/lib/testing/storyboard/account-policy.d.ts +2 -0
  154. package/dist/lib/testing/storyboard/account-policy.js +35 -0
  155. package/dist/lib/testing/storyboard/account-policy.mjs +11 -0
  156. package/dist/lib/testing/storyboard/context.js +6 -0
  157. package/dist/lib/testing/storyboard/context.mjs +6 -0
  158. package/dist/lib/testing/storyboard/request-builder.js +12 -2
  159. package/dist/lib/testing/storyboard/request-builder.mjs +12 -2
  160. package/dist/lib/testing/storyboard/runner.js +3 -2
  161. package/dist/lib/testing/storyboard/runner.mjs +3 -2
  162. package/dist/lib/testing/storyboard/validations.d.mts +1 -1
  163. package/dist/lib/testing/storyboard/validations.d.ts +1 -1
  164. package/dist/lib/version.d.mts +3 -3
  165. package/dist/lib/version.d.ts +3 -3
  166. package/dist/lib/version.js +3 -3
  167. package/dist/lib/version.mjs +3 -3
  168. package/dist/lib/wholesale-feed-sync/sync.d.mts +1 -0
  169. package/dist/lib/wholesale-feed-sync/sync.d.ts +1 -0
  170. package/dist/lib/wholesale-feed-sync/sync.js +92 -21
  171. package/dist/lib/wholesale-feed-sync/sync.mjs +92 -21
  172. package/dist/lib/wholesale-feed-sync/types.d.mts +4 -3
  173. package/dist/lib/wholesale-feed-sync/types.d.ts +4 -3
  174. package/docs/TYPE-SUMMARY.md +2 -2
  175. package/docs/guides/BUILD-AN-AGENT.md +2 -2
  176. package/docs/guides/BUYER-QUICKSTART-3.2.md +2 -0
  177. package/docs/guides/FIRST-CALL-TO-A-SELLER.md +104 -0
  178. package/docs/guides/SIGNING-GUIDE.md +16 -7
  179. package/docs/guides/account-resolution.md +86 -0
  180. package/docs/llms.txt +3 -2
  181. package/docs/migration-14.x-rc-worksheet.md +4 -4
  182. package/docs/migration-4.x-to-5.x.md +1 -0
  183. package/docs/migration-agent-resolution-3.3.md +123 -0
  184. package/docs/recipes/verifying-inbound-webhooks.md +56 -15
  185. package/package.json +2 -2
@@ -2,7 +2,8 @@ import { parse as parseTld } from "tldts";
2
2
  import { createA2AClient, createMCPClient } from "../../protocols/index.mjs";
3
3
  import { isDevelopmentBrandDomain } from "../../brand/domain.mjs";
4
4
  import { readBrandJsonUrl, readIdentityPosture } from "./capabilities-types.mjs";
5
- import { canonicalizeOrigin } from "./canonicalize.mjs";
5
+ import { checkDelegatedOperatorAuthorization } from "./operator-authorization.mjs";
6
+ import { fetchLegacyBrandJson } from "./legacy-brand.mjs";
6
7
  import {
7
8
  checkOriginConsistency,
8
9
  checkRequiredOrigins,
@@ -12,7 +13,7 @@ import { AgentResolverError } from "./errors.mjs";
12
13
  import { eTldPlusOne, sameEtldPlusOne } from "./etld.mjs";
13
14
  import { MAX_BRAND_JSON_BYTES, MAX_JWKS_BYTES, safeFetchJson, SafeFetchError } from "./fetch-helpers.mjs";
14
15
  import { unwrapProtocolResponse } from "../protocol-response.mjs";
15
- import { selectAgentByUrl, AgentSelectorError } from "./select-agent.mjs";
16
+ import { selectAgentByUrl, agentJwksUri, AgentSelectorError, canonicalAgentUrl } from "./select-agent.mjs";
16
17
  const DEFAULT_TIMEOUT_MS = 1e4;
17
18
  async function resolveAgent(agentUrl, options = {}) {
18
19
  const trace = [];
@@ -50,7 +51,10 @@ async function resolveAgent(agentUrl, options = {}) {
50
51
  ["agent_url"]
51
52
  );
52
53
  }
53
- const brandJsonUrl = readBrandJsonUrl(capabilitiesPayload);
54
+ let brandJsonUrl = readBrandJsonUrl(capabilitiesPayload);
55
+ const identity = capabilitiesPayload?.identity;
56
+ const legacyFallback = options.legacyWebhookFallback === true && identity?.brand_json_url === void 0 && permitsLegacyWebhookDiscovery(capabilitiesPayload);
57
+ if (legacyFallback) brandJsonUrl = `${new URL(agentUrl).origin}/.well-known/brand.json`;
54
58
  const identityPosture = readIdentityPosture(capabilitiesPayload);
55
59
  const acceptsScheme = (url) => {
56
60
  if (!url) return false;
@@ -68,24 +72,43 @@ async function resolveAgent(agentUrl, options = {}) {
68
72
  ["agent_url"]
69
73
  );
70
74
  }
75
+ if (!legacyFallback && options.expectedBrandJsonUrl !== void 0 && canonicalAgentUrl(brandJsonUrl) !== canonicalAgentUrl(options.expectedBrandJsonUrl)) {
76
+ throw new AgentResolverError(
77
+ "request_signature_brand_origin_mismatch",
78
+ "Cached operator mapping disagrees with identity.brand_json_url",
79
+ { agent_url: agentUrl },
80
+ ["agent_url"]
81
+ );
82
+ }
71
83
  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;
84
+ let agentEtld1 = "";
85
+ let brandEtld1 = "";
86
+ let sameOrigin = true;
87
+ if (!legacyFallback) {
88
+ try {
89
+ agentEtld1 = eTldPlusOne(agentUrl);
90
+ brandEtld1 = eTldPlusOne(brandJsonUrl);
91
+ sameOrigin = sameEtldPlusOne(agentUrl, brandJsonUrl);
92
+ } catch {
93
+ if (allowPrivateIp) {
94
+ const agentHost = new URL(agentUrl).hostname;
95
+ const brandHost = new URL(brandJsonUrl).hostname;
96
+ const agentIsExplicitDevelopmentHost = agentHost === "localhost" || isDevelopmentBrandDomain(agentHost) || parseTld(agentHost, { extractHostname: false }).isIp;
97
+ const brandIsExplicitDevelopmentHost = brandHost === "localhost" || isDevelopmentBrandDomain(brandHost) || parseTld(brandHost, { extractHostname: false }).isIp;
98
+ if (agentIsExplicitDevelopmentHost && brandIsExplicitDevelopmentHost) {
99
+ agentEtld1 = agentHost;
100
+ brandEtld1 = brandHost;
101
+ sameOrigin = agentHost === brandHost;
102
+ } else {
103
+ const detail = { agent_url: agentUrl, brand_json_url: brandJsonUrl };
104
+ pushTrace(trace, { step: 3, name: "etld1_binding", ok: false, detail });
105
+ throw new AgentResolverError(
106
+ "request_signature_brand_origin_mismatch",
107
+ `Cannot compute eTLD+1 for agent or brand.json host`,
108
+ detail,
109
+ ["agent_url", "brand_json_url"]
110
+ );
111
+ }
89
112
  } else {
90
113
  const detail = { agent_url: agentUrl, brand_json_url: brandJsonUrl };
91
114
  pushTrace(trace, { step: 3, name: "etld1_binding", ok: false, detail });
@@ -96,25 +119,24 @@ async function resolveAgent(agentUrl, options = {}) {
96
119
  ["agent_url", "brand_json_url"]
97
120
  );
98
121
  }
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
122
  }
109
123
  }
110
124
  let brandJson;
111
125
  let brandJsonFetchedAt;
126
+ let brandJsonCacheControl;
112
127
  try {
113
- const fetched = await safeFetchJson(brandJsonUrl, "brand.json", {
114
- allowPrivateIp,
115
- timeoutMs,
116
- maxBodyBytes: brandJsonCap
117
- });
128
+ const fetchOptions = { allowPrivateIp, timeoutMs, maxBodyBytes: brandJsonCap, lookup: options.lookup };
129
+ const fetched = legacyFallback ? await fetchLegacyBrandJson(agentUrl, fetchOptions) : await safeFetchJson(brandJsonUrl, "brand.json", fetchOptions);
130
+ if (legacyFallback && "url" in fetched) brandJsonUrl = fetched.url;
131
+ brandJsonCacheControl = fetched.headers["cache-control"];
132
+ if (legacyFallback && options.expectedBrandJsonUrl !== void 0 && canonicalAgentUrl(brandJsonUrl) !== canonicalAgentUrl(options.expectedBrandJsonUrl) && canonicalAgentUrl(`${new URL(agentUrl).origin}/.well-known/brand.json`) !== canonicalAgentUrl(options.expectedBrandJsonUrl)) {
133
+ throw new AgentResolverError(
134
+ "request_signature_brand_origin_mismatch",
135
+ "Cached mapping disagrees with legacy discovery",
136
+ { agent_url: agentUrl },
137
+ ["agent_url"]
138
+ );
139
+ }
118
140
  brandJson = fetched.body;
119
141
  brandJsonFetchedAt = fetched.fetchedAt;
120
142
  pushTrace(trace, {
@@ -125,6 +147,7 @@ async function resolveAgent(agentUrl, options = {}) {
125
147
  url: brandJsonUrl
126
148
  });
127
149
  } catch (err) {
150
+ if (err instanceof AgentResolverError) throw err;
128
151
  if (err instanceof SafeFetchError && /strict-JSON parse/.test(err.message)) {
129
152
  const detail2 = {
130
153
  brand_json_url: brandJsonUrl,
@@ -168,7 +191,17 @@ async function resolveAgent(agentUrl, options = {}) {
168
191
  ["agent_url"]
169
192
  );
170
193
  }
171
- operatorAuthorizationValidUntil = delegation.validUntil;
194
+ if (options.requiredOperatorBrand !== void 0 || options.requiredOperatorScope !== void 0 || options.requiredOperatorCountry !== void 0) {
195
+ const authorization = checkDelegatedOperatorAuthorization(brandJson, agentEtld1, now(), options);
196
+ if (!authorization)
197
+ throw new AgentResolverError(
198
+ "request_signature_brand_origin_mismatch",
199
+ "Delegated operator does not satisfy the receiver account authorization policy",
200
+ { agent_url: agentUrl },
201
+ ["agent_url"]
202
+ );
203
+ operatorAuthorizationValidUntil = authorization.validUntil;
204
+ }
172
205
  pushTrace(trace, {
173
206
  step: 3,
174
207
  name: "etld1_binding",
@@ -185,7 +218,7 @@ async function resolveAgent(agentUrl, options = {}) {
185
218
  }
186
219
  let agentEntry;
187
220
  try {
188
- agentEntry = selectAgentByUrl(brandJson, agentUrl);
221
+ agentEntry = selectAgentByUrl(brandJson, agentUrl, options);
189
222
  pushTrace(trace, { step: 5, name: "select_agent", ok: true, detail: { url: agentEntry.url } });
190
223
  } catch (err) {
191
224
  if (err instanceof AgentSelectorError) {
@@ -208,17 +241,19 @@ async function resolveAgent(agentUrl, options = {}) {
208
241
  pushTrace(trace, { step: 5, name: "select_agent", ok: false, detail });
209
242
  throw new AgentResolverError(code, err.message, detail, attacker);
210
243
  }
211
- throw err;
244
+ throw new AgentResolverError(
245
+ "request_signature_brand_json_malformed",
246
+ "Matched agent has an invalid URL or JWKS source",
247
+ { brand_json_url: brandJsonUrl },
248
+ ["brand_json_url"]
249
+ );
212
250
  }
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`;
251
+ const jwksUri = agentJwksUri(agentEntry);
216
252
  pushTrace(trace, { step: 6, name: "resolve_jwks_uri", ok: true, detail: { jwks_uri: jwksUri } });
217
- const consistencyResults = runConsistencyChecks({
253
+ const consistencyResults = legacyFallback ? [] : runConsistencyChecks({
218
254
  capabilitiesPayload,
219
255
  identityPosture,
220
256
  jwksUri,
221
- publisherPinnedWebhookSigning: options.publisherPinned?.webhook_signing === true,
222
257
  extraDeclaredPurposes: options.declaredPurposes ?? []
223
258
  });
224
259
  const failedConsistency = consistencyResults.filter((r) => r.ok === false);
@@ -251,7 +286,8 @@ async function resolveAgent(agentUrl, options = {}) {
251
286
  const fetched = await safeFetchJson(jwksUri, "jwks", {
252
287
  allowPrivateIp,
253
288
  timeoutMs,
254
- maxBodyBytes: jwksCap
289
+ maxBodyBytes: jwksCap,
290
+ lookup: options.lookup
255
291
  });
256
292
  if (!fetched.body || typeof fetched.body !== "object" || !Array.isArray(fetched.body.keys)) {
257
293
  throw new SafeFetchError("jwks", "fetch_failed", "JWKS document has no keys[] array");
@@ -288,8 +324,11 @@ async function resolveAgent(agentUrl, options = {}) {
288
324
  );
289
325
  }
290
326
  return {
291
- agentUrl,
327
+ agentUrl: canonicalAgentUrl(agentEntry.url),
292
328
  brandJsonUrl,
329
+ brandJson,
330
+ ...legacyFallback && { legacyWebhookFallback: true },
331
+ ...brandJsonCacheControl !== void 0 && { brandJsonCacheControl },
293
332
  agentEntry,
294
333
  jwksUri,
295
334
  jwks,
@@ -316,95 +355,14 @@ function defaultFetchCapabilities(agentUrl, protocol) {
316
355
  return client.callTool("get_adcp_capabilities", {});
317
356
  };
318
357
  }
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) {
358
+ function findAuthorizedOperator(brandJson, agentEtld1, _now, _options) {
330
359
  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;
348
- }
349
- }
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);
360
+ const obj = brandJson;
361
+ if (!obj.house || typeof obj.house !== "object" || Array.isArray(obj.house) || !Array.isArray(obj.brands)) {
362
+ return void 0;
366
363
  }
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);
381
- }
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;
399
- }
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);
364
+ if (!Array.isArray(obj.authorized_operators)) return void 0;
365
+ return obj.authorized_operators.some((op) => op && typeof op === "object" && op.domain === agentEtld1) ? {} : void 0;
408
366
  }
409
367
  function pushTrace(trace, step) {
410
368
  trace.push(step);
@@ -426,14 +384,25 @@ function runConsistencyChecks(args) {
426
384
  const result = checkOriginConsistency({
427
385
  purpose,
428
386
  declaredOrigin,
429
- resolvedJwksUri: args.jwksUri,
430
- publisherPinned: purpose === "webhook_signing" && args.publisherPinnedWebhookSigning
387
+ resolvedJwksUri: args.jwksUri
431
388
  });
432
389
  if (result.ok === false) results.push(result);
433
390
  }
434
391
  }
435
392
  return results;
436
393
  }
394
+ function permitsLegacyWebhookDiscovery(payload) {
395
+ if (!payload || typeof payload !== "object") return true;
396
+ const caps = payload;
397
+ const major = (value) => typeof value === "string" ? Number(/^(\d+)\./.exec(value)?.[1]) : value;
398
+ 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))
399
+ return false;
400
+ if (caps.adcp_version !== void 0) return typeof caps.adcp_version === "string" && /^3\./.test(caps.adcp_version);
401
+ if (Array.isArray(caps.adcp?.supported_versions))
402
+ return caps.adcp.supported_versions.some((v) => typeof v === "string" && /^3\./.test(v));
403
+ if (Array.isArray(caps.adcp?.major_versions)) return caps.adcp.major_versions.includes(3);
404
+ return true;
405
+ }
437
406
  export {
438
407
  resolveAgent
439
408
  };
@@ -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
@@ -23,10 +23,17 @@ class ResolvedAgentJwksResolver {
23
23
  cacheTtlSeconds;
24
24
  unknownKidCooldownSeconds;
25
25
  resolveAgentFn;
26
+ canonicalAgentUrl;
27
+ legacyWebhookFallback;
28
+ operatorRecord;
29
+ get resolvedAgentUrl() {
30
+ return this.canonicalAgentUrl;
31
+ }
26
32
  keys = /* @__PURE__ */ new Map();
27
33
  expiresAt = 0;
28
34
  operatorAuthorizationValidUntil;
29
35
  lastUnknownKidRefresh = Number.NEGATIVE_INFINITY;
36
+ lastDiscoveryAttemptAt = Number.NEGATIVE_INFINITY;
30
37
  inFlight;
31
38
  async resolve(keyid) {
32
39
  return (await this.resolveWithMetadata(keyid)).jwk;
@@ -35,6 +42,15 @@ class ResolvedAgentJwksResolver {
35
42
  const now = this.now();
36
43
  let refreshed = false;
37
44
  if (now >= this.expiresAt) {
45
+ if (!this.inFlight && now - this.lastDiscoveryAttemptAt < 30) {
46
+ const expiredAuthorization = this.operatorAuthorizationValidUntil !== void 0 && now >= this.operatorAuthorizationValidUntil;
47
+ throw new AgentResolverError(
48
+ expiredAuthorization ? "request_signature_brand_origin_mismatch" : "request_signature_brand_json_unreachable",
49
+ "Expired operator mapping cannot be used during the discovery cooldown",
50
+ { agent_url: this.agentUrl },
51
+ ["agent_url"]
52
+ );
53
+ }
38
54
  await this.refresh();
39
55
  refreshed = true;
40
56
  }
@@ -44,14 +60,22 @@ class ResolvedAgentJwksResolver {
44
60
  this.lastUnknownKidRefresh = now;
45
61
  return this.resolution(null);
46
62
  }
47
- if (now - this.lastUnknownKidRefresh < this.unknownKidCooldownSeconds) return this.resolution(null);
63
+ if (now - Math.max(this.lastUnknownKidRefresh, this.lastDiscoveryAttemptAt) < Math.max(30, this.unknownKidCooldownSeconds))
64
+ return this.resolution(null);
48
65
  await this.refresh();
49
66
  this.lastUnknownKidRefresh = now;
50
67
  return this.resolution(this.keys.get(keyid) ?? null);
51
68
  }
69
+ async forceRefresh() {
70
+ this.expiresAt = 0;
71
+ await this.refresh();
72
+ }
52
73
  resolution(jwk) {
53
74
  return {
54
75
  jwk,
76
+ ...this.operatorRecord !== void 0 && { operatorRecord: this.operatorRecord },
77
+ ...this.legacyWebhookFallback && { legacyWebhookFallback: true },
78
+ ...this.resolvedAgentUrl !== void 0 && { agentUrl: this.resolvedAgentUrl },
55
79
  ...this.operatorAuthorizationValidUntil !== void 0 && {
56
80
  operatorAuthorizationValidUntil: this.operatorAuthorizationValidUntil
57
81
  }
@@ -59,23 +83,11 @@ class ResolvedAgentJwksResolver {
59
83
  }
60
84
  async refresh() {
61
85
  if (this.inFlight) return this.inFlight;
86
+ this.lastDiscoveryAttemptAt = this.now();
62
87
  this.inFlight = (async () => {
63
88
  const resolution = await this.resolveAgentFn(this.agentUrl, {
64
- protocol: this.protocol,
65
- ...this.options.fetchCapabilities && { fetchCapabilities: this.options.fetchCapabilities },
66
- ...this.options.allowPrivateIp !== void 0 && { allowPrivateIp: this.options.allowPrivateIp },
67
- ...this.options.bodyCaps && { bodyCaps: this.options.bodyCaps },
68
- ...this.options.timeoutMs !== void 0 && { timeoutMs: this.options.timeoutMs },
69
- ...this.options.now && { now: this.options.now },
70
- ...this.options.requiredOperatorBrand !== void 0 && {
71
- requiredOperatorBrand: this.options.requiredOperatorBrand
72
- },
73
- ...this.options.requiredOperatorScope !== void 0 && {
74
- requiredOperatorScope: this.options.requiredOperatorScope
75
- },
76
- ...this.options.requiredOperatorCountry !== void 0 && {
77
- requiredOperatorCountry: this.options.requiredOperatorCountry
78
- }
89
+ ...this.options,
90
+ protocol: this.protocol
79
91
  });
80
92
  const refreshedAt = this.now();
81
93
  if (resolution.operatorAuthorizationValidUntil !== void 0 && refreshedAt >= resolution.operatorAuthorizationValidUntil) {
@@ -89,13 +101,28 @@ class ResolvedAgentJwksResolver {
89
101
  const next = /* @__PURE__ */ new Map();
90
102
  for (const candidate of resolution.jwks.keys) {
91
103
  if (candidate && typeof candidate === "object" && typeof candidate.kid === "string") {
104
+ if (next.has(candidate.kid))
105
+ throw new AgentResolverError(
106
+ "request_signature_key_unknown",
107
+ "JWKS has duplicate key identifiers",
108
+ { agent_url: this.agentUrl },
109
+ ["agent_url"]
110
+ );
92
111
  next.set(candidate.kid, candidate);
93
112
  }
94
113
  }
95
114
  this.keys = next;
115
+ this.canonicalAgentUrl = resolution.agentUrl;
116
+ this.operatorRecord = resolution.brandJson === void 0 ? void 0 : { url: resolution.brandJsonUrl, document: resolution.brandJson };
117
+ this.legacyWebhookFallback = resolution.legacyWebhookFallback;
96
118
  this.operatorAuthorizationValidUntil = resolution.operatorAuthorizationValidUntil;
97
119
  this.expiresAt = Math.min(
98
- refreshedAt + this.cacheTtlSeconds,
120
+ refreshedAt + Math.min(
121
+ this.cacheTtlSeconds,
122
+ 1800,
123
+ Math.max(30, cacheLifetime(resolution.brandJsonCacheControl)),
124
+ Math.max(60, cacheLifetime(resolution.jwksCacheControl))
125
+ ),
99
126
  resolution.operatorAuthorizationValidUntil ?? Number.POSITIVE_INFINITY
100
127
  );
101
128
  })().finally(() => {
@@ -104,6 +131,12 @@ class ResolvedAgentJwksResolver {
104
131
  return this.inFlight;
105
132
  }
106
133
  }
134
+ function cacheLifetime(cacheControl) {
135
+ if (!cacheControl) return Infinity;
136
+ if (/\bno-cache\b|\bno-store\b/i.test(cacheControl)) return 0;
137
+ const match = /(?:^|,)\s*max-age\s*=\s*"?(\d+)/i.exec(cacheControl);
138
+ return match ? Number(match[1]) : Infinity;
139
+ }
107
140
  export {
108
141
  ResolvedAgentJwksResolver
109
142
  };