@adcp/sdk 15.0.0 → 15.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 (142) hide show
  1. package/bin/adcp-grade.js +46 -2
  2. package/bin/adcp.js +7 -2
  3. package/dist/lib/canonical-references/index.d.mts +19 -1
  4. package/dist/lib/canonical-references/index.d.ts +19 -1
  5. package/dist/lib/canonical-references/index.js +81 -9
  6. package/dist/lib/canonical-references/index.mjs +80 -9
  7. package/dist/lib/client/core.d.mts +1 -0
  8. package/dist/lib/client/core.d.ts +1 -0
  9. package/dist/lib/core/AgentClient.d.mts +9 -1
  10. package/dist/lib/core/AgentClient.d.ts +9 -1
  11. package/dist/lib/core/AgentClient.js +10 -0
  12. package/dist/lib/core/AgentClient.mjs +10 -0
  13. package/dist/lib/core/SingleAgentClient.d.mts +13 -2
  14. package/dist/lib/core/SingleAgentClient.d.ts +13 -2
  15. package/dist/lib/core/SingleAgentClient.js +23 -5
  16. package/dist/lib/core/SingleAgentClient.mjs +23 -5
  17. package/dist/lib/index.d.mts +1 -1
  18. package/dist/lib/index.d.ts +1 -1
  19. package/dist/lib/reporting/gcs/adapter.d.mts +31 -0
  20. package/dist/lib/reporting/gcs/adapter.d.ts +31 -0
  21. package/dist/lib/reporting/gcs/adapter.js +342 -0
  22. package/dist/lib/reporting/gcs/adapter.mjs +325 -0
  23. package/dist/lib/reporting/gcs/bucket-policy.d.mts +3 -0
  24. package/dist/lib/reporting/gcs/bucket-policy.d.ts +3 -0
  25. package/dist/lib/reporting/gcs/bucket-policy.js +31 -0
  26. package/dist/lib/reporting/gcs/bucket-policy.mjs +7 -0
  27. package/dist/lib/reporting/gcs/errors.d.mts +5 -0
  28. package/dist/lib/reporting/gcs/errors.d.ts +5 -0
  29. package/dist/lib/reporting/gcs/errors.js +41 -0
  30. package/dist/lib/reporting/gcs/errors.mjs +16 -0
  31. package/dist/lib/reporting/gcs/fence.d.mts +36 -0
  32. package/dist/lib/reporting/gcs/fence.d.ts +36 -0
  33. package/dist/lib/reporting/gcs/fence.js +250 -0
  34. package/dist/lib/reporting/gcs/fence.mjs +230 -0
  35. package/dist/lib/reporting/gcs/index.d.mts +7 -0
  36. package/dist/lib/reporting/gcs/index.d.ts +7 -0
  37. package/dist/lib/reporting/gcs/index.js +41 -0
  38. package/dist/lib/reporting/gcs/index.mjs +12 -0
  39. package/dist/lib/reporting/gcs/io.d.mts +7 -0
  40. package/dist/lib/reporting/gcs/io.d.ts +7 -0
  41. package/dist/lib/reporting/gcs/io.js +95 -0
  42. package/dist/lib/reporting/gcs/io.mjs +69 -0
  43. package/dist/lib/reporting/gcs/reader.d.mts +43 -0
  44. package/dist/lib/reporting/gcs/reader.d.ts +43 -0
  45. package/dist/lib/reporting/gcs/reader.js +193 -0
  46. package/dist/lib/reporting/gcs/reader.mjs +169 -0
  47. package/dist/lib/reporting/gcs/validation.d.mts +5 -0
  48. package/dist/lib/reporting/gcs/validation.d.ts +5 -0
  49. package/dist/lib/reporting/gcs/validation.js +49 -0
  50. package/dist/lib/reporting/gcs/validation.mjs +25 -0
  51. package/dist/lib/reporting/ledger/handler.d.mts +1 -0
  52. package/dist/lib/reporting/ledger/handler.d.ts +1 -0
  53. package/dist/lib/reporting/ledger/index.d.mts +1 -0
  54. package/dist/lib/reporting/ledger/index.d.ts +1 -0
  55. package/dist/lib/reporting/ledger/index.js +2 -0
  56. package/dist/lib/reporting/ledger/index.mjs +1 -0
  57. package/dist/lib/reporting/ledger/managed-postgres.d.mts +16 -1
  58. package/dist/lib/reporting/ledger/managed-postgres.d.ts +16 -1
  59. package/dist/lib/reporting/ledger/managed-postgres.js +243 -2
  60. package/dist/lib/reporting/ledger/managed-postgres.mjs +248 -2
  61. package/dist/lib/reporting/ledger/object-writes.d.mts +69 -0
  62. package/dist/lib/reporting/ledger/object-writes.d.ts +69 -0
  63. package/dist/lib/reporting/ledger/object-writes.js +147 -0
  64. package/dist/lib/reporting/ledger/object-writes.mjs +114 -0
  65. package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
  66. package/dist/lib/server/a2a-adapter.d.mts +50 -1
  67. package/dist/lib/server/a2a-adapter.d.ts +50 -1
  68. package/dist/lib/server/a2a-adapter.js +230 -7
  69. package/dist/lib/server/a2a-adapter.mjs +237 -7
  70. package/dist/lib/server/auth-signature.d.mts +84 -46
  71. package/dist/lib/server/auth-signature.d.ts +84 -46
  72. package/dist/lib/server/auth-signature.js +48 -14
  73. package/dist/lib/server/auth-signature.mjs +58 -15
  74. package/dist/lib/server/create-adcp-server.js +5 -16
  75. package/dist/lib/server/create-adcp-server.mjs +5 -16
  76. package/dist/lib/server/index.d.mts +1 -1
  77. package/dist/lib/server/index.d.ts +1 -1
  78. package/dist/lib/server/index.js +2 -0
  79. package/dist/lib/server/index.mjs +3 -1
  80. package/dist/lib/signing/agent-fetch.js +3 -33
  81. package/dist/lib/signing/agent-fetch.mjs +3 -33
  82. package/dist/lib/signing/middleware.d.mts +9 -1
  83. package/dist/lib/signing/middleware.d.ts +9 -1
  84. package/dist/lib/signing/operation-resolution.d.mts +37 -0
  85. package/dist/lib/signing/operation-resolution.d.ts +37 -0
  86. package/dist/lib/signing/operation-resolution.js +154 -0
  87. package/dist/lib/signing/operation-resolution.mjs +125 -0
  88. package/dist/lib/signing/postgres-replay-store.d.mts +1 -1
  89. package/dist/lib/signing/postgres-replay-store.d.ts +1 -1
  90. package/dist/lib/signing/redis-replay-store.d.mts +1 -1
  91. package/dist/lib/signing/redis-replay-store.d.ts +1 -1
  92. package/dist/lib/signing/server.d.mts +1 -0
  93. package/dist/lib/signing/server.d.ts +1 -0
  94. package/dist/lib/signing/server.js +7 -0
  95. package/dist/lib/signing/server.mjs +8 -0
  96. package/dist/lib/signing/verifier.d.mts +36 -1
  97. package/dist/lib/signing/verifier.d.ts +36 -1
  98. package/dist/lib/signing/verifier.js +66 -34
  99. package/dist/lib/signing/verifier.mjs +61 -34
  100. package/dist/lib/testing/storyboard/request-signing/a2a-dispatch.d.mts +12 -0
  101. package/dist/lib/testing/storyboard/request-signing/a2a-dispatch.d.ts +12 -0
  102. package/dist/lib/testing/storyboard/request-signing/a2a-dispatch.js +29 -2
  103. package/dist/lib/testing/storyboard/request-signing/a2a-dispatch.mjs +27 -1
  104. package/dist/lib/testing/storyboard/request-signing/a2a-operation-resolution.d.mts +61 -0
  105. package/dist/lib/testing/storyboard/request-signing/a2a-operation-resolution.d.ts +61 -0
  106. package/dist/lib/testing/storyboard/request-signing/a2a-operation-resolution.js +298 -0
  107. package/dist/lib/testing/storyboard/request-signing/a2a-operation-resolution.mjs +276 -0
  108. package/dist/lib/testing/storyboard/request-signing/grader.d.mts +31 -1
  109. package/dist/lib/testing/storyboard/request-signing/grader.d.ts +31 -1
  110. package/dist/lib/testing/storyboard/request-signing/grader.js +11 -0
  111. package/dist/lib/testing/storyboard/request-signing/grader.mjs +11 -0
  112. package/dist/lib/testing/storyboard/request-signing/index.d.mts +5 -4
  113. package/dist/lib/testing/storyboard/request-signing/index.d.ts +5 -4
  114. package/dist/lib/testing/storyboard/request-signing/index.js +13 -0
  115. package/dist/lib/testing/storyboard/request-signing/index.mjs +18 -2
  116. package/dist/lib/testing/storyboard/request-signing/types.d.mts +43 -0
  117. package/dist/lib/testing/storyboard/request-signing/types.d.ts +43 -0
  118. package/dist/lib/testing/storyboard/request-signing/types.js +3 -0
  119. package/dist/lib/testing/storyboard/request-signing/types.mjs +2 -0
  120. package/dist/lib/testing/storyboard/request-signing/vector-loader.d.mts +42 -1
  121. package/dist/lib/testing/storyboard/request-signing/vector-loader.d.ts +42 -1
  122. package/dist/lib/testing/storyboard/request-signing/vector-loader.js +100 -1
  123. package/dist/lib/testing/storyboard/request-signing/vector-loader.mjs +104 -3
  124. package/dist/lib/testing/storyboard/validations.d.mts +1 -1
  125. package/dist/lib/testing/storyboard/validations.d.ts +1 -1
  126. package/dist/lib/version.d.mts +3 -3
  127. package/dist/lib/version.d.ts +3 -3
  128. package/dist/lib/version.js +3 -3
  129. package/dist/lib/version.mjs +3 -3
  130. package/docs/TYPE-SUMMARY.md +2 -2
  131. package/docs/guides/BUILD-AN-AGENT.md +9 -4
  132. package/docs/guides/CREATIVE-DELIVERY.md +34 -0
  133. package/docs/guides/REPORTING-GCS-FENCE.md +52 -0
  134. package/docs/guides/REPORTING-GCS-MANAGED.md +56 -0
  135. package/docs/guides/REPORTING-OPERATIONS.md +4 -0
  136. package/docs/guides/SIGNING-GUIDE.md +20 -6
  137. package/docs/guides/VALIDATE-YOUR-AGENT.md +4 -0
  138. package/docs/llms.txt +2 -2
  139. package/docs/migration-14.x-rc-worksheet.md +5 -4
  140. package/docs/migration-buyer-agent-registry.md +1 -1
  141. package/package.json +19 -1
  142. package/skills/build-seller-agent/specialisms/signed-requests.md +2 -0
