@adcp/sdk 14.0.0-beta.14 → 14.0.0-beta.15

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 (127) hide show
  1. package/dist/lib/auth/oauth/authorization-required.d.mts +25 -11
  2. package/dist/lib/auth/oauth/authorization-required.d.ts +25 -11
  3. package/dist/lib/auth/oauth/authorization-required.js +18 -4
  4. package/dist/lib/auth/oauth/authorization-required.mjs +17 -4
  5. package/dist/lib/auth/oauth/index.d.mts +1 -1
  6. package/dist/lib/auth/oauth/index.d.ts +1 -1
  7. package/dist/lib/auth/oauth/index.js +2 -0
  8. package/dist/lib/auth/oauth/index.mjs +2 -0
  9. package/dist/lib/auth/oauth/resource-url.d.mts +8 -0
  10. package/dist/lib/auth/oauth/resource-url.d.ts +8 -0
  11. package/dist/lib/auth/oauth/resource-url.js +42 -0
  12. package/dist/lib/auth/oauth/resource-url.mjs +40 -0
  13. package/dist/lib/core/ConversationTypes.d.mts +10 -0
  14. package/dist/lib/core/ConversationTypes.d.ts +10 -0
  15. package/dist/lib/core/SingleAgentClient.d.mts +5 -2
  16. package/dist/lib/core/SingleAgentClient.d.ts +5 -2
  17. package/dist/lib/core/SingleAgentClient.js +133 -28
  18. package/dist/lib/core/SingleAgentClient.mjs +138 -29
  19. package/dist/lib/core/TaskExecutor.d.mts +1 -0
  20. package/dist/lib/core/TaskExecutor.d.ts +1 -0
  21. package/dist/lib/core/TaskExecutor.js +7 -1
  22. package/dist/lib/core/TaskExecutor.mjs +7 -1
  23. package/dist/lib/core/webhook-registration.d.mts +8 -1
  24. package/dist/lib/core/webhook-registration.d.ts +8 -1
  25. package/dist/lib/core/webhook-registration.js +65 -5
  26. package/dist/lib/core/webhook-registration.mjs +61 -4
  27. package/dist/lib/errors/index.d.mts +9 -0
  28. package/dist/lib/errors/index.d.ts +9 -0
  29. package/dist/lib/errors/index.js +33 -0
  30. package/dist/lib/errors/index.mjs +32 -0
  31. package/dist/lib/index.d.mts +3 -2
  32. package/dist/lib/index.d.ts +3 -2
  33. package/dist/lib/index.js +4 -0
  34. package/dist/lib/index.mjs +4 -0
  35. package/dist/lib/media-buy/compatibility.js +14 -2
  36. package/dist/lib/media-buy/compatibility.mjs +14 -2
  37. package/dist/lib/protocols/index.js +23 -7
  38. package/dist/lib/protocols/index.mjs +24 -8
  39. package/dist/lib/protocols/mcp.js +8 -6
  40. package/dist/lib/protocols/mcp.mjs +9 -7
  41. package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
  42. package/dist/lib/server/decisioning/async-outcome.d.mts +11 -0
  43. package/dist/lib/server/decisioning/async-outcome.d.ts +11 -0
  44. package/dist/lib/server/decisioning/context.d.mts +11 -1
  45. package/dist/lib/server/decisioning/context.d.ts +11 -1
  46. package/dist/lib/server/decisioning/index.d.mts +3 -2
  47. package/dist/lib/server/decisioning/index.d.ts +3 -2
  48. package/dist/lib/server/decisioning/index.js +9 -0
  49. package/dist/lib/server/decisioning/index.mjs +10 -0
  50. package/dist/lib/server/decisioning/runtime/from-platform.d.mts +5 -2
  51. package/dist/lib/server/decisioning/runtime/from-platform.d.ts +5 -2
  52. package/dist/lib/server/decisioning/runtime/from-platform.js +40 -8
  53. package/dist/lib/server/decisioning/runtime/from-platform.mjs +40 -8
  54. package/dist/lib/server/decisioning/runtime/postgres-task-registry.d.mts +8 -1
  55. package/dist/lib/server/decisioning/runtime/postgres-task-registry.d.ts +8 -1
  56. package/dist/lib/server/decisioning/runtime/postgres-task-registry.js +10 -1
  57. package/dist/lib/server/decisioning/runtime/postgres-task-registry.mjs +9 -1
  58. package/dist/lib/server/decisioning/runtime/postgres-task-settlement.d.mts +79 -0
  59. package/dist/lib/server/decisioning/runtime/postgres-task-settlement.d.ts +79 -0
  60. package/dist/lib/server/decisioning/runtime/postgres-task-settlement.js +391 -0
  61. package/dist/lib/server/decisioning/runtime/postgres-task-settlement.mjs +369 -0
  62. package/dist/lib/server/decisioning/runtime/task-registry.d.mts +2 -0
  63. package/dist/lib/server/decisioning/runtime/task-registry.d.ts +2 -0
  64. package/dist/lib/server/decisioning/runtime/task-registry.js +2 -1
  65. package/dist/lib/server/decisioning/runtime/task-registry.mjs +2 -1
  66. package/dist/lib/server/decisioning/runtime/to-context.js +15 -11
  67. package/dist/lib/server/decisioning/runtime/to-context.mjs +15 -11
  68. package/dist/lib/server/index.d.mts +2 -2
  69. package/dist/lib/server/index.d.ts +2 -2
  70. package/dist/lib/server/index.js +4 -0
  71. package/dist/lib/server/index.mjs +4 -0
  72. package/dist/lib/server/webhook-delivery/index.d.mts +2 -2
  73. package/dist/lib/server/webhook-delivery/index.d.ts +2 -2
  74. package/dist/lib/server/webhook-delivery/index.js +4 -0
  75. package/dist/lib/server/webhook-delivery/index.mjs +5 -1
  76. package/dist/lib/server/webhook-delivery/pg.d.mts +3 -1
  77. package/dist/lib/server/webhook-delivery/pg.d.ts +3 -1
  78. package/dist/lib/server/webhook-delivery/pg.js +25 -2
  79. package/dist/lib/server/webhook-delivery/pg.mjs +25 -2
  80. package/dist/lib/server/webhook-delivery/recovery.d.mts +44 -0
  81. package/dist/lib/server/webhook-delivery/recovery.d.ts +44 -0
  82. package/dist/lib/server/webhook-delivery/recovery.js +146 -43
  83. package/dist/lib/server/webhook-delivery/recovery.mjs +144 -43
  84. package/dist/lib/signing/agent-resolver/index.d.mts +1 -1
  85. package/dist/lib/signing/agent-resolver/index.d.ts +1 -1
  86. package/dist/lib/signing/agent-resolver/resolve-agent.d.mts +9 -0
  87. package/dist/lib/signing/agent-resolver/resolve-agent.d.ts +9 -0
  88. package/dist/lib/signing/agent-resolver/resolved-agent-jwks.d.mts +4 -1
  89. package/dist/lib/signing/agent-resolver/resolved-agent-jwks.d.ts +4 -1
  90. package/dist/lib/signing/agent-resolver/resolved-agent-jwks.js +17 -4
  91. package/dist/lib/signing/agent-resolver/resolved-agent-jwks.mjs +17 -4
  92. package/dist/lib/signing/jwks.d.mts +7 -0
  93. package/dist/lib/signing/jwks.d.ts +7 -0
  94. package/dist/lib/signing/server.d.mts +2 -2
  95. package/dist/lib/signing/server.d.ts +2 -2
  96. package/dist/lib/signing/webhook-verifier.d.mts +6 -0
  97. package/dist/lib/signing/webhook-verifier.d.ts +6 -0
  98. package/dist/lib/signing/webhook-verifier.js +38 -4
  99. package/dist/lib/signing/webhook-verifier.mjs +38 -4
  100. package/dist/lib/testing/client.d.mts +1 -0
  101. package/dist/lib/testing/client.d.ts +1 -0
  102. package/dist/lib/testing/client.js +6 -2
  103. package/dist/lib/testing/client.mjs +6 -2
  104. package/dist/lib/testing/compliance/comply.d.mts +2 -2
  105. package/dist/lib/testing/compliance/comply.d.ts +2 -2
  106. package/dist/lib/testing/compliance/comply.js +41 -31
  107. package/dist/lib/testing/compliance/comply.mjs +44 -21
  108. package/dist/lib/testing/storyboard/oauth-metadata-graph/grader.d.mts +1 -7
  109. package/dist/lib/testing/storyboard/oauth-metadata-graph/grader.d.ts +1 -7
  110. package/dist/lib/testing/storyboard/oauth-metadata-graph/grader.js +6 -42
  111. package/dist/lib/testing/storyboard/oauth-metadata-graph/grader.mjs +10 -40
  112. package/dist/lib/testing/storyboard/validations.d.mts +1 -1
  113. package/dist/lib/testing/storyboard/validations.d.ts +1 -1
  114. package/dist/lib/utils/adcp-version-config.d.mts +2 -0
  115. package/dist/lib/utils/adcp-version-config.d.ts +2 -0
  116. package/dist/lib/utils/adcp-version-config.js +5 -0
  117. package/dist/lib/utils/adcp-version-config.mjs +4 -0
  118. package/dist/lib/version.d.mts +3 -3
  119. package/dist/lib/version.d.ts +3 -3
  120. package/dist/lib/version.js +3 -3
  121. package/dist/lib/version.mjs +3 -3
  122. package/docs/llms.txt +3 -1
  123. package/docs/migration-12-to-14.md +6 -5
  124. package/docs/migration-13-to-14.md +48 -7
  125. package/docs/migration-task-registry-scoping.md +164 -11
  126. package/package.json +1 -1
  127. package/skills/build-decisioning-platform/advanced/REFERENCE.md +1 -1
