@adcp/sdk 13.0.4 → 13.1.1
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.
- package/bin/adcp-grade.js +4 -3
- package/bin/adcp.js +18 -0
- package/dist/lib/conformance/invariants/uniformError.d.ts.map +1 -1
- package/dist/lib/conformance/invariants/uniformError.js +21 -0
- package/dist/lib/conformance/invariants/uniformError.js.map +1 -1
- package/dist/lib/conformance/invariants/uniformError.mjs +22 -1
- package/dist/lib/conformance/invariants/uniformError.mjs.map +1 -1
- package/dist/lib/core/AgentClient.d.mts +1 -1
- package/dist/lib/core/AgentClient.d.ts +1 -1
- package/dist/lib/core/AgentClient.d.ts.map +1 -1
- package/dist/lib/core/AgentClient.js +1 -1
- package/dist/lib/core/AgentClient.js.map +1 -1
- package/dist/lib/core/AgentClient.mjs +1 -1
- package/dist/lib/core/AgentClient.mjs.map +1 -1
- package/dist/lib/core/SingleAgentClient.d.mts +1 -1
- package/dist/lib/core/SingleAgentClient.d.ts +1 -1
- package/dist/lib/core/SingleAgentClient.d.ts.map +1 -1
- package/dist/lib/core/SingleAgentClient.js +88 -48
- package/dist/lib/core/SingleAgentClient.js.map +1 -1
- package/dist/lib/core/SingleAgentClient.mjs +88 -48
- package/dist/lib/core/SingleAgentClient.mjs.map +1 -1
- package/dist/lib/core/TaskExecutor.d.ts.map +1 -1
- package/dist/lib/core/TaskExecutor.js +8 -4
- package/dist/lib/core/TaskExecutor.js.map +1 -1
- package/dist/lib/core/TaskExecutor.mjs +8 -4
- package/dist/lib/core/TaskExecutor.mjs.map +1 -1
- package/dist/lib/index.d.mts +1 -1
- package/dist/lib/index.d.ts +1 -1
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/index.mjs.map +1 -1
- package/dist/lib/net/agent-transport-fetch.d.mts +15 -0
- package/dist/lib/net/agent-transport-fetch.d.ts +15 -0
- package/dist/lib/net/agent-transport-fetch.d.ts.map +1 -1
- package/dist/lib/net/agent-transport-fetch.js +37 -2
- package/dist/lib/net/agent-transport-fetch.js.map +1 -1
- package/dist/lib/net/agent-transport-fetch.mjs +37 -2
- package/dist/lib/net/agent-transport-fetch.mjs.map +1 -1
- package/dist/lib/net/credential-headers.d.mts +1 -0
- package/dist/lib/net/credential-headers.d.ts +2 -0
- package/dist/lib/net/credential-headers.d.ts.map +1 -0
- package/dist/lib/net/credential-headers.js +34 -0
- package/dist/lib/net/credential-headers.js.map +1 -0
- package/dist/lib/net/credential-headers.mjs +10 -0
- package/dist/lib/net/credential-headers.mjs.map +1 -0
- package/dist/lib/protocols/a2a-native-v1.d.mts +18 -0
- package/dist/lib/protocols/a2a-native-v1.d.ts +19 -0
- package/dist/lib/protocols/a2a-native-v1.d.ts.map +1 -0
- package/dist/lib/protocols/a2a-native-v1.js +183 -0
- package/dist/lib/protocols/a2a-native-v1.js.map +1 -0
- package/dist/lib/protocols/a2a-native-v1.mjs +167 -0
- package/dist/lib/protocols/a2a-native-v1.mjs.map +1 -0
- package/dist/lib/protocols/a2a-push-notification.d.mts +13 -0
- package/dist/lib/protocols/a2a-push-notification.d.ts +14 -0
- package/dist/lib/protocols/a2a-push-notification.d.ts.map +1 -0
- package/dist/lib/protocols/a2a-push-notification.js +42 -0
- package/dist/lib/protocols/a2a-push-notification.js.map +1 -0
- package/dist/lib/protocols/a2a-push-notification.mjs +23 -0
- package/dist/lib/protocols/a2a-push-notification.mjs.map +1 -0
- package/dist/lib/protocols/a2a.d.mts +15 -5
- package/dist/lib/protocols/a2a.d.ts +15 -5
- package/dist/lib/protocols/a2a.d.ts.map +1 -1
- package/dist/lib/protocols/a2a.js +102 -13
- package/dist/lib/protocols/a2a.js.map +1 -1
- package/dist/lib/protocols/a2a.mjs +90 -12
- package/dist/lib/protocols/a2a.mjs.map +1 -1
- package/dist/lib/protocols/index.d.mts +3 -0
- package/dist/lib/protocols/index.d.ts +3 -0
- package/dist/lib/protocols/index.d.ts.map +1 -1
- package/dist/lib/protocols/index.js +18 -6
- package/dist/lib/protocols/index.js.map +1 -1
- package/dist/lib/protocols/index.mjs +18 -6
- package/dist/lib/protocols/index.mjs.map +1 -1
- package/dist/lib/protocols/mcp-modern.d.ts.map +1 -1
- package/dist/lib/protocols/mcp-modern.js +3 -1
- package/dist/lib/protocols/mcp-modern.js.map +1 -1
- package/dist/lib/protocols/mcp-modern.mjs +3 -1
- package/dist/lib/protocols/mcp-modern.mjs.map +1 -1
- package/dist/lib/protocols/mcp-tasks.d.ts.map +1 -1
- package/dist/lib/protocols/mcp-tasks.js +2 -1
- package/dist/lib/protocols/mcp-tasks.js.map +1 -1
- package/dist/lib/protocols/mcp-tasks.mjs +2 -1
- package/dist/lib/protocols/mcp-tasks.mjs.map +1 -1
- package/dist/lib/protocols/mcp.d.mts +8 -5
- package/dist/lib/protocols/mcp.d.ts +8 -5
- package/dist/lib/protocols/mcp.d.ts.map +1 -1
- package/dist/lib/protocols/mcp.js +12 -4
- package/dist/lib/protocols/mcp.js.map +1 -1
- package/dist/lib/protocols/mcp.mjs +12 -4
- package/dist/lib/protocols/mcp.mjs.map +1 -1
- package/dist/lib/protocols/rawResponseCapture.d.mts +9 -1
- package/dist/lib/protocols/rawResponseCapture.d.ts +9 -1
- package/dist/lib/protocols/rawResponseCapture.d.ts.map +1 -1
- package/dist/lib/protocols/rawResponseCapture.js +153 -10
- package/dist/lib/protocols/rawResponseCapture.js.map +1 -1
- package/dist/lib/protocols/rawResponseCapture.mjs +153 -10
- package/dist/lib/protocols/rawResponseCapture.mjs.map +1 -1
- package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
- package/dist/lib/signing/agent-fetch.d.mts +3 -1
- package/dist/lib/signing/agent-fetch.d.ts +3 -1
- package/dist/lib/signing/agent-fetch.d.ts.map +1 -1
- package/dist/lib/signing/agent-fetch.js +4 -2
- package/dist/lib/signing/agent-fetch.js.map +1 -1
- package/dist/lib/signing/agent-fetch.mjs +4 -2
- package/dist/lib/signing/agent-fetch.mjs.map +1 -1
- package/dist/lib/signing/protocol-methods.d.mts +24 -0
- package/dist/lib/signing/protocol-methods.d.ts +25 -0
- package/dist/lib/signing/protocol-methods.d.ts.map +1 -0
- package/dist/lib/signing/protocol-methods.js +51 -0
- package/dist/lib/signing/protocol-methods.js.map +1 -0
- package/dist/lib/signing/protocol-methods.mjs +25 -0
- package/dist/lib/signing/protocol-methods.mjs.map +1 -0
- package/dist/lib/signing/types.d.mts +2 -0
- package/dist/lib/signing/types.d.ts +2 -0
- package/dist/lib/signing/types.d.ts.map +1 -1
- package/dist/lib/signing/types.js.map +1 -1
- package/dist/lib/signing/types.mjs.map +1 -1
- package/dist/lib/signing/verifier.d.ts.map +1 -1
- package/dist/lib/signing/verifier.js +6 -2
- package/dist/lib/signing/verifier.js.map +1 -1
- package/dist/lib/signing/verifier.mjs +6 -2
- package/dist/lib/signing/verifier.mjs.map +1 -1
- package/dist/lib/testing/client.d.ts.map +1 -1
- package/dist/lib/testing/client.js +4 -1
- package/dist/lib/testing/client.js.map +1 -1
- package/dist/lib/testing/client.mjs +4 -1
- package/dist/lib/testing/client.mjs.map +1 -1
- package/dist/lib/testing/compliance/comply.d.ts.map +1 -1
- package/dist/lib/testing/compliance/comply.js +3 -0
- package/dist/lib/testing/compliance/comply.js.map +1 -1
- package/dist/lib/testing/compliance/comply.mjs +3 -0
- package/dist/lib/testing/compliance/comply.mjs.map +1 -1
- package/dist/lib/testing/storyboard/agent-routing.d.mts +3 -1
- package/dist/lib/testing/storyboard/agent-routing.d.ts +3 -1
- package/dist/lib/testing/storyboard/agent-routing.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/agent-routing.js +5 -2
- package/dist/lib/testing/storyboard/agent-routing.js.map +1 -1
- package/dist/lib/testing/storyboard/agent-routing.mjs +4 -2
- package/dist/lib/testing/storyboard/agent-routing.mjs.map +1 -1
- package/dist/lib/testing/storyboard/compliance.d.mts +1 -1
- package/dist/lib/testing/storyboard/compliance.d.ts +1 -1
- package/dist/lib/testing/storyboard/compliance.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/compliance.js +4 -1
- package/dist/lib/testing/storyboard/compliance.js.map +1 -1
- package/dist/lib/testing/storyboard/compliance.mjs +4 -1
- package/dist/lib/testing/storyboard/compliance.mjs.map +1 -1
- package/dist/lib/testing/storyboard/native-a2a-compliance.d.mts +7 -0
- package/dist/lib/testing/storyboard/native-a2a-compliance.d.ts +8 -0
- package/dist/lib/testing/storyboard/native-a2a-compliance.d.ts.map +1 -0
- package/dist/lib/testing/storyboard/native-a2a-compliance.js +45 -0
- package/dist/lib/testing/storyboard/native-a2a-compliance.js.map +1 -0
- package/dist/lib/testing/storyboard/native-a2a-compliance.mjs +21 -0
- package/dist/lib/testing/storyboard/native-a2a-compliance.mjs.map +1 -0
- package/dist/lib/testing/storyboard/probes.d.mts +2 -0
- package/dist/lib/testing/storyboard/probes.d.ts +2 -0
- package/dist/lib/testing/storyboard/probes.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/probes.js +3 -2
- package/dist/lib/testing/storyboard/probes.js.map +1 -1
- package/dist/lib/testing/storyboard/probes.mjs +3 -2
- package/dist/lib/testing/storyboard/probes.mjs.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/a2a-dispatch.d.mts +31 -0
- package/dist/lib/testing/storyboard/request-signing/a2a-dispatch.d.ts +32 -0
- package/dist/lib/testing/storyboard/request-signing/a2a-dispatch.d.ts.map +1 -0
- package/dist/lib/testing/storyboard/request-signing/a2a-dispatch.js +279 -0
- package/dist/lib/testing/storyboard/request-signing/a2a-dispatch.js.map +1 -0
- package/dist/lib/testing/storyboard/request-signing/a2a-dispatch.mjs +244 -0
- package/dist/lib/testing/storyboard/request-signing/a2a-dispatch.mjs.map +1 -0
- package/dist/lib/testing/storyboard/request-signing/builder.d.mts +4 -1
- package/dist/lib/testing/storyboard/request-signing/builder.d.ts +4 -1
- package/dist/lib/testing/storyboard/request-signing/builder.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/builder.js +19 -0
- package/dist/lib/testing/storyboard/request-signing/builder.js.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/builder.mjs +19 -0
- package/dist/lib/testing/storyboard/request-signing/builder.mjs.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/grader.d.mts +5 -6
- package/dist/lib/testing/storyboard/request-signing/grader.d.ts +5 -6
- package/dist/lib/testing/storyboard/request-signing/grader.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/grader.js +82 -13
- package/dist/lib/testing/storyboard/request-signing/grader.js.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/grader.mjs +86 -13
- package/dist/lib/testing/storyboard/request-signing/grader.mjs.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/index.d.mts +1 -0
- package/dist/lib/testing/storyboard/request-signing/index.d.ts +1 -0
- package/dist/lib/testing/storyboard/request-signing/index.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/index.js +7 -0
- package/dist/lib/testing/storyboard/request-signing/index.js.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/index.mjs +8 -0
- package/dist/lib/testing/storyboard/request-signing/index.mjs.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/probe-dispatch.d.mts +2 -2
- package/dist/lib/testing/storyboard/request-signing/probe-dispatch.d.ts +2 -2
- package/dist/lib/testing/storyboard/request-signing/probe-dispatch.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/probe-dispatch.js +6 -5
- package/dist/lib/testing/storyboard/request-signing/probe-dispatch.js.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/probe-dispatch.mjs +6 -5
- package/dist/lib/testing/storyboard/request-signing/probe-dispatch.mjs.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/types.d.mts +1 -0
- package/dist/lib/testing/storyboard/request-signing/types.d.ts +1 -0
- package/dist/lib/testing/storyboard/request-signing/types.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/types.js.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/types.mjs.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/vector-loader.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/vector-loader.js +1 -0
- package/dist/lib/testing/storyboard/request-signing/vector-loader.js.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/vector-loader.mjs +1 -0
- package/dist/lib/testing/storyboard/request-signing/vector-loader.mjs.map +1 -1
- package/dist/lib/testing/storyboard/runner.d.mts +28 -1
- package/dist/lib/testing/storyboard/runner.d.ts +28 -1
- package/dist/lib/testing/storyboard/runner.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/runner.js +251 -55
- package/dist/lib/testing/storyboard/runner.js.map +1 -1
- package/dist/lib/testing/storyboard/runner.mjs +255 -60
- package/dist/lib/testing/storyboard/runner.mjs.map +1 -1
- package/dist/lib/testing/storyboard/types.d.mts +4 -2
- package/dist/lib/testing/storyboard/types.d.ts +4 -2
- package/dist/lib/testing/storyboard/types.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/types.js +1 -0
- package/dist/lib/testing/storyboard/types.js.map +1 -1
- package/dist/lib/testing/storyboard/types.mjs +1 -0
- package/dist/lib/testing/storyboard/types.mjs.map +1 -1
- package/dist/lib/testing/storyboard/validations.d.mts +1 -1
- package/dist/lib/testing/storyboard/validations.d.ts +1 -1
- package/dist/lib/testing/storyboard/validations.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/validations.js +97 -22
- package/dist/lib/testing/storyboard/validations.js.map +1 -1
- package/dist/lib/testing/storyboard/validations.mjs +98 -22
- package/dist/lib/testing/storyboard/validations.mjs.map +1 -1
- package/dist/lib/version.d.mts +3 -3
- package/dist/lib/version.d.ts +3 -3
- package/dist/lib/version.js +3 -3
- package/dist/lib/version.js.map +1 -1
- package/dist/lib/version.mjs +3 -3
- package/dist/lib/version.mjs.map +1 -1
- package/docs/guides/BUILD-AN-AGENT.md +1 -1
- package/docs/llms.txt +5 -3
- package/package.json +2 -1
|
@@ -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';\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}\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 coverContentDigest: CoverContentDigestPredicate = (_url, _init) => {\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 });\n }\n return createSigningFetch(upstream, toSignerKey(signing), { shouldSign, coverContentDigest });\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 /**\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), 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 cacheKey = buildCapabilityCacheKey(options.sellerAgentUri, options.sellerAuthToken);\n return buildAgentSigningFetch({\n signing: options.signing,\n upstream: options.upstream,\n getCapability: () => cache.get(cacheKey),\n });\n}\n"],"mappings":"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","names":[]}
|
|
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';\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 0.3 `message/send` / `message/stream`: the op name lives on the first\n * data-kind part as `data.skill`.\n * - A2A 1.x `SendMessage` / `SendStreamingMessage`: proto-JSON DataParts omit\n * the legacy `kind` discriminator and carry the op at `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 const isLegacyA2a = rpc.method === 'message/send' || rpc.method === 'message/stream';\n const isNativeA2a = rpc.method === 'SendMessage' || rpc.method === 'SendStreamingMessage';\n if (isLegacyA2a || isNativeA2a) {\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 ((isNativeA2a || 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}\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 coverContentDigest: CoverContentDigestPredicate = (_url, _init) => {\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 });\n }\n return createSigningFetch(upstream, toSignerKey(signing), { shouldSign, coverContentDigest });\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 /**\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), 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 cacheKey = buildCapabilityCacheKey(options.sellerAgentUri, options.sellerAuthToken);\n return buildAgentSigningFetch({\n signing: options.signing,\n upstream: options.upstream,\n getCapability: () => cache.get(cacheKey),\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAKA,mBAAqE;AACrE,yBAAwC;AACxC,8BAKO;AAGP,oCAA8C;AAG9C,MAAM,oCAAoC;AAU1C,SAAS,kBAA6B;AACpC,QAAM,IAAK,WAAqC;AAChD,MAAI,OAAO,MAAM,YAAY;AAC3B,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,WAAW,MAAmC;AACrD,MAAI,SAAS,UAAa,SAAS,KAAM,QAAO;AAChD,MAAI,OAAO,SAAS,SAAU,QAAO,KAAK,SAAS,OAAO;AAC1D,MAAI,gBAAgB,WAAY,QAAO,OAAO,KAAK,IAAI,EAAE,SAAS,MAAM;AACxE,MAAI,gBAAgB,YAAa,QAAO,OAAO,KAAK,IAAI,EAAE,SAAS,MAAM;AAMzE,QAAM,IAAI;AAAA,IACR,oFAAoF,aAAa,IAAI,CAAC;AAAA,EACxG;AACF;AAEA,SAAS,aAAa,MAAuB;AAC3C,MAAI,QAAQ,OAAO,SAAS,UAAU;AACpC,UAAM,OAAQ,KAA6C,aAAa;AACxE,QAAI,KAAM,QAAO;AAAA,EACnB;AACA,SAAO,OAAO;AAChB;AAkBO,SAAS,qBAAqB,MAAmC;AACtE,QAAM,OAAO,WAAW,IAAI;AAC5B,MAAI,CAAC,KAAM,QAAO;AAClB,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,IAAI;AAAA,EAC1B,QAAQ;AACN,WAAO;AAAA,EACT;AACA,MAAI,CAAC,UAAU,OAAO,WAAW,SAAU,QAAO;AAClD,QAAM,MAAM;AAEZ,MAAI,IAAI,WAAW,cAAc;AAC/B,UAAM,SAAS,IAAI;AACnB,WAAO,OAAO,QAAQ,SAAS,WAAW,OAAO,OAAO;AAAA,EAC1D;AAEA,QAAM,cAAc,IAAI,WAAW,kBAAkB,IAAI,WAAW;AACpE,QAAM,cAAc,IAAI,WAAW,iBAAiB,IAAI,WAAW;AACnE,MAAI,eAAe,aAAa;AAC9B,UAAM,SAAS,IAAI;AACnB,UAAM,QAAQ,QAAQ,SAAS;AAC/B,QAAI,CAAC,MAAM,QAAQ,KAAK,EAAG,QAAO;AAClC,eAAW,QAAQ,OAAO;AACxB,UAAI,QAAQ,OAAO,SAAS,UAAU;AACpC,cAAM,IAAI;AACV,aAAK,eAAe,EAAE,SAAS,WAAW,OAAO,EAAE,MAAM,UAAU,UAAU;AAC3E,iBAAO,EAAE,KAAK;AAAA,QAChB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AASA,SAAS,6BAA6B,MAAwB;AAC5D,QAAM,OAAO,WAAW,IAAI;AAC5B,MAAI,CAAC,KAAM,QAAO;AAClB,MAAI,KAAK,SAAS,kCAAmC,QAAO;AAC5D,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,IAAI;AAAA,EAC1B,QAAQ;AACN,WAAO;AAAA,EACT;AACA,aAAO,6DAA8B,MAAM;AAC7C;AAqBO,SAAS,oBACd,WACA,YACA,QACS;AACT,MAAI,CAAC,UAAW,QAAO;AACvB,MAAI,OAAO,aAAa,SAAS,SAAS,EAAG,QAAO;AACpD,MAAI,CAAC,YAAY,UAAW,QAAO;AACnC,MAAI,WAAW,cAAc,SAAS,SAAS,EAAG,QAAO;AACzD,MAAI,WAAW,UAAU,SAAS,SAAS,EAAG,QAAO;AACrD,MAAI,OAAO,kBAAkB,WAAW,eAAe,SAAS,SAAS,EAAG,QAAO;AACnF,SAAO;AACT;AAWO,SAAS,0BAA0B,QAAkD;AAC1F,MAAI,WAAW,YAAa,QAAO;AACnC,SAAO;AACT;AAQO,SAAS,YAAY,QAAoD;AAC9E,SAAO;AAAA,IACL,OAAO,OAAO;AAAA,IACd,KAAK,OAAO;AAAA,IACZ,YAAY,OAAO;AAAA,EACrB;AACF;AAGO,SAAS,sBAAsB,QAA8E;AAClH,SAAO,OAAO,SAAS;AACzB;AAGO,SAAS,wBACd,QAC6C;AAC7C,SAAO,OAAO,SAAS;AACzB;AAyBO,SAAS,uBAAuB,SAAmD;AACxF,QAAM,EAAE,SAAS,cAAc,IAAI;AAKnC,QAAM,mBAAmB,QAAQ;AACjC,QAAM,WAAsB,qBAAqB,CAAC,OAAO,SAAS,gBAAgB,EAAE,OAAO,IAAI;AAE/F,QAAM,aAAa,CAAC,MAAc,SAA2C;AAC3E,QAAI,6BAA6B,MAAM,IAAI,EAAG,QAAO;AACrD,UAAM,YAAY,qBAAqB,MAAM,IAAI;AACjD,UAAM,QAAQ,cAAc;AAC5B,WAAO,oBAAoB,WAAW,OAAO,gBAAgB,OAAO;AAAA,EACtE;AAEA,QAAM,qBAAkD,CAAC,MAAM,UAAU;AACvE,UAAM,QAAQ,cAAc;AAC5B,WAAO,0BAA0B,OAAO,gBAAgB,qBAAqB;AAAA,EAC/E;AAEA,MAAI,wBAAwB,OAAO,GAAG;AAQpC,UAAM,SAAS,uBAAuB,QAAQ,QAAQ;AACtD,eAAO,4CAAwB,UAAU,QAAQ,EAAE,YAAY,mBAAmB,CAAC;AAAA,EACrF;AACA,aAAO,iCAAmB,UAAU,YAAY,OAAO,GAAG,EAAE,YAAY,mBAAmB,CAAC;AAC9F;AAEA,SAAS,uBAAuB,UAAyD;AACvF,QAAM,QAAQ,SAAS;AACvB,QAAM,YAAY,SAAS;AAC3B,QAAM,cAAc,SAAS;AAC7B,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,MAAM,CAAC,YAAwB,SAAS,KAAK,OAAO;AAAA,EACtD;AACF;AA8FO,SAAS,uBAAuB,SAAmD;AACxF,QAAM,QAAQ,QAAQ,SAAS;AAC/B,QAAM,eAAW,iDAAwB,QAAQ,gBAAgB,QAAQ,eAAe;AACxF,SAAO,uBAAuB;AAAA,IAC5B,SAAS,QAAQ;AAAA,IACjB,UAAU,QAAQ;AAAA,IAClB,eAAe,MAAM,MAAM,IAAI,QAAQ;AAAA,EACzC,CAAC;AACH;","names":[]}
|
|
@@ -46,14 +46,16 @@ function extractAdcpOperation(body) {
|
|
|
46
46
|
const params = rpc.params;
|
|
47
47
|
return typeof params?.name === "string" ? params.name : void 0;
|
|
48
48
|
}
|
|
49
|
-
|
|
49
|
+
const isLegacyA2a = rpc.method === "message/send" || rpc.method === "message/stream";
|
|
50
|
+
const isNativeA2a = rpc.method === "SendMessage" || rpc.method === "SendStreamingMessage";
|
|
51
|
+
if (isLegacyA2a || isNativeA2a) {
|
|
50
52
|
const params = rpc.params;
|
|
51
53
|
const parts = params?.message?.parts;
|
|
52
54
|
if (!Array.isArray(parts)) return void 0;
|
|
53
55
|
for (const part of parts) {
|
|
54
56
|
if (part && typeof part === "object") {
|
|
55
57
|
const p = part;
|
|
56
|
-
if (p.kind === "data" && typeof p.data?.skill === "string") {
|
|
58
|
+
if ((isNativeA2a || p.kind === "data") && typeof p.data?.skill === "string") {
|
|
57
59
|
return p.data.skill;
|
|
58
60
|
}
|
|
59
61
|
}
|
|
@@ -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';\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}\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 coverContentDigest: CoverContentDigestPredicate = (_url, _init) => {\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 });\n }\n return createSigningFetch(upstream, toSignerKey(signing), { shouldSign, coverContentDigest });\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 /**\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), 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 cacheKey = buildCapabilityCacheKey(options.sellerAgentUri, options.sellerAuthToken);\n return buildAgentSigningFetch({\n signing: options.signing,\n upstream: options.upstream,\n getCapability: () => cache.get(cacheKey),\n });\n}\n"],"mappings":"AAKA,SAAS,0BAA4D;AACrE,SAAS,+BAA+B;AACxC;AAAA,EACE;AAAA,EACA;AAAA,OAGK;AAGP,SAAS,qCAAqC;AAG9C,MAAM,oCAAoC;AAU1C,SAAS,kBAA6B;AACpC,QAAM,IAAK,WAAqC;AAChD,MAAI,OAAO,MAAM,YAAY;AAC3B,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,WAAW,MAAmC;AACrD,MAAI,SAAS,UAAa,SAAS,KAAM,QAAO;AAChD,MAAI,OAAO,SAAS,SAAU,QAAO,KAAK,SAAS,OAAO;AAC1D,MAAI,gBAAgB,WAAY,QAAO,OAAO,KAAK,IAAI,EAAE,SAAS,MAAM;AACxE,MAAI,gBAAgB,YAAa,QAAO,OAAO,KAAK,IAAI,EAAE,SAAS,MAAM;AAMzE,QAAM,IAAI;AAAA,IACR,oFAAoF,aAAa,IAAI,CAAC;AAAA,EACxG;AACF;AAEA,SAAS,aAAa,MAAuB;AAC3C,MAAI,QAAQ,OAAO,SAAS,UAAU;AACpC,UAAM,OAAQ,KAA6C,aAAa;AACxE,QAAI,KAAM,QAAO;AAAA,EACnB;AACA,SAAO,OAAO;AAChB;AAgBO,SAAS,qBAAqB,MAAmC;AACtE,QAAM,OAAO,WAAW,IAAI;AAC5B,MAAI,CAAC,KAAM,QAAO;AAClB,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,IAAI;AAAA,EAC1B,QAAQ;AACN,WAAO;AAAA,EACT;AACA,MAAI,CAAC,UAAU,OAAO,WAAW,SAAU,QAAO;AAClD,QAAM,MAAM;AAEZ,MAAI,IAAI,WAAW,cAAc;AAC/B,UAAM,SAAS,IAAI;AACnB,WAAO,OAAO,QAAQ,SAAS,WAAW,OAAO,OAAO;AAAA,EAC1D;AAEA,MAAI,IAAI,WAAW,kBAAkB,IAAI,WAAW,kBAAkB;AACpE,UAAM,SAAS,IAAI;AACnB,UAAM,QAAQ,QAAQ,SAAS;AAC/B,QAAI,CAAC,MAAM,QAAQ,KAAK,EAAG,QAAO;AAClC,eAAW,QAAQ,OAAO;AACxB,UAAI,QAAQ,OAAO,SAAS,UAAU;AACpC,cAAM,IAAI;AACV,YAAI,EAAE,SAAS,UAAU,OAAO,EAAE,MAAM,UAAU,UAAU;AAC1D,iBAAO,EAAE,KAAK;AAAA,QAChB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AASA,SAAS,6BAA6B,MAAwB;AAC5D,QAAM,OAAO,WAAW,IAAI;AAC5B,MAAI,CAAC,KAAM,QAAO;AAClB,MAAI,KAAK,SAAS,kCAAmC,QAAO;AAC5D,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,IAAI;AAAA,EAC1B,QAAQ;AACN,WAAO;AAAA,EACT;AACA,SAAO,8BAA8B,MAAM;AAC7C;AAqBO,SAAS,oBACd,WACA,YACA,QACS;AACT,MAAI,CAAC,UAAW,QAAO;AACvB,MAAI,OAAO,aAAa,SAAS,SAAS,EAAG,QAAO;AACpD,MAAI,CAAC,YAAY,UAAW,QAAO;AACnC,MAAI,WAAW,cAAc,SAAS,SAAS,EAAG,QAAO;AACzD,MAAI,WAAW,UAAU,SAAS,SAAS,EAAG,QAAO;AACrD,MAAI,OAAO,kBAAkB,WAAW,eAAe,SAAS,SAAS,EAAG,QAAO;AACnF,SAAO;AACT;AAWO,SAAS,0BAA0B,QAAkD;AAC1F,MAAI,WAAW,YAAa,QAAO;AACnC,SAAO;AACT;AAQO,SAAS,YAAY,QAAoD;AAC9E,SAAO;AAAA,IACL,OAAO,OAAO;AAAA,IACd,KAAK,OAAO;AAAA,IACZ,YAAY,OAAO;AAAA,EACrB;AACF;AAGO,SAAS,sBAAsB,QAA8E;AAClH,SAAO,OAAO,SAAS;AACzB;AAGO,SAAS,wBACd,QAC6C;AAC7C,SAAO,OAAO,SAAS;AACzB;AAyBO,SAAS,uBAAuB,SAAmD;AACxF,QAAM,EAAE,SAAS,cAAc,IAAI;AAKnC,QAAM,mBAAmB,QAAQ;AACjC,QAAM,WAAsB,qBAAqB,CAAC,OAAO,SAAS,gBAAgB,EAAE,OAAO,IAAI;AAE/F,QAAM,aAAa,CAAC,MAAc,SAA2C;AAC3E,QAAI,6BAA6B,MAAM,IAAI,EAAG,QAAO;AACrD,UAAM,YAAY,qBAAqB,MAAM,IAAI;AACjD,UAAM,QAAQ,cAAc;AAC5B,WAAO,oBAAoB,WAAW,OAAO,gBAAgB,OAAO;AAAA,EACtE;AAEA,QAAM,qBAAkD,CAAC,MAAM,UAAU;AACvE,UAAM,QAAQ,cAAc;AAC5B,WAAO,0BAA0B,OAAO,gBAAgB,qBAAqB;AAAA,EAC/E;AAEA,MAAI,wBAAwB,OAAO,GAAG;AAQpC,UAAM,SAAS,uBAAuB,QAAQ,QAAQ;AACtD,WAAO,wBAAwB,UAAU,QAAQ,EAAE,YAAY,mBAAmB,CAAC;AAAA,EACrF;AACA,SAAO,mBAAmB,UAAU,YAAY,OAAO,GAAG,EAAE,YAAY,mBAAmB,CAAC;AAC9F;AAEA,SAAS,uBAAuB,UAAyD;AACvF,QAAM,QAAQ,SAAS;AACvB,QAAM,YAAY,SAAS;AAC3B,QAAM,cAAc,SAAS;AAC7B,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,MAAM,CAAC,YAAwB,SAAS,KAAK,OAAO;AAAA,EACtD;AACF;AA8FO,SAAS,uBAAuB,SAAmD;AACxF,QAAM,QAAQ,QAAQ,SAAS;AAC/B,QAAM,WAAW,wBAAwB,QAAQ,gBAAgB,QAAQ,eAAe;AACxF,SAAO,uBAAuB;AAAA,IAC5B,SAAS,QAAQ;AAAA,IACjB,UAAU,QAAQ;AAAA,IAClB,eAAe,MAAM,MAAM,IAAI,QAAQ;AAAA,EACzC,CAAC;AACH;","names":[]}
|
|
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';\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 0.3 `message/send` / `message/stream`: the op name lives on the first\n * data-kind part as `data.skill`.\n * - A2A 1.x `SendMessage` / `SendStreamingMessage`: proto-JSON DataParts omit\n * the legacy `kind` discriminator and carry the op at `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 const isLegacyA2a = rpc.method === 'message/send' || rpc.method === 'message/stream';\n const isNativeA2a = rpc.method === 'SendMessage' || rpc.method === 'SendStreamingMessage';\n if (isLegacyA2a || isNativeA2a) {\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 ((isNativeA2a || 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}\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 coverContentDigest: CoverContentDigestPredicate = (_url, _init) => {\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 });\n }\n return createSigningFetch(upstream, toSignerKey(signing), { shouldSign, coverContentDigest });\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 /**\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), 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 cacheKey = buildCapabilityCacheKey(options.sellerAgentUri, options.sellerAuthToken);\n return buildAgentSigningFetch({\n signing: options.signing,\n upstream: options.upstream,\n getCapability: () => cache.get(cacheKey),\n });\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A2A 1.0 JSON-RPC method names mapped to their stable 0.3 spellings.
|
|
3
|
+
*
|
|
4
|
+
* Kept in sync with @a2a-js/sdk-v1's `V1_TO_LEGACY_JSONRPC` table. We keep
|
|
5
|
+
* this small synchronous copy so importing the general signing verifier does
|
|
6
|
+
* not eagerly load the 1.0 SDK client. Tests compare every entry against the
|
|
7
|
+
* official SDK's exported `v1MethodToLegacyJsonRpc()` translator.
|
|
8
|
+
*/
|
|
9
|
+
export declare const A2A_V1_TO_LEGACY_JSONRPC: Readonly<{
|
|
10
|
+
readonly SendMessage: "message/send";
|
|
11
|
+
readonly SendStreamingMessage: "message/stream";
|
|
12
|
+
readonly GetTask: "tasks/get";
|
|
13
|
+
readonly CancelTask: "tasks/cancel";
|
|
14
|
+
readonly SubscribeToTask: "tasks/resubscribe";
|
|
15
|
+
readonly CreateTaskPushNotificationConfig: "tasks/pushNotificationConfig/set";
|
|
16
|
+
readonly GetTaskPushNotificationConfig: "tasks/pushNotificationConfig/get";
|
|
17
|
+
readonly ListTaskPushNotificationConfigs: "tasks/pushNotificationConfig/list";
|
|
18
|
+
readonly DeleteTaskPushNotificationConfig: "tasks/pushNotificationConfig/delete";
|
|
19
|
+
readonly GetExtendedAgentCard: "agent/getAuthenticatedExtendedCard";
|
|
20
|
+
}>;
|
|
21
|
+
/** Canonicalize either official 1.0 or legacy JSON-RPC spelling to legacy. */
|
|
22
|
+
export declare function canonicalA2AProtocolMethod(method: string): string;
|
|
23
|
+
/** Compare capability method declarations independent of A2A wire version. */
|
|
24
|
+
export declare function protocolMethodListIncludes(methods: readonly string[], candidate: string): boolean;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A2A 1.0 JSON-RPC method names mapped to their stable 0.3 spellings.
|
|
3
|
+
*
|
|
4
|
+
* Kept in sync with @a2a-js/sdk-v1's `V1_TO_LEGACY_JSONRPC` table. We keep
|
|
5
|
+
* this small synchronous copy so importing the general signing verifier does
|
|
6
|
+
* not eagerly load the 1.0 SDK client. Tests compare every entry against the
|
|
7
|
+
* official SDK's exported `v1MethodToLegacyJsonRpc()` translator.
|
|
8
|
+
*/
|
|
9
|
+
export declare const A2A_V1_TO_LEGACY_JSONRPC: Readonly<{
|
|
10
|
+
readonly SendMessage: "message/send";
|
|
11
|
+
readonly SendStreamingMessage: "message/stream";
|
|
12
|
+
readonly GetTask: "tasks/get";
|
|
13
|
+
readonly CancelTask: "tasks/cancel";
|
|
14
|
+
readonly SubscribeToTask: "tasks/resubscribe";
|
|
15
|
+
readonly CreateTaskPushNotificationConfig: "tasks/pushNotificationConfig/set";
|
|
16
|
+
readonly GetTaskPushNotificationConfig: "tasks/pushNotificationConfig/get";
|
|
17
|
+
readonly ListTaskPushNotificationConfigs: "tasks/pushNotificationConfig/list";
|
|
18
|
+
readonly DeleteTaskPushNotificationConfig: "tasks/pushNotificationConfig/delete";
|
|
19
|
+
readonly GetExtendedAgentCard: "agent/getAuthenticatedExtendedCard";
|
|
20
|
+
}>;
|
|
21
|
+
/** Canonicalize either official 1.0 or legacy JSON-RPC spelling to legacy. */
|
|
22
|
+
export declare function canonicalA2AProtocolMethod(method: string): string;
|
|
23
|
+
/** Compare capability method declarations independent of A2A wire version. */
|
|
24
|
+
export declare function protocolMethodListIncludes(methods: readonly string[], candidate: string): boolean;
|
|
25
|
+
//# sourceMappingURL=protocol-methods.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol-methods.d.ts","sourceRoot":"","sources":["../../../src/lib/signing/protocol-methods.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,eAAO,MAAM,wBAAwB;;;;;;;;;;;EAWiB,CAAC;AAEvD,8EAA8E;AAC9E,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEjE;AAED,8EAA8E;AAC9E,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAGjG"}
|
|
@@ -0,0 +1,51 @@
|
|
|
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 protocol_methods_exports = {};
|
|
20
|
+
__export(protocol_methods_exports, {
|
|
21
|
+
A2A_V1_TO_LEGACY_JSONRPC: () => A2A_V1_TO_LEGACY_JSONRPC,
|
|
22
|
+
canonicalA2AProtocolMethod: () => canonicalA2AProtocolMethod,
|
|
23
|
+
protocolMethodListIncludes: () => protocolMethodListIncludes
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(protocol_methods_exports);
|
|
26
|
+
const A2A_V1_TO_LEGACY_JSONRPC = Object.freeze({
|
|
27
|
+
SendMessage: "message/send",
|
|
28
|
+
SendStreamingMessage: "message/stream",
|
|
29
|
+
GetTask: "tasks/get",
|
|
30
|
+
CancelTask: "tasks/cancel",
|
|
31
|
+
SubscribeToTask: "tasks/resubscribe",
|
|
32
|
+
CreateTaskPushNotificationConfig: "tasks/pushNotificationConfig/set",
|
|
33
|
+
GetTaskPushNotificationConfig: "tasks/pushNotificationConfig/get",
|
|
34
|
+
ListTaskPushNotificationConfigs: "tasks/pushNotificationConfig/list",
|
|
35
|
+
DeleteTaskPushNotificationConfig: "tasks/pushNotificationConfig/delete",
|
|
36
|
+
GetExtendedAgentCard: "agent/getAuthenticatedExtendedCard"
|
|
37
|
+
});
|
|
38
|
+
function canonicalA2AProtocolMethod(method) {
|
|
39
|
+
return A2A_V1_TO_LEGACY_JSONRPC[method] ?? method;
|
|
40
|
+
}
|
|
41
|
+
function protocolMethodListIncludes(methods, candidate) {
|
|
42
|
+
const canonicalCandidate = canonicalA2AProtocolMethod(candidate);
|
|
43
|
+
return methods.some((method) => canonicalA2AProtocolMethod(method) === canonicalCandidate);
|
|
44
|
+
}
|
|
45
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
46
|
+
0 && (module.exports = {
|
|
47
|
+
A2A_V1_TO_LEGACY_JSONRPC,
|
|
48
|
+
canonicalA2AProtocolMethod,
|
|
49
|
+
protocolMethodListIncludes
|
|
50
|
+
});
|
|
51
|
+
//# sourceMappingURL=protocol-methods.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/lib/signing/protocol-methods.ts"],"sourcesContent":["/**\n * A2A 1.0 JSON-RPC method names mapped to their stable 0.3 spellings.\n *\n * Kept in sync with @a2a-js/sdk-v1's `V1_TO_LEGACY_JSONRPC` table. We keep\n * this small synchronous copy so importing the general signing verifier does\n * not eagerly load the 1.0 SDK client. Tests compare every entry against the\n * official SDK's exported `v1MethodToLegacyJsonRpc()` translator.\n */\nexport const A2A_V1_TO_LEGACY_JSONRPC = Object.freeze({\n SendMessage: 'message/send',\n SendStreamingMessage: 'message/stream',\n GetTask: 'tasks/get',\n CancelTask: 'tasks/cancel',\n SubscribeToTask: 'tasks/resubscribe',\n CreateTaskPushNotificationConfig: 'tasks/pushNotificationConfig/set',\n GetTaskPushNotificationConfig: 'tasks/pushNotificationConfig/get',\n ListTaskPushNotificationConfigs: 'tasks/pushNotificationConfig/list',\n DeleteTaskPushNotificationConfig: 'tasks/pushNotificationConfig/delete',\n GetExtendedAgentCard: 'agent/getAuthenticatedExtendedCard',\n} as const satisfies Readonly<Record<string, string>>);\n\n/** Canonicalize either official 1.0 or legacy JSON-RPC spelling to legacy. */\nexport function canonicalA2AProtocolMethod(method: string): string {\n return A2A_V1_TO_LEGACY_JSONRPC[method as keyof typeof A2A_V1_TO_LEGACY_JSONRPC] ?? method;\n}\n\n/** Compare capability method declarations independent of A2A wire version. */\nexport function protocolMethodListIncludes(methods: readonly string[], candidate: string): boolean {\n const canonicalCandidate = canonicalA2AProtocolMethod(candidate);\n return methods.some(method => canonicalA2AProtocolMethod(method) === canonicalCandidate);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQO,MAAM,2BAA2B,OAAO,OAAO;AAAA,EACpD,aAAa;AAAA,EACb,sBAAsB;AAAA,EACtB,SAAS;AAAA,EACT,YAAY;AAAA,EACZ,iBAAiB;AAAA,EACjB,kCAAkC;AAAA,EAClC,+BAA+B;AAAA,EAC/B,iCAAiC;AAAA,EACjC,kCAAkC;AAAA,EAClC,sBAAsB;AACxB,CAAqD;AAG9C,SAAS,2BAA2B,QAAwB;AACjE,SAAO,yBAAyB,MAA+C,KAAK;AACtF;AAGO,SAAS,2BAA2B,SAA4B,WAA4B;AACjG,QAAM,qBAAqB,2BAA2B,SAAS;AAC/D,SAAO,QAAQ,KAAK,YAAU,2BAA2B,MAAM,MAAM,kBAAkB;AACzF;","names":[]}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
const A2A_V1_TO_LEGACY_JSONRPC = Object.freeze({
|
|
2
|
+
SendMessage: "message/send",
|
|
3
|
+
SendStreamingMessage: "message/stream",
|
|
4
|
+
GetTask: "tasks/get",
|
|
5
|
+
CancelTask: "tasks/cancel",
|
|
6
|
+
SubscribeToTask: "tasks/resubscribe",
|
|
7
|
+
CreateTaskPushNotificationConfig: "tasks/pushNotificationConfig/set",
|
|
8
|
+
GetTaskPushNotificationConfig: "tasks/pushNotificationConfig/get",
|
|
9
|
+
ListTaskPushNotificationConfigs: "tasks/pushNotificationConfig/list",
|
|
10
|
+
DeleteTaskPushNotificationConfig: "tasks/pushNotificationConfig/delete",
|
|
11
|
+
GetExtendedAgentCard: "agent/getAuthenticatedExtendedCard"
|
|
12
|
+
});
|
|
13
|
+
function canonicalA2AProtocolMethod(method) {
|
|
14
|
+
return A2A_V1_TO_LEGACY_JSONRPC[method] ?? method;
|
|
15
|
+
}
|
|
16
|
+
function protocolMethodListIncludes(methods, candidate) {
|
|
17
|
+
const canonicalCandidate = canonicalA2AProtocolMethod(candidate);
|
|
18
|
+
return methods.some((method) => canonicalA2AProtocolMethod(method) === canonicalCandidate);
|
|
19
|
+
}
|
|
20
|
+
export {
|
|
21
|
+
A2A_V1_TO_LEGACY_JSONRPC,
|
|
22
|
+
canonicalA2AProtocolMethod,
|
|
23
|
+
protocolMethodListIncludes
|
|
24
|
+
};
|
|
25
|
+
//# sourceMappingURL=protocol-methods.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/lib/signing/protocol-methods.ts"],"sourcesContent":["/**\n * A2A 1.0 JSON-RPC method names mapped to their stable 0.3 spellings.\n *\n * Kept in sync with @a2a-js/sdk-v1's `V1_TO_LEGACY_JSONRPC` table. We keep\n * this small synchronous copy so importing the general signing verifier does\n * not eagerly load the 1.0 SDK client. Tests compare every entry against the\n * official SDK's exported `v1MethodToLegacyJsonRpc()` translator.\n */\nexport const A2A_V1_TO_LEGACY_JSONRPC = Object.freeze({\n SendMessage: 'message/send',\n SendStreamingMessage: 'message/stream',\n GetTask: 'tasks/get',\n CancelTask: 'tasks/cancel',\n SubscribeToTask: 'tasks/resubscribe',\n CreateTaskPushNotificationConfig: 'tasks/pushNotificationConfig/set',\n GetTaskPushNotificationConfig: 'tasks/pushNotificationConfig/get',\n ListTaskPushNotificationConfigs: 'tasks/pushNotificationConfig/list',\n DeleteTaskPushNotificationConfig: 'tasks/pushNotificationConfig/delete',\n GetExtendedAgentCard: 'agent/getAuthenticatedExtendedCard',\n} as const satisfies Readonly<Record<string, string>>);\n\n/** Canonicalize either official 1.0 or legacy JSON-RPC spelling to legacy. */\nexport function canonicalA2AProtocolMethod(method: string): string {\n return A2A_V1_TO_LEGACY_JSONRPC[method as keyof typeof A2A_V1_TO_LEGACY_JSONRPC] ?? method;\n}\n\n/** Compare capability method declarations independent of A2A wire version. */\nexport function protocolMethodListIncludes(methods: readonly string[], candidate: string): boolean {\n const canonicalCandidate = canonicalA2AProtocolMethod(candidate);\n return methods.some(method => canonicalA2AProtocolMethod(method) === canonicalCandidate);\n}\n"],"mappings":"AAQO,MAAM,2BAA2B,OAAO,OAAO;AAAA,EACpD,aAAa;AAAA,EACb,sBAAsB;AAAA,EACtB,SAAS;AAAA,EACT,YAAY;AAAA,EACZ,iBAAiB;AAAA,EACjB,kCAAkC;AAAA,EAClC,+BAA+B;AAAA,EAC/B,iCAAiC;AAAA,EACjC,kCAAkC;AAAA,EAClC,sBAAsB;AACxB,CAAqD;AAG9C,SAAS,2BAA2B,QAAwB;AACjE,SAAO,yBAAyB,MAA+C,KAAK;AACtF;AAGO,SAAS,2BAA2B,SAA4B,WAA4B;AACjG,QAAM,qBAAqB,2BAA2B,SAAS;AAC/D,SAAO,QAAQ,KAAK,YAAU,2BAA2B,MAAM,MAAM,kBAAkB;AACzF;","names":[]}
|
|
@@ -18,6 +18,8 @@ export interface VerifierCapability {
|
|
|
18
18
|
* AdCP tool names such as `create_media_buy`.
|
|
19
19
|
*/
|
|
20
20
|
protocol_methods_required_for?: string[];
|
|
21
|
+
protocol_methods_warn_for?: string[];
|
|
22
|
+
protocol_methods_supported_for?: string[];
|
|
21
23
|
}
|
|
22
24
|
export interface AdcpJsonWebKey {
|
|
23
25
|
kid: string;
|
|
@@ -18,6 +18,8 @@ export interface VerifierCapability {
|
|
|
18
18
|
* AdCP tool names such as `create_media_buy`.
|
|
19
19
|
*/
|
|
20
20
|
protocol_methods_required_for?: string[];
|
|
21
|
+
protocol_methods_warn_for?: string[];
|
|
22
|
+
protocol_methods_supported_for?: string[];
|
|
21
23
|
}
|
|
22
24
|
export interface AdcpJsonWebKey {
|
|
23
25
|
kid: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/lib/signing/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,mBAAmB,GAAG,UAAU,GAAG,WAAW,GAAG,QAAQ,CAAC;AAEtE,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,OAAO,CAAC;IACnB,qBAAqB,EAAE,mBAAmB,CAAC;IAC3C,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB;;;;OAIG;IACH,6BAA6B,CAAC,EAAE,MAAM,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/lib/signing/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,mBAAmB,GAAG,UAAU,GAAG,WAAW,GAAG,QAAQ,CAAC;AAEtE,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,OAAO,CAAC;IACnB,qBAAqB,EAAE,mBAAmB,CAAC;IAC3C,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB;;;;OAIG;IACH,6BAA6B,CAAC,EAAE,MAAM,EAAE,CAAC;IACzC,yBAAyB,CAAC,EAAE,MAAM,EAAE,CAAC;IACrC,8BAA8B,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3C;AAED,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oEAAoE;IACpE,CAAC,CAAC,EAAE,MAAM,CAAC;IACX,+CAA+C;IAC/C,CAAC,CAAC,EAAE,MAAM,CAAC;IACX;;;;;OAKG;IACH,CAAC,CAAC,EAAE,MAAM,CAAC;IACX,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,YAAY,GAAG,CAAC;IAAE,MAAM,EAAE,UAAU,CAAA;CAAE,GAAG,cAAc,CAAC,GAAG;IAAE,MAAM,EAAE,UAAU,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnH,eAAO,MAAM,mBAAmB,4BAA4B,CAAC;AAC7D,eAAO,MAAM,oBAAoB,6BAA6B,CAAC;AAC/D,eAAO,MAAM,YAAY,aAA4C,CAAC;AACtE;;;GAGG;AACH,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,mBAAmB,CAAC;AAC1D,eAAO,MAAM,4BAA4B,MAAM,CAAC;AAChD,eAAO,MAAM,4BAA4B,KAAK,CAAC;AAC/C,eAAO,MAAM,oBAAoB,EAAE,aAAa,CAAC,MAAM,CAA4C,CAAC;AACpG,eAAO,MAAM,6BAA6B,EAAE,aAAa,CAAC,MAAM,CAK/D,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/lib/signing/types.ts"],"sourcesContent":["export type ContentDigestPolicy = 'required' | 'forbidden' | 'either';\n\nexport interface VerifierCapability {\n supported: boolean;\n covers_content_digest: ContentDigestPolicy;\n required_for: string[];\n /**\n * Shadow-mode bridge between `supported_for` and `required_for`: the seller\n * verifies signatures when present and logs failures but does NOT reject\n * unsigned requests. Counterparties SHOULD sign ops in this list so sellers\n * can surface failure rates before flipping to `required_for`. Precedence:\n * `required_for` > `warn_for` > `supported_for`.\n */\n warn_for?: string[];\n supported_for?: string[];\n /**\n * JSON-RPC protocol methods (for example `tasks/cancel`) that require\n * request signatures. Kept separate from `required_for`, whose namespace is\n * AdCP tool names such as `create_media_buy`.\n */\n protocol_methods_required_for?: string[];\n}\n\nexport interface AdcpJsonWebKey {\n kid: string;\n kty: string;\n crv?: string;\n alg?: string;\n use?: string;\n key_ops?: string[];\n adcp_use?: string;\n /** Public-key X coordinate (Ed25519) or x-coordinate (EC P-256). */\n x?: string;\n /** Public-key Y coordinate (EC P-256 only). */\n y?: string;\n /**\n * Private scalar (RFC 7518 §6.2.2.1 / §6.1.2). Present only when the JWK\n * represents a private key — never published at `jwks_uri`. Test vectors\n * ship this under `_private_d_for_test_only` in their `keys.json`; runtime\n * signers load it into `d`.\n */\n d?: string;\n [extra: string]: unknown;\n}\n\nexport interface RevocationSnapshot {\n issuer: string;\n updated: string;\n next_update: string;\n revoked_kids: string[];\n revoked_jtis: string[];\n}\n\n/**\n * Narrow \"successfully verified\" shape. Middleware sets `req.verifiedSigner`\n * to a value of this type only after a real signature was validated — the\n * absence of `req.verifiedSigner` is the signal that the request went through\n * the verifier as unsigned (and the operation didn't require signing).\n *\n * `keyid` is always non-empty. Pre-3.x releases returned a `keyid: ''`\n * sentinel on the unsigned path; consumers MUST now branch on\n * `req.verifiedSigner === undefined` instead.\n */\nexport interface VerifiedSigner {\n keyid: string;\n agent_url?: string;\n verified_at: number;\n}\n\n/**\n * Discriminated union returned by `verifyRequestSignature`.\n *\n * - `{ status: 'verified', ... }` — a signature was present and passed all\n * pipeline steps. Shape extends {@link VerifiedSigner}.\n * - `{ status: 'unsigned' }` — no signature headers were present, and the\n * operation was not in `capability.required_for` (or no operation was\n * supplied). The request may still be accepted by the server's own auth\n * path; the verifier just has no signer to attest to.\n *\n * A caller that needs to distinguish \"unsigned but acceptable\" from\n * \"verified\" should branch on `.status`. Most middleware consumers should\n * instead rely on `req.verifiedSigner` being populated — that reads cleanly\n * as \"did a real signature check succeed.\"\n */\nexport type VerifyResult = ({ status: 'verified' } & VerifiedSigner) | { status: 'unsigned'; verified_at: number };\n\nexport const REQUEST_SIGNING_TAG = 'adcp/request-signing/v1';\nexport const RESPONSE_SIGNING_TAG = 'adcp/response-signing/v1';\nexport const ALLOWED_ALGS = new Set(['ed25519', 'ecdsa-p256-sha256']);\n/**\n * Wire-format algorithm identifier — the string that appears in\n * `Signature-Input`'s `alg` parameter. Same vocabulary as `ALLOWED_ALGS`.\n */\nexport type AdcpSignAlg = 'ed25519' | 'ecdsa-p256-sha256';\nexport const MAX_SIGNATURE_WINDOW_SECONDS = 300;\nexport const CLOCK_SKEW_TOLERANCE_SECONDS = 60;\nexport const MANDATORY_COMPONENTS: ReadonlyArray<string> = ['@method', '@target-uri', '@authority'];\nexport const RESPONSE_MANDATORY_COMPONENTS: ReadonlyArray<string> = [\n '@status',\n '@method;req',\n '@authority;req',\n '@target-uri;req',\n];\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;
|
|
1
|
+
{"version":3,"sources":["../../../src/lib/signing/types.ts"],"sourcesContent":["export type ContentDigestPolicy = 'required' | 'forbidden' | 'either';\n\nexport interface VerifierCapability {\n supported: boolean;\n covers_content_digest: ContentDigestPolicy;\n required_for: string[];\n /**\n * Shadow-mode bridge between `supported_for` and `required_for`: the seller\n * verifies signatures when present and logs failures but does NOT reject\n * unsigned requests. Counterparties SHOULD sign ops in this list so sellers\n * can surface failure rates before flipping to `required_for`. Precedence:\n * `required_for` > `warn_for` > `supported_for`.\n */\n warn_for?: string[];\n supported_for?: string[];\n /**\n * JSON-RPC protocol methods (for example `tasks/cancel`) that require\n * request signatures. Kept separate from `required_for`, whose namespace is\n * AdCP tool names such as `create_media_buy`.\n */\n protocol_methods_required_for?: string[];\n protocol_methods_warn_for?: string[];\n protocol_methods_supported_for?: string[];\n}\n\nexport interface AdcpJsonWebKey {\n kid: string;\n kty: string;\n crv?: string;\n alg?: string;\n use?: string;\n key_ops?: string[];\n adcp_use?: string;\n /** Public-key X coordinate (Ed25519) or x-coordinate (EC P-256). */\n x?: string;\n /** Public-key Y coordinate (EC P-256 only). */\n y?: string;\n /**\n * Private scalar (RFC 7518 §6.2.2.1 / §6.1.2). Present only when the JWK\n * represents a private key — never published at `jwks_uri`. Test vectors\n * ship this under `_private_d_for_test_only` in their `keys.json`; runtime\n * signers load it into `d`.\n */\n d?: string;\n [extra: string]: unknown;\n}\n\nexport interface RevocationSnapshot {\n issuer: string;\n updated: string;\n next_update: string;\n revoked_kids: string[];\n revoked_jtis: string[];\n}\n\n/**\n * Narrow \"successfully verified\" shape. Middleware sets `req.verifiedSigner`\n * to a value of this type only after a real signature was validated — the\n * absence of `req.verifiedSigner` is the signal that the request went through\n * the verifier as unsigned (and the operation didn't require signing).\n *\n * `keyid` is always non-empty. Pre-3.x releases returned a `keyid: ''`\n * sentinel on the unsigned path; consumers MUST now branch on\n * `req.verifiedSigner === undefined` instead.\n */\nexport interface VerifiedSigner {\n keyid: string;\n agent_url?: string;\n verified_at: number;\n}\n\n/**\n * Discriminated union returned by `verifyRequestSignature`.\n *\n * - `{ status: 'verified', ... }` — a signature was present and passed all\n * pipeline steps. Shape extends {@link VerifiedSigner}.\n * - `{ status: 'unsigned' }` — no signature headers were present, and the\n * operation was not in `capability.required_for` (or no operation was\n * supplied). The request may still be accepted by the server's own auth\n * path; the verifier just has no signer to attest to.\n *\n * A caller that needs to distinguish \"unsigned but acceptable\" from\n * \"verified\" should branch on `.status`. Most middleware consumers should\n * instead rely on `req.verifiedSigner` being populated — that reads cleanly\n * as \"did a real signature check succeed.\"\n */\nexport type VerifyResult = ({ status: 'verified' } & VerifiedSigner) | { status: 'unsigned'; verified_at: number };\n\nexport const REQUEST_SIGNING_TAG = 'adcp/request-signing/v1';\nexport const RESPONSE_SIGNING_TAG = 'adcp/response-signing/v1';\nexport const ALLOWED_ALGS = new Set(['ed25519', 'ecdsa-p256-sha256']);\n/**\n * Wire-format algorithm identifier — the string that appears in\n * `Signature-Input`'s `alg` parameter. Same vocabulary as `ALLOWED_ALGS`.\n */\nexport type AdcpSignAlg = 'ed25519' | 'ecdsa-p256-sha256';\nexport const MAX_SIGNATURE_WINDOW_SECONDS = 300;\nexport const CLOCK_SKEW_TOLERANCE_SECONDS = 60;\nexport const MANDATORY_COMPONENTS: ReadonlyArray<string> = ['@method', '@target-uri', '@authority'];\nexport const RESPONSE_MANDATORY_COMPONENTS: ReadonlyArray<string> = [\n '@status',\n '@method;req',\n '@authority;req',\n '@target-uri;req',\n];\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAwFO,MAAM,sBAAsB;AAC5B,MAAM,uBAAuB;AAC7B,MAAM,eAAe,oBAAI,IAAI,CAAC,WAAW,mBAAmB,CAAC;AAM7D,MAAM,+BAA+B;AACrC,MAAM,+BAA+B;AACrC,MAAM,uBAA8C,CAAC,WAAW,eAAe,YAAY;AAC3F,MAAM,gCAAuD;AAAA,EAClE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/lib/signing/types.ts"],"sourcesContent":["export type ContentDigestPolicy = 'required' | 'forbidden' | 'either';\n\nexport interface VerifierCapability {\n supported: boolean;\n covers_content_digest: ContentDigestPolicy;\n required_for: string[];\n /**\n * Shadow-mode bridge between `supported_for` and `required_for`: the seller\n * verifies signatures when present and logs failures but does NOT reject\n * unsigned requests. Counterparties SHOULD sign ops in this list so sellers\n * can surface failure rates before flipping to `required_for`. Precedence:\n * `required_for` > `warn_for` > `supported_for`.\n */\n warn_for?: string[];\n supported_for?: string[];\n /**\n * JSON-RPC protocol methods (for example `tasks/cancel`) that require\n * request signatures. Kept separate from `required_for`, whose namespace is\n * AdCP tool names such as `create_media_buy`.\n */\n protocol_methods_required_for?: string[];\n}\n\nexport interface AdcpJsonWebKey {\n kid: string;\n kty: string;\n crv?: string;\n alg?: string;\n use?: string;\n key_ops?: string[];\n adcp_use?: string;\n /** Public-key X coordinate (Ed25519) or x-coordinate (EC P-256). */\n x?: string;\n /** Public-key Y coordinate (EC P-256 only). */\n y?: string;\n /**\n * Private scalar (RFC 7518 §6.2.2.1 / §6.1.2). Present only when the JWK\n * represents a private key — never published at `jwks_uri`. Test vectors\n * ship this under `_private_d_for_test_only` in their `keys.json`; runtime\n * signers load it into `d`.\n */\n d?: string;\n [extra: string]: unknown;\n}\n\nexport interface RevocationSnapshot {\n issuer: string;\n updated: string;\n next_update: string;\n revoked_kids: string[];\n revoked_jtis: string[];\n}\n\n/**\n * Narrow \"successfully verified\" shape. Middleware sets `req.verifiedSigner`\n * to a value of this type only after a real signature was validated — the\n * absence of `req.verifiedSigner` is the signal that the request went through\n * the verifier as unsigned (and the operation didn't require signing).\n *\n * `keyid` is always non-empty. Pre-3.x releases returned a `keyid: ''`\n * sentinel on the unsigned path; consumers MUST now branch on\n * `req.verifiedSigner === undefined` instead.\n */\nexport interface VerifiedSigner {\n keyid: string;\n agent_url?: string;\n verified_at: number;\n}\n\n/**\n * Discriminated union returned by `verifyRequestSignature`.\n *\n * - `{ status: 'verified', ... }` — a signature was present and passed all\n * pipeline steps. Shape extends {@link VerifiedSigner}.\n * - `{ status: 'unsigned' }` — no signature headers were present, and the\n * operation was not in `capability.required_for` (or no operation was\n * supplied). The request may still be accepted by the server's own auth\n * path; the verifier just has no signer to attest to.\n *\n * A caller that needs to distinguish \"unsigned but acceptable\" from\n * \"verified\" should branch on `.status`. Most middleware consumers should\n * instead rely on `req.verifiedSigner` being populated — that reads cleanly\n * as \"did a real signature check succeed.\"\n */\nexport type VerifyResult = ({ status: 'verified' } & VerifiedSigner) | { status: 'unsigned'; verified_at: number };\n\nexport const REQUEST_SIGNING_TAG = 'adcp/request-signing/v1';\nexport const RESPONSE_SIGNING_TAG = 'adcp/response-signing/v1';\nexport const ALLOWED_ALGS = new Set(['ed25519', 'ecdsa-p256-sha256']);\n/**\n * Wire-format algorithm identifier — the string that appears in\n * `Signature-Input`'s `alg` parameter. Same vocabulary as `ALLOWED_ALGS`.\n */\nexport type AdcpSignAlg = 'ed25519' | 'ecdsa-p256-sha256';\nexport const MAX_SIGNATURE_WINDOW_SECONDS = 300;\nexport const CLOCK_SKEW_TOLERANCE_SECONDS = 60;\nexport const MANDATORY_COMPONENTS: ReadonlyArray<string> = ['@method', '@target-uri', '@authority'];\nexport const RESPONSE_MANDATORY_COMPONENTS: ReadonlyArray<string> = [\n '@status',\n '@method;req',\n '@authority;req',\n '@target-uri;req',\n];\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../../src/lib/signing/types.ts"],"sourcesContent":["export type ContentDigestPolicy = 'required' | 'forbidden' | 'either';\n\nexport interface VerifierCapability {\n supported: boolean;\n covers_content_digest: ContentDigestPolicy;\n required_for: string[];\n /**\n * Shadow-mode bridge between `supported_for` and `required_for`: the seller\n * verifies signatures when present and logs failures but does NOT reject\n * unsigned requests. Counterparties SHOULD sign ops in this list so sellers\n * can surface failure rates before flipping to `required_for`. Precedence:\n * `required_for` > `warn_for` > `supported_for`.\n */\n warn_for?: string[];\n supported_for?: string[];\n /**\n * JSON-RPC protocol methods (for example `tasks/cancel`) that require\n * request signatures. Kept separate from `required_for`, whose namespace is\n * AdCP tool names such as `create_media_buy`.\n */\n protocol_methods_required_for?: string[];\n protocol_methods_warn_for?: string[];\n protocol_methods_supported_for?: string[];\n}\n\nexport interface AdcpJsonWebKey {\n kid: string;\n kty: string;\n crv?: string;\n alg?: string;\n use?: string;\n key_ops?: string[];\n adcp_use?: string;\n /** Public-key X coordinate (Ed25519) or x-coordinate (EC P-256). */\n x?: string;\n /** Public-key Y coordinate (EC P-256 only). */\n y?: string;\n /**\n * Private scalar (RFC 7518 §6.2.2.1 / §6.1.2). Present only when the JWK\n * represents a private key — never published at `jwks_uri`. Test vectors\n * ship this under `_private_d_for_test_only` in their `keys.json`; runtime\n * signers load it into `d`.\n */\n d?: string;\n [extra: string]: unknown;\n}\n\nexport interface RevocationSnapshot {\n issuer: string;\n updated: string;\n next_update: string;\n revoked_kids: string[];\n revoked_jtis: string[];\n}\n\n/**\n * Narrow \"successfully verified\" shape. Middleware sets `req.verifiedSigner`\n * to a value of this type only after a real signature was validated — the\n * absence of `req.verifiedSigner` is the signal that the request went through\n * the verifier as unsigned (and the operation didn't require signing).\n *\n * `keyid` is always non-empty. Pre-3.x releases returned a `keyid: ''`\n * sentinel on the unsigned path; consumers MUST now branch on\n * `req.verifiedSigner === undefined` instead.\n */\nexport interface VerifiedSigner {\n keyid: string;\n agent_url?: string;\n verified_at: number;\n}\n\n/**\n * Discriminated union returned by `verifyRequestSignature`.\n *\n * - `{ status: 'verified', ... }` — a signature was present and passed all\n * pipeline steps. Shape extends {@link VerifiedSigner}.\n * - `{ status: 'unsigned' }` — no signature headers were present, and the\n * operation was not in `capability.required_for` (or no operation was\n * supplied). The request may still be accepted by the server's own auth\n * path; the verifier just has no signer to attest to.\n *\n * A caller that needs to distinguish \"unsigned but acceptable\" from\n * \"verified\" should branch on `.status`. Most middleware consumers should\n * instead rely on `req.verifiedSigner` being populated — that reads cleanly\n * as \"did a real signature check succeed.\"\n */\nexport type VerifyResult = ({ status: 'verified' } & VerifiedSigner) | { status: 'unsigned'; verified_at: number };\n\nexport const REQUEST_SIGNING_TAG = 'adcp/request-signing/v1';\nexport const RESPONSE_SIGNING_TAG = 'adcp/response-signing/v1';\nexport const ALLOWED_ALGS = new Set(['ed25519', 'ecdsa-p256-sha256']);\n/**\n * Wire-format algorithm identifier — the string that appears in\n * `Signature-Input`'s `alg` parameter. Same vocabulary as `ALLOWED_ALGS`.\n */\nexport type AdcpSignAlg = 'ed25519' | 'ecdsa-p256-sha256';\nexport const MAX_SIGNATURE_WINDOW_SECONDS = 300;\nexport const CLOCK_SKEW_TOLERANCE_SECONDS = 60;\nexport const MANDATORY_COMPONENTS: ReadonlyArray<string> = ['@method', '@target-uri', '@authority'];\nexport const RESPONSE_MANDATORY_COMPONENTS: ReadonlyArray<string> = [\n '@status',\n '@method;req',\n '@authority;req',\n '@target-uri;req',\n];\n"],"mappings":"AAwFO,MAAM,sBAAsB;AAC5B,MAAM,uBAAuB;AAC7B,MAAM,eAAe,oBAAI,IAAI,CAAC,WAAW,mBAAmB,CAAC;AAM7D,MAAM,+BAA+B;AACrC,MAAM,+BAA+B;AACrC,MAAM,uBAA8C,CAAC,WAAW,eAAe,YAAY;AAC3F,MAAM,gCAAuD;AAAA,EAClE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verifier.d.ts","sourceRoot":"","sources":["../../../src/lib/signing/verifier.ts"],"names":[],"mappings":"AACA,OAAO,EAKL,KAAK,WAAW,EACjB,MAAM,gBAAgB,CAAC;AAKxB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAC3C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"verifier.d.ts","sourceRoot":"","sources":["../../../src/lib/signing/verifier.ts"],"names":[],"mappings":"AACA,OAAO,EAKL,KAAK,WAAW,EACjB,MAAM,gBAAgB,CAAC;AAKxB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAC3C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAGpD,OAAO,EAML,KAAK,kBAAkB,EACvB,KAAK,YAAY,EAClB,MAAM,SAAS,CAAC;AAEjB,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,kBAAkB,CAAC;IAC/B,IAAI,EAAE,YAAY,CAAC;IACnB,WAAW,EAAE,WAAW,CAAC;IACzB,eAAe,EAAE,eAAe,CAAC;IACjC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;CAC1D;AAED,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,WAAW,EACpB,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,YAAY,CAAC,CAgNvB"}
|
|
@@ -28,6 +28,7 @@ var import_errors = require('./errors.js');
|
|
|
28
28
|
var import_parser = require('./parser.js');
|
|
29
29
|
var import_crypto = require('./crypto.js');
|
|
30
30
|
var import_webhook_auth_detection = require('./webhook-auth-detection.js');
|
|
31
|
+
var import_protocol_methods = require('./protocol-methods.js');
|
|
31
32
|
var import_types = require('./types.js');
|
|
32
33
|
async function verifyRequestSignature(request, options) {
|
|
33
34
|
const now = options.now ? options.now() : Math.floor(Date.now() / 1e3);
|
|
@@ -51,12 +52,15 @@ async function verifyRequestSignature(request, options) {
|
|
|
51
52
|
);
|
|
52
53
|
}
|
|
53
54
|
const protocolMethods = jsonRpcProtocolMethods(request.body);
|
|
54
|
-
const requiredProtocolMethod = protocolMethods.find(
|
|
55
|
+
const requiredProtocolMethod = protocolMethods.find(
|
|
56
|
+
(wireMethod) => (0, import_protocol_methods.protocolMethodListIncludes)(protocolMethodsRequiredFor, wireMethod)
|
|
57
|
+
);
|
|
55
58
|
if (requiredProtocolMethod) {
|
|
59
|
+
const declaredMethod = (0, import_protocol_methods.canonicalA2AProtocolMethod)(requiredProtocolMethod);
|
|
56
60
|
throw new import_errors.RequestSignatureError(
|
|
57
61
|
"request_signature_required",
|
|
58
62
|
0,
|
|
59
|
-
`Protocol method "${
|
|
63
|
+
`Protocol method "${declaredMethod}" requires a signed request` + (requiredProtocolMethod === declaredMethod ? "" : ` (wire method "${requiredProtocolMethod}")`)
|
|
60
64
|
);
|
|
61
65
|
}
|
|
62
66
|
if (carriesWebhookAuthentication(request)) {
|