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