@adcp/sdk 14.0.0-beta.11 → 14.0.0-beta.13

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 (235) hide show
  1. package/bin/adcp-storyboard-sandbox.js +16 -0
  2. package/bin/adcp.js +28 -30
  3. package/dist/lib/index.d.mts +3 -3
  4. package/dist/lib/index.d.ts +3 -3
  5. package/dist/lib/index.d.ts.map +1 -1
  6. package/dist/lib/index.js +2 -0
  7. package/dist/lib/index.js.map +1 -1
  8. package/dist/lib/index.mjs +3 -1
  9. package/dist/lib/index.mjs.map +1 -1
  10. package/dist/lib/schemas/index.d.mts +385 -1
  11. package/dist/lib/schemas/index.d.ts +385 -1
  12. package/dist/lib/schemas/index.d.ts.map +1 -1
  13. package/dist/lib/schemas/index.js +13 -0
  14. package/dist/lib/schemas/index.js.map +1 -1
  15. package/dist/lib/schemas/index.mjs +11 -0
  16. package/dist/lib/schemas/index.mjs.map +1 -1
  17. package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
  18. package/dist/lib/server/create-adcp-server.d.mts +2 -2
  19. package/dist/lib/server/create-adcp-server.d.ts +2 -2
  20. package/dist/lib/server/create-adcp-server.d.ts.map +1 -1
  21. package/dist/lib/server/create-adcp-server.js +6 -1
  22. package/dist/lib/server/create-adcp-server.js.map +1 -1
  23. package/dist/lib/server/create-adcp-server.mjs +6 -1
  24. package/dist/lib/server/create-adcp-server.mjs.map +1 -1
  25. package/dist/lib/server/decisioning/index.d.mts +3 -3
  26. package/dist/lib/server/decisioning/index.d.ts +3 -3
  27. package/dist/lib/server/decisioning/index.d.ts.map +1 -1
  28. package/dist/lib/server/decisioning/index.js.map +1 -1
  29. package/dist/lib/server/decisioning/index.mjs.map +1 -1
  30. package/dist/lib/server/decisioning/runtime/from-platform.d.mts +24 -17
  31. package/dist/lib/server/decisioning/runtime/from-platform.d.ts +24 -17
  32. package/dist/lib/server/decisioning/runtime/from-platform.d.ts.map +1 -1
  33. package/dist/lib/server/decisioning/runtime/from-platform.js +71 -42
  34. package/dist/lib/server/decisioning/runtime/from-platform.js.map +1 -1
  35. package/dist/lib/server/decisioning/runtime/from-platform.mjs +75 -43
  36. package/dist/lib/server/decisioning/runtime/from-platform.mjs.map +1 -1
  37. package/dist/lib/server/decisioning/runtime/postgres-task-registry.d.mts +15 -21
  38. package/dist/lib/server/decisioning/runtime/postgres-task-registry.d.ts +15 -21
  39. package/dist/lib/server/decisioning/runtime/postgres-task-registry.d.ts.map +1 -1
  40. package/dist/lib/server/decisioning/runtime/postgres-task-registry.js +100 -30
  41. package/dist/lib/server/decisioning/runtime/postgres-task-registry.js.map +1 -1
  42. package/dist/lib/server/decisioning/runtime/postgres-task-registry.mjs +100 -30
  43. package/dist/lib/server/decisioning/runtime/postgres-task-registry.mjs.map +1 -1
  44. package/dist/lib/server/decisioning/runtime/task-registry.d.mts +30 -16
  45. package/dist/lib/server/decisioning/runtime/task-registry.d.ts +30 -16
  46. package/dist/lib/server/decisioning/runtime/task-registry.d.ts.map +1 -1
  47. package/dist/lib/server/decisioning/runtime/task-registry.js +40 -20
  48. package/dist/lib/server/decisioning/runtime/task-registry.js.map +1 -1
  49. package/dist/lib/server/decisioning/runtime/task-registry.mjs +38 -19
  50. package/dist/lib/server/decisioning/runtime/task-registry.mjs.map +1 -1
  51. package/dist/lib/server/decisioning/runtime/to-context.d.mts +2 -2
  52. package/dist/lib/server/decisioning/runtime/to-context.d.ts +2 -2
  53. package/dist/lib/server/decisioning/runtime/to-context.d.ts.map +1 -1
  54. package/dist/lib/server/decisioning/runtime/to-context.js +2 -2
  55. package/dist/lib/server/decisioning/runtime/to-context.js.map +1 -1
  56. package/dist/lib/server/decisioning/runtime/to-context.mjs +2 -2
  57. package/dist/lib/server/decisioning/runtime/to-context.mjs.map +1 -1
  58. package/dist/lib/server/decisioning/specialisms/creative-ad-server.d.mts +3 -2
  59. package/dist/lib/server/decisioning/specialisms/creative-ad-server.d.ts +3 -2
  60. package/dist/lib/server/decisioning/specialisms/creative-ad-server.d.ts.map +1 -1
  61. package/dist/lib/server/decisioning/specialisms/creative-ad-server.js.map +1 -1
  62. package/dist/lib/server/decisioning/specialisms/creative.d.mts +10 -10
  63. package/dist/lib/server/decisioning/specialisms/creative.d.ts +10 -10
  64. package/dist/lib/server/decisioning/specialisms/creative.d.ts.map +1 -1
  65. package/dist/lib/server/decisioning/specialisms/creative.js.map +1 -1
  66. package/dist/lib/server/index.d.mts +1 -1
  67. package/dist/lib/server/index.d.ts +1 -1
  68. package/dist/lib/server/index.d.ts.map +1 -1
  69. package/dist/lib/server/index.js.map +1 -1
  70. package/dist/lib/server/index.mjs.map +1 -1
  71. package/dist/lib/signing/agent-context.d.mts +1 -0
  72. package/dist/lib/signing/agent-context.d.ts +1 -0
  73. package/dist/lib/signing/agent-context.d.ts.map +1 -1
  74. package/dist/lib/signing/agent-context.js +37 -2
  75. package/dist/lib/signing/agent-context.js.map +1 -1
  76. package/dist/lib/signing/agent-context.mjs +38 -3
  77. package/dist/lib/signing/agent-context.mjs.map +1 -1
  78. package/dist/lib/signing/agent-fetch.d.mts +1 -2
  79. package/dist/lib/signing/agent-fetch.d.ts +1 -2
  80. package/dist/lib/signing/agent-fetch.d.ts.map +1 -1
  81. package/dist/lib/signing/agent-fetch.js +2 -0
  82. package/dist/lib/signing/agent-fetch.js.map +1 -1
  83. package/dist/lib/signing/agent-fetch.mjs +2 -0
  84. package/dist/lib/signing/agent-fetch.mjs.map +1 -1
  85. package/dist/lib/signing/agent-resolver/etld.js.map +1 -1
  86. package/dist/lib/signing/agent-resolver/etld.mjs.map +1 -1
  87. package/dist/lib/testing/client.d.mts +6 -0
  88. package/dist/lib/testing/client.d.ts +6 -0
  89. package/dist/lib/testing/client.d.ts.map +1 -1
  90. package/dist/lib/testing/client.js +76 -4
  91. package/dist/lib/testing/client.js.map +1 -1
  92. package/dist/lib/testing/client.mjs +75 -4
  93. package/dist/lib/testing/client.mjs.map +1 -1
  94. package/dist/lib/testing/compliance/comply.d.ts.map +1 -1
  95. package/dist/lib/testing/compliance/comply.js +6 -0
  96. package/dist/lib/testing/compliance/comply.js.map +1 -1
  97. package/dist/lib/testing/compliance/comply.mjs +7 -1
  98. package/dist/lib/testing/compliance/comply.mjs.map +1 -1
  99. package/dist/lib/testing/storyboard/default-invariants.d.ts.map +1 -1
  100. package/dist/lib/testing/storyboard/default-invariants.js +11 -2
  101. package/dist/lib/testing/storyboard/default-invariants.js.map +1 -1
  102. package/dist/lib/testing/storyboard/default-invariants.mjs +11 -2
  103. package/dist/lib/testing/storyboard/default-invariants.mjs.map +1 -1
  104. package/dist/lib/testing/storyboard/index.d.mts +1 -1
  105. package/dist/lib/testing/storyboard/index.d.ts +1 -1
  106. package/dist/lib/testing/storyboard/index.d.ts.map +1 -1
  107. package/dist/lib/testing/storyboard/index.js.map +1 -1
  108. package/dist/lib/testing/storyboard/index.mjs.map +1 -1
  109. package/dist/lib/testing/storyboard/loader.d.ts.map +1 -1
  110. package/dist/lib/testing/storyboard/loader.js +14 -0
  111. package/dist/lib/testing/storyboard/loader.js.map +1 -1
  112. package/dist/lib/testing/storyboard/loader.mjs +14 -0
  113. package/dist/lib/testing/storyboard/loader.mjs.map +1 -1
  114. package/dist/lib/testing/storyboard/phase-condition.d.mts +41 -0
  115. package/dist/lib/testing/storyboard/phase-condition.d.ts +42 -0
  116. package/dist/lib/testing/storyboard/phase-condition.d.ts.map +1 -0
  117. package/dist/lib/testing/storyboard/phase-condition.js +215 -0
  118. package/dist/lib/testing/storyboard/phase-condition.js.map +1 -0
  119. package/dist/lib/testing/storyboard/phase-condition.mjs +189 -0
  120. package/dist/lib/testing/storyboard/phase-condition.mjs.map +1 -0
  121. package/dist/lib/testing/storyboard/request-builder.d.ts.map +1 -1
  122. package/dist/lib/testing/storyboard/request-builder.js +15 -2
  123. package/dist/lib/testing/storyboard/request-builder.js.map +1 -1
  124. package/dist/lib/testing/storyboard/request-builder.mjs +15 -2
  125. package/dist/lib/testing/storyboard/request-builder.mjs.map +1 -1
  126. package/dist/lib/testing/storyboard/request-signing/functional-dispatch.d.mts +15 -0
  127. package/dist/lib/testing/storyboard/request-signing/functional-dispatch.d.ts +16 -0
  128. package/dist/lib/testing/storyboard/request-signing/functional-dispatch.d.ts.map +1 -0
  129. package/dist/lib/testing/storyboard/request-signing/functional-dispatch.js +106 -0
  130. package/dist/lib/testing/storyboard/request-signing/functional-dispatch.js.map +1 -0
  131. package/dist/lib/testing/storyboard/request-signing/functional-dispatch.mjs +82 -0
  132. package/dist/lib/testing/storyboard/request-signing/functional-dispatch.mjs.map +1 -0
  133. package/dist/lib/testing/storyboard/request-signing/test-kit.d.mts +15 -0
  134. package/dist/lib/testing/storyboard/request-signing/test-kit.d.ts +15 -0
  135. package/dist/lib/testing/storyboard/request-signing/test-kit.d.ts.map +1 -1
  136. package/dist/lib/testing/storyboard/request-signing/test-kit.js +58 -0
  137. package/dist/lib/testing/storyboard/request-signing/test-kit.js.map +1 -1
  138. package/dist/lib/testing/storyboard/request-signing/test-kit.mjs +58 -0
  139. package/dist/lib/testing/storyboard/request-signing/test-kit.mjs.map +1 -1
  140. package/dist/lib/testing/storyboard/request-signing/vector-loader.d.mts +2 -0
  141. package/dist/lib/testing/storyboard/request-signing/vector-loader.d.ts +2 -0
  142. package/dist/lib/testing/storyboard/request-signing/vector-loader.d.ts.map +1 -1
  143. package/dist/lib/testing/storyboard/request-signing/vector-loader.js +7 -1
  144. package/dist/lib/testing/storyboard/request-signing/vector-loader.js.map +1 -1
  145. package/dist/lib/testing/storyboard/request-signing/vector-loader.mjs +6 -1
  146. package/dist/lib/testing/storyboard/request-signing/vector-loader.mjs.map +1 -1
  147. package/dist/lib/testing/storyboard/runner.d.ts.map +1 -1
  148. package/dist/lib/testing/storyboard/runner.js +67 -24
  149. package/dist/lib/testing/storyboard/runner.js.map +1 -1
  150. package/dist/lib/testing/storyboard/runner.mjs +67 -24
  151. package/dist/lib/testing/storyboard/runner.mjs.map +1 -1
  152. package/dist/lib/testing/storyboard/task-map.d.ts.map +1 -1
  153. package/dist/lib/testing/storyboard/task-map.js +6 -2
  154. package/dist/lib/testing/storyboard/task-map.js.map +1 -1
  155. package/dist/lib/testing/storyboard/task-map.mjs +6 -2
  156. package/dist/lib/testing/storyboard/task-map.mjs.map +1 -1
  157. package/dist/lib/testing/storyboard/types.d.mts +5 -4
  158. package/dist/lib/testing/storyboard/types.d.ts +5 -4
  159. package/dist/lib/testing/storyboard/types.d.ts.map +1 -1
  160. package/dist/lib/testing/storyboard/types.js.map +1 -1
  161. package/dist/lib/testing/storyboard/types.mjs.map +1 -1
  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/testing/storyboard/webhook-assertions.d.ts.map +1 -1
  165. package/dist/lib/testing/storyboard/webhook-assertions.js +60 -4
  166. package/dist/lib/testing/storyboard/webhook-assertions.js.map +1 -1
  167. package/dist/lib/testing/storyboard/webhook-assertions.mjs +60 -4
  168. package/dist/lib/testing/storyboard/webhook-assertions.mjs.map +1 -1
  169. package/dist/lib/testing/storyboard/webhook-receiver.d.mts +15 -0
  170. package/dist/lib/testing/storyboard/webhook-receiver.d.ts +15 -0
  171. package/dist/lib/testing/storyboard/webhook-receiver.d.ts.map +1 -1
  172. package/dist/lib/testing/storyboard/webhook-receiver.js +57 -6
  173. package/dist/lib/testing/storyboard/webhook-receiver.js.map +1 -1
  174. package/dist/lib/testing/storyboard/webhook-receiver.mjs +57 -6
  175. package/dist/lib/testing/storyboard/webhook-receiver.mjs.map +1 -1
  176. package/dist/lib/testing/types.d.mts +12 -0
  177. package/dist/lib/testing/types.d.ts +12 -0
  178. package/dist/lib/testing/types.d.ts.map +1 -1
  179. package/dist/lib/testing/types.js.map +1 -1
  180. package/dist/lib/types/comply-test-controller.d.ts +1 -14
  181. package/dist/lib/types/core.generated.d.mts +6 -3
  182. package/dist/lib/types/core.generated.d.ts +6 -3
  183. package/dist/lib/types/core.generated.d.ts.map +1 -1
  184. package/dist/lib/types/core.generated.js.map +1 -1
  185. package/dist/lib/types/get-products.d.ts +1 -14
  186. package/dist/lib/types/index.d.mts +1 -1
  187. package/dist/lib/types/index.d.ts +1 -1
  188. package/dist/lib/types/index.d.ts.map +1 -1
  189. package/dist/lib/types/index.js.map +1 -1
  190. package/dist/lib/types/index.mjs.map +1 -1
  191. package/dist/lib/types/list-transformers.d.ts +9 -5
  192. package/dist/lib/types/schemas.generated.d.ts +23 -352
  193. package/dist/lib/types/schemas.generated.d.ts.map +1 -1
  194. package/dist/lib/types/schemas.generated.js +7 -4
  195. package/dist/lib/types/schemas.generated.js.map +1 -1
  196. package/dist/lib/types/schemas.generated.mjs +6 -4
  197. package/dist/lib/types/schemas.generated.mjs.map +1 -1
  198. package/dist/lib/types/server-payload-aliases.d.mts +2 -1
  199. package/dist/lib/types/server-payload-aliases.d.ts +2 -1
  200. package/dist/lib/types/server-payload-aliases.d.ts.map +1 -1
  201. package/dist/lib/types/server-payload-aliases.js.map +1 -1
  202. package/dist/lib/types/tools.generated.d.mts +6 -3
  203. package/dist/lib/types/tools.generated.d.ts +6 -3
  204. package/dist/lib/types/tools.generated.d.ts.map +1 -1
  205. package/dist/lib/types/tools.generated.js.map +1 -1
  206. package/dist/lib/utils/build-creative-return-builders.d.mts +5 -1
  207. package/dist/lib/utils/build-creative-return-builders.d.ts +5 -1
  208. package/dist/lib/utils/build-creative-return-builders.d.ts.map +1 -1
  209. package/dist/lib/utils/build-creative-return-builders.js +9 -3
  210. package/dist/lib/utils/build-creative-return-builders.js.map +1 -1
  211. package/dist/lib/utils/build-creative-return-builders.mjs +7 -2
  212. package/dist/lib/utils/build-creative-return-builders.mjs.map +1 -1
  213. package/dist/lib/utils/response-unwrapper.js +1 -1
  214. package/dist/lib/utils/response-unwrapper.js.map +1 -1
  215. package/dist/lib/utils/response-unwrapper.mjs +1 -1
  216. package/dist/lib/utils/response-unwrapper.mjs.map +1 -1
  217. package/dist/lib/validation/schema-loader.d.ts.map +1 -1
  218. package/dist/lib/validation/schema-loader.js +16 -7
  219. package/dist/lib/validation/schema-loader.js.map +1 -1
  220. package/dist/lib/validation/schema-loader.mjs +16 -7
  221. package/dist/lib/validation/schema-loader.mjs.map +1 -1
  222. package/dist/lib/version.d.mts +3 -3
  223. package/dist/lib/version.d.ts +3 -3
  224. package/dist/lib/version.js +3 -3
  225. package/dist/lib/version.js.map +1 -1
  226. package/dist/lib/version.mjs +3 -3
  227. package/dist/lib/version.mjs.map +1 -1
  228. package/docs/llms.txt +2 -2
  229. package/docs/migration-5.x-to-6.x.md +3 -2
  230. package/docs/migration-task-registry-scoping.md +61 -0
  231. package/examples/hello_seller_adapter_guaranteed.ts +1 -1
  232. package/package.json +5 -5
  233. package/skills/build-decisioning-creative-template/SKILL.md +2 -2
  234. package/skills/build-decisioning-platform/SKILL.md +4 -2
  235. package/skills/build-decisioning-platform/advanced/REFERENCE.md +7 -6
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/lib/signing/agent-fetch.ts"],"sourcesContent":["import type {\n AgentRequestSigningConfig,\n AgentRequestSigningConfigInline,\n AgentRequestSigningConfigProvider,\n} from '../types/adcp';\nimport { createSigningFetch, type CoverContentDigestPredicate } from './fetch';\nimport { createSigningFetchAsync } from './fetch-async';\nimport {\n buildCapabilityCacheKey,\n defaultCapabilityCache,\n type CachedCapability,\n type CapabilityCache,\n} from './capability-cache';\nimport type { ContentDigestPolicy, VerifierCapability } from './types';\nimport type { SignerKey } from './signer';\nimport { containsWebhookAuthentication } from './webhook-auth-detection';\nimport { ADCP_VERSION } from '../version';\nimport { requestSigningEncodingForVersion } from './content-digest';\nexport { requestSigningEncodingForVersion } from './content-digest';\n\ntype FetchLike = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;\nconst MAX_WEBHOOK_AUTH_INSPECTION_BYTES = 1_048_576;\n\n/**\n * Resolve `globalThis.fetch` at the moment of each outbound request — not at\n * module import, and not at factory call. Late resolution lets a polyfill\n * installed after `buildAgentSigningFetch` returns (but before first request)\n * still take effect. The helper throws a clear error on environments lacking\n * global `fetch` instead of binding `undefined` and failing cryptically inside\n * the signing pipeline.\n */\nfunction defaultUpstream(): FetchLike {\n const f = (globalThis as { fetch?: FetchLike }).fetch;\n if (typeof f !== 'function') {\n throw new TypeError(\n 'buildAgentSigningFetch: no upstream fetch provided and globalThis.fetch is unavailable. Pass `upstream: yourFetch` explicitly.'\n );\n }\n return f;\n}\n\nfunction bodyToUtf8(body: unknown): string | undefined {\n if (body === undefined || body === null) return undefined;\n if (typeof body === 'string') return body.length ? body : undefined;\n if (body instanceof Uint8Array) return Buffer.from(body).toString('utf8');\n if (body instanceof ArrayBuffer) return Buffer.from(body).toString('utf8');\n // FormData / Blob / ReadableStream / async iterables fall through. Throw\n // rather than return `undefined` — a silent pass-through would ship the\n // request unsigned with no hint to the caller, defeating the seller's\n // `required_for` contract. Matches the strict posture of `createSigningFetch`\n // which also refuses unsupported body shapes.\n throw new TypeError(\n `buildAgentSigningFetch cannot extract an AdCP operation name from a body of type ${describeBody(body)}. The signer only supports string, Uint8Array, and ArrayBuffer bodies because the signature must cover the exact wire bytes.`\n );\n}\n\nfunction describeBody(body: unknown): string {\n if (body && typeof body === 'object') {\n const ctor = (body as { constructor?: { name?: string } }).constructor?.name;\n if (ctor) return ctor;\n }\n return typeof body;\n}\n\n/**\n * Extract the AdCP operation name from a JSON-RPC request body, if any.\n *\n * - MCP tool calls: `method === \"tools/call\"` → `params.name` is the op name.\n * - A2A `message/send` / `message/stream`: the op name lives on the first\n * data-kind part as `data.skill`.\n * - All other JSON-RPC methods (`initialize`, `tools/list`, notifications)\n * return `undefined` — those are protocol-layer housekeeping, not AdCP\n * operations subject to request-signing policy.\n *\n * Throws if the body is of a shape the signer can't read (Blob, FormData,\n * ReadableStream). The MCP / A2A SDKs both emit JSON strings today; a future\n * SDK version switching to streams would silently break signing otherwise.\n */\nexport function extractAdcpOperation(body: unknown): string | undefined {\n const text = bodyToUtf8(body);\n if (!text) return undefined;\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch {\n return undefined;\n }\n if (!parsed || typeof parsed !== 'object') return undefined;\n const rpc = parsed as { method?: unknown; params?: unknown };\n\n if (rpc.method === 'tools/call') {\n const params = rpc.params as { name?: unknown } | undefined;\n return typeof params?.name === 'string' ? params.name : undefined;\n }\n\n if (rpc.method === 'message/send' || rpc.method === 'message/stream') {\n const params = rpc.params as { message?: { parts?: unknown } } | undefined;\n const parts = params?.message?.parts;\n if (!Array.isArray(parts)) return undefined;\n for (const part of parts) {\n if (part && typeof part === 'object') {\n const p = part as { kind?: unknown; data?: { skill?: unknown } };\n if (p.kind === 'data' && typeof p.data?.skill === 'string') {\n return p.data.skill;\n }\n }\n }\n }\n\n return undefined;\n}\n\n/**\n * Detect webhook receiver credentials in the outbound JSON-RPC payload. The\n * verifier rejects unsigned requests carrying these credentials regardless of\n * the seller's operation-level capability advertisement, so the client must\n * sign them even while the capability cache is cold or silent for that op.\n * The scan is bounded; if the body exceeds the inspection budget, sign it.\n */\nfunction carriesWebhookAuthentication(body: unknown): boolean {\n const text = bodyToUtf8(body);\n if (!text) return false;\n if (text.length > MAX_WEBHOOK_AUTH_INSPECTION_BYTES) return true;\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch {\n return false;\n }\n return containsWebhookAuthentication(parsed);\n}\n\n/**\n * Decide whether an outbound AdCP call should be signed given the seller's\n * advertised capability block and the buyer's override list.\n *\n * Precedence matches the AdCP spec (`required_for` > `warn_for` >\n * `supported_for`):\n * 1. `always_sign` on the buyer config — pilot-time override, signs even\n * if the seller hasn't listed the op.\n * 2. Seller `required_for` — seller rejects unsigned requests, MUST sign.\n * 3. Seller `warn_for` — shadow mode. Seller verifies when present and\n * logs failures without rejecting; counterparties SHOULD sign so the\n * seller can surface failure rates before flipping to `required_for`.\n * 4. Seller `supported_for` — sign only if the buyer opted in via\n * `sign_supported: true` (defaults off).\n *\n * Returns false when the capability is unknown (cold cache) except for ops\n * in `always_sign`, so the priming `get_adcp_capabilities` call itself is\n * never signed.\n */\nexport function shouldSignOperation(\n operation: string | undefined,\n capability: VerifierCapability | undefined,\n config: AgentRequestSigningConfig\n): boolean {\n if (!operation) return false;\n if (config.always_sign?.includes(operation)) return true;\n if (!capability?.supported) return false;\n if (capability.required_for?.includes(operation)) return true;\n if (capability.warn_for?.includes(operation)) return true;\n if (config.sign_supported && capability.supported_for?.includes(operation)) return true;\n return false;\n}\n\n/**\n * Resolve the seller's content-digest policy into a concrete per-request\n * coverage decision.\n *\n * - `required` → must cover content-digest.\n * - `forbidden` → must NOT cover content-digest.\n * - `either` / absent → default to covering (body-binding is the safer\n * choice; the seller has explicitly allowed both forms).\n */\nexport function resolveCoverContentDigest(policy: ContentDigestPolicy | undefined): boolean {\n if (policy === 'forbidden') return false;\n return true;\n}\n\n/**\n * Convert an inline `AgentRequestSigningConfig` into the `SignerKey` shape\n * expected by `signRequest` / `createSigningFetch`. Provider-backed configs\n * have no in-process key material — callers must route through\n * `createSigningFetchAsync(upstream, config.provider, ...)` instead.\n */\nexport function toSignerKey(config: AgentRequestSigningConfigInline): SignerKey {\n return {\n keyid: config.kid,\n alg: config.alg,\n privateKey: config.private_key as SignerKey['privateKey'],\n };\n}\n\n/** Narrow predicate for the inline shape (kind absent or `'inline'`). */\nexport function isInlineSigningConfig(config: AgentRequestSigningConfig): config is AgentRequestSigningConfigInline {\n return config.kind !== 'provider';\n}\n\n/** Narrow predicate for the provider shape. */\nexport function isProviderSigningConfig(\n config: AgentRequestSigningConfig\n): config is AgentRequestSigningConfigProvider {\n return config.kind === 'provider';\n}\n\nexport interface BuildAgentSigningFetchOptions {\n /**\n * Upstream fetch to wrap. Defaults to `globalThis.fetch` when omitted —\n * use a Node 18+ / browser / worker global, or pass a polyfill / a\n * decorated fetch (retries, telemetry) to compose.\n */\n upstream?: FetchLike;\n signing: AgentRequestSigningConfig;\n /** Lazy accessor for the current cached capability — re-read on every call. */\n getCapability: () => CachedCapability | undefined;\n /** Trusted SDK/endpoint version pin; never inferred from the request body. */\n adcpVersion?: string;\n}\n\n/**\n * Build a fetch wrapper suitable for injection into MCP/A2A transports. On\n * every outbound request:\n * 1. Sign immediately when the payload carries webhook authentication.\n * 2. Extract the AdCP operation name from the JSON-RPC body (MCP tool-call\n * or A2A message/send). Non-AdCP JSON-RPC methods (e.g., `initialize`)\n * pass through unsigned.\n * 3. Consult the cached seller capability to decide whether to sign.\n * 4. Resolve the seller's content-digest policy into a per-request toggle.\n * 5. Delegate to `createSigningFetch` with the decision baked in.\n */\nexport function buildAgentSigningFetch(options: BuildAgentSigningFetchOptions): FetchLike {\n const { signing, getCapability } = options;\n // Resolve `globalThis.fetch` per-call when no explicit upstream was passed\n // so a polyfill installed between factory creation and first request still\n // takes effect. Eager resolution would freeze whatever was global at\n // factory-call time.\n const explicitUpstream = options.upstream;\n const upstream: FetchLike = explicitUpstream ?? ((input, init) => defaultUpstream()(input, init));\n\n const shouldSign = (_url: string, init: RequestInit | undefined): boolean => {\n if (carriesWebhookAuthentication(init?.body)) return true;\n const operation = extractAdcpOperation(init?.body);\n const entry = getCapability();\n return shouldSignOperation(operation, entry?.requestSigning, signing);\n };\n\n const binaryEncoding = requestSigningEncodingForVersion(options.adcpVersion ?? ADCP_VERSION);\n const coverContentDigest: CoverContentDigestPredicate = (_url, _init) => {\n if (binaryEncoding === 'rfc8941-base64') return true;\n const entry = getCapability();\n return resolveCoverContentDigest(entry?.requestSigning?.covers_content_digest);\n };\n\n if (isProviderSigningConfig(signing)) {\n // Freeze the provider's identity fields at factory time and pass a\n // snapshot view down to the async signer. TypeScript `readonly` on the\n // `SigningProvider` interface is compile-time only — if a downstream\n // adapter mutates `keyid`/`algorithm`/`fingerprint` between context\n // build and outbound request, the wire `keyid` would drift away from\n // the cache key the connection was bound to. The frozen view binds the\n // wire identity to the snapshot already used for cache routing.\n const frozen = freezeProviderIdentity(signing.provider);\n return createSigningFetchAsync(upstream, frozen, { shouldSign, coverContentDigest, binaryEncoding });\n }\n return createSigningFetch(upstream, toSignerKey(signing), { shouldSign, coverContentDigest, binaryEncoding });\n}\n\nfunction freezeProviderIdentity(provider: AgentRequestSigningConfigProvider['provider']) {\n const keyid = provider.keyid;\n const algorithm = provider.algorithm;\n const fingerprint = provider.fingerprint;\n return {\n keyid,\n algorithm,\n fingerprint,\n sign: (payload: Uint8Array) => provider.sign(payload),\n };\n}\n\nexport interface CreateAgentSignedFetchOptions {\n /** This agent's RFC 9421 signing identity (kid, alg, private_key, agent_url). */\n signing: AgentRequestSigningConfig;\n /** Trusted endpoint pin used to select the 3.0/3.1 or 3.2 request profile. */\n adcpVersion?: string;\n /**\n * Target seller's `agent_uri` — the base URL whose `get_adcp_capabilities`\n * response gates whether each operation gets signed. The preset keys a\n * capability-cache entry off this URL so repeat calls reuse the same\n * advertisement.\n *\n * For multi-seller buyers, build one signed fetch per seller (or use\n * {@link buildAgentSigningFetch} directly and supply your own\n * `getCapability` that dispatches on the target URL).\n */\n sellerAgentUri: string;\n /**\n * Optional auth token the seller expects alongside the signing headers.\n * Included in the capability cache key so a token rotation naturally\n * invalidates the cached advertisement.\n */\n sellerAuthToken?: string;\n /**\n * Capability cache. Defaults to the shared {@link defaultCapabilityCache}.\n *\n * **The shared default is load-bearing.** `ProtocolClient` and\n * `buildAgentSigningContext` write the seller's `get_adcp_capabilities`\n * response into `defaultCapabilityCache`; this preset reads from the same\n * instance so a single priming call serves every subsequent signing\n * decision. Passing a fresh `new CapabilityCache()` here without also\n * priming it will silently disable `required_for` enforcement: cold cache\n * → `shouldSignOperation` returns `false` → every required op ships\n * unsigned → seller rejects. The SDK emits no error on this path because\n * \"decided not to sign\" is the spec-correct behavior on a cold cache.\n *\n * Pass an explicit cache only when you've also primed it — call\n * {@link ensureCapabilityLoaded} against your cache instance after\n * construction, or seed it via `cache.set(buildCapabilityCacheKey(uri,\n * token, fingerprint, adcpVersion), entry)` — or when you genuinely want each caller to re-fetch\n * the seller's capability block.\n *\n * Note: the cache stores **public** seller capability advertisements\n * (the contents of `get_adcp_capabilities` responses), not buyer\n * credentials. The shared default is not a multi-tenant security\n * boundary — different sellers, different auth tokens, and different\n * signing keys already get separate entries via\n * {@link buildCapabilityCacheKey}.\n */\n cache?: CapabilityCache;\n /** Upstream fetch. Defaults to `globalThis.fetch`. */\n upstream?: FetchLike;\n}\n\n/**\n * One-call preset for the single-seller case: bundles\n * {@link buildAgentSigningFetch} with a {@link CapabilityCache} lookup keyed\n * by `sellerAgentUri`, so adapter authors don't have to wire the cache and\n * capability accessor themselves.\n *\n * ```ts\n * // fetch.ts\n * import { createAgentSignedFetch } from '@adcp/sdk/signing';\n *\n * export const signedFetch = createAgentSignedFetch({\n * signing: {\n * kid: 'my-agent-2026',\n * alg: 'ed25519',\n * private_key: JSON.parse(process.env.ADCP_PRIV_KEY!),\n * agent_url: 'https://agent.example.com',\n * },\n * sellerAgentUri: 'https://seller.example.com',\n * });\n * ```\n *\n * ```ts\n * // any other module\n * import { signedFetch } from './fetch';\n *\n * await signedFetch('https://seller.example.com/mcp', {\n * method: 'POST',\n * headers: { 'Content-Type': 'application/json' },\n * body: JSON.stringify(payload),\n * });\n * ```\n *\n * Signing only happens on operations the seller advertises as\n * `required_for` / `warn_for` (or `supported_for` when the buyer config\n * opts in) — so the `get_adcp_capabilities` priming call itself is always\n * unsigned, as the spec requires.\n *\n * For multi-seller adapters, construct one preset per seller, or use\n * {@link buildAgentSigningFetch} directly with a request-dispatching\n * `getCapability` callback.\n */\nexport function createAgentSignedFetch(options: CreateAgentSignedFetchOptions): FetchLike {\n const cache = options.cache ?? defaultCapabilityCache;\n const adcpVersion = options.adcpVersion ?? ADCP_VERSION;\n const cacheKey = buildCapabilityCacheKey(options.sellerAgentUri, options.sellerAuthToken, undefined, adcpVersion);\n return buildAgentSigningFetch({\n signing: options.signing,\n upstream: options.upstream,\n getCapability: () => cache.get(cacheKey),\n adcpVersion,\n });\n}\n"],"mappings":"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","names":["requestSigningEncodingForVersion"]}
1
+ {"version":3,"sources":["../../../src/lib/signing/agent-fetch.ts"],"sourcesContent":["import type {\n AgentRequestSigningConfig,\n AgentRequestSigningConfigInline,\n AgentRequestSigningConfigProvider,\n} from '../types/adcp';\nimport { createSigningFetch, type CoverContentDigestPredicate } from './fetch';\nimport { createSigningFetchAsync } from './fetch-async';\nimport {\n buildCapabilityCacheKey,\n defaultCapabilityCache,\n type CachedCapability,\n type CapabilityCache,\n} from './capability-cache';\nimport type { ContentDigestPolicy, VerifierCapability } from './types';\nimport type { SignerKey } from './signer';\nimport { containsWebhookAuthentication } from './webhook-auth-detection';\nimport { ADCP_VERSION } from '../version';\nimport { requestSigningEncodingForVersion } from './content-digest';\nexport { requestSigningEncodingForVersion } from './content-digest';\n\ntype FetchLike = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;\nconst MAX_WEBHOOK_AUTH_INSPECTION_BYTES = 1_048_576;\n\n/**\n * Resolve `globalThis.fetch` at the moment of each outbound request — not at\n * module import, and not at factory call. Late resolution lets a polyfill\n * installed after `buildAgentSigningFetch` returns (but before first request)\n * still take effect. The helper throws a clear error on environments lacking\n * global `fetch` instead of binding `undefined` and failing cryptically inside\n * the signing pipeline.\n */\nfunction defaultUpstream(): FetchLike {\n const f = (globalThis as { fetch?: FetchLike }).fetch;\n if (typeof f !== 'function') {\n throw new TypeError(\n 'buildAgentSigningFetch: no upstream fetch provided and globalThis.fetch is unavailable. Pass `upstream: yourFetch` explicitly.'\n );\n }\n return f;\n}\n\nfunction bodyToUtf8(body: unknown): string | undefined {\n if (body === undefined || body === null) return undefined;\n if (typeof body === 'string') return body.length ? body : undefined;\n if (body instanceof Uint8Array) return Buffer.from(body).toString('utf8');\n if (body instanceof ArrayBuffer) return Buffer.from(body).toString('utf8');\n // FormData / Blob / ReadableStream / async iterables fall through. Throw\n // rather than return `undefined` — a silent pass-through would ship the\n // request unsigned with no hint to the caller, defeating the seller's\n // `required_for` contract. Matches the strict posture of `createSigningFetch`\n // which also refuses unsupported body shapes.\n throw new TypeError(\n `buildAgentSigningFetch cannot extract an AdCP operation name from a body of type ${describeBody(body)}. The signer only supports string, Uint8Array, and ArrayBuffer bodies because the signature must cover the exact wire bytes.`\n );\n}\n\nfunction describeBody(body: unknown): string {\n if (body && typeof body === 'object') {\n const ctor = (body as { constructor?: { name?: string } }).constructor?.name;\n if (ctor) return ctor;\n }\n return typeof body;\n}\n\n/**\n * Extract the AdCP operation name from a JSON-RPC request body, if any.\n *\n * - MCP tool calls: `method === \"tools/call\"` → `params.name` is the op name.\n * - A2A `message/send` / `message/stream`: the op name lives on the first\n * data-kind part as `data.skill`.\n * - All other JSON-RPC methods (`initialize`, `tools/list`, notifications)\n * return `undefined` — those are protocol-layer housekeeping, not AdCP\n * operations subject to request-signing policy.\n *\n * Throws if the body is of a shape the signer can't read (Blob, FormData,\n * ReadableStream). The MCP / A2A SDKs both emit JSON strings today; a future\n * SDK version switching to streams would silently break signing otherwise.\n */\nexport function extractAdcpOperation(body: unknown): string | undefined {\n const text = bodyToUtf8(body);\n if (!text) return undefined;\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch {\n return undefined;\n }\n if (!parsed || typeof parsed !== 'object') return undefined;\n const rpc = parsed as { method?: unknown; params?: unknown };\n\n if (rpc.method === 'tools/call') {\n const params = rpc.params as { name?: unknown } | undefined;\n return typeof params?.name === 'string' ? params.name : undefined;\n }\n\n if (rpc.method === 'message/send' || rpc.method === 'message/stream') {\n const params = rpc.params as { message?: { parts?: unknown } } | undefined;\n const parts = params?.message?.parts;\n if (!Array.isArray(parts)) return undefined;\n for (const part of parts) {\n if (part && typeof part === 'object') {\n const p = part as { kind?: unknown; data?: { skill?: unknown } };\n if (p.kind === 'data' && typeof p.data?.skill === 'string') {\n return p.data.skill;\n }\n }\n }\n }\n\n return undefined;\n}\n\n/**\n * Detect webhook receiver credentials in the outbound JSON-RPC payload. The\n * verifier rejects unsigned requests carrying these credentials regardless of\n * the seller's operation-level capability advertisement, so the client must\n * sign them even while the capability cache is cold or silent for that op.\n * The scan is bounded; if the body exceeds the inspection budget, sign it.\n */\nfunction carriesWebhookAuthentication(body: unknown): boolean {\n const text = bodyToUtf8(body);\n if (!text) return false;\n if (text.length > MAX_WEBHOOK_AUTH_INSPECTION_BYTES) return true;\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch {\n return false;\n }\n return containsWebhookAuthentication(parsed);\n}\n\n/**\n * Decide whether an outbound AdCP call should be signed given the seller's\n * advertised capability block and the buyer's override list.\n *\n * Precedence matches the AdCP spec (`required_for` > `warn_for` >\n * `supported_for`):\n * 1. `always_sign` on the buyer config — pilot-time override, signs even\n * if the seller hasn't listed the op.\n * 2. Seller `required_for` — seller rejects unsigned requests, MUST sign.\n * 3. Seller `warn_for` — shadow mode. Seller verifies when present and\n * logs failures without rejecting; counterparties SHOULD sign so the\n * seller can surface failure rates before flipping to `required_for`.\n * 4. Seller `supported_for` — sign only if the buyer opted in via\n * `sign_supported: true` (defaults off).\n *\n * Returns false when the capability is unknown (cold cache) except for ops\n * in `always_sign`.\n */\nexport function shouldSignOperation(\n operation: string | undefined,\n capability: VerifierCapability | undefined,\n config: AgentRequestSigningConfig\n): boolean {\n if (!operation) return false;\n if (config.always_sign?.includes(operation)) return true;\n if (!capability?.supported) return false;\n if (capability.required_for?.includes(operation)) return true;\n if (capability.warn_for?.includes(operation)) return true;\n if (config.sign_supported && capability.supported_for?.includes(operation)) return true;\n return false;\n}\n\n/**\n * Resolve the seller's content-digest policy into a concrete per-request\n * coverage decision.\n *\n * - `required` → must cover content-digest.\n * - `forbidden` → must NOT cover content-digest.\n * - `either` / absent → default to covering (body-binding is the safer\n * choice; the seller has explicitly allowed both forms).\n */\nexport function resolveCoverContentDigest(policy: ContentDigestPolicy | undefined): boolean {\n if (policy === 'forbidden') return false;\n return true;\n}\n\n/**\n * Convert an inline `AgentRequestSigningConfig` into the `SignerKey` shape\n * expected by `signRequest` / `createSigningFetch`. Provider-backed configs\n * have no in-process key material — callers must route through\n * `createSigningFetchAsync(upstream, config.provider, ...)` instead.\n */\nexport function toSignerKey(config: AgentRequestSigningConfigInline): SignerKey {\n return {\n keyid: config.kid,\n alg: config.alg,\n privateKey: config.private_key as SignerKey['privateKey'],\n };\n}\n\n/** Narrow predicate for the inline shape (kind absent or `'inline'`). */\nexport function isInlineSigningConfig(config: AgentRequestSigningConfig): config is AgentRequestSigningConfigInline {\n return config.kind !== 'provider';\n}\n\n/** Narrow predicate for the provider shape. */\nexport function isProviderSigningConfig(\n config: AgentRequestSigningConfig\n): config is AgentRequestSigningConfigProvider {\n return config.kind === 'provider';\n}\n\nexport interface BuildAgentSigningFetchOptions {\n /**\n * Upstream fetch to wrap. Defaults to `globalThis.fetch` when omitted —\n * use a Node 18+ / browser / worker global, or pass a polyfill / a\n * decorated fetch (retries, telemetry) to compose.\n */\n upstream?: FetchLike;\n signing: AgentRequestSigningConfig;\n /** Lazy accessor for the current cached capability — re-read on every call. */\n getCapability: () => CachedCapability | undefined;\n /** Trusted SDK/endpoint version pin; never inferred from the request body. */\n adcpVersion?: string;\n}\n\n/**\n * Build a fetch wrapper suitable for injection into MCP/A2A transports. On\n * every outbound request:\n * 1. Sign immediately when the payload carries webhook authentication.\n * 2. Extract the AdCP operation name from the JSON-RPC body (MCP tool-call\n * or A2A message/send). Non-AdCP JSON-RPC methods (e.g., `initialize`)\n * pass through unsigned.\n * 3. Consult the cached seller capability to decide whether to sign.\n * 4. Resolve the seller's content-digest policy into a per-request toggle.\n * 5. Delegate to `createSigningFetch` with the decision baked in.\n */\nexport function buildAgentSigningFetch(options: BuildAgentSigningFetchOptions): FetchLike {\n const { signing, getCapability } = options;\n // Resolve `globalThis.fetch` per-call when no explicit upstream was passed\n // so a polyfill installed between factory creation and first request still\n // takes effect. Eager resolution would freeze whatever was global at\n // factory-call time.\n const explicitUpstream = options.upstream;\n const upstream: FetchLike = explicitUpstream ?? ((input, init) => defaultUpstream()(input, init));\n\n const shouldSign = (_url: string, init: RequestInit | undefined): boolean => {\n if (carriesWebhookAuthentication(init?.body)) return true;\n const operation = extractAdcpOperation(init?.body);\n const entry = getCapability();\n return shouldSignOperation(operation, entry?.requestSigning, signing);\n };\n\n const binaryEncoding = requestSigningEncodingForVersion(options.adcpVersion ?? ADCP_VERSION);\n const coverContentDigest: CoverContentDigestPredicate = (_url, _init) => {\n if (binaryEncoding === 'rfc8941-base64') return true;\n const entry = getCapability();\n return resolveCoverContentDigest(entry?.requestSigning?.covers_content_digest);\n };\n\n if (isProviderSigningConfig(signing)) {\n // Freeze the provider's identity fields at factory time and pass a\n // snapshot view down to the async signer. TypeScript `readonly` on the\n // `SigningProvider` interface is compile-time only — if a downstream\n // adapter mutates `keyid`/`algorithm`/`fingerprint` between context\n // build and outbound request, the wire `keyid` would drift away from\n // the cache key the connection was bound to. The frozen view binds the\n // wire identity to the snapshot already used for cache routing.\n const frozen = freezeProviderIdentity(signing.provider);\n return createSigningFetchAsync(upstream, frozen, { shouldSign, coverContentDigest, binaryEncoding });\n }\n return createSigningFetch(upstream, toSignerKey(signing), { shouldSign, coverContentDigest, binaryEncoding });\n}\n\nfunction freezeProviderIdentity(provider: AgentRequestSigningConfigProvider['provider']) {\n const keyid = provider.keyid;\n const algorithm = provider.algorithm;\n const adcpUse = provider.adcpUse;\n const fingerprint = provider.fingerprint;\n return {\n keyid,\n algorithm,\n adcpUse,\n fingerprint,\n sign: (payload: Uint8Array) => provider.sign(payload),\n };\n}\n\nexport interface CreateAgentSignedFetchOptions {\n /** This agent's RFC 9421 signing identity (kid, alg, private_key, agent_url). */\n signing: AgentRequestSigningConfig;\n /** Trusted endpoint pin used to select the 3.0/3.1 or 3.2 request profile. */\n adcpVersion?: string;\n /**\n * Target seller's `agent_uri` — the base URL whose `get_adcp_capabilities`\n * response gates whether each operation gets signed. The preset keys a\n * capability-cache entry off this URL so repeat calls reuse the same\n * advertisement.\n *\n * For multi-seller buyers, build one signed fetch per seller (or use\n * {@link buildAgentSigningFetch} directly and supply your own\n * `getCapability` that dispatches on the target URL).\n */\n sellerAgentUri: string;\n /**\n * Optional auth token the seller expects alongside the signing headers.\n * Included in the capability cache key so a token rotation naturally\n * invalidates the cached advertisement.\n */\n sellerAuthToken?: string;\n /**\n * Capability cache. Defaults to the shared {@link defaultCapabilityCache}.\n *\n * **The shared default is load-bearing.** `ProtocolClient` and\n * `buildAgentSigningContext` write the seller's `get_adcp_capabilities`\n * response into `defaultCapabilityCache`; this preset reads from the same\n * instance so a single priming call serves every subsequent signing\n * decision. Passing a fresh `new CapabilityCache()` here without also\n * priming it will silently disable `required_for` enforcement: cold cache\n * → `shouldSignOperation` returns `false` → every required op ships\n * unsigned → seller rejects. The SDK emits no error on this path because\n * \"decided not to sign\" is the spec-correct behavior on a cold cache.\n *\n * Pass an explicit cache only when you've also primed it — call\n * {@link ensureCapabilityLoaded} against your cache instance after\n * construction, or seed it via `cache.set(buildCapabilityCacheKey(uri,\n * token, fingerprint, adcpVersion), entry)` — or when you genuinely want each caller to re-fetch\n * the seller's capability block.\n *\n * Note: the cache stores **public** seller capability advertisements\n * (the contents of `get_adcp_capabilities` responses), not buyer\n * credentials. The shared default is not a multi-tenant security\n * boundary — different sellers, different auth tokens, and different\n * signing keys already get separate entries via\n * {@link buildCapabilityCacheKey}.\n */\n cache?: CapabilityCache;\n /** Upstream fetch. Defaults to `globalThis.fetch`. */\n upstream?: FetchLike;\n}\n\n/**\n * One-call preset for the single-seller case: bundles\n * {@link buildAgentSigningFetch} with a {@link CapabilityCache} lookup keyed\n * by `sellerAgentUri`, so adapter authors don't have to wire the cache and\n * capability accessor themselves.\n *\n * ```ts\n * // fetch.ts\n * import { createAgentSignedFetch } from '@adcp/sdk/signing';\n *\n * export const signedFetch = createAgentSignedFetch({\n * signing: {\n * kid: 'my-agent-2026',\n * alg: 'ed25519',\n * private_key: JSON.parse(process.env.ADCP_PRIV_KEY!),\n * agent_url: 'https://agent.example.com',\n * },\n * sellerAgentUri: 'https://seller.example.com',\n * });\n * ```\n *\n * ```ts\n * // any other module\n * import { signedFetch } from './fetch';\n *\n * await signedFetch('https://seller.example.com/mcp', {\n * method: 'POST',\n * headers: { 'Content-Type': 'application/json' },\n * body: JSON.stringify(payload),\n * });\n * ```\n *\n * Signing only happens on operations the seller advertises as\n * `required_for` / `warn_for` (or `supported_for` when the buyer config\n * opts in) — so the `get_adcp_capabilities` priming call itself is always\n * unsigned, as the spec requires.\n *\n * For multi-seller adapters, construct one preset per seller, or use\n * {@link buildAgentSigningFetch} directly with a request-dispatching\n * `getCapability` callback.\n */\nexport function createAgentSignedFetch(options: CreateAgentSignedFetchOptions): FetchLike {\n const cache = options.cache ?? defaultCapabilityCache;\n const adcpVersion = options.adcpVersion ?? ADCP_VERSION;\n const cacheKey = buildCapabilityCacheKey(options.sellerAgentUri, options.sellerAuthToken, undefined, adcpVersion);\n return buildAgentSigningFetch({\n signing: options.signing,\n upstream: options.upstream,\n getCapability: () => cache.get(cacheKey),\n adcpVersion,\n });\n}\n"],"mappings":"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","names":["requestSigningEncodingForVersion"]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/lib/signing/agent-resolver/etld.ts"],"sourcesContent":["/**\n * eTLD+1 computation against a pinned, dated PSL snapshot. Backed by\n * `tldts`, which ships its PSL bundled into the published package — pinning\n * the dependency in `package.json` pins the snapshot. Runtime PSL fetches are\n * never performed: a runtime fetch creates both a denial-of-service oracle\n * (PSL host outage stalls verification) and a non-deterministic eTLD+1\n * across deployments running on different snapshot ages\n * (security.mdx §\"Quickstart: implement a `brand_json_url`-based verifier\"\n * step 3).\n *\n * Both ICANN and PRIVATE PSL sections are in scope: platforms like\n * `vercel.app`, `pages.dev`, `github.io` MUST be treated as suffixes so an\n * attacker on `attacker.vercel.app` cannot pretend to share an eTLD+1 with\n * a legitimate publisher on `legit.vercel.app`.\n */\nimport { parse as parseTld } from 'tldts';\n\nimport { canonicalizeHost } from './canonicalize';\n\nexport const PSL_SNAPSHOT_VERSION = 'tldts@7';\n\nexport class EtldComputationError extends Error {\n constructor(\n message: string,\n readonly meta: { hostOrUrl?: string }\n ) {\n super(message);\n this.name = 'EtldComputationError';\n }\n}\n\n/**\n * Compute eTLD+1 for a hostname or URL. Caller should pass either a bare\n * hostname (`buyer.example.com`) or a full URL (`https://buyer.example.com/mcp`).\n *\n * Returns the registrable domain in canonical form (ASCII-lowercased, IDNA-2008\n * A-label). Throws when the input has no PSL match — typically because the host\n * is an IP literal or a single-label name.\n */\nexport function eTldPlusOne(hostOrUrl: string): string {\n const host = canonicalizeHost(extractHost(hostOrUrl));\n const result = parseTld(host, { allowPrivateDomains: true });\n if (result.isIp) {\n throw new EtldComputationError(`Cannot compute eTLD+1 for IP literal`, { hostOrUrl });\n }\n if (!result.domain) {\n throw new EtldComputationError(`No PSL match for hostname`, { hostOrUrl });\n }\n return canonicalizeHost(result.domain);\n}\n\n/**\n * Convenience: do two hostnames share an eTLD+1? Returns false when either\n * input has no PSL match (rather than throwing) — callers comparing two\n * hostnames typically want a non-match boolean, not an exception.\n */\nexport function sameEtldPlusOne(a: string, b: string): boolean {\n let aEtld: string;\n let bEtld: string;\n try {\n aEtld = eTldPlusOne(a);\n bEtld = eTldPlusOne(b);\n } catch {\n return false;\n }\n return aEtld === bEtld;\n}\n\nfunction extractHost(hostOrUrl: string): string {\n if (hostOrUrl.includes('://')) {\n return new URL(hostOrUrl).hostname;\n }\n return hostOrUrl;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAeA,mBAAkC;AAElC,0BAAiC;AAE1B,MAAM,uBAAuB;AAE7B,MAAM,6BAA6B,MAAM;AAAA,EAC9C,YACE,SACS,MACT;AACA,UAAM,OAAO;AAFJ;AAGT,SAAK,OAAO;AAAA,EACd;AAAA,EAJW;AAKb;AAUO,SAAS,YAAY,WAA2B;AACrD,QAAM,WAAO,sCAAiB,YAAY,SAAS,CAAC;AACpD,QAAM,aAAS,aAAAA,OAAS,MAAM,EAAE,qBAAqB,KAAK,CAAC;AAC3D,MAAI,OAAO,MAAM;AACf,UAAM,IAAI,qBAAqB,wCAAwC,EAAE,UAAU,CAAC;AAAA,EACtF;AACA,MAAI,CAAC,OAAO,QAAQ;AAClB,UAAM,IAAI,qBAAqB,6BAA6B,EAAE,UAAU,CAAC;AAAA,EAC3E;AACA,aAAO,sCAAiB,OAAO,MAAM;AACvC;AAOO,SAAS,gBAAgB,GAAW,GAAoB;AAC7D,MAAI;AACJ,MAAI;AACJ,MAAI;AACF,YAAQ,YAAY,CAAC;AACrB,YAAQ,YAAY,CAAC;AAAA,EACvB,QAAQ;AACN,WAAO;AAAA,EACT;AACA,SAAO,UAAU;AACnB;AAEA,SAAS,YAAY,WAA2B;AAC9C,MAAI,UAAU,SAAS,KAAK,GAAG;AAC7B,WAAO,IAAI,IAAI,SAAS,EAAE;AAAA,EAC5B;AACA,SAAO;AACT;","names":["parseTld"]}
1
+ {"version":3,"sources":["../../../../src/lib/signing/agent-resolver/etld.ts"],"sourcesContent":["/**\n * eTLD+1 computation against a pinned, dated PSL snapshot. Backed by\n * `tldts`, which ships its PSL bundled into the published package — pinning\n * the resolved version in `package-lock.json` pins this repository's snapshot.\n * Runtime PSL fetches are never performed: a runtime fetch creates both a\n * denial-of-service oracle (PSL host outage stalls verification) and a\n * non-deterministic eTLD+1 across deployments running on different snapshot ages\n * (security.mdx §\"Quickstart: implement a `brand_json_url`-based verifier\"\n * step 3).\n *\n * Both ICANN and PRIVATE PSL sections are in scope: platforms like\n * `vercel.app`, `pages.dev`, `github.io` MUST be treated as suffixes so an\n * attacker on `attacker.vercel.app` cannot pretend to share an eTLD+1 with\n * a legitimate publisher on `legit.vercel.app`.\n */\nimport { parse as parseTld } from 'tldts';\n\nimport { canonicalizeHost } from './canonicalize';\n\nexport const PSL_SNAPSHOT_VERSION = 'tldts@7';\n\nexport class EtldComputationError extends Error {\n constructor(\n message: string,\n readonly meta: { hostOrUrl?: string }\n ) {\n super(message);\n this.name = 'EtldComputationError';\n }\n}\n\n/**\n * Compute eTLD+1 for a hostname or URL. Caller should pass either a bare\n * hostname (`buyer.example.com`) or a full URL (`https://buyer.example.com/mcp`).\n *\n * Returns the registrable domain in canonical form (ASCII-lowercased, IDNA-2008\n * A-label). Throws when the input has no PSL match — typically because the host\n * is an IP literal or a single-label name.\n */\nexport function eTldPlusOne(hostOrUrl: string): string {\n const host = canonicalizeHost(extractHost(hostOrUrl));\n const result = parseTld(host, { allowPrivateDomains: true });\n if (result.isIp) {\n throw new EtldComputationError(`Cannot compute eTLD+1 for IP literal`, { hostOrUrl });\n }\n if (!result.domain) {\n throw new EtldComputationError(`No PSL match for hostname`, { hostOrUrl });\n }\n return canonicalizeHost(result.domain);\n}\n\n/**\n * Convenience: do two hostnames share an eTLD+1? Returns false when either\n * input has no PSL match (rather than throwing) — callers comparing two\n * hostnames typically want a non-match boolean, not an exception.\n */\nexport function sameEtldPlusOne(a: string, b: string): boolean {\n let aEtld: string;\n let bEtld: string;\n try {\n aEtld = eTldPlusOne(a);\n bEtld = eTldPlusOne(b);\n } catch {\n return false;\n }\n return aEtld === bEtld;\n}\n\nfunction extractHost(hostOrUrl: string): string {\n if (hostOrUrl.includes('://')) {\n return new URL(hostOrUrl).hostname;\n }\n return hostOrUrl;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAeA,mBAAkC;AAElC,0BAAiC;AAE1B,MAAM,uBAAuB;AAE7B,MAAM,6BAA6B,MAAM;AAAA,EAC9C,YACE,SACS,MACT;AACA,UAAM,OAAO;AAFJ;AAGT,SAAK,OAAO;AAAA,EACd;AAAA,EAJW;AAKb;AAUO,SAAS,YAAY,WAA2B;AACrD,QAAM,WAAO,sCAAiB,YAAY,SAAS,CAAC;AACpD,QAAM,aAAS,aAAAA,OAAS,MAAM,EAAE,qBAAqB,KAAK,CAAC;AAC3D,MAAI,OAAO,MAAM;AACf,UAAM,IAAI,qBAAqB,wCAAwC,EAAE,UAAU,CAAC;AAAA,EACtF;AACA,MAAI,CAAC,OAAO,QAAQ;AAClB,UAAM,IAAI,qBAAqB,6BAA6B,EAAE,UAAU,CAAC;AAAA,EAC3E;AACA,aAAO,sCAAiB,OAAO,MAAM;AACvC;AAOO,SAAS,gBAAgB,GAAW,GAAoB;AAC7D,MAAI;AACJ,MAAI;AACJ,MAAI;AACF,YAAQ,YAAY,CAAC;AACrB,YAAQ,YAAY,CAAC;AAAA,EACvB,QAAQ;AACN,WAAO;AAAA,EACT;AACA,SAAO,UAAU;AACnB;AAEA,SAAS,YAAY,WAA2B;AAC9C,MAAI,UAAU,SAAS,KAAK,GAAG;AAC7B,WAAO,IAAI,IAAI,SAAS,EAAE;AAAA,EAC5B;AACA,SAAO;AACT;","names":["parseTld"]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/lib/signing/agent-resolver/etld.ts"],"sourcesContent":["/**\n * eTLD+1 computation against a pinned, dated PSL snapshot. Backed by\n * `tldts`, which ships its PSL bundled into the published package — pinning\n * the dependency in `package.json` pins the snapshot. Runtime PSL fetches are\n * never performed: a runtime fetch creates both a denial-of-service oracle\n * (PSL host outage stalls verification) and a non-deterministic eTLD+1\n * across deployments running on different snapshot ages\n * (security.mdx §\"Quickstart: implement a `brand_json_url`-based verifier\"\n * step 3).\n *\n * Both ICANN and PRIVATE PSL sections are in scope: platforms like\n * `vercel.app`, `pages.dev`, `github.io` MUST be treated as suffixes so an\n * attacker on `attacker.vercel.app` cannot pretend to share an eTLD+1 with\n * a legitimate publisher on `legit.vercel.app`.\n */\nimport { parse as parseTld } from 'tldts';\n\nimport { canonicalizeHost } from './canonicalize';\n\nexport const PSL_SNAPSHOT_VERSION = 'tldts@7';\n\nexport class EtldComputationError extends Error {\n constructor(\n message: string,\n readonly meta: { hostOrUrl?: string }\n ) {\n super(message);\n this.name = 'EtldComputationError';\n }\n}\n\n/**\n * Compute eTLD+1 for a hostname or URL. Caller should pass either a bare\n * hostname (`buyer.example.com`) or a full URL (`https://buyer.example.com/mcp`).\n *\n * Returns the registrable domain in canonical form (ASCII-lowercased, IDNA-2008\n * A-label). Throws when the input has no PSL match — typically because the host\n * is an IP literal or a single-label name.\n */\nexport function eTldPlusOne(hostOrUrl: string): string {\n const host = canonicalizeHost(extractHost(hostOrUrl));\n const result = parseTld(host, { allowPrivateDomains: true });\n if (result.isIp) {\n throw new EtldComputationError(`Cannot compute eTLD+1 for IP literal`, { hostOrUrl });\n }\n if (!result.domain) {\n throw new EtldComputationError(`No PSL match for hostname`, { hostOrUrl });\n }\n return canonicalizeHost(result.domain);\n}\n\n/**\n * Convenience: do two hostnames share an eTLD+1? Returns false when either\n * input has no PSL match (rather than throwing) — callers comparing two\n * hostnames typically want a non-match boolean, not an exception.\n */\nexport function sameEtldPlusOne(a: string, b: string): boolean {\n let aEtld: string;\n let bEtld: string;\n try {\n aEtld = eTldPlusOne(a);\n bEtld = eTldPlusOne(b);\n } catch {\n return false;\n }\n return aEtld === bEtld;\n}\n\nfunction extractHost(hostOrUrl: string): string {\n if (hostOrUrl.includes('://')) {\n return new URL(hostOrUrl).hostname;\n }\n return hostOrUrl;\n}\n"],"mappings":"AAeA,SAAS,SAAS,gBAAgB;AAElC,SAAS,wBAAwB;AAE1B,MAAM,uBAAuB;AAE7B,MAAM,6BAA6B,MAAM;AAAA,EAC9C,YACE,SACS,MACT;AACA,UAAM,OAAO;AAFJ;AAGT,SAAK,OAAO;AAAA,EACd;AAAA,EAJW;AAKb;AAUO,SAAS,YAAY,WAA2B;AACrD,QAAM,OAAO,iBAAiB,YAAY,SAAS,CAAC;AACpD,QAAM,SAAS,SAAS,MAAM,EAAE,qBAAqB,KAAK,CAAC;AAC3D,MAAI,OAAO,MAAM;AACf,UAAM,IAAI,qBAAqB,wCAAwC,EAAE,UAAU,CAAC;AAAA,EACtF;AACA,MAAI,CAAC,OAAO,QAAQ;AAClB,UAAM,IAAI,qBAAqB,6BAA6B,EAAE,UAAU,CAAC;AAAA,EAC3E;AACA,SAAO,iBAAiB,OAAO,MAAM;AACvC;AAOO,SAAS,gBAAgB,GAAW,GAAoB;AAC7D,MAAI;AACJ,MAAI;AACJ,MAAI;AACF,YAAQ,YAAY,CAAC;AACrB,YAAQ,YAAY,CAAC;AAAA,EACvB,QAAQ;AACN,WAAO;AAAA,EACT;AACA,SAAO,UAAU;AACnB;AAEA,SAAS,YAAY,WAA2B;AAC9C,MAAI,UAAU,SAAS,KAAK,GAAG;AAC7B,WAAO,IAAI,IAAI,SAAS,EAAE;AAAA,EAC5B;AACA,SAAO;AACT;","names":[]}
1
+ {"version":3,"sources":["../../../../src/lib/signing/agent-resolver/etld.ts"],"sourcesContent":["/**\n * eTLD+1 computation against a pinned, dated PSL snapshot. Backed by\n * `tldts`, which ships its PSL bundled into the published package — pinning\n * the resolved version in `package-lock.json` pins this repository's snapshot.\n * Runtime PSL fetches are never performed: a runtime fetch creates both a\n * denial-of-service oracle (PSL host outage stalls verification) and a\n * non-deterministic eTLD+1 across deployments running on different snapshot ages\n * (security.mdx §\"Quickstart: implement a `brand_json_url`-based verifier\"\n * step 3).\n *\n * Both ICANN and PRIVATE PSL sections are in scope: platforms like\n * `vercel.app`, `pages.dev`, `github.io` MUST be treated as suffixes so an\n * attacker on `attacker.vercel.app` cannot pretend to share an eTLD+1 with\n * a legitimate publisher on `legit.vercel.app`.\n */\nimport { parse as parseTld } from 'tldts';\n\nimport { canonicalizeHost } from './canonicalize';\n\nexport const PSL_SNAPSHOT_VERSION = 'tldts@7';\n\nexport class EtldComputationError extends Error {\n constructor(\n message: string,\n readonly meta: { hostOrUrl?: string }\n ) {\n super(message);\n this.name = 'EtldComputationError';\n }\n}\n\n/**\n * Compute eTLD+1 for a hostname or URL. Caller should pass either a bare\n * hostname (`buyer.example.com`) or a full URL (`https://buyer.example.com/mcp`).\n *\n * Returns the registrable domain in canonical form (ASCII-lowercased, IDNA-2008\n * A-label). Throws when the input has no PSL match — typically because the host\n * is an IP literal or a single-label name.\n */\nexport function eTldPlusOne(hostOrUrl: string): string {\n const host = canonicalizeHost(extractHost(hostOrUrl));\n const result = parseTld(host, { allowPrivateDomains: true });\n if (result.isIp) {\n throw new EtldComputationError(`Cannot compute eTLD+1 for IP literal`, { hostOrUrl });\n }\n if (!result.domain) {\n throw new EtldComputationError(`No PSL match for hostname`, { hostOrUrl });\n }\n return canonicalizeHost(result.domain);\n}\n\n/**\n * Convenience: do two hostnames share an eTLD+1? Returns false when either\n * input has no PSL match (rather than throwing) — callers comparing two\n * hostnames typically want a non-match boolean, not an exception.\n */\nexport function sameEtldPlusOne(a: string, b: string): boolean {\n let aEtld: string;\n let bEtld: string;\n try {\n aEtld = eTldPlusOne(a);\n bEtld = eTldPlusOne(b);\n } catch {\n return false;\n }\n return aEtld === bEtld;\n}\n\nfunction extractHost(hostOrUrl: string): string {\n if (hostOrUrl.includes('://')) {\n return new URL(hostOrUrl).hostname;\n }\n return hostOrUrl;\n}\n"],"mappings":"AAeA,SAAS,SAAS,gBAAgB;AAElC,SAAS,wBAAwB;AAE1B,MAAM,uBAAuB;AAE7B,MAAM,6BAA6B,MAAM;AAAA,EAC9C,YACE,SACS,MACT;AACA,UAAM,OAAO;AAFJ;AAGT,SAAK,OAAO;AAAA,EACd;AAAA,EAJW;AAKb;AAUO,SAAS,YAAY,WAA2B;AACrD,QAAM,OAAO,iBAAiB,YAAY,SAAS,CAAC;AACpD,QAAM,SAAS,SAAS,MAAM,EAAE,qBAAqB,KAAK,CAAC;AAC3D,MAAI,OAAO,MAAM;AACf,UAAM,IAAI,qBAAqB,wCAAwC,EAAE,UAAU,CAAC;AAAA,EACtF;AACA,MAAI,CAAC,OAAO,QAAQ;AAClB,UAAM,IAAI,qBAAqB,6BAA6B,EAAE,UAAU,CAAC;AAAA,EAC3E;AACA,SAAO,iBAAiB,OAAO,MAAM;AACvC;AAOO,SAAS,gBAAgB,GAAW,GAAoB;AAC7D,MAAI;AACJ,MAAI;AACJ,MAAI;AACF,YAAQ,YAAY,CAAC;AACrB,YAAQ,YAAY,CAAC;AAAA,EACvB,QAAQ;AACN,WAAO;AAAA,EACT;AACA,SAAO,UAAU;AACnB;AAEA,SAAS,YAAY,WAA2B;AAC9C,MAAI,UAAU,SAAS,KAAK,GAAG;AAC7B,WAAO,IAAI,IAAI,SAAS,EAAE;AAAA,EAC5B;AACA,SAAO;AACT;","names":[]}
@@ -54,6 +54,12 @@ export declare function getOrDiscoverProfile(client: TestClient, options: TestOp
54
54
  profile: AgentProfile;
55
55
  step: TestStepResult;
56
56
  }>;
57
+ /**
58
+ * Reuse the already-observed capability advertisement for signing decisions.
59
+ * This avoids a second discovery call whose failure could otherwise downgrade
60
+ * a functional compliance dispatch to unsigned.
61
+ */
62
+ export declare function seedTestClientSigningCapability(client: TestClient, profile: AgentProfile, adcpVersion?: string): void;
57
63
  /**
58
64
  * Result of {@link runStep}. The optional `caughtError` carries the raw
59
65
  * thrown value (typed `unknown`, narrow via `instanceof` at the call site)
@@ -54,6 +54,12 @@ export declare function getOrDiscoverProfile(client: TestClient, options: TestOp
54
54
  profile: AgentProfile;
55
55
  step: TestStepResult;
56
56
  }>;
57
+ /**
58
+ * Reuse the already-observed capability advertisement for signing decisions.
59
+ * This avoids a second discovery call whose failure could otherwise downgrade
60
+ * a functional compliance dispatch to unsigned.
61
+ */
62
+ export declare function seedTestClientSigningCapability(client: TestClient, profile: AgentProfile, adcpVersion?: string): void;
57
63
  /**
58
64
  * Result of {@link runStep}. The optional `caughtError` carries the raw
59
65
  * thrown value (typed `unknown`, narrow via `instanceof` at the call site)
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/lib/testing/client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/lib/testing/client.ts"],"names":[],"mappings":"AAAA;;GAEG;AAOH,OAAO,KAAK,EAIV,kBAAkB,EAClB,gBAAgB,EAChB,cAAc,EACf,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,YAAY,EAAc,MAAM,EAAE,MAAM,SAAS,CAAC;AAkC7F;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,GAAG,SAAS,CAU7E;AAID;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,WAAW,GAAG,cAAc,CAMjE;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,WAAW,GAAG,gBAAgB,CAOrE;AAaD;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAE/D;AAED;;GAEG;AACH,wBAAgB,SAAS,IAAI,MAAM,CAElC;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,GAAE,KAAK,GAAG,KAAa,EAAE,OAAO,GAAE,WAAgB,4BA2H5G;AAED,MAAM,MAAM,UAAU,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC7D,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,UAAU,CAAC;IACnB,YAAY,EAAE,OAAO,CAAC;CACvB;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,GAAG,UAAU,CAEpF;AAED,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,GAAG,oBAAoB,CAMxG;AAoID;;GAEG;AACH,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC;IAAE,OAAO,EAAE,YAAY,CAAC;IAAC,IAAI,EAAE,cAAc,CAAA;CAAE,CAAC,CAS1D;AAED;;;;GAIG;AACH,wBAAgB,+BAA+B,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,YAAY,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAwBrH;AAqDD;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,cAAc,CAAC,CAAC;IAC/B,MAAM,CAAC,EAAE,CAAC,CAAC;IACX,IAAI,EAAE,cAAc,CAAC;IACrB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,wBAAsB,OAAO,CAAC,CAAC,EAC7B,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACnB,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CA4B5B;AA0BD;;;;;;;;;;GAUG;AACH,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,UAAU,EAClB,MAAM,CAAC,EAAE,WAAW;AACpB,iFAAiF;AACjF,iBAAiB,CAAC,EAAE,MAAM,GACzB,OAAO,CAAC;IAAE,OAAO,EAAE,YAAY,CAAC;IAAC,IAAI,EAAE,cAAc,CAAA;CAAE,CAAC,CAiF1D;AAED;;;GAGG;AACH,wBAAsB,yBAAyB,CAC7C,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE,YAAY,EACrB,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC;IAAE,YAAY,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC;IAAC,KAAK,EAAE,cAAc,EAAE,CAAA;CAAE,CAAC,CAkF3E;AAED;;GAEG;AACH,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE,YAAY,GACpB,OAAO,CAAC;IAAE,OAAO,EAAE,YAAY,CAAC,mBAAmB,CAAC,CAAC;IAAC,IAAI,EAAE,cAAc,CAAA;CAAE,CAAC,CAsF/E;AAED;;GAEG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE,YAAY,EACrB,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC;IACT,OAAO,EAAE,YAAY,CAAC,mBAAmB,CAAC,CAAC;IAC3C,UAAU,EAAE,kBAAkB,CAAC,SAAS,CAAC,CAAC;IAC1C,IAAI,EAAE,cAAc,CAAC;IACrB,UAAU,CAAC,EAAE,cAAc,CAAC;CAC7B,CAAC,CAoFD;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,mBAAmB,CAAC,EAAE,MAAM,GAAG,cAAc,CAqEpH"}
@@ -31,6 +31,7 @@ __export(client_exports, {
31
31
  resolveAuthPrincipal: () => resolveAuthPrincipal,
32
32
  resolveBrand: () => resolveBrand,
33
33
  runStep: () => runStep,
34
+ seedTestClientSigningCapability: () => seedTestClientSigningCapability,
34
35
  setAgentTesterLogger: () => setAgentTesterLogger,
35
36
  validateResponseSchema: () => validateResponseSchema
36
37
  });
@@ -45,9 +46,12 @@ var import_capabilities = require('../utils/capabilities.js');
45
46
  var import_probe_policy = require('../utils/probe-policy.js');
46
47
  var import_ssrf_fetch = require('../net/ssrf-fetch.js');
47
48
  var import_version = require('../version.js');
49
+ var import_node_crypto = require("node:crypto");
48
50
  var import_client_hooks = require('../validation/client-hooks.js');
49
51
  var import_error_extraction = require('../utils/error-extraction.js');
52
+ var import_client = require('../signing/client.js');
50
53
  const TEST_CLIENT_VERSION_OPTIONS = /* @__PURE__ */ Symbol("adcp.testClientVersionOptions");
54
+ const TEST_CLIENT_SCOPE_SALT = (0, import_node_crypto.randomBytes)(32);
51
55
  function resolveAuthPrincipal(options) {
52
56
  if (!options.auth) return void 0;
53
57
  switch (options.auth.type) {
@@ -124,6 +128,9 @@ function createTestClient(agentUrl, protocol = "mcp", options = {}) {
124
128
  agentConfig.auth_token = options.auth.token;
125
129
  }
126
130
  }
131
+ if (options.functional_request_signing) {
132
+ agentConfig.request_signing = options.functional_request_signing;
133
+ }
127
134
  const multiClient = new import_ADCPMultiAgentClient.ADCPMultiAgentClient([agentConfig], {
128
135
  headers,
129
136
  validation: {
@@ -138,14 +145,21 @@ function createTestClient(agentUrl, protocol = "mcp", options = {}) {
138
145
  });
139
146
  const client = multiClient.agent("test");
140
147
  const authMode = authReuseMode(options);
148
+ const requestScopeMode = requestScopeReuseMode(options);
149
+ const requestSigningMode = requestSigningReuseMode(options.functional_request_signing);
141
150
  Object.defineProperty(client, TEST_CLIENT_VERSION_OPTIONS, {
142
151
  value: {
152
+ agentUrl,
153
+ protocol,
143
154
  adcpVersion: multiClient.getAdcpVersion(),
144
155
  ...options.wireAdcpVersion !== void 0 && { wireAdcpVersion: options.wireAdcpVersion },
145
156
  versionEnvelope: options.versionEnvelope ?? "auto",
146
157
  strictResponseSchemaValidation: options.strictResponseSchemaValidation !== false,
147
158
  ...authMode !== void 0 && { authMode },
159
+ ...requestScopeMode !== void 0 && { requestScopeMode },
160
+ ...requestSigningMode !== void 0 && { requestSigningMode },
148
161
  ...options.transport?.trustedFetchFn && { fetchFn: options.transport.trustedFetchFn },
162
+ ...options.transport?.allowPrivateIp !== void 0 && { allowPrivateIp: options.transport.allowPrivateIp },
149
163
  ...options.transport?.maxResponseBytes !== void 0 && {
150
164
  maxResponseBytes: options.transport.maxResponseBytes
151
165
  },
@@ -162,7 +176,7 @@ function getOrCreateClient(agentUrl, options) {
162
176
  }
163
177
  function getOrCreateClientResolution(agentUrl, options) {
164
178
  const shared = options._client;
165
- if (shared && isExecutableTestClient(shared) && testClientMatchesVersionOptions(shared, options)) {
179
+ if (shared && isExecutableTestClient(shared) && testClientMatchesVersionOptions(shared, agentUrl, options)) {
166
180
  return { client: shared, reusedShared: true };
167
181
  }
168
182
  return { client: createTestClient(agentUrl, options.protocol || "mcp", options), reusedShared: false };
@@ -173,17 +187,19 @@ function isExecutableTestClient(client) {
173
187
  if (typeof candidate["executeTask"] === "function" || typeof candidate["resetContext"] === "function") return true;
174
188
  return Object.entries(candidate).some(([key, value]) => key !== "getAgentInfo" && typeof value === "function");
175
189
  }
176
- function testClientMatchesVersionOptions(client, options) {
190
+ function testClientMatchesVersionOptions(client, agentUrl, options) {
177
191
  const effectiveOptions = withTestKitAuthDefaults(options);
178
192
  const meta = client[TEST_CLIENT_VERSION_OPTIONS];
179
193
  const expectedAuthMode = authReuseMode(effectiveOptions);
194
+ const expectedRequestScopeMode = requestScopeReuseMode(effectiveOptions);
195
+ const expectedRequestSigningMode = requestSigningReuseMode(effectiveOptions.functional_request_signing);
180
196
  if (!meta) {
181
- return effectiveOptions.adcpVersion === void 0 && effectiveOptions.versionEnvelope === void 0 && effectiveOptions.transport === void 0;
197
+ return expectedRequestSigningMode === void 0 && effectiveOptions.adcpVersion === void 0 && effectiveOptions.versionEnvelope === void 0 && effectiveOptions.transport === void 0;
182
198
  }
183
199
  const expectedAdcpVersion = effectiveOptions.adcpVersion ?? import_version.ADCP_VERSION;
184
200
  const expectedWireAdcpVersion = effectiveOptions.wireAdcpVersion;
185
201
  const expectedVersionEnvelope = effectiveOptions.versionEnvelope ?? "auto";
186
- return meta.adcpVersion === expectedAdcpVersion && meta.wireAdcpVersion === expectedWireAdcpVersion && meta.versionEnvelope === expectedVersionEnvelope && meta.strictResponseSchemaValidation === (effectiveOptions.strictResponseSchemaValidation !== false) && meta.authMode === expectedAuthMode && meta.fetchFn === effectiveOptions.transport?.trustedFetchFn && meta.maxResponseBytes === effectiveOptions.transport?.maxResponseBytes && meta.requestTimeoutMs === effectiveOptions.transport?.requestTimeoutMs;
202
+ return meta.agentUrl === agentUrl && meta.protocol === (effectiveOptions.protocol ?? "mcp") && meta.adcpVersion === expectedAdcpVersion && meta.wireAdcpVersion === expectedWireAdcpVersion && meta.versionEnvelope === expectedVersionEnvelope && meta.strictResponseSchemaValidation === (effectiveOptions.strictResponseSchemaValidation !== false) && meta.authMode === expectedAuthMode && meta.requestScopeMode === expectedRequestScopeMode && meta.requestSigningMode === expectedRequestSigningMode && meta.fetchFn === effectiveOptions.transport?.trustedFetchFn && meta.allowPrivateIp === effectiveOptions.transport?.allowPrivateIp && meta.maxResponseBytes === effectiveOptions.transport?.maxResponseBytes && meta.requestTimeoutMs === effectiveOptions.transport?.requestTimeoutMs;
187
203
  }
188
204
  function withTestKitAuthDefaults(options) {
189
205
  if (options.auth) return options;
@@ -214,8 +230,46 @@ function withTestKitAuthDefaults(options) {
214
230
  function authReuseMode(options) {
215
231
  return options.auth?.type;
216
232
  }
233
+ function requestScopeReuseMode(options) {
234
+ const headers = options.headers ? Object.fromEntries(
235
+ Object.entries(options.headers).map(([name, value]) => [name.toLowerCase(), value]).sort(([a], [b]) => a.localeCompare(b))
236
+ ) : void 0;
237
+ if (!options.auth && !headers && !options.test_session_id && !options.userAgent) return void 0;
238
+ return testClientScopeDisambiguator(
239
+ JSON.stringify({
240
+ auth: options.auth,
241
+ headers,
242
+ testSessionId: options.test_session_id,
243
+ userAgent: options.userAgent
244
+ })
245
+ );
246
+ }
247
+ function testClientScopeDisambiguator(value) {
248
+ return Buffer.from((0, import_node_crypto.hkdfSync)("sha256", value, TEST_CLIENT_SCOPE_SALT, "adcp-test-client-cache", 8)).toString("hex");
249
+ }
250
+ function requestSigningReuseMode(config) {
251
+ if (!config) return void 0;
252
+ const kind = config.kind === "provider" ? "provider" : "inline";
253
+ const identity = config.kind === "provider" ? {
254
+ algorithm: config.provider.algorithm,
255
+ keyid: config.provider.keyid,
256
+ adcpUse: config.provider.adcpUse,
257
+ fingerprint: config.provider.fingerprint
258
+ } : { algorithm: config.alg, keyid: config.kid, privateKey: config.private_key };
259
+ const fingerprint = (0, import_node_crypto.createHmac)("sha256", "").update(
260
+ JSON.stringify({
261
+ identity,
262
+ agentUrl: config.agent_url,
263
+ jwksUri: config.jwks_uri,
264
+ signSupported: config.sign_supported === true,
265
+ alwaysSign: config.always_sign ?? []
266
+ })
267
+ ).digest("hex").slice(0, 16);
268
+ return `${kind}:${fingerprint}`;
269
+ }
217
270
  async function getOrDiscoverProfile(client, options) {
218
271
  if (options._profile) {
272
+ seedTestClientSigningCapability(client, options._profile, options.adcpVersion);
219
273
  return {
220
274
  profile: options._profile,
221
275
  step: { step: "Discover agent capabilities", passed: true, duration_ms: 0 }
@@ -223,6 +277,22 @@ async function getOrDiscoverProfile(client, options) {
223
277
  }
224
278
  return discoverAgentProfile(client, options.signal, options.adcpVersion);
225
279
  }
280
+ function seedTestClientSigningCapability(client, profile, adcpVersion) {
281
+ const raw = profile.raw_capabilities;
282
+ if (!raw || typeof raw !== "object") return;
283
+ const clientAccess = client;
284
+ if (typeof clientAccess.getAgent !== "function") return;
285
+ const agent = clientAccess.getAgent.call(client);
286
+ const meta = client[TEST_CLIENT_VERSION_OPTIONS];
287
+ const effectiveVersion = meta?.wireAdcpVersion ?? meta?.adcpVersion ?? (typeof clientAccess.getAdcpVersion === "function" ? clientAccess.getAdcpVersion.call(client) : void 0) ?? adcpVersion;
288
+ const signingContext = (0, import_client.buildAgentSigningContext)(agent, { adcpVersion: effectiveVersion });
289
+ if (!signingContext) return;
290
+ signingContext.cache.set(signingContext.capabilityCacheKey, {
291
+ requestSigning: raw.request_signing,
292
+ adcpVersion: profile.adcp_major_versions?.[0],
293
+ fetchedAt: Math.floor(Date.now() / 1e3)
294
+ });
295
+ }
226
296
  function raceWithSignal(promise, signal) {
227
297
  if (!signal) return promise;
228
298
  if (signal.aborted) {
@@ -354,6 +424,7 @@ async function discoverAgentProfile(client, signal, schemaAdcpVersion) {
354
424
  profile.capabilities_probe_error = err?.message || String(err);
355
425
  }
356
426
  }
427
+ seedTestClientSigningCapability(client, profile, schemaAdcpVersion);
357
428
  return { profile, step };
358
429
  }
359
430
  async function discoverAgentCapabilities(client, profile, options) {
@@ -667,6 +738,7 @@ function zodCodeToKeyword(code) {
667
738
  resolveAuthPrincipal,
668
739
  resolveBrand,
669
740
  runStep,
741
+ seedTestClientSigningCapability,
670
742
  setAgentTesterLogger,
671
743
  validateResponseSchema
672
744
  });