@@ -59,6 +59,7 @@ var import_AsyncHandler = require('./AsyncHandler.js');
59
59
  var import_webhooks = require('../webhooks/index.js');
60
60
  var import_webhook_registration = require('./webhook-registration.js');
61
61
  var import_server = require('../signing/server.js');
62
+ var import_errors2 = require('../signing/agent-resolver/errors.js');
62
63
  var import_a2a_artifacts = require('../utils/a2a-artifacts.js');
63
64
  var import_task_status = require('./task-status.js');
64
65
  var import_ProtocolResponseParser = require('./ProtocolResponseParser.js');
@@ -79,6 +80,14 @@ var import_augment_response = require('../v2/projection/augment-response.js');
79
80
  var import_legacy_metadata = require('../v2/projection/legacy-metadata.js');
80
81
  var import_types = require('../v2/projection/types.js');
81
82
  var import_jcs = require('../utils/jcs.js');
83
+ function presentedStaticTransportCredential(authToken, headers, authProvider) {
84
+ if (authProvider) return false;
85
+ if (authToken) return true;
86
+ return Object.keys(headers ?? {}).some((key) => {
87
+ const normalized = key.toLowerCase();
88
+ return normalized === "authorization" || normalized === "x-adcp-auth";
89
+ });
90
+ }
82
91
  const CAPABILITY_DISCOVERY_CONTEXT = /* @__PURE__ */ Symbol("capabilityDiscoveryContext");