@@ -1,7 +1,18 @@
1
1
  import { RequestSignatureError } from "../signing/errors.mjs";
2
2
  import { InMemoryReplayStore } from "../signing/replay.mjs";
3
3
  import { InMemoryRevocationStore } from "../signing/revocation.mjs";
4
- import { verifyRequestSignature } from "../signing/verifier.mjs";
4
+ import {
5
+ isUnresolvableOperation,
6
+ resolveRequestOperation,
7
+ describeUnresolvable
8
+ } from "../signing/operation-resolution.mjs";
9
+ import {
10
+ assertUnsignedOperationNotRequired,
11
+ assertUnsignedProtocolMethodNotRequired,
12
+ assertUnsignedWebhookAuthenticationAbsent,
13
+ unresolvableOperationError,
14
+ verifyRequestSignature
15
+ } from "../signing/verifier.mjs";
5
16
  import { markVerifiedHttpSig } from "./decisioning/buyer-agent.mjs";
6
17
  import {
7
18
  AuthError,
@@ -91,8 +102,18 @@ function principalForVerifiedSigner(signer, makePrincipal) {
91
102
  }
92
103
  function requireSignatureWhenPresent(signatureAuth, fallbackAuth, options = {}) {
93
104
  const requiredFor = new Set(options.requiredFor ?? []);
94
- const resolveOperation = options.resolveOperation;
105
+ const unsignedCapability = {
106
+ required_for: [...requiredFor],
107
+ ...options.protocolMethodsRequiredFor ? { protocol_methods_required_for: [...options.protocolMethodsRequiredFor] } : {}
108
+ };
109
+ const resolveOperation = options.resolveOperation ?? (requiredFor.size > 0 || unsignedCapability.protocol_methods_required_for ? adcpOperationResolver : void 0);
95
110
  const combined = async (req) => {
111
+ const request = req;
112
+ const operation = resolveOperation?.(request);
113
+ if (isUnresolvableOperation(operation)) {
114
+ logUnresolvable(request);
115
+ throw signatureAuthError(unresolvableOperationError());
116
+ }
96
117
  if (hasSignatureHeader(req)) {
97
118
  const result = await signatureAuth(req);
98
119
  if (result === null) {
@@ -100,6 +121,12 @@ function requireSignatureWhenPresent(signatureAuth, fallbackAuth, options = {})
100
121
  }
101
122
  return result;
102
123
  }
124
+ const body = bodyText(request);
125
+ try {
126
+ assertUnsignedWebhookAuthenticationAbsent(body);
127
+ } catch (err) {
128
+ throw asSignatureAuthError(err);
129
+ }
103
130
  let fallbackResult = null;
104
131
  let fallbackError;
105
132
  let fallbackThrew = false;
@@ -110,23 +137,16 @@ function requireSignatureWhenPresent(signatureAuth, fallbackAuth, options = {})
110
137
  fallbackError = err;
111
138
  }
112
139
  if (fallbackResult !== null) return fallbackResult;
113
- if (requiredFor.size > 0 && resolveOperation) {
114
- const operation = resolveOperation(req);
115
- if (operation && requiredFor.has(operation)) {
116
- throw new AuthError(`Signature required for ${operation}.`, {
117
- cause: new RequestSignatureError(
118
- "request_signature_required",
119
- 0,
120
- `Operation ${operation} requires an RFC 9421 request signature when no other credentials are presented.`
121
- )
122
- });
123
- }
140
+ try {
141
+ assertUnsignedOperationNotRequired(operation, unsignedCapability);
142
+ assertUnsignedProtocolMethodNotRequired(body, unsignedCapability);
143
+ } catch (err) {
144
+ throw asSignatureAuthError(err);
124
145
  }
125
146
  if (fallbackThrew) throw fallbackError;
126
147
  return null;
127
148
  };
128
- const anyChildNeedsRawBody = authenticatorNeedsRawBody(signatureAuth) || authenticatorNeedsRawBody(fallbackAuth);
129
- if (anyChildNeedsRawBody || resolveOperation) {
149
+ if (resolveOperation || authenticatorNeedsRawBody(signatureAuth) || authenticatorNeedsRawBody(fallbackAuth)) {
130
150
  tagAuthenticatorNeedsRawBody(combined);
131
151
  }
132
152
  tagAuthenticatorPresenceGated(combined);
@@ -135,9 +155,13 @@ function requireSignatureWhenPresent(signatureAuth, fallbackAuth, options = {})
135
155
  function requireAuthenticatedOrSigned(options) {
136
156
  return requireSignatureWhenPresent(options.signature, options.fallback, {
137
157
  requiredFor: options.requiredFor,
158
+ protocolMethodsRequiredFor: options.protocolMethodsRequiredFor,
138
159
  resolveOperation: options.resolveOperation
139
160
  });
140
161
  }
162
+ function adcpOperationResolver(req) {
163
+ return resolveRequestOperation(operationInput(req));
164
+ }
141
165
  function mcpToolNameResolver(req) {
142
166
  const raw = req.rawBody;
143
167
  if (!raw) return void 0;
@@ -150,6 +174,24 @@ function mcpToolNameResolver(req) {
150
174
  return void 0;
151
175
  }
152
176
  }
177
+ function operationInput(req) {
178
+ const candidate = req;
179
+ return { rawBody: candidate.rawBody, method: candidate.method, url: candidate.url };
180
+ }
181
+ function bodyText(req) {
182
+ const raw = req.rawBody;
183
+ return typeof raw === "string" ? raw : raw?.toString("utf8");
184
+ }
185
+ function signatureAuthError(err) {
186
+ return new AuthError(`Signature rejected (${err.code}).`, { cause: err });
187
+ }
188
+ function asSignatureAuthError(err) {
189
+ return err instanceof RequestSignatureError ? signatureAuthError(err) : err;
190
+ }
191
+ function logUnresolvable(req) {
192
+ const reason = describeUnresolvable(operationInput(req));
193
+ console.error(`[adcp/auth] request body does not resolve to one operation: ${reason ?? "unknown"}`);
194
+ }
153
195
  const SAFE_KEYID = /^[A-Za-z0-9._-]{1,256}$/;
154
196
  function hasSignatureHeader(req) {
155
197
  return nonEmptyHeader(req.headers["signature-input"]) || nonEmptyHeader(req.headers["signature"]);
@@ -182,6 +224,7 @@ function firstHeader(value) {
182
224
  return void 0;
183
225
  }
184
226
  export {
227
+ adcpOperationResolver,
185
228
  mcpToolNameResolver,
186
229
  principalForVerifiedSigner,
187
230
  requireAuthenticatedOrSigned,
@@ -69,6 +69,7 @@ var import_diagnostic_limits = require('../validation/diagnostic-limits.js');
69
69
  var import_idempotency2 = require('./idempotency/index.js');
70
70
  var import_webhook_emitter = require('./webhook-emitter.js');
71
71
  var import_middleware = require('../signing/middleware.js');
72
+ var import_operation_resolution = require('../signing/operation-resolution.js');
72
73
  var import_account_mode = require('./account-mode.js');
73
74
  var import_test_controller_bridge = require('./test-controller-bridge.js');
74
75
  var import_version2 = require('../version.js');
@@ -1431,22 +1432,10 @@ function buildSignedRequestsPreTransport(signedRequests, effectiveRequestSigning
1431
1432
  revocationStore: signedRequests.revocationStore,
1432
1433
  adcpVersion,
1433
1434
  ...signedRequests.agentUrlForKeyid ? { agentUrlForKeyid: signedRequests.agentUrlForKeyid } : {},
1434
- resolveOperation: (req) => {
1435
- const raw = req.rawBody;
1436
- if (!raw) return void 0;
1437
- try {
1438
- const parsed = JSON.parse(raw);
1439
- const messages = Array.isArray(parsed) ? parsed : [parsed];
1440
- const operations = messages.flatMap((message) => {
1441
- if (message == null || typeof message !== "object") return [];
1442
- const candidate = message;
1443
- return candidate.method === "tools/call" && typeof candidate.params?.name === "string" ? [candidate.params.name] : [];
1444
- });
1445
- return operations.find((operation) => requiredFor.includes(operation)) ?? operations[0];
1446
- } catch {
1447
- }
1448
- return void 0;
1449
- }
1435
+ // One resolver for MCP `tools/call` and A2A message methods; a body that
1436
+ // does not resolve to exactly one operation is rejected by the verifier
1437
+ // (`request_body_malformed`), never treated as "not in required_for".
1438
+ resolveOperation: (req) => (0, import_operation_resolution.resolveRequestOperation)({ rawBody: req.rawBody, method: req.method, url: req.url })
1450
1439
  });
1451
1440
  return async function adcpPreTransport(req, res) {
1452
1441
  const serveContext = (0, import_auth.getServeRequestContext)(req);
@@ -104,6 +104,7 @@ import {
104
104
  createWebhookEmitter
105
105
  } from "./webhook-emitter.mjs";
106
106
  import { createExpressVerifier } from "../signing/middleware.mjs";
107
+ import { resolveRequestOperation } from "../signing/operation-resolution.mjs";
107
108
  import { isSandboxOrMockAccount } from "./account-mode.mjs";
108
109
  import {
109
110
  isSandboxRequest as isSandboxRequestForSeeding,
@@ -1503,22 +1504,10 @@ function buildSignedRequestsPreTransport(signedRequests, effectiveRequestSigning
1503
1504
  revocationStore: signedRequests.revocationStore,
1504
1505
  adcpVersion,
1505
1506
  ...signedRequests.agentUrlForKeyid ? { agentUrlForKeyid: signedRequests.agentUrlForKeyid } : {},
1506
- resolveOperation: (req) => {
1507
- const raw = req.rawBody;
1508
- if (!raw) return void 0;
1509
- try {
1510
- const parsed = JSON.parse(raw);
1511
- const messages = Array.isArray(parsed) ? parsed : [parsed];
1512
- const operations = messages.flatMap((message) => {
1513
- if (message == null || typeof message !== "object") return [];
1514
- const candidate = message;
1515
- return candidate.method === "tools/call" && typeof candidate.params?.name === "string" ? [candidate.params.name] : [];
1516
- });
1517
- return operations.find((operation) => requiredFor.includes(operation)) ?? operations[0];
1518
- } catch {
1519
- }
1520
- return void 0;
1521
- }
1507
+ // One resolver for MCP `tools/call` and A2A message methods; a body that
1508
+ // does not resolve to exactly one operation is rejected by the verifier
1509
+ // (`request_body_malformed`), never treated as "not in required_for".
1510
+ resolveOperation: (req) => resolveRequestOperation({ rawBody: req.rawBody, method: req.method, url: req.url })
1522
1511
  });
1523
1512
  return async function adcpPreTransport(req, res) {
1524
1513
  const serveContext = getServeRequestContext(req);
@@ -49,7 +49,7 @@ export { verifyApiKey, verifyBearer, anyOf, extractBearerToken, respondUnauthori
49
49
  export type { Authenticator, AuthPrincipal, AuthResult, VerifyApiKeyOptions, VerifyBearerOptions, RespondUnauthorizedOptions, ServeRequestContext, } from './auth.mjs';
50
50
  export { verifyIntrospection } from './auth-introspection.mjs';
51
51
  export type { VerifyIntrospectionOptions, IntrospectionCacheOptions, IntrospectionResponse, } from './auth-introspection.mjs';
52
- export { verifySignatureAsAuthenticator, requireSignatureWhenPresent, requireAuthenticatedOrSigned, mcpToolNameResolver, } from './auth-signature.mjs';
52
+ export { verifySignatureAsAuthenticator, requireSignatureWhenPresent, requireAuthenticatedOrSigned, mcpToolNameResolver, adcpOperationResolver, } from './auth-signature.mjs';
53
53
  export type { VerifySignatureAsAuthenticatorOptions, RequireSignatureWhenPresentOptions, RequireAuthenticatedOrSignedOptions, } from './auth-signature.mjs';
54
54
  export { InMemoryStateStore, StateError, PatchConflictError, DEFAULT_MAX_DOCUMENT_BYTES, SESSION_KEY_FIELD, createSessionedStore, scopedStore, patchWithRetry, isPutIfMatchConflict, validateCollection, validateId, validatePayloadSize, validateWrite, } from './state-store.mjs';
55
55
  export type { AdcpStateStore, InMemoryStateStoreOptions, ListOptions, ListResult, PatchWithRetryOptions, PutIfMatchResult, StateErrorCode, VersionedDocument, } from './state-store.mjs';
@@ -49,7 +49,7 @@ export { verifyApiKey, verifyBearer, anyOf, extractBearerToken, respondUnauthori
49
49
  export type { Authenticator, AuthPrincipal, AuthResult, VerifyApiKeyOptions, VerifyBearerOptions, RespondUnauthorizedOptions, ServeRequestContext, } from './auth';
50
50
  export { verifyIntrospection } from './auth-introspection';
51
51
  export type { VerifyIntrospectionOptions, IntrospectionCacheOptions, IntrospectionResponse, } from './auth-introspection';
52
- export { verifySignatureAsAuthenticator, requireSignatureWhenPresent, requireAuthenticatedOrSigned, mcpToolNameResolver, } from './auth-signature';
52
+ export { verifySignatureAsAuthenticator, requireSignatureWhenPresent, requireAuthenticatedOrSigned, mcpToolNameResolver, adcpOperationResolver, } from './auth-signature';
53
53
  export type { VerifySignatureAsAuthenticatorOptions, RequireSignatureWhenPresentOptions, RequireAuthenticatedOrSignedOptions, } from './auth-signature';
54
54
  export { InMemoryStateStore, StateError, PatchConflictError, DEFAULT_MAX_DOCUMENT_BYTES, SESSION_KEY_FIELD, createSessionedStore, scopedStore, patchWithRetry, isPutIfMatchConflict, validateCollection, validateId, validatePayloadSize, validateWrite, } from './state-store';
55
55
  export type { AdcpStateStore, InMemoryStateStoreOptions, ListOptions, ListResult, PatchWithRetryOptions, PutIfMatchResult, StateErrorCode, VersionedDocument, } from './state-store';
@@ -87,6 +87,7 @@ __export(server_exports, {
87
87
  WebhookAuthenticationResolutionError: () => import_webhook_delivery.WebhookAuthenticationResolutionError,
88
88
  WebhookDeliveryTerminalError: () => import_webhook_delivery.WebhookDeliveryTerminalError,
89
89
  adcpError: () => import_errors.adcpError,
90
+ adcpOperationResolver: () => import_auth_signature.adcpOperationResolver,
90
91
  anyOf: () => import_auth.anyOf,
91
92
  applyTargetingInput: () => import_targeting_input.applyTargetingInput,
92
93
  assertCreativeTransition: () => import_state_machine.assertCreativeTransition,
@@ -415,6 +416,7 @@ var import_media_buy_action_resolver = require('./media-buy-action-resolver.js')
415
416
  WebhookAuthenticationResolutionError,
416
417
  WebhookDeliveryTerminalError,
417
418
  adcpError,
419
+ adcpOperationResolver,
418
420
  anyOf,
419
421
  applyTargetingInput,
420
422
  assertCreativeTransition,
@@ -119,7 +119,8 @@ import {
119
119
  verifySignatureAsAuthenticator,
120
120
  requireSignatureWhenPresent,
121
121
  requireAuthenticatedOrSigned,
122
- mcpToolNameResolver
122
+ mcpToolNameResolver,
123
+ adcpOperationResolver
123
124
  } from "./auth-signature.mjs";
124
125
  import {
125
126
  InMemoryStateStore,
@@ -363,6 +364,7 @@ export {
363
364
  WebhookAuthenticationResolutionError,
364
365
  WebhookDeliveryTerminalError,
365
366
  adcpError,
367
+ adcpOperationResolver,
366
368
  anyOf,
367
369
  applyTargetingInput,
368
370
  assertCreativeTransition,
@@ -35,6 +35,7 @@ var import_capability_cache = require('./capability-cache.js');
35
35
  var import_webhook_auth_detection = require('./webhook-auth-detection.js');
36
36
  var import_version = require('../version.js');
37
37
  var import_content_digest = require('./content-digest.js');
38
+ var import_operation_resolution = require('./operation-resolution.js');
38
39
  var import_content_digest2 = require('./content-digest.js');
39
40
  const MAX_WEBHOOK_AUTH_INSPECTION_BYTES = 1048576;
40
41
  function defaultUpstream() {
@@ -62,42 +63,11 @@ function describeBody(body) {
62
63
  }
63
64
  return typeof body;
64
65
  }
65
- const A2A_SEND_METHODS = /* @__PURE__ */ new Set([
66
- "message/send",
67
- "message/stream",
68
- "SendMessage",
69
- "SendStreamingMessage"
70
- ]);
71
66
  function extractAdcpOperation(body) {
72
67
  const text = bodyToUtf8(body);
73
68
  if (!text) return void 0;
74
- let parsed;
75
- try {
76
- parsed = JSON.parse(text);
77
- } catch {
78
- return void 0;
79
- }
80
- if (!parsed || typeof parsed !== "object") return void 0;
81
- const rpc = parsed;
82
- if (rpc.method === "tools/call") {
83
- const params = rpc.params;
84
- return typeof params?.name === "string" ? params.name : void 0;
85
- }
86
- if (typeof rpc.method === "string" && A2A_SEND_METHODS.has(rpc.method)) {
87
- const params = rpc.params;
88
- const parts = params?.message?.parts;
89
- if (!Array.isArray(parts)) return void 0;
90
- for (const part of parts) {
91
- if (part && typeof part === "object") {
92
- const p = part;
93
- const isDataPart = p.kind === void 0 || p.kind === "data";
94
- if (isDataPart && typeof p.data?.skill === "string") {
95
- return p.data.skill;
96
- }
97
- }
98
- }
99
- }
100
- return void 0;
69
+ const operation = (0, import_operation_resolution.resolveRequestOperation)({ rawBody: text, method: "POST" });
70
+ return typeof operation === "string" ? operation : void 0;
101
71
  }
102
72
  function carriesWebhookAuthentication(body) {
103
73
  const text = bodyToUtf8(body);
@@ -7,6 +7,7 @@ import {
7
7
  import { containsWebhookAuthentication } from "./webhook-auth-detection.mjs";
8
8
  import { ADCP_VERSION } from "../version.mjs";
9
9
  import { requestSigningEncodingForVersion } from "./content-digest.mjs";
10
+ import { resolveRequestOperation } from "./operation-resolution.mjs";
10
11
  import { requestSigningEncodingForVersion as requestSigningEncodingForVersion2 } from "./content-digest.mjs";
11
12
  const MAX_WEBHOOK_AUTH_INSPECTION_BYTES = 1048576;
12
13
  function defaultUpstream() {
@@ -34,42 +35,11 @@ function describeBody(body) {
34
35
  }
35
36
  return typeof body;
36
37
  }
37
- const A2A_SEND_METHODS = /* @__PURE__ */ new Set([
38
- "message/send",
39
- "message/stream",
40
- "SendMessage",
41
- "SendStreamingMessage"
42
- ]);
43
38
  function extractAdcpOperation(body) {
44
39
  const text = bodyToUtf8(body);
45
40
  if (!text) return void 0;
46
- let parsed;
47
- try {
48
- parsed = JSON.parse(text);
49
- } catch {
50
- return void 0;
51
- }
52
- if (!parsed || typeof parsed !== "object") return void 0;
53
- const rpc = parsed;
54
- if (rpc.method === "tools/call") {
55
- const params = rpc.params;
56
- return typeof params?.name === "string" ? params.name : void 0;
57
- }
58
- if (typeof rpc.method === "string" && A2A_SEND_METHODS.has(rpc.method)) {
59
- const params = rpc.params;
60
- const parts = params?.message?.parts;
61
- if (!Array.isArray(parts)) return void 0;
62
- for (const part of parts) {
63
- if (part && typeof part === "object") {
64
- const p = part;
65
- const isDataPart = p.kind === void 0 || p.kind === "data";
66
- if (isDataPart && typeof p.data?.skill === "string") {
67
- return p.data.skill;
68
- }
69
- }
70
- }
71
- }
72
- return void 0;
41
+ const operation = resolveRequestOperation({ rawBody: text, method: "POST" });
42
+ return typeof operation === "string" ? operation : void 0;
73
43
  }
74
44
  function carriesWebhookAuthentication(body) {
75
45
  const text = bodyToUtf8(body);
@@ -1,4 +1,5 @@
1
1
  import { type VerifyRequestOptions } from './verifier.mjs';
2
+ import type { ResolvedOperation } from './operation-resolution.mjs';
2
3
  import type { VerifiedSigner } from './types.mjs';
3
4
  declare module 'http' {
4
5
  interface IncomingMessage {
@@ -56,6 +57,13 @@ export interface ExpressMiddlewareOptions extends Omit<VerifyRequestOptions, 'op
56
57
  * the verifier will then treat the request as "not in any required_for"
57
58
  * and accept unsigned traffic rather than rejecting.
58
59
  *
60
+ * Return {@link UNRESOLVABLE_OPERATION} for a body that does not resolve to
61
+ * exactly one operation; the verifier then rejects it with
62
+ * `request_body_malformed`, signed or not. `resolveRequestOperation` (and
63
+ * `adcpOperationResolver` from `@adcp/sdk/server`) implements the MCP and A2A
64
+ * rules, including that fail-closed result, and is what `createAdcpServer`
65
+ * auto-wires.
66
+ *
59
67
  * SECURITY: a `resolveOperation` that always returns `undefined` — for
60
68
  * example, a routing helper that silently fails to match — disables
61
69
  * `required_for` enforcement globally. Unsigned requests on signed-only
@@ -64,7 +72,7 @@ export interface ExpressMiddlewareOptions extends Omit<VerifyRequestOptions, 'op
64
72
  * asserting `resolveOperation(req)` is non-undefined for sample signed
65
73
  * requests is the simplest guard.
66
74
  */
67
- resolveOperation: (req: ExpressLike) => string | undefined;
75
+ resolveOperation: (req: ExpressLike) => ResolvedOperation;
68
76
  /**
69
77
  * Override how the request's full URL is reconstructed. Use when the server
70
78
  * sits behind a TLS-terminating or path-rewriting load balancer, since
@@ -1,4 +1,5 @@
1
1
  import { type VerifyRequestOptions } from './verifier';
2
+ import type { ResolvedOperation } from './operation-resolution';
2
3
  import type { VerifiedSigner } from './types';
3
4
  declare module 'http' {
4
5
  interface IncomingMessage {
@@ -56,6 +57,13 @@ export interface ExpressMiddlewareOptions extends Omit<VerifyRequestOptions, 'op
56
57
  * the verifier will then treat the request as "not in any required_for"
57
58
  * and accept unsigned traffic rather than rejecting.
58
59
  *
60
+ * Return {@link UNRESOLVABLE_OPERATION} for a body that does not resolve to
61
+ * exactly one operation; the verifier then rejects it with
62
+ * `request_body_malformed`, signed or not. `resolveRequestOperation` (and
63
+ * `adcpOperationResolver` from `@adcp/sdk/server`) implements the MCP and A2A
64
+ * rules, including that fail-closed result, and is what `createAdcpServer`
65
+ * auto-wires.
66
+ *
59
67
  * SECURITY: a `resolveOperation` that always returns `undefined` — for
60
68
  * example, a routing helper that silently fails to match — disables
61
69
  * `required_for` enforcement globally. Unsigned requests on signed-only
@@ -64,7 +72,7 @@ export interface ExpressMiddlewareOptions extends Omit<VerifyRequestOptions, 'op
64
72
  * asserting `resolveOperation(req)` is non-undefined for sample signed
65
73
  * requests is the simplest guard.
66
74
  */
67
- resolveOperation: (req: ExpressLike) => string | undefined;
75
+ resolveOperation: (req: ExpressLike) => ResolvedOperation;
68
76
  /**
69
77
  * Override how the request's full URL is reconstructed. Use when the server
70
78
  * sits behind a TLS-terminating or path-rewriting load balancer, since
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Distinguished "this body does not resolve to exactly one operation" result.
3
+ * A `Symbol.for` key so it survives duplicate copies of the SDK in one process.
4
+ */
5
+ export declare const UNRESOLVABLE_OPERATION: unique symbol;
6
+ export type UnresolvableOperation = typeof UNRESOLVABLE_OPERATION;
7
+ /** What a `resolveOperation` callback may return. */
8
+ export type ResolvedOperation = string | undefined | UnresolvableOperation;
9
+ export declare function isUnresolvableOperation(value: unknown): value is UnresolvableOperation;
10
+ /** JSON-RPC methods that carry an A2A Message and therefore an invocation DataPart. */
11
+ export declare const A2A_MESSAGE_METHODS: ReadonlySet<string>;
12
+ export interface OperationResolutionInput {
13
+ /** The exact request body bytes, decoded as UTF-8. */
14
+ rawBody?: string | Buffer;
15
+ /** HTTP method. When present and not `POST`, the request carries no operation. */
16
+ method?: string;
17
+ /** Request URL (path may be relative). Selects the A2A HTTP+JSON binding. */
18
+ url?: string;
19
+ }
20
+ export type OperationResolution = {
21
+ kind: 'operation';
22
+ operation: string;
23
+ } | {
24
+ kind: 'none';
25
+ } | {
26
+ kind: 'unresolvable';
27
+ reason: string;
28
+ };
29
+ /**
30
+ * Resolve the operation a request carries, with a diagnostic reason when it
31
+ * cannot be resolved. Pure and synchronous; never throws.
32
+ */
33
+ export declare function resolveRequestOperationDetailed(input: OperationResolutionInput): OperationResolution;
34
+ /** {@link resolveRequestOperationDetailed} collapsed to the `resolveOperation` return shape. */
35
+ export declare function resolveRequestOperation(input: OperationResolutionInput): ResolvedOperation;
36
+ /** Diagnostic reason for an unresolvable body. Server-side logs only. */
37
+ export declare function describeUnresolvable(input: OperationResolutionInput): string | undefined;
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Distinguished "this body does not resolve to exactly one operation" result.
3
+ * A `Symbol.for` key so it survives duplicate copies of the SDK in one process.
4
+ */
5
+ export declare const UNRESOLVABLE_OPERATION: unique symbol;
6
+ export type UnresolvableOperation = typeof UNRESOLVABLE_OPERATION;
7
+ /** What a `resolveOperation` callback may return. */
8
+ export type ResolvedOperation = string | undefined | UnresolvableOperation;
9
+ export declare function isUnresolvableOperation(value: unknown): value is UnresolvableOperation;
10
+ /** JSON-RPC methods that carry an A2A Message and therefore an invocation DataPart. */
11
+ export declare const A2A_MESSAGE_METHODS: ReadonlySet<string>;
12
+ export interface OperationResolutionInput {
13
+ /** The exact request body bytes, decoded as UTF-8. */
14
+ rawBody?: string | Buffer;
15
+ /** HTTP method. When present and not `POST`, the request carries no operation. */
16
+ method?: string;
17
+ /** Request URL (path may be relative). Selects the A2A HTTP+JSON binding. */
18
+ url?: string;
19
+ }
20
+ export type OperationResolution = {
21
+ kind: 'operation';
22
+ operation: string;
23
+ } | {
24
+ kind: 'none';
25
+ } | {
26
+ kind: 'unresolvable';
27
+ reason: string;
28
+ };
29
+ /**
30
+ * Resolve the operation a request carries, with a diagnostic reason when it
31
+ * cannot be resolved. Pure and synchronous; never throws.
32
+ */
33
+ export declare function resolveRequestOperationDetailed(input: OperationResolutionInput): OperationResolution;
34
+ /** {@link resolveRequestOperationDetailed} collapsed to the `resolveOperation` return shape. */
35
+ export declare function resolveRequestOperation(input: OperationResolutionInput): ResolvedOperation;
36
+ /** Diagnostic reason for an unresolvable body. Server-side logs only. */
37
+ export declare function describeUnresolvable(input: OperationResolutionInput): string | undefined;
@@ -0,0 +1,154 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var operation_resolution_exports = {};
20
+ __export(operation_resolution_exports, {
21
+ A2A_MESSAGE_METHODS: () => A2A_MESSAGE_METHODS,
22
+ UNRESOLVABLE_OPERATION: () => UNRESOLVABLE_OPERATION,
23
+ describeUnresolvable: () => describeUnresolvable,
24
+ isUnresolvableOperation: () => isUnresolvableOperation,
25
+ resolveRequestOperation: () => resolveRequestOperation,
26
+ resolveRequestOperationDetailed: () => resolveRequestOperationDetailed
27
+ });
28
+ module.exports = __toCommonJS(operation_resolution_exports);
29
+ var import_strict_json = require('./agent-resolver/strict-json.js');
30
+ const UNRESOLVABLE_OPERATION = /* @__PURE__ */ Symbol.for("@adcp/client.signing.unresolvableOperation");
31
+ function isUnresolvableOperation(value) {
32
+ return value === UNRESOLVABLE_OPERATION;
33
+ }
34
+ const A2A_MESSAGE_METHODS = /* @__PURE__ */ new Set([
35
+ "SendMessage",
36
+ "SendStreamingMessage",
37
+ "message/send",
38
+ "message/stream"
39
+ ]);
40
+ const CONTENT_MEMBERS = ["text", "data", "raw", "url", "file"];
41
+ const FILE_MEMBERS = /* @__PURE__ */ new Set(["raw", "url", "file"]);
42
+ const HTTP_JSON_MESSAGE_PATH = /(?:^|\/)message:(?:send|stream)$/;
43
+ const ENVELOPE_MEMBERS = ["method", "params"];
44
+ const TOOLS_CALL_PARAMS_MEMBERS = ["name"];
45
+ const CARRIER_MEMBERS = ["message"];
46
+ const MESSAGE_MEMBERS = ["parts", "taskId"];
47
+ const PART_MEMBERS = [...CONTENT_MEMBERS, "kind"];
48
+ const DATA_MEMBERS = ["skill"];
49
+ function resolveRequestOperationDetailed(input) {
50
+ if (input.method !== void 0 && input.method.toUpperCase() !== "POST") return { kind: "none" };
51
+ const raw = typeof input.rawBody === "string" ? input.rawBody : input.rawBody?.toString("utf8");
52
+ if (raw === void 0 || raw.trim() === "") return unresolvable("request body is empty");
53
+ if (raw.charCodeAt(0) === 65279) return unresolvable("body starts with a byte order mark");
54
+ let body;
55
+ try {
56
+ body = (0, import_strict_json.parseStrictJson)(raw);
57
+ } catch (err) {
58
+ return unresolvable(err instanceof import_strict_json.StrictJsonError ? `body is not strict JSON (${err.code})` : "body is not JSON");
59
+ }
60
+ if (!isPlainObject(body)) {
61
+ return unresolvable(Array.isArray(body) ? "JSON-RPC batches are not supported" : "body is not a JSON object");
62
+ }
63
+ if (isHttpJsonMessagePath(input.url)) {
64
+ return resolveMessageCarrier(body);
65
+ }
66
+ if (hasCaseVariantMember(body, ENVELOPE_MEMBERS)) return unresolvable("case variant of an envelope member");
67
+ const method = body.method;
68
+ if (method === void 0 && isJsonRpcResponse(body)) {
69
+ return { kind: "none" };
70
+ }
71
+ if (typeof method !== "string") return unresolvable("JSON-RPC request has no string method");
72
+ if (method === "tools/call") {
73
+ const params = body.params;
74
+ if (!isPlainObject(params)) return unresolvable("tools/call params is not an object");
75
+ if (hasCaseVariantMember(params, TOOLS_CALL_PARAMS_MEMBERS)) return unresolvable("case variant of a params member");
76
+ const name = params.name;
77
+ if (typeof name !== "string" || name.length === 0) return unresolvable("tools/call params.name is not a string");
78
+ return { kind: "operation", operation: name };
79
+ }
80
+ if (A2A_MESSAGE_METHODS.has(method)) {
81
+ const params = body.params;
82
+ if (!isPlainObject(params)) return unresolvable("message method params is not an object");
83
+ return resolveMessageCarrier(params);
84
+ }
85
+ return { kind: "none" };
86
+ }
87
+ function resolveRequestOperation(input) {
88
+ const resolution = resolveRequestOperationDetailed(input);
89
+ if (resolution.kind === "operation") return resolution.operation;
90
+ return resolution.kind === "none" ? void 0 : UNRESOLVABLE_OPERATION;
91
+ }
92
+ function describeUnresolvable(input) {
93
+ const resolution = resolveRequestOperationDetailed(input);
94
+ return resolution.kind === "unresolvable" ? resolution.reason : void 0;
95
+ }
96
+ function resolveMessageCarrier(container) {
97
+ if (hasCaseVariantMember(container, CARRIER_MEMBERS)) return unresolvable("case variant of the message member");
98
+ const message = container.message;
99
+ if (!isPlainObject(message)) return unresolvable("request carries no message object");
100
+ if (hasCaseVariantMember(message, MESSAGE_MEMBERS)) return unresolvable("case variant of a message member");
101
+ const parts = message.parts;
102
+ if (!Array.isArray(parts)) return unresolvable("message.parts is not an array");
103
+ let invocation;
104
+ for (const part of parts) {
105
+ if (!isPlainObject(part)) return unresolvable("a Part is not an object");
106
+ if (hasCaseVariantMember(part, PART_MEMBERS)) return unresolvable("case variant of a Part member");
107
+ const present = CONTENT_MEMBERS.filter((member2) => Object.prototype.hasOwnProperty.call(part, member2));
108
+ if (present.length !== 1) return unresolvable("a Part must carry exactly one content member");
109
+ const member = present[0];
110
+ if (FILE_MEMBERS.has(member)) return unresolvable("FileParts are not allowed on an AdCP invocation");
111
+ const kind = part.kind;
112
+ if (kind !== void 0 && kind !== member) return unresolvable("a Part kind disagrees with its content member");
113
+ if (member === "data") {
114
+ if (invocation !== void 0) return unresolvable("message carries more than one DataPart");
115
+ if (!isPlainObject(part.data)) return unresolvable("DataPart data is not an object");
116
+ invocation = part.data;
117
+ }
118
+ }
119
+ if (invocation === void 0) return unresolvable("message carries no DataPart");
120
+ if (hasCaseVariantMember(invocation, DATA_MEMBERS)) return unresolvable("case variant of skill");
121
+ const skill = invocation.skill;
122
+ if (typeof skill !== "string" || skill.length === 0) return unresolvable("DataPart skill is not a non-empty string");
123
+ return { kind: "operation", operation: skill };
124
+ }
125
+ function isJsonRpcResponse(body) {
126
+ const has = (key) => Object.prototype.hasOwnProperty.call(body, key);
127
+ return (has("result") || has("error")) && !has("params");
128
+ }
129
+ function unresolvable(reason) {
130
+ return { kind: "unresolvable", reason };
131
+ }
132
+ function isHttpJsonMessagePath(url) {
133
+ if (!url) return false;
134
+ const queryStart = url.search(/[?#]/);
135
+ return HTTP_JSON_MESSAGE_PATH.test(queryStart === -1 ? url : url.slice(0, queryStart));
136
+ }
137
+ function isPlainObject(value) {
138
+ return value !== null && typeof value === "object" && !Array.isArray(value);
139
+ }
140
+ function hasCaseVariantMember(obj, recognized) {
141
+ return Object.keys(obj).some((key) => {
142
+ const lowered = key.toLowerCase();
143
+ return recognized.some((name) => name.toLowerCase() === lowered && name !== key);
144
+ });
145
+ }
146
+ // Annotate the CommonJS export names for ESM import in node:
147
+ 0 && (module.exports = {
148
+ A2A_MESSAGE_METHODS,
149
+ UNRESOLVABLE_OPERATION,
150
+ describeUnresolvable,
151
+ isUnresolvableOperation,
152
+ resolveRequestOperation,
153
+ resolveRequestOperationDetailed
154
+ });