83
92
  function a2aUrlString(input) {
84
93
  return typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
@@ -214,6 +223,7 @@ const RECORDLESS_HMAC_READ_ONLY_TASKS = /* @__PURE__ */ new Set([
214
223
  "get_plan_audit_logs"
215
224
  ]);
216
225
  const TASK_SCOPED_STATE_LIMIT = 1e4;
226
+ const WEBHOOK_JWKS_RESOLVER_LIMIT = 1024;
217
227
  function isDeferredClientFinalizationContext(value) {
218
228
  if (!value || typeof value !== "object" || Array.isArray(value)) return false;
219
229
  const context = value;
@@ -488,7 +498,10 @@ function snapshotTaskOptions(options) {
488
498
  return {
489
499
  ...options,
490
500
  ...options.transport !== void 0 && { transport: { ...options.transport } },
491
- ...options.metadata !== void 0 && { metadata: structuredClone(options.metadata) }
501
+ ...options.metadata !== void 0 && { metadata: structuredClone(options.metadata) },
502
+ ...options.delegatedOperatorAuthorization !== void 0 && {
503
+ delegatedOperatorAuthorization: { ...options.delegatedOperatorAuthorization }
504
+ }
492
505
  };
493
506
  }
494
507
  const PRIMARY_ADCP_TASK_NAMES = {
@@ -821,20 +834,33 @@ class SingleAgentClient {
821
834
  persistedAgentUrl.username = "";
822
835
  persistedAgentUrl.password = "";
823
836
  persistedAgentUrl.hash = "";
837
+ const delegatedOperatorAuthorization = this.effectiveDelegatedOperatorAuthorization(
838
+ args.delegatedOperatorAuthorization
839
+ );
840
+ const registration = {
841
+ agentId: args.agent.id,
842
+ agentUrl: persistedAgentUrl.toString(),
843
+ protocol: args.agent.protocol,
844
+ operationId: args.operationId,
845
+ taskType: args.taskType,
846
+ callbackUrl: args.callbackUrl,
847
+ method: "POST",
848
+ mode: args.mode,
849
+ authorizationContextVersion: 1,
850
+ ...delegatedOperatorAuthorization !== void 0 && { delegatedOperatorAuthorization },
851
+ ...previewMode && { previewMode },
852
+ createdAt: nowMs,
853
+ expiresAt: nowMs + ttlSeconds * 1e3
854
+ };
855
+ (0, import_webhook_registration.validateWebhookRegistrationAuthorization)(registration);
824
856
  try {
825
- await this.webhookRegistrationStore.putIfAbsent({
826
- agentId: args.agent.id,
827
- agentUrl: persistedAgentUrl.toString(),
828
- protocol: args.agent.protocol,
829
- operationId: args.operationId,
830
- taskType: args.taskType,
831
- callbackUrl: args.callbackUrl,
832
- method: "POST",
833
- mode: args.mode,
834
- ...previewMode && { previewMode },
835
- createdAt: nowMs,
836
- expiresAt: nowMs + ttlSeconds * 1e3
837
- });
857
+ await this.webhookRegistrationStore.putIfAbsent(registration);
858
+ const persisted = await this.webhookRegistrationStore.get(args.agent.id, args.operationId);
859
+ if (!persisted || persisted.authorizationContextVersion !== 1 || (0, import_webhook_registration.canonicalDelegatedOperatorAuthorization)(persisted.delegatedOperatorAuthorization) !== (0, import_webhook_registration.canonicalDelegatedOperatorAuthorization)(delegatedOperatorAuthorization)) {
860
+ throw new Error(
861
+ "Webhook registration store did not preserve the versioned delegated-operator authorization context."
862
+ );
863
+ }
838
864
  } catch (cause) {
839
865
  if (args.mode === "rfc9421" || !RECORDLESS_HMAC_READ_ONLY_TASKS.has(args.taskType)) {
840
866
  const error = new import_errors.ConfigurationError("Could not persist trusted webhook registration for this operation.");
@@ -843,6 +869,28 @@ class SingleAgentClient {
843
869
  }
844
870
  }
845
871
  }
872
+ configuredDelegatedOperatorAuthorization() {
873
+ const options = this.config.webhookVerification?.resolverOptions;
874
+ if (!options) return void 0;
875
+ const context = {
876
+ ...options.requiredOperatorBrand !== void 0 && { brand: options.requiredOperatorBrand },
877
+ ...options.requiredOperatorScope !== void 0 && { scope: options.requiredOperatorScope },
878
+ ...options.requiredOperatorCountry !== void 0 && { country: options.requiredOperatorCountry }
879
+ };
880
+ return Object.keys(context).length > 0 ? context : void 0;
881
+ }
882
+ effectiveDelegatedOperatorAuthorization(perCall) {
883
+ const selected = perCall ?? this.configuredDelegatedOperatorAuthorization();
884
+ if (selected === void 0) return void 0;
885
+ return { ...selected };
886
+ }
887
+ delegatedOperatorAuthorizationForRegistration(registration) {
888
+ (0, import_webhook_registration.validateWebhookRegistrationAuthorization)(registration);
889
+ if (registration.authorizationContextVersion === 1) {
890
+ return registration.delegatedOperatorAuthorization === void 0 ? void 0 : { ...registration.delegatedOperatorAuthorization };
891
+ }
892
+ return void 0;
893
+ }
846
894
  async markWebhookDurableSettlementRequired(operationId) {
847
895
  const mark = this.webhookRegistrationStore.markRequiresDurableSettlement;
848
896
  if (!mark) {
@@ -855,11 +903,42 @@ class SingleAgentClient {
855
903
  webhookJwksFor(registration) {
856
904
  const configured = this.config.webhookVerification?.jwks;
857
905
  if (configured) return configured;
858
- const key = `${registration.protocol}\0${registration.agentUrl}`;
906
+ if (registration.authorizationContextVersion !== 1) {
907
+ throw new import_errors.ConfigurationError(
908
+ "Automatic seller-key discovery cannot verify a legacy webhook registration without a persisted authorization context."
909
+ );
910
+ }
911
+ const delegatedOperatorAuthorization = this.delegatedOperatorAuthorizationForRegistration(registration);
912
+ const key = JSON.stringify([
913
+ registration.protocol,
914
+ registration.agentUrl,
915
+ registration.authorizationContextVersion ?? 0,
916
+ (0, import_webhook_registration.canonicalDelegatedOperatorAuthorization)(delegatedOperatorAuthorization)
917
+ ]);
859
918
  const existing = this.webhookJwksResolvers.get(key);
860
- if (existing) return existing;
919
+ if (existing) {
920
+ this.webhookJwksResolvers.delete(key);
921
+ this.webhookJwksResolvers.set(key, existing);
922
+ return existing;
923
+ }
924
+ const configuredResolverOptions = this.config.webhookVerification?.resolverOptions;
925
+ const {
926
+ requiredOperatorBrand: _configuredBrand,
927
+ requiredOperatorScope: _configuredScope,
928
+ requiredOperatorCountry: _configuredCountry,
929
+ ...resolverOptionsWithoutOperatorContext
930
+ } = configuredResolverOptions ?? {};
861
931
  const resolver = new import_server.ResolvedAgentJwksResolver(registration.agentUrl, registration.protocol, {
862
- ...this.config.webhookVerification?.resolverOptions,
932
+ ...resolverOptionsWithoutOperatorContext,
933
+ ...delegatedOperatorAuthorization?.brand !== void 0 && {
934
+ requiredOperatorBrand: delegatedOperatorAuthorization.brand
935
+ },
936
+ ...delegatedOperatorAuthorization?.scope !== void 0 && {
937
+ requiredOperatorScope: delegatedOperatorAuthorization.scope
938
+ },
939
+ ...delegatedOperatorAuthorization?.country !== void 0 && {
940
+ requiredOperatorCountry: delegatedOperatorAuthorization.country
941
+ },
863
942
  fetchCapabilities: (agentUrl) => {
864
943
  const configuredFetch = this.config.webhookVerification?.fetchCapabilities;
865
944
  if (configuredFetch) return configuredFetch(agentUrl, registration.protocol);
@@ -884,6 +963,10 @@ class SingleAgentClient {
884
963
  );
885
964
  }
886
965
  });
966
+ if (this.webhookJwksResolvers.size >= WEBHOOK_JWKS_RESOLVER_LIMIT) {
967
+ const oldest = this.webhookJwksResolvers.keys().next().value;
968
+ if (oldest !== void 0) this.webhookJwksResolvers.delete(oldest);
969
+ }
887
970
  this.webhookJwksResolvers.set(key, resolver);
888
971
  return resolver;
889
972
  }
@@ -1551,8 +1634,8 @@ class SingleAgentClient {
1551
1634
  *
1552
1635
  * Special handling for authentication errors (401):
1553
1636
  * - If any endpoint returns 401, we know the server exists but requires auth
1554
- * - We fetch OAuth metadata and throw AuthenticationRequiredError
1555
- * - This gives consumers clear guidance on how to authenticate
1637
+ * - A validated MCP protected-resource metadata chain yields NeedsAuthorizationError
1638
+ * - Otherwise AuthenticationRequiredError carries the advertised challenge
1556
1639
  *
1557
1640
  * Note: This is async and called lazily on first agent interaction
1558
1641
  */
@@ -1629,6 +1712,7 @@ class SingleAgentClient {
1629
1712
  }
1630
1713
  const uniqueUrls = [...new Set(urlsToTry)];
1631
1714
  let got401 = false;
1715
+ let first401Error;
1632
1716
  let firstWorkingUrl;
1633
1717
  for (const url of uniqueUrls) {
1634
1718
  const result = await testEndpoint(url);
@@ -1638,18 +1722,22 @@ class SingleAgentClient {
1638
1722
  }
1639
1723
  if (result.status === 401) {
1640
1724
  got401 = true;
1725
+ first401Error ??= result.error;
1641
1726
  }
1642
1727
  }
1643
1728
  if (firstWorkingUrl) {
1644
1729
  return firstWorkingUrl;
1645
1730
  }
1646
1731
  if (got401) {
1732
+ if (presentedStaticTransportCredential(authToken, agentHeaders, authProvider)) {
1733
+ throw new import_errors.AuthenticationCredentialsRejectedError(providedUri, first401Error);
1734
+ }
1647
1735
  const requirements = await (0, import_authorization_required.discoverAuthorizationRequirements)(providedUri, {
1648
1736
  allowPrivateIp: transport?.allowPrivateIp ?? (0, import_net.isLikelyPrivateUrl)(providedUri),
1649
1737
  fetchFn: transport?.trustedFetchFn,
1650
1738
  signal: options?.signal
1651
1739
  });
1652
- if (requirements) {
1740
+ if (requirements && (0, import_authorization_required.hasValidatedMcpAuthorizationRequirements)(requirements, providedUri)) {
1653
1741
  throw new import_authorization_required.NeedsAuthorizationError(requirements);
1654
1742
  }
1655
1743
  const challenge = await (0, import_authorization_required.probeAuthChallenge)(providedUri, {
@@ -1657,12 +1745,7 @@ class SingleAgentClient {
1657
1745
  fetchFn: transport?.trustedFetchFn,
1658
1746
  signal: options?.signal
1659
1747
  });
1660
- const oauthMetadata = await (0, import_discovery.discoverOAuthMetadata)(providedUri, {
1661
- trustedFetchFn: transport?.trustedFetchFn,
1662
- allowPrivateIp: transport?.allowPrivateIp ?? (0, import_net.isLikelyPrivateUrl)(providedUri),
1663
- signal: options?.signal
1664
- });
1665
- throw new import_errors.AuthenticationRequiredError(providedUri, oauthMetadata || void 0, void 0, challenge ?? void 0);
1748
+ throw new import_errors.AuthenticationRequiredError(providedUri, void 0, void 0, challenge ?? void 0);
1666
1749
  }
1667
1750
  throw new Error(
1668
1751
  `Failed to discover MCP endpoint. Tried:
@@ -1801,6 +1884,16 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
1801
1884
  message: "Webhook registration state contains invalid timestamps."
1802
1885
  };
1803
1886
  }
1887
+ try {
1888
+ (0, import_webhook_registration.validateWebhookRegistrationAuthorization)(registration);
1889
+ } catch (cause) {
1890
+ return {
1891
+ ok: false,
1892
+ code: "webhook_registration_store_unavailable",
1893
+ message: "Webhook registration state contains invalid authorization context.",
1894
+ cause
1895
+ };
1896
+ }
1804
1897
  try {
1805
1898
  const registeredAgentUrl = new URL(registration.agentUrl);
1806
1899
  if (registeredAgentUrl.username || registeredAgentUrl.password) {
@@ -1893,6 +1986,7 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
1893
1986
  const normalizedHeaders = normalizeRfc9421WebhookHeaders(options.headers);
1894
1987
  if (!normalizedHeaders.ok) return normalizedHeaders.failure;
1895
1988
  try {
1989
+ const delegatedOperatorAuthorization = this.delegatedOperatorAuthorizationForRegistration(registration);
1896
1990
  await (0, import_server.verifyWebhookSignature)(
1897
1991
  {
1898
1992
  method: options.requestMethod,
@@ -1905,13 +1999,24 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
1905
1999
  replayStore: this.webhookReplayStore,
1906
2000
  revocationStore: this.webhookRevocationStore,
1907
2001
  ...this.config.webhookVerification?.now && { now: this.config.webhookVerification.now },
1908
- agentUrlForKeyid: () => registration.agentUrl
2002
+ agentUrlForKeyid: () => registration.agentUrl,
2003
+ ...registration.authorizationContextVersion === 1 && {
2004
+ replayScopeBinding: `delegated-operator:v1:${crypto.createHash("sha256").update((0, import_webhook_registration.canonicalDelegatedOperatorAuthorization)(delegatedOperatorAuthorization)).digest("base64url")}`
2005
+ }
1909
2006
  }
1910
2007
  );
1911
2008
  } catch (cause) {
1912
2009
  if (cause instanceof import_server.WebhookSignatureError) {
1913
2010
  return { ok: false, code: cause.code, message: cause.message, cause };
1914
2011
  }
2012
+ if (cause instanceof import_errors2.AgentResolverError && !cause.code.endsWith("_unreachable")) {
2013
+ return {
2014
+ ok: false,
2015
+ code: "webhook_signature_key_unknown",
2016
+ message: "The registered seller is not authorized to supply this webhook signing key.",
2017
+ cause
2018
+ };
2019
+ }
1915
2020
  return {
1916
2021
  ok: false,
1917
2022
  code: "webhook_verification_unavailable",
@@ -4503,7 +4608,7 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
4503
4608
  */
4504
4609
  async getAdcpCapabilities(params, inputHandler, options) {
4505
4610
  return (0, import_task_deadline.withTaskDeadline)(
4506
- options,
4611
+ snapshotTaskOptions(options),
4507
4612
  (effectiveOptions) => this.getAdcpCapabilitiesWithinDeadline(params, inputHandler, effectiveOptions)
4508
4613
  );
4509
4614
  }
@@ -35,6 +35,7 @@ import { createMCPRequestHeaders } from "../auth/index.mjs";
35
35
  import { isAbortOrTimeoutError } from "../protocols/abort.mjs";
36
36
  import { ProtocolClient, normalizeTransportOptions } from "../protocols/index.mjs";
37
37
  import {
38
+ AuthenticationCredentialsRejectedError,
38
39
  AuthenticationRequiredError,
39
40
  ConfigurationError,
40
41
  FeatureUnsupportedError,
@@ -46,6 +47,7 @@ import {
46
47
  import { createAgentTransportFetch, isLikelyPrivateUrl } from "../net/index.mjs";
47
48
  import {
48
49
  discoverAuthorizationRequirements,
50
+ hasValidatedMcpAuthorizationRequirements,
49
51
  NeedsAuthorizationError,
50
52
  probeAuthChallenge
51
53
  } from "../auth/oauth/authorization-required.mjs";
@@ -58,7 +60,9 @@ import {
58
60
  } from "./AsyncHandler.mjs";
59
61
  import { verifyWebhookRequest } from "../webhooks/index.mjs";
60
62
  import {
61
- InMemoryWebhookRegistrationStore
63
+ canonicalDelegatedOperatorAuthorization,
64
+ InMemoryWebhookRegistrationStore,
65
+ validateWebhookRegistrationAuthorization
62
66
  } from "./webhook-registration.mjs";
63
67
  import {
64
68
  InMemoryReplayStore,
@@ -68,6 +72,7 @@ import {
68
72
  ResolvedAgentJwksResolver,
69
73
  canonicalTargetUri
70
74
  } from "../signing/server.mjs";
75
+ import { AgentResolverError } from "../signing/agent-resolver/errors.mjs";
71
76
  import { getLatestA2ADataPartFromTask } from "../utils/a2a-artifacts.mjs";
72
77
  import { extractAdcpTaskStatusFromPayload, isAdcpStatus } from "./task-status.mjs";
73
78
  import { responseParser } from "./ProtocolResponseParser.mjs";
@@ -86,6 +91,14 @@ import {
86
91
  throwIfAborted,
87
92
  withAbortSignal
88
93
  } from "../protocols/abort.mjs";
94
+ function presentedStaticTransportCredential(authToken, headers, authProvider) {
95
+ if (authProvider) return false;
96
+ if (authToken) return true;
97
+ return Object.keys(headers ?? {}).some((key) => {
98
+ const normalized = key.toLowerCase();
99
+ return normalized === "authorization" || normalized === "x-adcp-auth";
100
+ });
101
+ }
89
102
  import {
90
103
  buildSyntheticCapabilities,
91
104
  buildSyntheticV3Capabilities,
@@ -261,6 +274,7 @@ const RECORDLESS_HMAC_READ_ONLY_TASKS = /* @__PURE__ */ new Set([
261
274
  "get_plan_audit_logs"
262
275
  ]);
263
276
  const TASK_SCOPED_STATE_LIMIT = 1e4;
277
+ const WEBHOOK_JWKS_RESOLVER_LIMIT = 1024;
264
278
  function isDeferredClientFinalizationContext(value) {
265
279
  if (!value || typeof value !== "object" || Array.isArray(value)) return false;
266
280
  const context = value;
@@ -535,7 +549,10 @@ function snapshotTaskOptions(options) {
535
549
  return {
536
550
  ...options,
537
551
  ...options.transport !== void 0 && { transport: { ...options.transport } },
538
- ...options.metadata !== void 0 && { metadata: structuredClone(options.metadata) }
552
+ ...options.metadata !== void 0 && { metadata: structuredClone(options.metadata) },
553
+ ...options.delegatedOperatorAuthorization !== void 0 && {
554
+ delegatedOperatorAuthorization: { ...options.delegatedOperatorAuthorization }
555
+ }
539
556
  };
540
557
  }
541
558
  const PRIMARY_ADCP_TASK_NAMES = {
@@ -868,20 +885,33 @@ class SingleAgentClient {
868
885
  persistedAgentUrl.username = "";
869
886
  persistedAgentUrl.password = "";
870
887
  persistedAgentUrl.hash = "";
888
+ const delegatedOperatorAuthorization = this.effectiveDelegatedOperatorAuthorization(
889
+ args.delegatedOperatorAuthorization
890
+ );
891
+ const registration = {
892
+ agentId: args.agent.id,
893
+ agentUrl: persistedAgentUrl.toString(),
894
+ protocol: args.agent.protocol,
895
+ operationId: args.operationId,
896
+ taskType: args.taskType,
897
+ callbackUrl: args.callbackUrl,
898
+ method: "POST",
899
+ mode: args.mode,
900
+ authorizationContextVersion: 1,
901
+ ...delegatedOperatorAuthorization !== void 0 && { delegatedOperatorAuthorization },
902
+ ...previewMode && { previewMode },
903
+ createdAt: nowMs,
904
+ expiresAt: nowMs + ttlSeconds * 1e3
905
+ };
906
+ validateWebhookRegistrationAuthorization(registration);
871
907
  try {
872
- await this.webhookRegistrationStore.putIfAbsent({
873
- agentId: args.agent.id,
874
- agentUrl: persistedAgentUrl.toString(),
875
- protocol: args.agent.protocol,
876
- operationId: args.operationId,
877
- taskType: args.taskType,
878
- callbackUrl: args.callbackUrl,
879
- method: "POST",
880
- mode: args.mode,
881
- ...previewMode && { previewMode },
882
- createdAt: nowMs,
883
- expiresAt: nowMs + ttlSeconds * 1e3
884
- });
908
+ await this.webhookRegistrationStore.putIfAbsent(registration);
909
+ const persisted = await this.webhookRegistrationStore.get(args.agent.id, args.operationId);
910
+ if (!persisted || persisted.authorizationContextVersion !== 1 || canonicalDelegatedOperatorAuthorization(persisted.delegatedOperatorAuthorization) !== canonicalDelegatedOperatorAuthorization(delegatedOperatorAuthorization)) {
911
+ throw new Error(
912
+ "Webhook registration store did not preserve the versioned delegated-operator authorization context."
913
+ );
914
+ }
885
915
  } catch (cause) {
886
916
  if (args.mode === "rfc9421" || !RECORDLESS_HMAC_READ_ONLY_TASKS.has(args.taskType)) {
887
917
  const error = new ConfigurationError("Could not persist trusted webhook registration for this operation.");
@@ -890,6 +920,28 @@ class SingleAgentClient {
890
920
  }
891
921
  }
892
922
  }
923
+ configuredDelegatedOperatorAuthorization() {
924
+ const options = this.config.webhookVerification?.resolverOptions;
925
+ if (!options) return void 0;
926
+ const context = {
927
+ ...options.requiredOperatorBrand !== void 0 && { brand: options.requiredOperatorBrand },
928
+ ...options.requiredOperatorScope !== void 0 && { scope: options.requiredOperatorScope },
929
+ ...options.requiredOperatorCountry !== void 0 && { country: options.requiredOperatorCountry }
930
+ };
931
+ return Object.keys(context).length > 0 ? context : void 0;
932
+ }
933
+ effectiveDelegatedOperatorAuthorization(perCall) {
934
+ const selected = perCall ?? this.configuredDelegatedOperatorAuthorization();
935
+ if (selected === void 0) return void 0;
936
+ return { ...selected };
937
+ }
938
+ delegatedOperatorAuthorizationForRegistration(registration) {
939
+ validateWebhookRegistrationAuthorization(registration);
940
+ if (registration.authorizationContextVersion === 1) {
941
+ return registration.delegatedOperatorAuthorization === void 0 ? void 0 : { ...registration.delegatedOperatorAuthorization };
942
+ }
943
+ return void 0;
944
+ }
893
945
  async markWebhookDurableSettlementRequired(operationId) {
894
946
  const mark = this.webhookRegistrationStore.markRequiresDurableSettlement;
895
947
  if (!mark) {
@@ -902,11 +954,42 @@ class SingleAgentClient {
902
954
  webhookJwksFor(registration) {
903
955
  const configured = this.config.webhookVerification?.jwks;
904
956
  if (configured) return configured;
905
- const key = `${registration.protocol}\0${registration.agentUrl}`;
957
+ if (registration.authorizationContextVersion !== 1) {
958
+ throw new ConfigurationError(
959
+ "Automatic seller-key discovery cannot verify a legacy webhook registration without a persisted authorization context."
960
+ );
961
+ }
962
+ const delegatedOperatorAuthorization = this.delegatedOperatorAuthorizationForRegistration(registration);
963
+ const key = JSON.stringify([
964
+ registration.protocol,
965
+ registration.agentUrl,
966
+ registration.authorizationContextVersion ?? 0,
967
+ canonicalDelegatedOperatorAuthorization(delegatedOperatorAuthorization)
968
+ ]);
906
969
  const existing = this.webhookJwksResolvers.get(key);
907
- if (existing) return existing;
970
+ if (existing) {
971
+ this.webhookJwksResolvers.delete(key);
972
+ this.webhookJwksResolvers.set(key, existing);
973
+ return existing;
974
+ }
975
+ const configuredResolverOptions = this.config.webhookVerification?.resolverOptions;
976
+ const {
977
+ requiredOperatorBrand: _configuredBrand,
978
+ requiredOperatorScope: _configuredScope,
979
+ requiredOperatorCountry: _configuredCountry,
980
+ ...resolverOptionsWithoutOperatorContext
981
+ } = configuredResolverOptions ?? {};
908
982
  const resolver = new ResolvedAgentJwksResolver(registration.agentUrl, registration.protocol, {
909
- ...this.config.webhookVerification?.resolverOptions,
983
+ ...resolverOptionsWithoutOperatorContext,
984
+ ...delegatedOperatorAuthorization?.brand !== void 0 && {
985
+ requiredOperatorBrand: delegatedOperatorAuthorization.brand
986
+ },
987
+ ...delegatedOperatorAuthorization?.scope !== void 0 && {
988
+ requiredOperatorScope: delegatedOperatorAuthorization.scope
989
+ },
990
+ ...delegatedOperatorAuthorization?.country !== void 0 && {
991
+ requiredOperatorCountry: delegatedOperatorAuthorization.country
992
+ },
910
993
  fetchCapabilities: (agentUrl) => {
911
994
  const configuredFetch = this.config.webhookVerification?.fetchCapabilities;
912
995
  if (configuredFetch) return configuredFetch(agentUrl, registration.protocol);
@@ -931,6 +1014,10 @@ class SingleAgentClient {
931
1014
  );
932
1015
  }
933
1016
  });
1017
+ if (this.webhookJwksResolvers.size >= WEBHOOK_JWKS_RESOLVER_LIMIT) {
1018
+ const oldest = this.webhookJwksResolvers.keys().next().value;
1019
+ if (oldest !== void 0) this.webhookJwksResolvers.delete(oldest);
1020
+ }
934
1021
  this.webhookJwksResolvers.set(key, resolver);
935
1022
  return resolver;
936
1023
  }
@@ -1598,8 +1685,8 @@ class SingleAgentClient {
1598
1685
  *
1599
1686
  * Special handling for authentication errors (401):
1600
1687
  * - If any endpoint returns 401, we know the server exists but requires auth
1601
- * - We fetch OAuth metadata and throw AuthenticationRequiredError
1602
- * - This gives consumers clear guidance on how to authenticate
1688
+ * - A validated MCP protected-resource metadata chain yields NeedsAuthorizationError
1689
+ * - Otherwise AuthenticationRequiredError carries the advertised challenge
1603
1690
  *
1604
1691
  * Note: This is async and called lazily on first agent interaction
1605
1692
  */
@@ -1676,6 +1763,7 @@ class SingleAgentClient {
1676
1763
  }
1677
1764
  const uniqueUrls = [...new Set(urlsToTry)];
1678
1765
  let got401 = false;
1766
+ let first401Error;
1679
1767
  let firstWorkingUrl;
1680
1768
  for (const url of uniqueUrls) {
1681
1769
  const result = await testEndpoint(url);
@@ -1685,18 +1773,22 @@ class SingleAgentClient {
1685
1773
  }
1686
1774
  if (result.status === 401) {
1687
1775
  got401 = true;
1776
+ first401Error ??= result.error;
1688
1777
  }
1689
1778
  }
1690
1779
  if (firstWorkingUrl) {
1691
1780
  return firstWorkingUrl;
1692
1781
  }
1693
1782
  if (got401) {
1783
+ if (presentedStaticTransportCredential(authToken, agentHeaders, authProvider)) {
1784
+ throw new AuthenticationCredentialsRejectedError(providedUri, first401Error);
1785
+ }
1694
1786
  const requirements = await discoverAuthorizationRequirements(providedUri, {
1695
1787
  allowPrivateIp: transport?.allowPrivateIp ?? isLikelyPrivateUrl(providedUri),
1696
1788
  fetchFn: transport?.trustedFetchFn,
1697
1789
  signal: options?.signal
1698
1790
  });
1699
- if (requirements) {
1791
+ if (requirements && hasValidatedMcpAuthorizationRequirements(requirements, providedUri)) {
1700
1792
  throw new NeedsAuthorizationError(requirements);
1701
1793
  }
1702
1794
  const challenge = await probeAuthChallenge(providedUri, {
@@ -1704,12 +1796,7 @@ class SingleAgentClient {
1704
1796
  fetchFn: transport?.trustedFetchFn,
1705
1797
  signal: options?.signal
1706
1798
  });
1707
- const oauthMetadata = await discoverOAuthMetadata(providedUri, {
1708
- trustedFetchFn: transport?.trustedFetchFn,
1709
- allowPrivateIp: transport?.allowPrivateIp ?? isLikelyPrivateUrl(providedUri),
1710
- signal: options?.signal
1711
- });
1712
- throw new AuthenticationRequiredError(providedUri, oauthMetadata || void 0, void 0, challenge ?? void 0);
1799
+ throw new AuthenticationRequiredError(providedUri, void 0, void 0, challenge ?? void 0);
1713
1800
  }
1714
1801
  throw new Error(
1715
1802
  `Failed to discover MCP endpoint. Tried:
@@ -1848,6 +1935,16 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
1848
1935
  message: "Webhook registration state contains invalid timestamps."
1849
1936
  };
1850
1937
  }
1938
+ try {
1939
+ validateWebhookRegistrationAuthorization(registration);
1940
+ } catch (cause) {
1941
+ return {
1942
+ ok: false,
1943
+ code: "webhook_registration_store_unavailable",
1944
+ message: "Webhook registration state contains invalid authorization context.",
1945
+ cause
1946
+ };
1947
+ }
1851
1948
  try {
1852
1949
  const registeredAgentUrl = new URL(registration.agentUrl);
1853
1950
  if (registeredAgentUrl.username || registeredAgentUrl.password) {
@@ -1940,6 +2037,7 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
1940
2037
  const normalizedHeaders = normalizeRfc9421WebhookHeaders(options.headers);
1941
2038
  if (!normalizedHeaders.ok) return normalizedHeaders.failure;
1942
2039
  try {
2040
+ const delegatedOperatorAuthorization = this.delegatedOperatorAuthorizationForRegistration(registration);
1943
2041
  await verifyRfc9421WebhookSignature(
1944
2042
  {
1945
2043
  method: options.requestMethod,
@@ -1952,13 +2050,24 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
1952
2050
  replayStore: this.webhookReplayStore,
1953
2051
  revocationStore: this.webhookRevocationStore,
1954
2052
  ...this.config.webhookVerification?.now && { now: this.config.webhookVerification.now },
1955
- agentUrlForKeyid: () => registration.agentUrl
2053
+ agentUrlForKeyid: () => registration.agentUrl,
2054
+ ...registration.authorizationContextVersion === 1 && {
2055
+ replayScopeBinding: `delegated-operator:v1:${crypto.createHash("sha256").update(canonicalDelegatedOperatorAuthorization(delegatedOperatorAuthorization)).digest("base64url")}`
2056
+ }
1956
2057
  }
1957
2058
  );
1958
2059
  } catch (cause) {
1959
2060
  if (cause instanceof WebhookSignatureError) {
1960
2061
  return { ok: false, code: cause.code, message: cause.message, cause };
1961
2062
  }
2063
+ if (cause instanceof AgentResolverError && !cause.code.endsWith("_unreachable")) {
2064
+ return {
2065
+ ok: false,
2066
+ code: "webhook_signature_key_unknown",
2067
+ message: "The registered seller is not authorized to supply this webhook signing key.",
2068
+ cause
2069
+ };
2070
+ }
1962
2071
  return {
1963
2072
  ok: false,
1964
2073
  code: "webhook_verification_unavailable",
@@ -4550,7 +4659,7 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
4550
4659
  */
4551
4660
  async getAdcpCapabilities(params, inputHandler, options) {
4552
4661
  return withTaskDeadline(
4553
- options,
4662
+ snapshotTaskOptions(options),
4554
4663
  (effectiveOptions) => this.getAdcpCapabilitiesWithinDeadline(params, inputHandler, effectiveOptions)
4555
4664
  );
4556
4665
  }
@@ -153,6 +153,7 @@ export declare class TaskExecutor {
153
153
  operationId: string;
154
154
  callbackUrl: string;
155
155
  mode: 'rfc9421' | 'hmac-sha256';
156
+ delegatedOperatorAuthorization?: TaskOptions['delegatedOperatorAuthorization'];
156
157
  }) => void | Promise<void>;
157
158
  /** Persist fail-closed routing provenance before a durable mutation claim can run. */
158
159
  onDurableSettlementRequired?: (operationId: string) => void | Promise<void>;
@@ -153,6 +153,7 @@ export declare class TaskExecutor {
153
153
  operationId: string;
154
154
  callbackUrl: string;
155
155
  mode: 'rfc9421' | 'hmac-sha256';
156
+ delegatedOperatorAuthorization?: TaskOptions['delegatedOperatorAuthorization'];
156
157
  }) => void | Promise<void>;
157
158
  /** Persist fail-closed routing provenance before a durable mutation claim can run. */
158
159
  onDurableSettlementRequired?: (operationId: string) => void | Promise<void>;
@@ -372,7 +372,10 @@ function snapshotTaskOptions(options) {
372
372
  return {
373
373
  ...options,
374
374
  ...options.transport !== void 0 && { transport: snapshotTransportOptions(options.transport) },
375
- ...options.metadata !== void 0 && { metadata: structuredClone(options.metadata) }
375
+ ...options.metadata !== void 0 && { metadata: structuredClone(options.metadata) },
376
+ ...options.delegatedOperatorAuthorization !== void 0 && {
377
+ delegatedOperatorAuthorization: { ...options.delegatedOperatorAuthorization }
378
+ }
376
379
  };
377
380
  }
378
381
  function snapshotTransportOptions(transport) {
@@ -851,6 +854,9 @@ class TaskExecutor {
851
854
  agent,
852
855
  taskType: taskName,
853
856
  operationId: taskId,
857
+ ...options.delegatedOperatorAuthorization !== void 0 && {
858
+ delegatedOperatorAuthorization: options.delegatedOperatorAuthorization
859
+ },
854
860
  ...registration
855
861
  });
856
862
  webhookRegistrationPersisted = true;
@@ -346,7 +346,10 @@ function snapshotTaskOptions(options) {
346
346
  return {
347
347
  ...options,
348
348
  ...options.transport !== void 0 && { transport: snapshotTransportOptions(options.transport) },
349
- ...options.metadata !== void 0 && { metadata: structuredClone(options.metadata) }
349
+ ...options.metadata !== void 0 && { metadata: structuredClone(options.metadata) },
350
+ ...options.delegatedOperatorAuthorization !== void 0 && {
351
+ delegatedOperatorAuthorization: { ...options.delegatedOperatorAuthorization }
352
+ }
350
353
  };
351
354
  }
352
355
  function snapshotTransportOptions(transport) {
@@ -825,6 +828,9 @@ class TaskExecutor {
825
828
  agent,
826
829
  taskType: taskName,
827
830
  operationId: taskId,
831
+ ...options.delegatedOperatorAuthorization !== void 0 && {
832
+ delegatedOperatorAuthorization: options.delegatedOperatorAuthorization
833
+ },
828
834
  ...registration
829
835
  });
830
836
  webhookRegistrationPersisted = true;