@adcp/sdk 12.1.0 → 13.0.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -3
- package/bin/adcp-fuzz.js +3 -1
- package/dist/lib/adapters/content-standards-adapter.d.mts +9 -9
- package/dist/lib/adapters/content-standards-adapter.d.ts +9 -9
- package/dist/lib/adapters/content-standards-adapter.d.ts.map +1 -1
- package/dist/lib/adapters/content-standards-adapter.js +22 -22
- package/dist/lib/adapters/content-standards-adapter.js.map +1 -1
- package/dist/lib/adapters/content-standards-adapter.mjs +18 -18
- package/dist/lib/adapters/content-standards-adapter.mjs.map +1 -1
- package/dist/lib/adapters/index.d.mts +2 -2
- package/dist/lib/adapters/index.d.ts +2 -2
- package/dist/lib/adapters/index.d.ts.map +1 -1
- package/dist/lib/adapters/index.js +10 -10
- package/dist/lib/adapters/index.js.map +1 -1
- package/dist/lib/adapters/index.mjs +9 -9
- package/dist/lib/adapters/index.mjs.map +1 -1
- package/dist/lib/auth/oauth/authorization-required.d.mts +3 -0
- package/dist/lib/auth/oauth/authorization-required.d.ts +3 -0
- package/dist/lib/auth/oauth/authorization-required.d.ts.map +1 -1
- package/dist/lib/auth/oauth/authorization-required.js +12 -9
- package/dist/lib/auth/oauth/authorization-required.js.map +1 -1
- package/dist/lib/auth/oauth/authorization-required.mjs +12 -9
- package/dist/lib/auth/oauth/authorization-required.mjs.map +1 -1
- package/dist/lib/auth/oauth/provider-cache.d.mts +7 -0
- package/dist/lib/auth/oauth/provider-cache.d.ts +8 -0
- package/dist/lib/auth/oauth/provider-cache.d.ts.map +1 -0
- package/dist/lib/auth/oauth/provider-cache.js +44 -0
- package/dist/lib/auth/oauth/provider-cache.js.map +1 -0
- package/dist/lib/auth/oauth/provider-cache.mjs +19 -0
- package/dist/lib/auth/oauth/provider-cache.mjs.map +1 -0
- package/dist/lib/conformance/invariants/uniformError.js +1 -1
- package/dist/lib/conformance/invariants/uniformError.js.map +1 -1
- package/dist/lib/conformance/invariants/uniformError.mjs +1 -1
- package/dist/lib/conformance/invariants/uniformError.mjs.map +1 -1
- package/dist/lib/conformance/runners.js +1 -1
- package/dist/lib/conformance/runners.js.map +1 -1
- package/dist/lib/conformance/runners.mjs +1 -1
- package/dist/lib/conformance/runners.mjs.map +1 -1
- package/dist/lib/conformance/seeder.js +5 -5
- package/dist/lib/conformance/seeder.js.map +1 -1
- package/dist/lib/conformance/seeder.mjs +5 -5
- package/dist/lib/conformance/seeder.mjs.map +1 -1
- package/dist/lib/core/ADCPMultiAgentClient.d.mts +26 -29
- package/dist/lib/core/ADCPMultiAgentClient.d.ts +26 -29
- package/dist/lib/core/ADCPMultiAgentClient.d.ts.map +1 -1
- package/dist/lib/core/ADCPMultiAgentClient.js +27 -19
- package/dist/lib/core/ADCPMultiAgentClient.js.map +1 -1
- package/dist/lib/core/ADCPMultiAgentClient.mjs +27 -19
- package/dist/lib/core/ADCPMultiAgentClient.mjs.map +1 -1
- package/dist/lib/core/AgentClient.d.mts +148 -138
- package/dist/lib/core/AgentClient.d.ts +148 -138
- package/dist/lib/core/AgentClient.d.ts.map +1 -1
- package/dist/lib/core/AgentClient.js +174 -85
- package/dist/lib/core/AgentClient.js.map +1 -1
- package/dist/lib/core/AgentClient.mjs +174 -85
- package/dist/lib/core/AgentClient.mjs.map +1 -1
- package/dist/lib/core/AsyncHandler.d.mts +18 -16
- package/dist/lib/core/AsyncHandler.d.ts +18 -16
- package/dist/lib/core/AsyncHandler.d.ts.map +1 -1
- package/dist/lib/core/AsyncHandler.js +22 -1
- package/dist/lib/core/AsyncHandler.js.map +1 -1
- package/dist/lib/core/AsyncHandler.mjs +22 -1
- package/dist/lib/core/AsyncHandler.mjs.map +1 -1
- package/dist/lib/core/ConversationTypes.d.mts +6 -2
- package/dist/lib/core/ConversationTypes.d.ts +6 -2
- package/dist/lib/core/ConversationTypes.d.ts.map +1 -1
- package/dist/lib/core/ConversationTypes.js.map +1 -1
- package/dist/lib/core/CreativeAgentClient.d.mts +41 -17
- package/dist/lib/core/CreativeAgentClient.d.ts +41 -17
- package/dist/lib/core/CreativeAgentClient.d.ts.map +1 -1
- package/dist/lib/core/CreativeAgentClient.js +39 -12
- package/dist/lib/core/CreativeAgentClient.js.map +1 -1
- package/dist/lib/core/CreativeAgentClient.mjs +39 -12
- package/dist/lib/core/CreativeAgentClient.mjs.map +1 -1
- package/dist/lib/core/SingleAgentClient.d.mts +129 -52
- package/dist/lib/core/SingleAgentClient.d.ts +129 -52
- package/dist/lib/core/SingleAgentClient.d.ts.map +1 -1
- package/dist/lib/core/SingleAgentClient.js +1748 -218
- package/dist/lib/core/SingleAgentClient.js.map +1 -1
- package/dist/lib/core/SingleAgentClient.mjs +1758 -218
- package/dist/lib/core/SingleAgentClient.mjs.map +1 -1
- package/dist/lib/core/TaskExecutor.d.mts +9 -0
- package/dist/lib/core/TaskExecutor.d.ts +9 -0
- package/dist/lib/core/TaskExecutor.d.ts.map +1 -1
- package/dist/lib/core/TaskExecutor.js +137 -44
- package/dist/lib/core/TaskExecutor.js.map +1 -1
- package/dist/lib/core/TaskExecutor.mjs +137 -44
- package/dist/lib/core/TaskExecutor.mjs.map +1 -1
- package/dist/lib/index.d.mts +111 -17
- package/dist/lib/index.d.ts +111 -17
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/index.js +94 -94
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/index.mjs +93 -94
- package/dist/lib/index.mjs.map +1 -1
- package/dist/lib/net/ssrf-fetch.d.mts +13 -1
- package/dist/lib/net/ssrf-fetch.d.ts +13 -1
- package/dist/lib/net/ssrf-fetch.d.ts.map +1 -1
- package/dist/lib/net/ssrf-fetch.js +14 -7
- package/dist/lib/net/ssrf-fetch.js.map +1 -1
- package/dist/lib/net/ssrf-fetch.mjs +14 -7
- package/dist/lib/net/ssrf-fetch.mjs.map +1 -1
- package/dist/lib/protocols/a2a.d.mts +2 -2
- package/dist/lib/protocols/a2a.d.ts +2 -2
- package/dist/lib/protocols/a2a.d.ts.map +1 -1
- package/dist/lib/protocols/a2a.js +14 -9
- package/dist/lib/protocols/a2a.js.map +1 -1
- package/dist/lib/protocols/a2a.mjs +14 -9
- package/dist/lib/protocols/a2a.mjs.map +1 -1
- package/dist/lib/protocols/index.d.mts +20 -3
- package/dist/lib/protocols/index.d.ts +20 -3
- package/dist/lib/protocols/index.d.ts.map +1 -1
- package/dist/lib/protocols/index.js +44 -31
- package/dist/lib/protocols/index.js.map +1 -1
- package/dist/lib/protocols/index.mjs +44 -32
- 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 +87 -12
- package/dist/lib/protocols/mcp-modern.js.map +1 -1
- package/dist/lib/protocols/mcp-modern.mjs +89 -12
- package/dist/lib/protocols/mcp-modern.mjs.map +1 -1
- package/dist/lib/protocols/mcp-tasks.d.mts +3 -0
- package/dist/lib/protocols/mcp-tasks.d.ts +3 -0
- package/dist/lib/protocols/mcp-tasks.d.ts.map +1 -1
- package/dist/lib/protocols/mcp-tasks.js +21 -11
- package/dist/lib/protocols/mcp-tasks.js.map +1 -1
- package/dist/lib/protocols/mcp-tasks.mjs +21 -11
- package/dist/lib/protocols/mcp-tasks.mjs.map +1 -1
- package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
- package/dist/lib/server/create-adcp-server.d.mts +6 -0
- package/dist/lib/server/create-adcp-server.d.ts +6 -0
- package/dist/lib/server/create-adcp-server.d.ts.map +1 -1
- package/dist/lib/server/create-adcp-server.js +295 -48
- package/dist/lib/server/create-adcp-server.js.map +1 -1
- package/dist/lib/server/create-adcp-server.mjs +301 -50
- package/dist/lib/server/create-adcp-server.mjs.map +1 -1
- package/dist/lib/server/decisioning/assembly-helpers.d.mts +27 -13
- package/dist/lib/server/decisioning/assembly-helpers.d.ts +27 -13
- package/dist/lib/server/decisioning/assembly-helpers.d.ts.map +1 -1
- package/dist/lib/server/decisioning/assembly-helpers.js +136 -14
- package/dist/lib/server/decisioning/assembly-helpers.js.map +1 -1
- package/dist/lib/server/decisioning/assembly-helpers.mjs +134 -13
- package/dist/lib/server/decisioning/assembly-helpers.mjs.map +1 -1
- package/dist/lib/server/decisioning/index.d.mts +13 -12
- package/dist/lib/server/decisioning/index.d.ts +13 -12
- package/dist/lib/server/decisioning/index.d.ts.map +1 -1
- package/dist/lib/server/decisioning/index.js +6 -0
- package/dist/lib/server/decisioning/index.js.map +1 -1
- package/dist/lib/server/decisioning/index.mjs +9 -3
- package/dist/lib/server/decisioning/index.mjs.map +1 -1
- package/dist/lib/server/decisioning/list-helpers.d.mts +13 -3
- package/dist/lib/server/decisioning/list-helpers.d.ts +13 -3
- package/dist/lib/server/decisioning/list-helpers.d.ts.map +1 -1
- package/dist/lib/server/decisioning/list-helpers.js +13 -3
- package/dist/lib/server/decisioning/list-helpers.js.map +1 -1
- package/dist/lib/server/decisioning/list-helpers.mjs +11 -2
- package/dist/lib/server/decisioning/list-helpers.mjs.map +1 -1
- package/dist/lib/server/decisioning/manifest-helpers.d.mts +5 -3
- package/dist/lib/server/decisioning/manifest-helpers.d.ts +5 -3
- package/dist/lib/server/decisioning/manifest-helpers.d.ts.map +1 -1
- package/dist/lib/server/decisioning/manifest-helpers.js.map +1 -1
- package/dist/lib/server/decisioning/manifest-helpers.mjs.map +1 -1
- package/dist/lib/server/decisioning/proposal/dispatch.d.mts +2 -2
- package/dist/lib/server/decisioning/proposal/dispatch.d.ts +2 -2
- package/dist/lib/server/decisioning/proposal/dispatch.d.ts.map +1 -1
- package/dist/lib/server/decisioning/proposal/dispatch.js.map +1 -1
- package/dist/lib/server/decisioning/proposal/dispatch.mjs.map +1 -1
- package/dist/lib/server/decisioning/proposal/index.d.mts +1 -1
- package/dist/lib/server/decisioning/proposal/index.d.ts +1 -1
- package/dist/lib/server/decisioning/proposal/index.d.ts.map +1 -1
- package/dist/lib/server/decisioning/proposal/index.js.map +1 -1
- package/dist/lib/server/decisioning/proposal/index.mjs.map +1 -1
- package/dist/lib/server/decisioning/proposal/lifecycle.d.mts +3 -2
- package/dist/lib/server/decisioning/proposal/lifecycle.d.ts +3 -2
- package/dist/lib/server/decisioning/proposal/lifecycle.d.ts.map +1 -1
- package/dist/lib/server/decisioning/proposal/lifecycle.js.map +1 -1
- package/dist/lib/server/decisioning/proposal/lifecycle.mjs.map +1 -1
- package/dist/lib/server/decisioning/proposal/mock-manager.d.mts +3 -3
- package/dist/lib/server/decisioning/proposal/mock-manager.d.ts +3 -3
- package/dist/lib/server/decisioning/proposal/mock-manager.d.ts.map +1 -1
- package/dist/lib/server/decisioning/proposal/mock-manager.js.map +1 -1
- package/dist/lib/server/decisioning/proposal/mock-manager.mjs.map +1 -1
- package/dist/lib/server/decisioning/proposal/types.d.mts +10 -6
- package/dist/lib/server/decisioning/proposal/types.d.ts +10 -6
- package/dist/lib/server/decisioning/proposal/types.d.ts.map +1 -1
- package/dist/lib/server/decisioning/proposal/types.js.map +1 -1
- package/dist/lib/server/decisioning/proposal/types.mjs.map +1 -1
- package/dist/lib/server/decisioning/runtime/from-platform.d.mts +21 -2
- package/dist/lib/server/decisioning/runtime/from-platform.d.ts +21 -2
- package/dist/lib/server/decisioning/runtime/from-platform.d.ts.map +1 -1
- package/dist/lib/server/decisioning/runtime/from-platform.js +754 -81
- package/dist/lib/server/decisioning/runtime/from-platform.js.map +1 -1
- package/dist/lib/server/decisioning/runtime/from-platform.mjs +763 -81
- package/dist/lib/server/decisioning/runtime/from-platform.mjs.map +1 -1
- package/dist/lib/server/decisioning/specialisms/brand-rights.d.mts +9 -9
- package/dist/lib/server/decisioning/specialisms/brand-rights.d.ts +9 -9
- package/dist/lib/server/decisioning/specialisms/brand-rights.d.ts.map +1 -1
- package/dist/lib/server/decisioning/specialisms/brand-rights.js.map +1 -1
- package/dist/lib/server/decisioning/specialisms/content-standards.d.mts +16 -16
- package/dist/lib/server/decisioning/specialisms/content-standards.d.ts +16 -16
- package/dist/lib/server/decisioning/specialisms/content-standards.d.ts.map +1 -1
- package/dist/lib/server/decisioning/specialisms/content-standards.js.map +1 -1
- package/dist/lib/server/decisioning/specialisms/creative-ad-server.d.mts +15 -13
- package/dist/lib/server/decisioning/specialisms/creative-ad-server.d.ts +15 -13
- 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 +12 -11
- package/dist/lib/server/decisioning/specialisms/creative.d.ts +12 -11
- 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/decisioning/specialisms/sales.d.mts +25 -15
- package/dist/lib/server/decisioning/specialisms/sales.d.ts +25 -15
- package/dist/lib/server/decisioning/specialisms/sales.d.ts.map +1 -1
- package/dist/lib/server/decisioning/specialisms/sales.js.map +1 -1
- package/dist/lib/server/index.d.mts +3 -3
- package/dist/lib/server/index.d.ts +3 -3
- package/dist/lib/server/index.d.ts.map +1 -1
- package/dist/lib/server/index.js +72 -72
- package/dist/lib/server/index.js.map +1 -1
- package/dist/lib/server/index.mjs +36 -36
- package/dist/lib/server/index.mjs.map +1 -1
- package/dist/lib/server/legacy/v5/index.d.mts +1 -0
- package/dist/lib/server/legacy/v5/index.d.ts +1 -0
- package/dist/lib/server/legacy/v5/index.d.ts.map +1 -1
- package/dist/lib/server/legacy/v5/index.js +74 -1
- package/dist/lib/server/legacy/v5/index.js.map +1 -1
- package/dist/lib/server/legacy/v5/index.mjs +75 -1
- package/dist/lib/server/legacy/v5/index.mjs.map +1 -1
- package/dist/lib/server/responses.d.mts +2 -2
- package/dist/lib/server/responses.d.ts +2 -2
- package/dist/lib/server/responses.js.map +1 -1
- package/dist/lib/server/responses.mjs.map +1 -1
- package/dist/lib/testing/client.d.ts.map +1 -1
- package/dist/lib/testing/client.js +13 -5
- package/dist/lib/testing/client.js.map +1 -1
- package/dist/lib/testing/client.mjs +13 -5
- package/dist/lib/testing/client.mjs.map +1 -1
- package/dist/lib/testing/compliance/comply.d.mts +1 -1
- package/dist/lib/testing/compliance/comply.d.ts +1 -1
- package/dist/lib/testing/compliance/comply.d.ts.map +1 -1
- package/dist/lib/testing/compliance/comply.js +16 -6
- package/dist/lib/testing/compliance/comply.js.map +1 -1
- package/dist/lib/testing/compliance/comply.mjs +16 -6
- package/dist/lib/testing/compliance/comply.mjs.map +1 -1
- package/dist/lib/testing/scenarios/brand-rights.js +6 -6
- package/dist/lib/testing/scenarios/brand-rights.js.map +1 -1
- package/dist/lib/testing/scenarios/brand-rights.mjs +6 -6
- package/dist/lib/testing/scenarios/brand-rights.mjs.map +1 -1
- package/dist/lib/testing/scenarios/creative.js +7 -7
- package/dist/lib/testing/scenarios/creative.js.map +1 -1
- package/dist/lib/testing/scenarios/creative.mjs +7 -7
- package/dist/lib/testing/scenarios/creative.mjs.map +1 -1
- package/dist/lib/testing/scenarios/discovery.js +2 -2
- package/dist/lib/testing/scenarios/discovery.js.map +1 -1
- package/dist/lib/testing/scenarios/discovery.mjs +2 -2
- package/dist/lib/testing/scenarios/discovery.mjs.map +1 -1
- package/dist/lib/testing/scenarios/edge-cases.js +11 -11
- package/dist/lib/testing/scenarios/edge-cases.js.map +1 -1
- package/dist/lib/testing/scenarios/edge-cases.mjs +11 -11
- package/dist/lib/testing/scenarios/edge-cases.mjs.map +1 -1
- package/dist/lib/testing/scenarios/governance.d.ts.map +1 -1
- package/dist/lib/testing/scenarios/governance.js +16 -22
- package/dist/lib/testing/scenarios/governance.js.map +1 -1
- package/dist/lib/testing/scenarios/governance.mjs +16 -22
- package/dist/lib/testing/scenarios/governance.mjs.map +1 -1
- package/dist/lib/testing/scenarios/media-buy.js +5 -5
- package/dist/lib/testing/scenarios/media-buy.js.map +1 -1
- package/dist/lib/testing/scenarios/media-buy.mjs +5 -5
- package/dist/lib/testing/scenarios/media-buy.mjs.map +1 -1
- package/dist/lib/testing/scenarios/schema-compliance.js +1 -1
- package/dist/lib/testing/scenarios/schema-compliance.js.map +1 -1
- package/dist/lib/testing/scenarios/schema-compliance.mjs +1 -1
- package/dist/lib/testing/scenarios/schema-compliance.mjs.map +1 -1
- package/dist/lib/testing/scenarios/trusted-match.d.ts.map +1 -1
- package/dist/lib/testing/scenarios/trusted-match.js +4 -3
- package/dist/lib/testing/scenarios/trusted-match.js.map +1 -1
- package/dist/lib/testing/scenarios/trusted-match.mjs +4 -3
- package/dist/lib/testing/scenarios/trusted-match.mjs.map +1 -1
- package/dist/lib/testing/storyboard/probes.d.mts +6 -0
- package/dist/lib/testing/storyboard/probes.d.ts +6 -0
- package/dist/lib/testing/storyboard/probes.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/probes.js +16 -5
- package/dist/lib/testing/storyboard/probes.js.map +1 -1
- package/dist/lib/testing/storyboard/probes.mjs +16 -5
- package/dist/lib/testing/storyboard/probes.mjs.map +1 -1
- package/dist/lib/testing/storyboard/runner.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/runner.js +6 -2
- package/dist/lib/testing/storyboard/runner.js.map +1 -1
- package/dist/lib/testing/storyboard/runner.mjs +6 -2
- package/dist/lib/testing/storyboard/runner.mjs.map +1 -1
- package/dist/lib/testing/storyboard/shape-drift-hints.d.mts +3 -4
- package/dist/lib/testing/storyboard/shape-drift-hints.d.ts +3 -4
- package/dist/lib/testing/storyboard/shape-drift-hints.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/shape-drift-hints.js +27 -27
- package/dist/lib/testing/storyboard/shape-drift-hints.js.map +1 -1
- package/dist/lib/testing/storyboard/shape-drift-hints.mjs +27 -27
- package/dist/lib/testing/storyboard/shape-drift-hints.mjs.map +1 -1
- package/dist/lib/testing/storyboard/task-map.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/task-map.js +43 -5
- package/dist/lib/testing/storyboard/task-map.js.map +1 -1
- package/dist/lib/testing/storyboard/task-map.mjs +43 -5
- package/dist/lib/testing/storyboard/task-map.mjs.map +1 -1
- package/dist/lib/testing/test-helpers.d.mts +2 -2
- package/dist/lib/testing/test-helpers.d.ts +2 -2
- package/dist/lib/testing/test-helpers.js.map +1 -1
- package/dist/lib/testing/test-helpers.mjs.map +1 -1
- package/dist/lib/testing/types.d.mts +7 -1
- package/dist/lib/testing/types.d.ts +7 -1
- package/dist/lib/testing/types.d.ts.map +1 -1
- package/dist/lib/testing/types.js.map +1 -1
- package/dist/lib/types/adcp.d.mts +14 -14
- package/dist/lib/types/adcp.d.ts +14 -14
- package/dist/lib/types/adcp.d.ts.map +1 -1
- package/dist/lib/types/adcp.js.map +1 -1
- package/dist/lib/types/adcp.mjs.map +1 -1
- package/dist/lib/types/index.d.mts +2 -2
- package/dist/lib/types/index.d.ts +2 -2
- 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/utils/capabilities.d.mts +6 -0
- package/dist/lib/utils/capabilities.d.ts +6 -0
- package/dist/lib/utils/capabilities.d.ts.map +1 -1
- package/dist/lib/utils/capabilities.js +4 -0
- package/dist/lib/utils/capabilities.js.map +1 -1
- package/dist/lib/utils/capabilities.mjs +4 -0
- package/dist/lib/utils/capabilities.mjs.map +1 -1
- package/dist/lib/utils/creative-delivery.d.mts +52 -10
- package/dist/lib/utils/creative-delivery.d.ts +52 -10
- package/dist/lib/utils/creative-delivery.d.ts.map +1 -1
- package/dist/lib/utils/creative-delivery.js +104 -9
- package/dist/lib/utils/creative-delivery.js.map +1 -1
- package/dist/lib/utils/creative-delivery.mjs +104 -8
- package/dist/lib/utils/creative-delivery.mjs.map +1 -1
- package/dist/lib/utils/format-assets.d.mts +41 -38
- package/dist/lib/utils/format-assets.d.ts +41 -38
- package/dist/lib/utils/format-assets.d.ts.map +1 -1
- package/dist/lib/utils/format-assets.js +3 -1
- package/dist/lib/utils/format-assets.js.map +1 -1
- package/dist/lib/utils/format-assets.mjs +3 -1
- package/dist/lib/utils/format-assets.mjs.map +1 -1
- package/dist/lib/utils/index.d.mts +8 -8
- package/dist/lib/utils/index.d.ts +8 -8
- package/dist/lib/utils/index.d.ts.map +1 -1
- package/dist/lib/utils/index.js +11 -9
- package/dist/lib/utils/index.js.map +1 -1
- package/dist/lib/utils/index.mjs +9 -8
- package/dist/lib/utils/index.mjs.map +1 -1
- package/dist/lib/utils/preview-utils.d.mts +11 -8
- package/dist/lib/utils/preview-utils.d.ts +11 -8
- package/dist/lib/utils/preview-utils.d.ts.map +1 -1
- package/dist/lib/utils/preview-utils.js +4 -4
- package/dist/lib/utils/preview-utils.js.map +1 -1
- package/dist/lib/utils/preview-utils.mjs +3 -3
- package/dist/lib/utils/preview-utils.mjs.map +1 -1
- package/dist/lib/utils/tool-request-schemas.d.mts +2 -2
- package/dist/lib/utils/tool-request-schemas.d.ts +2 -2
- package/dist/lib/v2/projection/aao-reference-formats.json +492 -0
- package/dist/lib/v2/projection/augment-response.d.mts +31 -11
- package/dist/lib/v2/projection/augment-response.d.ts +31 -11
- package/dist/lib/v2/projection/augment-response.d.ts.map +1 -1
- package/dist/lib/v2/projection/augment-response.js +233 -27
- package/dist/lib/v2/projection/augment-response.js.map +1 -1
- package/dist/lib/v2/projection/augment-response.mjs +232 -27
- package/dist/lib/v2/projection/augment-response.mjs.map +1 -1
- package/dist/lib/v2/projection/builders.d.mts +7 -6
- package/dist/lib/v2/projection/builders.d.ts +7 -6
- package/dist/lib/v2/projection/builders.d.ts.map +1 -1
- package/dist/lib/v2/projection/builders.js +11 -8
- package/dist/lib/v2/projection/builders.js.map +1 -1
- package/dist/lib/v2/projection/builders.mjs +9 -6
- package/dist/lib/v2/projection/builders.mjs.map +1 -1
- package/dist/lib/v2/projection/catalog-snapshot.d.mts +47 -0
- package/dist/lib/v2/projection/catalog-snapshot.d.ts +48 -0
- package/dist/lib/v2/projection/catalog-snapshot.d.ts.map +1 -0
- package/dist/lib/v2/projection/catalog-snapshot.js +291 -0
- package/dist/lib/v2/projection/catalog-snapshot.js.map +1 -0
- package/dist/lib/v2/projection/catalog-snapshot.mjs +265 -0
- package/dist/lib/v2/projection/catalog-snapshot.mjs.map +1 -0
- package/dist/lib/v2/projection/catalog.d.mts +23 -2
- package/dist/lib/v2/projection/catalog.d.ts +23 -2
- package/dist/lib/v2/projection/catalog.d.ts.map +1 -1
- package/dist/lib/v2/projection/catalog.js +34 -10
- package/dist/lib/v2/projection/catalog.js.map +1 -1
- package/dist/lib/v2/projection/catalog.mjs +33 -10
- package/dist/lib/v2/projection/catalog.mjs.map +1 -1
- package/dist/lib/v2/projection/creative-delivery.d.mts +157 -0
- package/dist/lib/v2/projection/creative-delivery.d.ts +158 -0
- package/dist/lib/v2/projection/creative-delivery.d.ts.map +1 -0
- package/dist/lib/v2/projection/creative-delivery.js +967 -0
- package/dist/lib/v2/projection/creative-delivery.js.map +1 -0
- package/dist/lib/v2/projection/creative-delivery.mjs +941 -0
- package/dist/lib/v2/projection/creative-delivery.mjs.map +1 -0
- package/dist/lib/v2/projection/index.d.mts +8 -6
- package/dist/lib/v2/projection/index.d.ts +8 -6
- package/dist/lib/v2/projection/index.d.ts.map +1 -1
- package/dist/lib/v2/projection/index.js +26 -4
- package/dist/lib/v2/projection/index.js.map +1 -1
- package/dist/lib/v2/projection/index.mjs +28 -4
- package/dist/lib/v2/projection/index.mjs.map +1 -1
- package/dist/lib/v2/projection/legacy-metadata.d.mts +21 -0
- package/dist/lib/v2/projection/legacy-metadata.d.ts +22 -0
- package/dist/lib/v2/projection/legacy-metadata.d.ts.map +1 -0
- package/dist/lib/v2/projection/legacy-metadata.js +73 -0
- package/dist/lib/v2/projection/legacy-metadata.js.map +1 -0
- package/dist/lib/v2/projection/legacy-metadata.mjs +45 -0
- package/dist/lib/v2/projection/legacy-metadata.mjs.map +1 -0
- package/dist/lib/v2/projection/types.d.mts +29 -23
- package/dist/lib/v2/projection/types.d.ts +29 -23
- package/dist/lib/v2/projection/types.d.ts.map +1 -1
- package/dist/lib/v2/projection/types.js.map +1 -1
- package/dist/lib/v2/projection/v1-to-v2.d.mts +29 -6
- package/dist/lib/v2/projection/v1-to-v2.d.ts +29 -6
- package/dist/lib/v2/projection/v1-to-v2.d.ts.map +1 -1
- package/dist/lib/v2/projection/v1-to-v2.js +218 -9
- package/dist/lib/v2/projection/v1-to-v2.js.map +1 -1
- package/dist/lib/v2/projection/v1-to-v2.mjs +219 -10
- package/dist/lib/v2/projection/v1-to-v2.mjs.map +1 -1
- package/dist/lib/v2/projection/v2-to-v1.d.mts +31 -2
- package/dist/lib/v2/projection/v2-to-v1.d.ts +31 -2
- package/dist/lib/v2/projection/v2-to-v1.d.ts.map +1 -1
- package/dist/lib/v2/projection/v2-to-v1.js +138 -7
- package/dist/lib/v2/projection/v2-to-v1.js.map +1 -1
- package/dist/lib/v2/projection/v2-to-v1.mjs +135 -6
- package/dist/lib/v2/projection/v2-to-v1.mjs.map +1 -1
- package/dist/lib/v2/projection/write-side.d.mts +12 -13
- package/dist/lib/v2/projection/write-side.d.ts +12 -13
- package/dist/lib/v2/projection/write-side.d.ts.map +1 -1
- package/dist/lib/v2/projection/write-side.js +16 -12
- package/dist/lib/v2/projection/write-side.js.map +1 -1
- package/dist/lib/v2/projection/write-side.mjs +16 -12
- package/dist/lib/v2/projection/write-side.mjs.map +1 -1
- package/dist/lib/version.d.mts +3 -3
- package/dist/lib/version.d.ts +3 -3
- package/dist/lib/version.d.ts.map +1 -1
- 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 +7 -2
- package/docs/guides/PREVIEW-ASSET-DURABILITY.md +6 -2
- package/docs/llms.txt +1 -0
- package/examples/README.md +1 -1
- package/examples/batch-preview-test.ts +3 -3
- package/examples/conversation-client.ts +2 -2
- package/examples/debug-preview-response.ts +1 -1
- package/examples/decisioning-platform-broadcast-tv.ts +10 -4
- package/examples/decisioning-platform-mock-seller.ts +16 -9
- package/examples/decisioning-platform-programmatic.ts +18 -7
- package/examples/error-compliant-server.ts +26 -16
- package/examples/generative-creative-demo.ts +7 -19
- package/examples/hello_creative_adapter_ad_server.ts +37 -16
- package/examples/hello_creative_adapter_template.ts +12 -10
- package/examples/hello_seller_adapter_guaranteed.ts +52 -23
- package/examples/hello_seller_adapter_multi_tenant.ts +6 -3
- package/examples/hello_seller_adapter_non_guaranteed.ts +54 -26
- package/examples/hello_seller_adapter_proposal_mode.ts +40 -14
- package/examples/hello_seller_adapter_social.ts +2 -7
- package/examples/inspect-card-formats.ts +1 -1
- package/examples/simple-getting-started.ts +1 -1
- package/examples/test-helpers-demo.ts +1 -1
- package/package.json +2 -2
- package/skills/build-decisioning-creative-template/SKILL.md +11 -11
- package/skills/build-decisioning-platform/advanced/REFERENCE.md +8 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/lib/net/ssrf-fetch.ts"],"sourcesContent":["/**\n * SSRF-safe HTTP fetch primitive.\n *\n * Used by compliance probes, storyboard runners, and (future) JWKS /\n * revocation-list resolvers — anywhere the library dispatches a request to a\n * URL that came from counterparty-controlled data and therefore might point at\n * the host's private network or a cloud metadata endpoint.\n *\n * Guarantees:\n * - Scheme: only `https:` by default; `http:` allowed under the dev-opt-in\n * `allowPrivateIp` flag. `file:`, `ftp:`, `data:`, etc. are always refused.\n * - DNS: resolves every A/AAAA record once, validates the full set, then\n * pins the outbound connection to the first validated address via an\n * undici `Agent` whose `connect.lookup` returns the pinned tuple.\n * Defeats DNS rebinding (attacker returns a public address to the guard\n * lookup and a private address to the connect-time lookup) and any other\n * TOCTOU gap between validation and connect.\n * - IMDS (`169.254.169.254`, `fe80::`) stays refused even under\n * `allowPrivateIp` — cloud metadata exfiltration is never a legitimate\n * dev-loop use case.\n * - Redirects are not followed (`redirect: 'manual'`). The 3xx response is\n * returned with `Location` populated so callers can inspect it, but they\n * MUST NOT re-dispatch to the `Location` URL themselves — that bypasses\n * every guard above. To follow a redirect safely, re-invoke\n * `ssrfSafeFetch` with the new URL so it runs through validation again.\n * - Response body is buffered up to `maxBodyBytes` (default 64 KiB) and\n * returned as raw bytes; the dispatcher is torn down after reading so\n * connection-reuse can't carry an attacker-controlled keepalive.\n *\n * Returns a fully-buffered result. Callers that need streaming or large bodies\n * should extend this primitive rather than bypass it.\n */\nimport { type LookupAddress, type LookupOptions } from 'dns';\nimport { lookup as dnsLookupAsync } from 'dns/promises';\nimport { Agent, fetch as undiciFetch } from 'undici';\nimport { isAlwaysBlocked, isPrivateIp } from './address-guards';\n\nconst DEFAULT_TIMEOUT_MS = 10_000;\nconst DEFAULT_MAX_BODY_BYTES = 64 * 1024;\nconst ALLOWED_SCHEMES = new Set(['https:', 'http:']);\n\nexport type SsrfRefusedCode =\n | 'invalid_url'\n | 'scheme_not_allowed'\n | 'non_https_without_opt_in'\n | 'dns_lookup_failed'\n | 'dns_empty'\n | 'always_blocked_address'\n | 'private_address'\n | 'body_exceeds_limit';\n\n/**\n * SSRF refusal codes that indicate runtime/network conditions rather than\n * policy refusal. Callers that want to fall back to a \"host unreachable\" or\n * \"treat as unknown\" path on these codes — rather than surfacing the\n * SsrfRefusedError to their own caller — can intersect against this set:\n *\n * ```ts\n * if (err instanceof SsrfRefusedError && SSRF_TRANSIENT_CODES.has(err.code)) {\n * // network condition, not a policy attack — treat as unreachable\n * } else if (err instanceof SsrfRefusedError) {\n * // policy refusal — must propagate; silently downgrading to \"unreachable\"\n * // reintroduces the catch-swallow class flagged in adcp-client#1618 review\n * throw err;\n * }\n * ```\n *\n * - `dns_lookup_failed`, `dns_empty`: name does not resolve. CLI fixtures\n * use `*.example.invalid` to provoke these — preserving the runtime-error\n * path matches pre-`ssrfSafeFetch` native-fetch behavior.\n * - `body_exceeds_limit`: response started OK (validated address, scheme,\n * etc.) but exceeded the caller's defensive cap. The host is real and\n * knows the URL; classifying as \"policy attack\" would be a misread.\n *\n * NOT in this set: `always_blocked_address`, `private_address`,\n * `scheme_not_allowed`, `non_https_without_opt_in`, `invalid_url`. Those\n * are policy refusals — silently downgrading them to \"unreachable\" or\n * \"suspect\" reintroduces the SSRF gap the gate was added to close.\n *\n * Added in adcp-client#1633.\n */\nexport const SSRF_TRANSIENT_CODES: ReadonlySet<SsrfRefusedCode> = new Set([\n 'dns_lookup_failed',\n 'dns_empty',\n 'body_exceeds_limit',\n]);\n\n/**\n * Thrown when the SSRF guard refuses a request before (or during) the fetch.\n * Network failures after the guard passes are not wrapped in this type —\n * callers that want to distinguish \"we refused this\" from \"the remote broke\"\n * can `instanceof SsrfRefusedError` the catch.\n */\nexport class SsrfRefusedError extends Error {\n readonly code: SsrfRefusedCode;\n readonly url: string;\n readonly hostname?: string;\n readonly address?: string;\n\n constructor(code: SsrfRefusedCode, message: string, meta: { url: string; hostname?: string; address?: string }) {\n super(message);\n this.name = 'SsrfRefusedError';\n this.code = code;\n this.url = meta.url;\n this.hostname = meta.hostname;\n this.address = meta.address;\n }\n}\n\nexport interface SsrfFetchOptions {\n method?: string;\n /** Lowercased keys preferred; values preserved verbatim. */\n headers?: Record<string, string>;\n body?: string | Uint8Array;\n /** Allow `http://` and private/loopback targets. Default false. */\n allowPrivateIp?: boolean;\n /** Overall timeout including DNS + connect + body read. Default 10_000 ms. */\n timeoutMs?: number;\n /** Hard cap on response body bytes. Default 64 KiB. */\n maxBodyBytes?: number;\n /** Caller-provided abort signal, composed with the internal timeout. */\n signal?: AbortSignal;\n}\n\nexport interface SsrfFetchResult {\n url: string;\n status: number;\n /** Response headers, lowercased. */\n headers: Record<string, string>;\n /** Raw response body bytes (empty Uint8Array if no body). */\n body: Uint8Array;\n /** The IP address we pinned the outbound connection to. */\n pinnedAddress: string;\n pinnedFamily: 4 | 6;\n}\n\n/**\n * GET/POST/etc. a URL with SSRF guarding + DNS pinning. Throws\n * {@link SsrfRefusedError} when the guard refuses; other errors (network\n * timeouts, remote resets) propagate as the native fetch error.\n */\nexport async function ssrfSafeFetch(url: string, options: SsrfFetchOptions = {}): Promise<SsrfFetchResult> {\n const allowPrivateIp = options.allowPrivateIp === true;\n const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const maxBodyBytes = options.maxBodyBytes ?? DEFAULT_MAX_BODY_BYTES;\n\n let parsed: URL;\n try {\n parsed = new URL(url);\n } catch {\n throw new SsrfRefusedError('invalid_url', `Invalid URL: ${url}`, { url });\n }\n\n // `URL.hostname` wraps IPv6 literals in brackets (`https://[::1]/` →\n // `[::1]`). `dns.lookup` and the address classifier both want the bare\n // form; strip brackets here so IPv6 localhost URLs work under\n // `allowPrivateIp` and so bracketed literals can't slip past classification\n // on a future Node release that tolerates them.\n const hostname = parsed.hostname.replace(/^\\[|\\]$/g, '');\n\n if (!ALLOWED_SCHEMES.has(parsed.protocol)) {\n throw new SsrfRefusedError(\n 'scheme_not_allowed',\n `Refusing to fetch URL with unsupported scheme: ${parsed.protocol}`,\n { url, hostname }\n );\n }\n if (parsed.protocol !== 'https:' && !allowPrivateIp) {\n throw new SsrfRefusedError('non_https_without_opt_in', `Refusing to fetch non-HTTPS URL: ${url}`, {\n url,\n hostname,\n });\n }\n\n let addresses: { address: string; family: number }[];\n try {\n addresses = await dnsLookupAsync(hostname, { all: true });\n } catch (err) {\n throw new SsrfRefusedError(\n 'dns_lookup_failed',\n `DNS lookup failed for ${hostname}: ${err instanceof Error ? err.message : String(err)}`,\n { url, hostname }\n );\n }\n if (addresses.length === 0) {\n throw new SsrfRefusedError('dns_empty', `DNS returned no addresses for ${hostname}`, {\n url,\n hostname,\n });\n }\n // Error messages intentionally do NOT include the resolved IP — a\n // counterparty-supplied hostname that resolves into the caller's internal\n // address space would otherwise leak network topology into compliance\n // reports and log aggregators. The address is still available on the\n // thrown error's `.address` field for programmatic debugging.\n for (const a of addresses) {\n if (isAlwaysBlocked(a.address)) {\n throw new SsrfRefusedError(\n 'always_blocked_address',\n `Refusing to fetch: ${hostname} resolves to an always-blocked address (link-local or cloud metadata)`,\n { url, hostname, address: a.address }\n );\n }\n }\n if (!allowPrivateIp) {\n for (const a of addresses) {\n if (isPrivateIp(a.address)) {\n throw new SsrfRefusedError(\n 'private_address',\n `Refusing to fetch: ${hostname} resolves to a private/loopback address`,\n { url, hostname, address: a.address }\n );\n }\n }\n }\n\n const pinned = addresses[0]!;\n const pinnedFamily = pinned.family === 6 ? 6 : 4;\n const dispatcher = new Agent({\n connect: {\n // All addresses were validated above; pin the connect to the first. The\n // custom lookup also means undici won't re-resolve and pick up a rebind.\n // undici's Agent may call lookup with `{ all: true }` (it does for HTTPS\n // targets under Node 22+), which expects the array form of the callback.\n lookup: (\n _h: string,\n opts: LookupOptions | undefined,\n cb: (err: NodeJS.ErrnoException | null, address: string | LookupAddress[], family?: number) => void\n ) => {\n if (opts?.all) {\n cb(null, [{ address: pinned.address, family: pinnedFamily }]);\n } else {\n cb(null, pinned.address, pinnedFamily);\n }\n },\n },\n });\n\n const ac = new AbortController();\n const onExternalAbort = () => ac.abort(options.signal?.reason);\n options.signal?.addEventListener('abort', onExternalAbort, { once: true });\n const timer = setTimeout(() => ac.abort(new Error('ssrf-fetch: timeout')), timeoutMs);\n\n try {\n const init: Parameters<typeof undiciFetch>[1] = {\n method: options.method ?? 'GET',\n redirect: 'manual',\n signal: ac.signal,\n headers: options.headers,\n dispatcher,\n };\n if (options.body !== undefined) init.body = options.body;\n\n const res = await undiciFetch(url, init);\n\n const headers: Record<string, string> = {};\n res.headers.forEach((v, k) => {\n headers[k.toLowerCase()] = v;\n });\n\n const reader = res.body?.getReader();\n if (!reader) {\n return {\n url,\n status: res.status,\n headers,\n body: new Uint8Array(),\n pinnedAddress: pinned.address,\n pinnedFamily,\n };\n }\n\n const chunks: Uint8Array[] = [];\n let bytes = 0;\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n bytes += value.byteLength;\n if (bytes > maxBodyBytes) {\n await reader.cancel();\n throw new SsrfRefusedError('body_exceeds_limit', `Response body exceeded ${maxBodyBytes} bytes`, {\n url,\n hostname: parsed.hostname,\n address: pinned.address,\n });\n }\n chunks.push(value);\n }\n\n const buf = new Uint8Array(bytes);\n let offset = 0;\n for (const c of chunks) {\n buf.set(c, offset);\n offset += c.byteLength;\n }\n\n return {\n url,\n status: res.status,\n headers,\n body: buf,\n pinnedAddress: pinned.address,\n pinnedFamily,\n };\n } finally {\n clearTimeout(timer);\n options.signal?.removeEventListener('abort', onExternalAbort);\n await dispatcher.close().catch(() => {});\n }\n}\n\n/**\n * Decode a UTF-8 byte buffer as JSON when the content-type declares JSON, or\n * fall back to a UTF-8 string. Handy shared helper for probe-style call sites\n * that don't care about binary bodies.\n */\nexport function decodeBodyAsJsonOrText(body: Uint8Array, contentType: string | undefined): unknown {\n if (body.byteLength === 0) return null;\n const text = Buffer.from(body.buffer, body.byteOffset, body.byteLength).toString('utf8');\n if (contentType?.toLowerCase().includes('application/json')) {\n try {\n return JSON.parse(text);\n } catch {\n return text;\n }\n }\n return text;\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/lib/net/ssrf-fetch.ts"],"sourcesContent":["/**\n * SSRF-safe HTTP fetch primitive.\n *\n * Used by compliance probes, storyboard runners, and (future) JWKS /\n * revocation-list resolvers — anywhere the library dispatches a request to a\n * URL that came from counterparty-controlled data and therefore might point at\n * the host's private network or a cloud metadata endpoint.\n *\n * Guarantees:\n * - Scheme: only `https:` by default; `http:` allowed under the dev-opt-in\n * `allowPrivateIp` flag. `file:`, `ftp:`, `data:`, etc. are always refused.\n * - DNS: resolves every A/AAAA record once, validates the full set, then\n * pins the outbound connection to the first validated address via an\n * undici `Agent` whose `connect.lookup` returns the pinned tuple.\n * Defeats DNS rebinding (attacker returns a public address to the guard\n * lookup and a private address to the connect-time lookup) and any other\n * TOCTOU gap between validation and connect.\n * - IMDS (`169.254.169.254`, `fe80::`) stays refused even under\n * `allowPrivateIp` — cloud metadata exfiltration is never a legitimate\n * dev-loop use case.\n * - Redirects are not followed (`redirect: 'manual'`). The 3xx response is\n * returned with `Location` populated so callers can inspect it, but they\n * MUST NOT re-dispatch to the `Location` URL themselves — that bypasses\n * every guard above. To follow a redirect safely, re-invoke\n * `ssrfSafeFetch` with the new URL so it runs through validation again.\n * - Response body is buffered up to `maxBodyBytes` (default 64 KiB) and\n * returned as raw bytes; the dispatcher is torn down after reading so\n * connection-reuse can't carry an attacker-controlled keepalive.\n *\n * Returns a fully-buffered result. Callers that need streaming or large bodies\n * should extend this primitive rather than bypass it.\n */\nimport { type LookupAddress, type LookupOptions } from 'dns';\nimport { lookup as dnsLookupAsync } from 'dns/promises';\nimport { Agent, fetch as undiciFetch } from 'undici';\nimport { isAlwaysBlocked, isPrivateIp } from './address-guards';\n\nconst DEFAULT_TIMEOUT_MS = 10_000;\nconst DEFAULT_MAX_BODY_BYTES = 64 * 1024;\nconst ALLOWED_SCHEMES = new Set(['https:', 'http:']);\n\nexport type SsrfRefusedCode =\n | 'invalid_url'\n | 'scheme_not_allowed'\n | 'non_https_without_opt_in'\n | 'dns_lookup_failed'\n | 'dns_empty'\n | 'always_blocked_address'\n | 'private_address'\n | 'body_exceeds_limit';\n\n/**\n * SSRF refusal codes that indicate runtime/network conditions rather than\n * policy refusal. Callers that want to fall back to a \"host unreachable\" or\n * \"treat as unknown\" path on these codes — rather than surfacing the\n * SsrfRefusedError to their own caller — can intersect against this set:\n *\n * ```ts\n * if (err instanceof SsrfRefusedError && SSRF_TRANSIENT_CODES.has(err.code)) {\n * // network condition, not a policy attack — treat as unreachable\n * } else if (err instanceof SsrfRefusedError) {\n * // policy refusal — must propagate; silently downgrading to \"unreachable\"\n * // reintroduces the catch-swallow class flagged in adcp-client#1618 review\n * throw err;\n * }\n * ```\n *\n * - `dns_lookup_failed`, `dns_empty`: name does not resolve. CLI fixtures\n * use `*.example.invalid` to provoke these — preserving the runtime-error\n * path matches pre-`ssrfSafeFetch` native-fetch behavior.\n * - `body_exceeds_limit`: response started OK (validated address, scheme,\n * etc.) but exceeded the caller's defensive cap. The host is real and\n * knows the URL; classifying as \"policy attack\" would be a misread.\n *\n * NOT in this set: `always_blocked_address`, `private_address`,\n * `scheme_not_allowed`, `non_https_without_opt_in`, `invalid_url`. Those\n * are policy refusals — silently downgrading them to \"unreachable\" or\n * \"suspect\" reintroduces the SSRF gap the gate was added to close.\n *\n * Added in adcp-client#1633.\n */\nexport const SSRF_TRANSIENT_CODES: ReadonlySet<SsrfRefusedCode> = new Set([\n 'dns_lookup_failed',\n 'dns_empty',\n 'body_exceeds_limit',\n]);\n\n/**\n * Thrown when the SSRF guard refuses a request before (or during) the fetch.\n * Network failures after the guard passes are not wrapped in this type —\n * callers that want to distinguish \"we refused this\" from \"the remote broke\"\n * can `instanceof SsrfRefusedError` the catch.\n */\nexport class SsrfRefusedError extends Error {\n readonly code: SsrfRefusedCode;\n readonly url: string;\n readonly hostname?: string;\n readonly address?: string;\n\n constructor(code: SsrfRefusedCode, message: string, meta: { url: string; hostname?: string; address?: string }) {\n super(message);\n this.name = 'SsrfRefusedError';\n this.code = code;\n this.url = meta.url;\n this.hostname = meta.hostname;\n this.address = meta.address;\n }\n}\n\nexport interface SsrfFetchOptions {\n method?: string;\n /** Lowercased keys preferred; values preserved verbatim. */\n headers?: Record<string, string>;\n body?: string | Uint8Array;\n /** Allow `http://` and private/loopback targets. Default false. */\n allowPrivateIp?: boolean;\n /** Overall timeout including DNS + connect + body read. Default 10_000 ms. */\n timeoutMs?: number;\n /** Hard cap on response body bytes. Default 64 KiB. */\n maxBodyBytes?: number;\n /** Caller-provided abort signal, composed with the internal timeout. */\n signal?: AbortSignal;\n /**\n * Trusted scoped fetch implementation. URL validation and address\n * classification still run before invocation, but DNS pinning is delegated\n * to this implementation because custom fetchers do not accept undici\n * dispatchers. The caller MUST enforce DNS-rebinding protection itself.\n *\n * This deliberately explicit name prevents callers from mistaking a custom\n * transport for the internally DNS-pinned path.\n */\n trustedFetchFn?: typeof fetch;\n}\n\nexport interface SsrfFetchResult {\n url: string;\n status: number;\n /** Response headers, lowercased. */\n headers: Record<string, string>;\n /** Raw response body bytes (empty Uint8Array if no body). */\n body: Uint8Array;\n /** The validated IP address. The connection used it only when `connectionPinned` is true. */\n pinnedAddress: string;\n pinnedFamily: 4 | 6;\n /** Whether the internal undici dispatcher pinned the connection to `pinnedAddress`. */\n connectionPinned: boolean;\n}\n\n/**\n * GET/POST/etc. a URL with SSRF guarding + DNS pinning. Throws\n * {@link SsrfRefusedError} when the guard refuses; other errors (network\n * timeouts, remote resets) propagate as the native fetch error.\n */\nexport async function ssrfSafeFetch(url: string, options: SsrfFetchOptions = {}): Promise<SsrfFetchResult> {\n const allowPrivateIp = options.allowPrivateIp === true;\n const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const maxBodyBytes = options.maxBodyBytes ?? DEFAULT_MAX_BODY_BYTES;\n\n let parsed: URL;\n try {\n parsed = new URL(url);\n } catch {\n throw new SsrfRefusedError('invalid_url', `Invalid URL: ${url}`, { url });\n }\n\n // `URL.hostname` wraps IPv6 literals in brackets (`https://[::1]/` →\n // `[::1]`). `dns.lookup` and the address classifier both want the bare\n // form; strip brackets here so IPv6 localhost URLs work under\n // `allowPrivateIp` and so bracketed literals can't slip past classification\n // on a future Node release that tolerates them.\n const hostname = parsed.hostname.replace(/^\\[|\\]$/g, '');\n\n if (!ALLOWED_SCHEMES.has(parsed.protocol)) {\n throw new SsrfRefusedError(\n 'scheme_not_allowed',\n `Refusing to fetch URL with unsupported scheme: ${parsed.protocol}`,\n { url, hostname }\n );\n }\n if (parsed.protocol !== 'https:' && !allowPrivateIp) {\n throw new SsrfRefusedError('non_https_without_opt_in', `Refusing to fetch non-HTTPS URL: ${url}`, {\n url,\n hostname,\n });\n }\n\n let addresses: { address: string; family: number }[];\n try {\n addresses = await dnsLookupAsync(hostname, { all: true });\n } catch (err) {\n throw new SsrfRefusedError(\n 'dns_lookup_failed',\n `DNS lookup failed for ${hostname}: ${err instanceof Error ? err.message : String(err)}`,\n { url, hostname }\n );\n }\n if (addresses.length === 0) {\n throw new SsrfRefusedError('dns_empty', `DNS returned no addresses for ${hostname}`, {\n url,\n hostname,\n });\n }\n // Error messages intentionally do NOT include the resolved IP — a\n // counterparty-supplied hostname that resolves into the caller's internal\n // address space would otherwise leak network topology into compliance\n // reports and log aggregators. The address is still available on the\n // thrown error's `.address` field for programmatic debugging.\n for (const a of addresses) {\n if (isAlwaysBlocked(a.address)) {\n throw new SsrfRefusedError(\n 'always_blocked_address',\n `Refusing to fetch: ${hostname} resolves to an always-blocked address (link-local or cloud metadata)`,\n { url, hostname, address: a.address }\n );\n }\n }\n if (!allowPrivateIp) {\n for (const a of addresses) {\n if (isPrivateIp(a.address)) {\n throw new SsrfRefusedError(\n 'private_address',\n `Refusing to fetch: ${hostname} resolves to a private/loopback address`,\n { url, hostname, address: a.address }\n );\n }\n }\n }\n\n const pinned = addresses[0]!;\n const pinnedFamily = pinned.family === 6 ? 6 : 4;\n const dispatcher = new Agent({\n connect: {\n // All addresses were validated above; pin the connect to the first. The\n // custom lookup also means undici won't re-resolve and pick up a rebind.\n // undici's Agent may call lookup with `{ all: true }` (it does for HTTPS\n // targets under Node 22+), which expects the array form of the callback.\n lookup: (\n _h: string,\n opts: LookupOptions | undefined,\n cb: (err: NodeJS.ErrnoException | null, address: string | LookupAddress[], family?: number) => void\n ) => {\n if (opts?.all) {\n cb(null, [{ address: pinned.address, family: pinnedFamily }]);\n } else {\n cb(null, pinned.address, pinnedFamily);\n }\n },\n },\n });\n\n const ac = new AbortController();\n const onExternalAbort = () => ac.abort(options.signal?.reason);\n options.signal?.addEventListener('abort', onExternalAbort, { once: true });\n const timer = setTimeout(() => ac.abort(new Error('ssrf-fetch: timeout')), timeoutMs);\n\n try {\n const res = options.trustedFetchFn\n ? await options.trustedFetchFn(url, {\n method: options.method ?? 'GET',\n redirect: 'manual',\n signal: ac.signal,\n headers: options.headers,\n ...(options.body !== undefined && { body: options.body as BodyInit }),\n })\n : await undiciFetch(url, {\n method: options.method ?? 'GET',\n redirect: 'manual',\n signal: ac.signal,\n headers: options.headers,\n dispatcher,\n ...(options.body !== undefined && { body: options.body }),\n });\n\n const headers: Record<string, string> = {};\n res.headers.forEach((v, k) => {\n headers[k.toLowerCase()] = v;\n });\n\n const reader = res.body?.getReader();\n if (!reader) {\n return {\n url,\n status: res.status,\n headers,\n body: new Uint8Array(),\n pinnedAddress: pinned.address,\n pinnedFamily,\n connectionPinned: !options.trustedFetchFn,\n };\n }\n\n const chunks: Uint8Array[] = [];\n let bytes = 0;\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n bytes += value.byteLength;\n if (bytes > maxBodyBytes) {\n await reader.cancel();\n throw new SsrfRefusedError('body_exceeds_limit', `Response body exceeded ${maxBodyBytes} bytes`, {\n url,\n hostname: parsed.hostname,\n address: pinned.address,\n });\n }\n chunks.push(value);\n }\n\n const buf = new Uint8Array(bytes);\n let offset = 0;\n for (const c of chunks) {\n buf.set(c, offset);\n offset += c.byteLength;\n }\n\n return {\n url,\n status: res.status,\n headers,\n body: buf,\n pinnedAddress: pinned.address,\n pinnedFamily,\n connectionPinned: !options.trustedFetchFn,\n };\n } finally {\n clearTimeout(timer);\n options.signal?.removeEventListener('abort', onExternalAbort);\n await dispatcher.close().catch(() => {});\n }\n}\n\n/**\n * Decode a UTF-8 byte buffer as JSON when the content-type declares JSON, or\n * fall back to a UTF-8 string. Handy shared helper for probe-style call sites\n * that don't care about binary bodies.\n */\nexport function decodeBodyAsJsonOrText(body: Uint8Array, contentType: string | undefined): unknown {\n if (body.byteLength === 0) return null;\n const text = Buffer.from(body.buffer, body.byteOffset, body.byteLength).toString('utf8');\n if (contentType?.toLowerCase().includes('application/json')) {\n try {\n return JSON.parse(text);\n } catch {\n return text;\n }\n }\n return text;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -41,7 +41,7 @@ export declare function closeA2AConnections(): void;
|
|
|
41
41
|
* @param agent The agent config (used for URL, auth token, signing).
|
|
42
42
|
* @param taskId The server-assigned A2A Task.id to cancel.
|
|
43
43
|
*/
|
|
44
|
-
export declare function cancelA2ATask(agent: AgentConfig, taskId: string): Promise<void>;
|
|
44
|
+
export declare function cancelA2ATask(agent: AgentConfig, taskId: string, fetchFn?: typeof fetch): Promise<void>;
|
|
45
45
|
/**
|
|
46
46
|
* Protocol-level session identifiers that ride on the A2A Message envelope
|
|
47
47
|
* (not in the skill parameters). `contextId` binds sends to a server-side
|
|
@@ -54,4 +54,4 @@ export interface A2ASessionIds {
|
|
|
54
54
|
contextId?: string;
|
|
55
55
|
taskId?: string;
|
|
56
56
|
}
|
|
57
|
-
export declare function callA2ATool(agentUrl: string, toolName: string, parameters: Record<string, unknown>, authToken?: string, debugLogs?: DebugLogEntry[], pushNotificationConfig?: PushNotificationConfig, customHeaders?: Record<string, string>, signingContext?: AgentSigningContext, session?: A2ASessionIds, signal?: AbortSignal, requestTimeoutMs?: number): Promise<unknown>;
|
|
57
|
+
export declare function callA2ATool(agentUrl: string, toolName: string, parameters: Record<string, unknown>, authToken?: string, debugLogs?: DebugLogEntry[], pushNotificationConfig?: PushNotificationConfig, customHeaders?: Record<string, string>, signingContext?: AgentSigningContext, session?: A2ASessionIds, signal?: AbortSignal, requestTimeoutMs?: number, fetchFn?: typeof fetch): Promise<unknown>;
|
|
@@ -41,7 +41,7 @@ export declare function closeA2AConnections(): void;
|
|
|
41
41
|
* @param agent The agent config (used for URL, auth token, signing).
|
|
42
42
|
* @param taskId The server-assigned A2A Task.id to cancel.
|
|
43
43
|
*/
|
|
44
|
-
export declare function cancelA2ATask(agent: AgentConfig, taskId: string): Promise<void>;
|
|
44
|
+
export declare function cancelA2ATask(agent: AgentConfig, taskId: string, fetchFn?: typeof fetch): Promise<void>;
|
|
45
45
|
/**
|
|
46
46
|
* Protocol-level session identifiers that ride on the A2A Message envelope
|
|
47
47
|
* (not in the skill parameters). `contextId` binds sends to a server-side
|
|
@@ -54,5 +54,5 @@ export interface A2ASessionIds {
|
|
|
54
54
|
contextId?: string;
|
|
55
55
|
taskId?: string;
|
|
56
56
|
}
|
|
57
|
-
export declare function callA2ATool(agentUrl: string, toolName: string, parameters: Record<string, unknown>, authToken?: string, debugLogs?: DebugLogEntry[], pushNotificationConfig?: PushNotificationConfig, customHeaders?: Record<string, string>, signingContext?: AgentSigningContext, session?: A2ASessionIds, signal?: AbortSignal, requestTimeoutMs?: number): Promise<unknown>;
|
|
57
|
+
export declare function callA2ATool(agentUrl: string, toolName: string, parameters: Record<string, unknown>, authToken?: string, debugLogs?: DebugLogEntry[], pushNotificationConfig?: PushNotificationConfig, customHeaders?: Record<string, string>, signingContext?: AgentSigningContext, session?: A2ASessionIds, signal?: AbortSignal, requestTimeoutMs?: number, fetchFn?: typeof fetch): Promise<unknown>;
|
|
58
58
|
//# sourceMappingURL=a2a.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"a2a.d.ts","sourceRoot":"","sources":["../../../src/lib/protocols/a2a.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AACvE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAMnD,OAAO,EAAiD,KAAK,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAI5G,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"a2a.d.ts","sourceRoot":"","sources":["../../../src/lib/protocols/a2a.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AACvE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAMnD,OAAO,EAAiD,KAAK,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAI5G,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAsIjD;;;;GAIG;AACH,wBAAgB,mBAAmB,IAAI,IAAI,CAG1C;AASD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAsB,aAAa,CAAC,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CA6E7G;AAkMD;;;;;;;GAOG;AACH,MAAM,WAAW,aAAa;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAsB,WAAW,CAC/B,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACnC,SAAS,CAAC,EAAE,MAAM,EAClB,SAAS,GAAE,aAAa,EAAO,EAC/B,sBAAsB,CAAC,EAAE,sBAAsB,EAC/C,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACtC,cAAc,CAAC,EAAE,mBAAmB,EACpC,OAAO,CAAC,EAAE,aAAa,EACvB,MAAM,CAAC,EAAE,WAAW,EACpB,gBAAgB,CAAC,EAAE,MAAM,EACzB,OAAO,CAAC,EAAE,OAAO,KAAK,GACrB,OAAO,CAAC,OAAO,CAAC,CAgClB"}
|
|
@@ -94,7 +94,7 @@ function closeA2AConnections() {
|
|
|
94
94
|
pendingA2AClients.clear();
|
|
95
95
|
}
|
|
96
96
|
const CANCEL_TIMEOUT_MS = 5e3;
|
|
97
|
-
async function cancelA2ATask(agent, taskId) {
|
|
97
|
+
async function cancelA2ATask(agent, taskId, fetchFn) {
|
|
98
98
|
if (agent.protocol !== "a2a") {
|
|
99
99
|
return;
|
|
100
100
|
}
|
|
@@ -121,7 +121,7 @@ async function cancelA2ATask(agent, taskId) {
|
|
|
121
121
|
signal: AbortSignal.timeout(CANCEL_TIMEOUT_MS)
|
|
122
122
|
};
|
|
123
123
|
if (agent.request_signing) {
|
|
124
|
-
const upstream = (input, ini) => fetch(input, ini);
|
|
124
|
+
const upstream = (input, ini) => fetchFn ? fetchFn(input, ini) : fetch(input, ini);
|
|
125
125
|
if ((0, import_agent_fetch.isInlineSigningConfig)(agent.request_signing)) {
|
|
126
126
|
const signed = (0, import_fetch.createSigningFetch)(upstream, (0, import_agent_fetch.toSignerKey)(agent.request_signing));
|
|
127
127
|
await signed(agentUrl, init);
|
|
@@ -133,12 +133,13 @@ async function cancelA2ATask(agent, taskId) {
|
|
|
133
133
|
return;
|
|
134
134
|
}
|
|
135
135
|
}
|
|
136
|
-
await fetch(agentUrl, init);
|
|
136
|
+
await (fetchFn ?? fetch)(agentUrl, init);
|
|
137
137
|
}
|
|
138
138
|
async function getOrCreateA2AClient(agentUrl, authToken, customHeaders, bypassCache = false) {
|
|
139
139
|
const signingContext = import_client2.signingContextStorage.getStore();
|
|
140
|
+
const fetchFn = callContextStorage.getStore()?.fetchFn;
|
|
140
141
|
const cacheKey = a2aCacheKey(agentUrl, authToken, signingContext?.cacheKey, customHeaders);
|
|
141
|
-
if (bypassCache) {
|
|
142
|
+
if (bypassCache || fetchFn) {
|
|
142
143
|
return createA2AClient(agentUrl, authToken);
|
|
143
144
|
}
|
|
144
145
|
const cached = a2aClientCache.get(cacheKey);
|
|
@@ -180,7 +181,10 @@ async function createA2AClient(agentUrl, authToken) {
|
|
|
180
181
|
function buildFetchImpl(authToken) {
|
|
181
182
|
const signingContext = import_client2.signingContextStorage.getStore();
|
|
182
183
|
const networkFetch = (0, import_transportDiagnostics.wrapFetchWithTransportDiagnostics)(
|
|
183
|
-
(0, import_responseSizeLimit.wrapFetchWithSizeLimit)((input, init) =>
|
|
184
|
+
(0, import_responseSizeLimit.wrapFetchWithSizeLimit)((input, init) => {
|
|
185
|
+
const scopedFetch = callContextStorage.getStore()?.fetchFn;
|
|
186
|
+
return scopedFetch ? scopedFetch(input, init) : fetch(input, init);
|
|
187
|
+
})
|
|
184
188
|
);
|
|
185
189
|
const baseFetch = async (url, options) => {
|
|
186
190
|
const context = callContextStorage.getStore();
|
|
@@ -247,7 +251,7 @@ function hasTerminalTaskWithDataArtifact(response) {
|
|
|
247
251
|
if (typeof status?.state !== "string" || !TERMINAL_A2A_STATES.has(status.state)) return false;
|
|
248
252
|
return (0, import_a2a_artifacts.getLatestA2ADataPartFromResponse)(response) !== void 0;
|
|
249
253
|
}
|
|
250
|
-
async function callA2ATool(agentUrl, toolName, parameters, authToken, debugLogs = [], pushNotificationConfig, customHeaders, signingContext, session, signal, requestTimeoutMs) {
|
|
254
|
+
async function callA2ATool(agentUrl, toolName, parameters, authToken, debugLogs = [], pushNotificationConfig, customHeaders, signingContext, session, signal, requestTimeoutMs, fetchFn) {
|
|
251
255
|
return (0, import_tracing.withSpan)(
|
|
252
256
|
"adcp.a2a.call_tool",
|
|
253
257
|
{
|
|
@@ -260,7 +264,8 @@ async function callA2ATool(agentUrl, toolName, parameters, authToken, debugLogs
|
|
|
260
264
|
debugLogs,
|
|
261
265
|
got401Ref: { value: false },
|
|
262
266
|
signal,
|
|
263
|
-
requestTimeoutMs
|
|
267
|
+
requestTimeoutMs,
|
|
268
|
+
fetchFn
|
|
264
269
|
};
|
|
265
270
|
return import_client2.signingContextStorage.run(
|
|
266
271
|
signingContext,
|
|
@@ -373,8 +378,8 @@ async function callA2AToolImpl(agentUrl, toolName, parameters, authToken, debugL
|
|
|
373
378
|
message: `A2A: Authentication required for ${agentUrl}`,
|
|
374
379
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
375
380
|
});
|
|
376
|
-
const challenge = await (0, import_authorization_required.probeAuthChallenge)(agentUrl);
|
|
377
|
-
const oauthMetadata = await (0, import_discovery.discoverOAuthMetadata)(agentUrl);
|
|
381
|
+
const challenge = await (0, import_authorization_required.probeAuthChallenge)(agentUrl, { fetchFn: context.fetchFn });
|
|
382
|
+
const oauthMetadata = await (0, import_discovery.discoverOAuthMetadata)(agentUrl, { fetch: context.fetchFn });
|
|
378
383
|
throw new import_errors.AuthenticationRequiredError(agentUrl, oauthMetadata || void 0, void 0, challenge ?? void 0);
|
|
379
384
|
}
|
|
380
385
|
throw error;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/lib/protocols/a2a.ts"],"sourcesContent":["// Official A2A client implementation - NO FALLBACKS\nimport { A2AClient as A2AClientImpl } from '@a2a-js/sdk/client';\nimport { AsyncLocalStorage } from 'node:async_hooks';\nimport { createHmac, randomUUID } from 'node:crypto';\nimport type { PushNotificationConfig } from '../types/tools.generated';\nimport type { DebugLogEntry } from '../types/adcp';\nimport { AuthenticationRequiredError, is401Error } from '../errors';\nimport { discoverOAuthMetadata } from '../auth/oauth/discovery';\nimport { probeAuthChallenge } from '../auth/oauth/authorization-required';\nimport { withSpan, injectTraceHeaders } from '../observability/tracing';\nimport { isAgentCardPath, buildCardUrls } from '../utils/a2a-discovery';\nimport { buildAgentSigningFetch, signingContextStorage, type AgentSigningContext } from '../signing/client';\nimport { toSignerKey, isInlineSigningConfig, isProviderSigningConfig } from '../signing/agent-fetch';\nimport { createSigningFetch, type FetchLike } from '../signing/fetch';\nimport { createSigningFetchAsync } from '../signing/fetch-async';\nimport type { AgentConfig } from '../types/adcp';\nimport { redactIdempotencyKeyInArgs } from '../utils/idempotency';\nimport { wrapFetchWithCapture } from './rawResponseCapture';\nimport { wrapFetchWithSizeLimit } from './responseSizeLimit';\nimport { wrapFetchWithTransportDiagnostics } from './transportDiagnostics';\nimport { DEFAULT_REQUEST_TIMEOUT_MS, resolveRequestTimeoutMs, withAbortSignal } from './abort';\nimport { getLatestA2ADataPartFromResponse } from '../utils/a2a-artifacts';\n\n// The A2A SDK client is used untyped: request/response shapes are validated at\n// runtime against the AdCP wire contract, not against the SDK's exported\n// types. Preserves the prior behaviour of the CommonJS `require` form.\nconst A2AClient: any = A2AClientImpl;\n\nif (!A2AClient) {\n throw new Error('A2A SDK client is required. Please install @a2a-js/sdk');\n}\n\n/**\n * Per-call state flowed through AsyncLocalStorage so concurrent callers\n * that share a cached A2AClient don't clobber each other's debugLogs,\n * customHeaders, or 401 flag.\n */\ninterface A2ACallContext {\n customHeaders?: Record<string, string>;\n debugLogs: DebugLogEntry[];\n got401Ref: { value: boolean };\n signal?: AbortSignal;\n requestTimeoutMs?: number;\n}\n\nconst callContextStorage = new AsyncLocalStorage<A2ACallContext>();\n\n/**\n * Cached A2AClient keyed by (agentUrl, authToken hash). Avoids re-fetching\n * /.well-known/agent.json on every tool call. The cached client's fetchImpl\n * reads per-call state from callContextStorage, so concurrent calls to the\n * same cache entry are safe.\n *\n * Process-global singleton — not suitable for multi-tenant servers that\n * want per-tenant isolation (use separate processes or explicit cache keys).\n */\nconst a2aClientCache = new Map<string, InstanceType<typeof A2AClient>>();\nconst pendingA2AClients = new Map<string, Promise<InstanceType<typeof A2AClient>>>();\n\n/**\n * Build the A2A connection-cache key. Mirrors the rationale in\n * `src/lib/protocols/mcp.ts:connectionCacheKey`: when the caller is using a\n * non-bearer scheme (RFC 7617 Basic from the CLI's `--auth-scheme basic`\n * shape, or any future caller-injected `Authorization` header), `authToken`\n * is undefined and the credential rides on the customHeaders bag. Hashing\n * only `authToken` would let two callers with different `user:pass`\n * credentials share a single cached A2AClient — single-CLI-process safe,\n * multi-tenant SDK consumer not safe.\n *\n * `customHeaders` also feed the key so tenant/routing headers cannot reuse\n * a card/client discovered for another caller.\n */\nfunction a2aCacheKey(\n agentUrl: string,\n authToken?: string,\n signingCacheKey?: string,\n customHeaders?: Record<string, string>\n): string {\n // 64-bit Map-key disambiguator — NOT a password hash. The cached client\n // closes over the full credential, so a hypothetical hash collision still\n // sends the original credential on the wire, just possibly cache-miss\n // and reconnect. Routed via `cacheDisambiguator` (HMAC-SHA256 with empty\n // key) instead of bare `createHash` so CodeQL's\n // `js/insufficient-password-hash` heuristic doesn't misclassify the\n // dataflow — see the helper docstring for the full rationale.\n const fingerprint = authToken ?? extractA2AAuthHeader(customHeaders);\n const tokenSuffix = fingerprint ? `::${cacheDisambiguator(fingerprint)}` : '';\n const headersKey = headersCacheDisambiguator(customHeaders);\n const headersSuffix = headersKey ? `::headers:${headersKey}` : '';\n const signingSuffix = signingCacheKey ? `::${signingCacheKey}` : '';\n return `${agentUrl}${tokenSuffix}${headersSuffix}${signingSuffix}`;\n}\n\n/**\n * Produce a stable 64-bit Map-key disambiguator from credential material.\n * Mirrors the helper in `src/lib/protocols/mcp.ts` — the two protocol\n * modules intentionally don't share runtime imports, so each carries its\n * own copy. See the MCP-side docstring for the full rationale.\n */\nfunction cacheDisambiguator(value: string): string {\n return createHmac('sha256', '').update(value).digest('hex').slice(0, 16);\n}\n\n/**\n * Case-insensitive lookup of `Authorization` on a header bag. Mirrors the\n * MCP-side helper; A2A keeps its own copy because the two protocol modules\n * intentionally don't share runtime imports.\n */\nfunction extractA2AAuthHeader(headers: Record<string, string> | undefined): string | undefined {\n if (!headers) return undefined;\n for (const [key, value] of Object.entries(headers)) {\n if (key.toLowerCase() === 'authorization' && value) return value;\n }\n return undefined;\n}\n\nfunction headersCacheDisambiguator(headers?: Record<string, string>): string | undefined {\n const entries = Object.entries(headers ?? {})\n .filter(([key]) => {\n const lower = key.toLowerCase();\n return lower !== 'traceparent' && lower !== 'tracestate' && lower !== 'baggage';\n })\n .map(([key, value]) => [key.toLowerCase(), value] as const)\n .sort(([a], [b]) => a.localeCompare(b));\n return entries.length > 0 ? cacheDisambiguator(JSON.stringify(entries)) : undefined;\n}\n\nfunction redactHeadersForDebug(headers: Record<string, string>): Record<string, string> {\n return Object.fromEntries(Object.keys(headers).map(key => [key, '***']));\n}\n\nfunction redactPushNotificationConfigForDebug(\n config: PushNotificationConfig | undefined\n): PushNotificationConfig | undefined {\n if (!config) return undefined;\n return {\n ...config,\n ...(config.token && { token: '***' }),\n ...(config.authentication && {\n authentication: {\n ...config.authentication,\n ...(config.authentication.credentials && { credentials: '***' }),\n },\n }),\n };\n}\n\n/**\n * Clear all cached A2A clients. Called by closeConnections('a2a').\n * A2A clients hold no persistent network resources (unlike MCP), so this\n * is just cache eviction.\n */\nexport function closeA2AConnections(): void {\n a2aClientCache.clear();\n pendingA2AClients.clear();\n}\n\n/**\n * Wall-clock cap on a fire-and-forget cancel. A2A sellers that accept the\n * TCP connect but never respond would otherwise pin the event loop past the\n * buyer's abort, which defeats the whole point of fire-and-forget.\n */\nconst CANCEL_TIMEOUT_MS = 5000;\n\n/**\n * Fire-and-forget A2A tasks/cancel for an in-flight task (A2A 0.3.0 §7.4).\n *\n * Sends a raw JSON-RPC 2.0 POST directly to the agent endpoint with the same\n * auth header shape as `callA2AToolImpl` (Bearer + x-adcp-auth). Does NOT\n * enter `callContextStorage` — debug-log capture and 401-cache-eviction are\n * intentionally skipped for best-effort cancellation.\n *\n * **Auth-code OAuth gap:** `authToken` is resolved by `getAuthToken(agent)`,\n * which returns `undefined` for authorization-code-flow sellers (those tokens\n * are managed by the OAuth provider path in `ProtocolClient.callTool`, not\n * accessible here). Cancel calls to those sellers go out unauthenticated and\n * will likely 401 non-fatally.\n *\n * **Phase 2 (adcp-client#1617 follow-up):** when `agent.request_signing` is\n * configured, the cancel POST is signed with the agent's signer key. The\n * `signingContextStorage` ALS scope around `callA2AToolImpl` does NOT extend\n * into `pollTaskCompletion` (sibling promise trees), so we can't replay the\n * captured ALS context — instead we rebuild a one-shot signer fetch from\n * `agent.request_signing` directly and use it for this single POST. Inline\n * keys go through `createSigningFetch` (sync); provider-backed configs use\n * `createSigningFetchAsync`. A `signed-requests` seller that requires\n * signing on `tasks/cancel` (or applies a uniform \"all mutating POSTs must\n * be signed\" policy) now accepts the cancel; one without signing config on\n * the agent gets the Phase 1 unsigned path.\n *\n * The caller is responsible for swallowing errors: cancel failure\n * (TaskNotCancelable, network error, auth rejection, network timeout) is\n * non-fatal because the buyer is already abandoning the task.\n *\n * @param agent The agent config (used for URL, auth token, signing).\n * @param taskId The server-assigned A2A Task.id to cancel.\n */\nexport async function cancelA2ATask(agent: AgentConfig, taskId: string): Promise<void> {\n // Defense-in-depth (ad-tech-protocol-expert review of #1640): the cancel\n // POST is JSON-RPC at the bare A2A endpoint. Calling this on an MCP agent\n // would POST `tasks/cancel` JSON-RPC at an MCP endpoint and 404. The\n // single call site (`pollTaskCompletion`) already gates on\n // `agent.protocol === 'a2a'`; this assertion catches future call sites\n // that forget the gate.\n if (agent.protocol !== 'a2a') {\n return;\n }\n const agentUrl = agent.agent_uri;\n const authToken = agent.auth_token;\n\n const headers: Record<string, string> = {\n 'content-type': 'application/json',\n accept: 'application/json',\n };\n if (authToken) {\n headers['Authorization'] = `Bearer ${authToken}`;\n headers['x-adcp-auth'] = authToken;\n }\n // JSON-RPC 2.0 §4.1.3: `id: null` flags the request as a *notification*,\n // and the server MUST NOT respond. A2A 0.3.0 §7.4 defines `tasks/cancel`\n // as a request/response method (returns the canceled `Task` or\n // `TaskNotCancelableError`), so a strict A2A server can legitimately\n // reject `id: null` as a protocol violation. Use a real id and just drop\n // the response on the floor — fire-and-forget is the caller's discipline,\n // not a wire-protocol claim.\n const body = JSON.stringify({\n jsonrpc: '2.0',\n id: randomUUID(),\n method: 'tasks/cancel',\n params: { id: taskId },\n });\n // Bound the cancel: a hung fetch would orphan-pin the event loop past the\n // buyer's abort, defeating fire-and-forget. AbortSignal.timeout() is the\n // standard primitive; the caller's `.catch()` swallows the AbortError.\n const init: RequestInit = {\n method: 'POST',\n headers,\n body,\n signal: AbortSignal.timeout(CANCEL_TIMEOUT_MS),\n };\n\n // adcp-client#1617 Phase 2: sign the cancel POST when the agent has a\n // signer configured. We bypass the `buildAgentSigningFetch` capability-\n // gate path because `tasks/cancel` is an A2A protocol method, not an\n // AdCP tool — the seller's `request_signing.supported_for` typically\n // lists AdCP tool names, not protocol-level methods. The right model\n // here: if the agent claims signing AT ALL, sign every mutating POST\n // we send to it on the cancel path. Sellers with uniform \"must be\n // signed\" policies accept this; sellers that only check signing on\n // specific AdCP tools simply ignore the extra signature.\n //\n // TODO(adcp#4318, adcp-client#1617): when the AdCP spec adds explicit\n // verifier coverage for A2A protocol methods (likely in 3.1 as a new\n // `protocol_methods_supported_for` / `protocol_methods_required_for`\n // field on `request_signing`), narrow this default by reading the\n // seller's advertised coverage from `getCapability()` and gating on\n // the `tasks/cancel` membership. The over-sign default stays as the\n // fallback for spec-silent sellers (3.0.x and earlier).\n if (agent.request_signing) {\n const upstream: FetchLike = (input, ini) => fetch(input as RequestInfo, ini);\n if (isInlineSigningConfig(agent.request_signing)) {\n const signed = createSigningFetch(upstream, toSignerKey(agent.request_signing));\n await signed(agentUrl, init);\n return;\n }\n if (isProviderSigningConfig(agent.request_signing)) {\n const signed = createSigningFetchAsync(upstream, agent.request_signing.provider);\n await signed(agentUrl, init);\n return;\n }\n }\n\n await fetch(agentUrl, init);\n}\n\nasync function getOrCreateA2AClient(\n agentUrl: string,\n authToken: string | undefined,\n customHeaders?: Record<string, string>,\n bypassCache = false\n): Promise<InstanceType<typeof A2AClient>> {\n const signingContext = signingContextStorage.getStore();\n const cacheKey = a2aCacheKey(agentUrl, authToken, signingContext?.cacheKey, customHeaders);\n if (bypassCache) {\n return createA2AClient(agentUrl, authToken);\n }\n const cached = a2aClientCache.get(cacheKey);\n if (cached) return cached;\n\n const pending = pendingA2AClients.get(cacheKey);\n if (pending) return pending;\n\n const promise = createA2AClient(agentUrl, authToken)\n .then(client => {\n a2aClientCache.set(cacheKey, client);\n return client;\n })\n .finally(() => {\n pendingA2AClients.delete(cacheKey);\n });\n\n pendingA2AClients.set(cacheKey, promise);\n return promise;\n}\n\nasync function createA2AClient(\n agentUrl: string,\n authToken: string | undefined\n): Promise<InstanceType<typeof A2AClient>> {\n const fetchImpl = buildFetchImpl(authToken);\n const cardUrls = buildCardUrls(agentUrl);\n\n const context = callContextStorage.getStore();\n context?.debugLogs.push({\n type: 'info',\n message: `A2A: Discovering agent card at ${cardUrls.join(', ')}`,\n timestamp: new Date().toISOString(),\n });\n\n let client: InstanceType<typeof A2AClient> | undefined;\n let lastError: Error = new Error(`A2A agent card not found at ${cardUrls.join(', ')}`);\n for (const cardUrl of cardUrls) {\n try {\n client = await A2AClient.fromCardUrl(cardUrl, { fetchImpl });\n break;\n } catch (err: unknown) {\n lastError = err as Error;\n if (context?.got401Ref.value) break;\n }\n }\n if (!client) throw lastError;\n\n return client;\n}\n\nfunction buildFetchImpl(authToken: string | undefined) {\n // The A2A client is cached per (url, authToken, signingCacheKey). We capture\n // the signing context at client-creation time so all subsequent calls that\n // share this cached client use the same signing identity — changing identity\n // requires a different cache entry, built on a separate call that enters ALS\n // with a different context.\n const signingContext = signingContextStorage.getStore();\n\n // Innermost wrapper: enforce response body size cap from the active\n // `responseSizeLimitStorage` slot. Pass-through when no slot is set.\n const networkFetch = wrapFetchWithTransportDiagnostics(\n wrapFetchWithSizeLimit((input, init) => fetch(input as any, init))\n );\n\n // Inner fetch handles auth/header injection and 401 detection. If the\n // agent has request-signing configured, we wrap it with the AdCP signing\n // fetch so the signature covers the exact bytes we're about to send (auth\n // headers included, since the signer re-reads the final header record).\n const baseFetch = async (url: string | URL | Request, options?: RequestInit): Promise<Response> => {\n const context = callContextStorage.getStore();\n\n const existingHeaders: Record<string, string> = {};\n if (options?.headers) {\n if (options.headers instanceof Headers) {\n options.headers.forEach((value, key) => {\n existingHeaders[key] = value;\n });\n } else if (Array.isArray(options.headers)) {\n for (const [key, value] of options.headers) {\n existingHeaders[key] = value;\n }\n } else {\n Object.assign(existingHeaders, options.headers);\n }\n }\n\n // Only inject trace context headers for actual tool requests, not discovery.\n // The agent card endpoint is external/untrusted — don't leak trace IDs to it.\n const urlString = typeof url === 'string' ? url : url.toString();\n const isDiscoveryRequest = isAgentCardPath(urlString);\n const traceHeaders = isDiscoveryRequest ? {} : injectTraceHeaders();\n const requestTimeoutMs = isDiscoveryRequest\n ? resolveRequestTimeoutMs(context?.requestTimeoutMs, DEFAULT_REQUEST_TIMEOUT_MS)\n : resolveRequestTimeoutMs(context?.requestTimeoutMs);\n\n // Merge: existing < trace < custom < auth (auth always wins)\n const headers: Record<string, string> = {\n ...existingHeaders,\n ...traceHeaders,\n ...context?.customHeaders,\n ...(authToken && {\n Authorization: `Bearer ${authToken}`,\n 'x-adcp-auth': authToken,\n }),\n };\n\n context?.debugLogs.push({\n type: 'info',\n message: `A2A: Fetch to ${urlString}`,\n timestamp: new Date().toISOString(),\n hasAuth: !!authToken,\n headers: redactHeadersForDebug(headers),\n });\n\n const response = await withAbortSignal<Response>([context?.signal, options?.signal], requestTimeoutMs, signal =>\n networkFetch(url as any, { ...options, headers, signal })\n );\n\n if (response.status === 401 && context) {\n context.got401Ref.value = true;\n }\n\n return response;\n };\n\n if (!signingContext) return wrapFetchWithCapture(baseFetch);\n\n // The signing wrapper assembles headers into the signature base. We invoke\n // it first so the signer sees the caller-supplied headers; baseFetch then\n // overlays auth/trace headers afterwards — A2A's auth scheme (bearer) is\n // not among the MANDATORY_COMPONENTS and is injected by the counterparty's\n // transport layer, not signed.\n const signingFetch = buildAgentSigningFetch({\n upstream: (input, init) => baseFetch(input as any, init),\n signing: signingContext.signing,\n getCapability: signingContext.getCapability,\n });\n return wrapFetchWithCapture(signingFetch as typeof fetch);\n}\n\n/**\n * Terminal A2A task states per A2A 0.3.0 §3.4. Only these can carry the\n * AdCP-mandated artifact + DataPart envelope (per transport-errors §A2A\n * Binding); intermediate states (`working`, `submitted`, `input-required`,\n * `auth-required`) carry no completion artifact.\n */\nconst TERMINAL_A2A_STATES = new Set(['completed', 'failed', 'rejected', 'canceled']);\n\n/**\n * Detect whether a JSON-RPC response carries a spec-compliant terminal-state\n * Task with at least one artifact containing a structured DataPart payload.\n * Per AdCP transport-errors §A2A Binding, the artifact's DataPart is the\n * canonical envelope for both the success arm (`completed`) and the error\n * arms (`failed` / `rejected` / `canceled`). The criterion intentionally\n * matches the unwrapper's terminal-state extraction in\n * `unwrapA2AResponse` — keeping protocol layer and unwrapper in lockstep\n * across all terminal states, not just the error arms.\n *\n * Used to short-circuit the generic \"A2A agent returned error\" throw when\n * a non-conformant seller surfaces both a transport-level `result.error`\n * hint and the canonical artifact envelope side-by-side. The DataPart is\n * authoritative; the throw would otherwise swallow it.\n */\nfunction hasTerminalTaskWithDataArtifact(response: unknown): boolean {\n if (!response || typeof response !== 'object') return false;\n const result = (response as { result?: unknown }).result;\n if (!result || typeof result !== 'object') return false;\n const r = result as { kind?: unknown; status?: unknown; artifacts?: unknown };\n if (r.kind !== 'task') return false;\n const status = r.status as { state?: unknown } | undefined;\n if (typeof status?.state !== 'string' || !TERMINAL_A2A_STATES.has(status.state)) return false;\n return getLatestA2ADataPartFromResponse(response) !== undefined;\n}\n\n/**\n * Protocol-level session identifiers that ride on the A2A Message envelope\n * (not in the skill parameters). `contextId` binds sends to a server-side\n * conversation; `taskId` resumes an existing non-terminal task.\n *\n * Callers (buyers) typically retain these across calls on a per-conversation\n * AgentClient; see AgentClient.getContextId() / getPendingTaskId().\n */\nexport interface A2ASessionIds {\n contextId?: string;\n taskId?: string;\n}\n\nexport async function callA2ATool(\n agentUrl: string,\n toolName: string,\n parameters: Record<string, unknown>,\n authToken?: string,\n debugLogs: DebugLogEntry[] = [],\n pushNotificationConfig?: PushNotificationConfig,\n customHeaders?: Record<string, string>,\n signingContext?: AgentSigningContext,\n session?: A2ASessionIds,\n signal?: AbortSignal,\n requestTimeoutMs?: number\n): Promise<unknown> {\n return withSpan(\n 'adcp.a2a.call_tool',\n {\n 'adcp.tool': toolName,\n 'http.url': agentUrl,\n },\n async () => {\n const context: A2ACallContext = {\n customHeaders,\n debugLogs,\n got401Ref: { value: false },\n signal,\n requestTimeoutMs,\n };\n return signingContextStorage.run(signingContext, () =>\n callContextStorage.run(context, () =>\n callA2AToolImpl(\n agentUrl,\n toolName,\n parameters,\n authToken,\n debugLogs,\n pushNotificationConfig,\n context,\n session\n )\n )\n );\n }\n );\n}\n\nasync function callA2AToolImpl(\n agentUrl: string,\n toolName: string,\n parameters: Record<string, unknown>,\n authToken: string | undefined,\n debugLogs: DebugLogEntry[],\n pushNotificationConfig: PushNotificationConfig | undefined,\n context: A2ACallContext,\n session: A2ASessionIds | undefined\n): Promise<unknown> {\n try {\n const client = await getOrCreateA2AClient(\n agentUrl,\n authToken,\n context.customHeaders,\n !!context.signal || context.requestTimeoutMs !== undefined\n );\n\n const requestPayload: {\n message: {\n messageId: string;\n role: string;\n kind: string;\n parts: Array<{ kind: string; data: { skill: string; parameters: Record<string, unknown> } }>;\n contextId?: string;\n taskId?: string;\n };\n configuration?: { pushNotificationConfig: PushNotificationConfig };\n } = {\n message: {\n messageId: `msg_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,\n role: 'user',\n kind: 'message',\n parts: [\n {\n kind: 'data',\n data: {\n skill: toolName,\n parameters: parameters,\n },\n },\n ],\n ...(session?.contextId && { contextId: session.contextId }),\n ...(session?.taskId && { taskId: session.taskId }),\n },\n };\n\n if (pushNotificationConfig) {\n requestPayload.configuration = {\n pushNotificationConfig: pushNotificationConfig,\n };\n }\n\n const payloadSize = JSON.stringify(requestPayload).length;\n const redactedParameters = redactIdempotencyKeyInArgs(parameters);\n const redactedPayload = {\n ...requestPayload,\n message: {\n ...requestPayload.message,\n parts: [\n {\n kind: 'data',\n data: { skill: toolName, parameters: redactedParameters },\n },\n ],\n },\n ...(requestPayload.configuration && {\n configuration: {\n pushNotificationConfig: redactPushNotificationConfigForDebug(\n requestPayload.configuration.pushNotificationConfig\n )!,\n },\n }),\n };\n debugLogs.push({\n type: 'info',\n message: `A2A: Calling skill ${toolName} with parameters: ${JSON.stringify(\n redactedParameters\n )}. Payload size: ${payloadSize} bytes`,\n timestamp: new Date().toISOString(),\n payloadSize,\n actualPayload: redactedPayload,\n });\n\n debugLogs.push({\n type: 'info',\n message: `A2A: Sending message via sendMessage()`,\n timestamp: new Date().toISOString(),\n skill: toolName,\n });\n\n const messageResponse = await client.sendMessage(requestPayload);\n\n debugLogs.push({\n type: messageResponse?.error ? 'error' : 'success',\n message: `A2A: Response received (${messageResponse?.error ? 'error' : 'success'})`,\n timestamp: new Date().toISOString(),\n response: messageResponse,\n skill: toolName,\n });\n\n if (messageResponse?.error || messageResponse?.result?.error) {\n // adcp-client#1575: when the seller emits a spec-compliant terminal-state\n // Task carrying an `adcp_error` DataPart (per AdCP transport-errors §A2A\n // Binding), the structured artifact is canonical — even if the seller\n // also surfaced a transport-level error string. Pass the response\n // through so the upstream unwrapper extracts `adcp_error.code` instead\n // of throwing a generic message that loses the AdCP error envelope.\n if (!hasTerminalTaskWithDataArtifact(messageResponse)) {\n const errorObj = messageResponse.error || messageResponse.result?.error;\n const errorMessage = errorObj.message || JSON.stringify(errorObj);\n throw new Error(`A2A agent returned error: ${errorMessage}`);\n }\n }\n\n return messageResponse;\n } catch (error: unknown) {\n if (is401Error(error, context.got401Ref.value)) {\n // Evict this cache entry — credential may have expired or been\n // revoked. Same disambiguator as the original `getOrCreateA2AClient`\n // call: when the credential rode on customHeaders.Authorization (Basic\n // case) rather than authToken, the cache key must reflect that or we\n // evict the wrong entry.\n const signingContext = signingContextStorage.getStore();\n a2aClientCache.delete(a2aCacheKey(agentUrl, authToken, signingContext?.cacheKey, context.customHeaders));\n\n debugLogs.push({\n type: 'error',\n message: `A2A: Authentication required for ${agentUrl}`,\n timestamp: new Date().toISOString(),\n });\n\n // Re-probe to surface the WWW-Authenticate scheme on the error envelope.\n // Basic-fronted agents (Apigee/Kong/AWS API GW with a BasicAuthentication\n // policy) would otherwise leave consumers chasing OAuth metadata that\n // doesn't exist. Matches the MCP discovery throw site in\n // `SingleAgentClient.discoverMCPEndpoint`.\n const challenge = await probeAuthChallenge(agentUrl);\n const oauthMetadata = await discoverOAuthMetadata(agentUrl);\n throw new AuthenticationRequiredError(agentUrl, oauthMetadata || undefined, undefined, challenge ?? undefined);\n }\n\n throw error;\n }\n}\n"],"mappings":"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","names":["import_client","A2AClientImpl"]}
|
|
1
|
+
{"version":3,"sources":["../../../src/lib/protocols/a2a.ts"],"sourcesContent":["// Official A2A client implementation - NO FALLBACKS\nimport { A2AClient as A2AClientImpl } from '@a2a-js/sdk/client';\nimport { AsyncLocalStorage } from 'node:async_hooks';\nimport { createHmac, randomUUID } from 'node:crypto';\nimport type { PushNotificationConfig } from '../types/tools.generated';\nimport type { DebugLogEntry } from '../types/adcp';\nimport { AuthenticationRequiredError, is401Error } from '../errors';\nimport { discoverOAuthMetadata } from '../auth/oauth/discovery';\nimport { probeAuthChallenge } from '../auth/oauth/authorization-required';\nimport { withSpan, injectTraceHeaders } from '../observability/tracing';\nimport { isAgentCardPath, buildCardUrls } from '../utils/a2a-discovery';\nimport { buildAgentSigningFetch, signingContextStorage, type AgentSigningContext } from '../signing/client';\nimport { toSignerKey, isInlineSigningConfig, isProviderSigningConfig } from '../signing/agent-fetch';\nimport { createSigningFetch, type FetchLike } from '../signing/fetch';\nimport { createSigningFetchAsync } from '../signing/fetch-async';\nimport type { AgentConfig } from '../types/adcp';\nimport { redactIdempotencyKeyInArgs } from '../utils/idempotency';\nimport { wrapFetchWithCapture } from './rawResponseCapture';\nimport { wrapFetchWithSizeLimit } from './responseSizeLimit';\nimport { wrapFetchWithTransportDiagnostics } from './transportDiagnostics';\nimport { DEFAULT_REQUEST_TIMEOUT_MS, resolveRequestTimeoutMs, withAbortSignal } from './abort';\nimport { getLatestA2ADataPartFromResponse } from '../utils/a2a-artifacts';\n\n// The A2A SDK client is used untyped: request/response shapes are validated at\n// runtime against the AdCP wire contract, not against the SDK's exported\n// types. Preserves the prior behaviour of the CommonJS `require` form.\nconst A2AClient: any = A2AClientImpl;\n\nif (!A2AClient) {\n throw new Error('A2A SDK client is required. Please install @a2a-js/sdk');\n}\n\n/**\n * Per-call state flowed through AsyncLocalStorage so concurrent callers\n * that share a cached A2AClient don't clobber each other's debugLogs,\n * customHeaders, or 401 flag.\n */\ninterface A2ACallContext {\n customHeaders?: Record<string, string>;\n debugLogs: DebugLogEntry[];\n got401Ref: { value: boolean };\n signal?: AbortSignal;\n requestTimeoutMs?: number;\n fetchFn?: typeof fetch;\n}\n\nconst callContextStorage = new AsyncLocalStorage<A2ACallContext>();\n\n/**\n * Cached A2AClient keyed by (agentUrl, authToken hash). Avoids re-fetching\n * /.well-known/agent.json on every tool call. The cached client's fetchImpl\n * reads per-call state from callContextStorage, so concurrent calls to the\n * same cache entry are safe.\n *\n * Process-global singleton — not suitable for multi-tenant servers that\n * want per-tenant isolation (use separate processes or explicit cache keys).\n */\nconst a2aClientCache = new Map<string, InstanceType<typeof A2AClient>>();\nconst pendingA2AClients = new Map<string, Promise<InstanceType<typeof A2AClient>>>();\n\n/**\n * Build the A2A connection-cache key. Mirrors the rationale in\n * `src/lib/protocols/mcp.ts:connectionCacheKey`: when the caller is using a\n * non-bearer scheme (RFC 7617 Basic from the CLI's `--auth-scheme basic`\n * shape, or any future caller-injected `Authorization` header), `authToken`\n * is undefined and the credential rides on the customHeaders bag. Hashing\n * only `authToken` would let two callers with different `user:pass`\n * credentials share a single cached A2AClient — single-CLI-process safe,\n * multi-tenant SDK consumer not safe.\n *\n * `customHeaders` also feed the key so tenant/routing headers cannot reuse a\n * card/client discovered for another caller. Scoped fetch calls bypass this\n * cache entirely (see `getOrCreateA2AClient`).\n */\nfunction a2aCacheKey(\n agentUrl: string,\n authToken?: string,\n signingCacheKey?: string,\n customHeaders?: Record<string, string>\n): string {\n // 64-bit Map-key disambiguator — NOT a password hash. The cached client\n // closes over the full credential, so a hypothetical hash collision still\n // sends the original credential on the wire, just possibly cache-miss\n // and reconnect. Routed via `cacheDisambiguator` (HMAC-SHA256 with empty\n // key) instead of bare `createHash` so CodeQL's\n // `js/insufficient-password-hash` heuristic doesn't misclassify the\n // dataflow — see the helper docstring for the full rationale.\n const fingerprint = authToken ?? extractA2AAuthHeader(customHeaders);\n const tokenSuffix = fingerprint ? `::${cacheDisambiguator(fingerprint)}` : '';\n const headersKey = headersCacheDisambiguator(customHeaders);\n const headersSuffix = headersKey ? `::headers:${headersKey}` : '';\n const signingSuffix = signingCacheKey ? `::${signingCacheKey}` : '';\n return `${agentUrl}${tokenSuffix}${headersSuffix}${signingSuffix}`;\n}\n\n/**\n * Produce a stable 64-bit Map-key disambiguator from credential material.\n * Mirrors the helper in `src/lib/protocols/mcp.ts` — the two protocol\n * modules intentionally don't share runtime imports, so each carries its\n * own copy. See the MCP-side docstring for the full rationale.\n */\nfunction cacheDisambiguator(value: string): string {\n return createHmac('sha256', '').update(value).digest('hex').slice(0, 16);\n}\n\n/**\n * Case-insensitive lookup of `Authorization` on a header bag. Mirrors the\n * MCP-side helper; A2A keeps its own copy because the two protocol modules\n * intentionally don't share runtime imports.\n */\nfunction extractA2AAuthHeader(headers: Record<string, string> | undefined): string | undefined {\n if (!headers) return undefined;\n for (const [key, value] of Object.entries(headers)) {\n if (key.toLowerCase() === 'authorization' && value) return value;\n }\n return undefined;\n}\n\nfunction headersCacheDisambiguator(headers?: Record<string, string>): string | undefined {\n const entries = Object.entries(headers ?? {})\n .filter(([key]) => {\n const lower = key.toLowerCase();\n return lower !== 'traceparent' && lower !== 'tracestate' && lower !== 'baggage';\n })\n .map(([key, value]) => [key.toLowerCase(), value] as const)\n .sort(([a], [b]) => a.localeCompare(b));\n return entries.length > 0 ? cacheDisambiguator(JSON.stringify(entries)) : undefined;\n}\n\nfunction redactHeadersForDebug(headers: Record<string, string>): Record<string, string> {\n return Object.fromEntries(Object.keys(headers).map(key => [key, '***']));\n}\n\nfunction redactPushNotificationConfigForDebug(\n config: PushNotificationConfig | undefined\n): PushNotificationConfig | undefined {\n if (!config) return undefined;\n return {\n ...config,\n ...(config.token && { token: '***' }),\n ...(config.authentication && {\n authentication: {\n ...config.authentication,\n ...(config.authentication.credentials && { credentials: '***' }),\n },\n }),\n };\n}\n\n/**\n * Clear all cached A2A clients. Called by closeConnections('a2a').\n * A2A clients hold no persistent network resources (unlike MCP), so this\n * is just cache eviction.\n */\nexport function closeA2AConnections(): void {\n a2aClientCache.clear();\n pendingA2AClients.clear();\n}\n\n/**\n * Wall-clock cap on a fire-and-forget cancel. A2A sellers that accept the\n * TCP connect but never respond would otherwise pin the event loop past the\n * buyer's abort, which defeats the whole point of fire-and-forget.\n */\nconst CANCEL_TIMEOUT_MS = 5000;\n\n/**\n * Fire-and-forget A2A tasks/cancel for an in-flight task (A2A 0.3.0 §7.4).\n *\n * Sends a raw JSON-RPC 2.0 POST directly to the agent endpoint with the same\n * auth header shape as `callA2AToolImpl` (Bearer + x-adcp-auth). Does NOT\n * enter `callContextStorage` — debug-log capture and 401-cache-eviction are\n * intentionally skipped for best-effort cancellation.\n *\n * **Auth-code OAuth gap:** `authToken` is resolved by `getAuthToken(agent)`,\n * which returns `undefined` for authorization-code-flow sellers (those tokens\n * are managed by the OAuth provider path in `ProtocolClient.callTool`, not\n * accessible here). Cancel calls to those sellers go out unauthenticated and\n * will likely 401 non-fatally.\n *\n * **Phase 2 (adcp-client#1617 follow-up):** when `agent.request_signing` is\n * configured, the cancel POST is signed with the agent's signer key. The\n * `signingContextStorage` ALS scope around `callA2AToolImpl` does NOT extend\n * into `pollTaskCompletion` (sibling promise trees), so we can't replay the\n * captured ALS context — instead we rebuild a one-shot signer fetch from\n * `agent.request_signing` directly and use it for this single POST. Inline\n * keys go through `createSigningFetch` (sync); provider-backed configs use\n * `createSigningFetchAsync`. A `signed-requests` seller that requires\n * signing on `tasks/cancel` (or applies a uniform \"all mutating POSTs must\n * be signed\" policy) now accepts the cancel; one without signing config on\n * the agent gets the Phase 1 unsigned path.\n *\n * The caller is responsible for swallowing errors: cancel failure\n * (TaskNotCancelable, network error, auth rejection, network timeout) is\n * non-fatal because the buyer is already abandoning the task.\n *\n * @param agent The agent config (used for URL, auth token, signing).\n * @param taskId The server-assigned A2A Task.id to cancel.\n */\nexport async function cancelA2ATask(agent: AgentConfig, taskId: string, fetchFn?: typeof fetch): Promise<void> {\n // Defense-in-depth (ad-tech-protocol-expert review of #1640): the cancel\n // POST is JSON-RPC at the bare A2A endpoint. Calling this on an MCP agent\n // would POST `tasks/cancel` JSON-RPC at an MCP endpoint and 404. The\n // single call site (`pollTaskCompletion`) already gates on\n // `agent.protocol === 'a2a'`; this assertion catches future call sites\n // that forget the gate.\n if (agent.protocol !== 'a2a') {\n return;\n }\n const agentUrl = agent.agent_uri;\n const authToken = agent.auth_token;\n\n const headers: Record<string, string> = {\n 'content-type': 'application/json',\n accept: 'application/json',\n };\n if (authToken) {\n headers['Authorization'] = `Bearer ${authToken}`;\n headers['x-adcp-auth'] = authToken;\n }\n // JSON-RPC 2.0 §4.1.3: `id: null` flags the request as a *notification*,\n // and the server MUST NOT respond. A2A 0.3.0 §7.4 defines `tasks/cancel`\n // as a request/response method (returns the canceled `Task` or\n // `TaskNotCancelableError`), so a strict A2A server can legitimately\n // reject `id: null` as a protocol violation. Use a real id and just drop\n // the response on the floor — fire-and-forget is the caller's discipline,\n // not a wire-protocol claim.\n const body = JSON.stringify({\n jsonrpc: '2.0',\n id: randomUUID(),\n method: 'tasks/cancel',\n params: { id: taskId },\n });\n // Bound the cancel: a hung fetch would orphan-pin the event loop past the\n // buyer's abort, defeating fire-and-forget. AbortSignal.timeout() is the\n // standard primitive; the caller's `.catch()` swallows the AbortError.\n const init: RequestInit = {\n method: 'POST',\n headers,\n body,\n signal: AbortSignal.timeout(CANCEL_TIMEOUT_MS),\n };\n\n // adcp-client#1617 Phase 2: sign the cancel POST when the agent has a\n // signer configured. We bypass the `buildAgentSigningFetch` capability-\n // gate path because `tasks/cancel` is an A2A protocol method, not an\n // AdCP tool — the seller's `request_signing.supported_for` typically\n // lists AdCP tool names, not protocol-level methods. The right model\n // here: if the agent claims signing AT ALL, sign every mutating POST\n // we send to it on the cancel path. Sellers with uniform \"must be\n // signed\" policies accept this; sellers that only check signing on\n // specific AdCP tools simply ignore the extra signature.\n //\n // TODO(adcp#4318, adcp-client#1617): when the AdCP spec adds explicit\n // verifier coverage for A2A protocol methods (likely in 3.1 as a new\n // `protocol_methods_supported_for` / `protocol_methods_required_for`\n // field on `request_signing`), narrow this default by reading the\n // seller's advertised coverage from `getCapability()` and gating on\n // the `tasks/cancel` membership. The over-sign default stays as the\n // fallback for spec-silent sellers (3.0.x and earlier).\n if (agent.request_signing) {\n const upstream: FetchLike = (input, ini) =>\n fetchFn ? fetchFn(input as RequestInfo | URL, ini) : fetch(input as RequestInfo, ini);\n if (isInlineSigningConfig(agent.request_signing)) {\n const signed = createSigningFetch(upstream, toSignerKey(agent.request_signing));\n await signed(agentUrl, init);\n return;\n }\n if (isProviderSigningConfig(agent.request_signing)) {\n const signed = createSigningFetchAsync(upstream, agent.request_signing.provider);\n await signed(agentUrl, init);\n return;\n }\n }\n\n await (fetchFn ?? fetch)(agentUrl, init);\n}\n\nasync function getOrCreateA2AClient(\n agentUrl: string,\n authToken: string | undefined,\n customHeaders?: Record<string, string>,\n bypassCache = false\n): Promise<InstanceType<typeof A2AClient>> {\n const signingContext = signingContextStorage.getStore();\n const fetchFn = callContextStorage.getStore()?.fetchFn;\n const cacheKey = a2aCacheKey(agentUrl, authToken, signingContext?.cacheKey, customHeaders);\n // Scoped fetchers often carry request/tenant-specific egress policy. Keep\n // those calls one-shot so neither agent-card discovery nor a client created\n // under one policy can be reused by another caller, and so short-lived\n // closure identities do not grow the global cache without bound.\n if (bypassCache || fetchFn) {\n return createA2AClient(agentUrl, authToken);\n }\n const cached = a2aClientCache.get(cacheKey);\n if (cached) return cached;\n\n const pending = pendingA2AClients.get(cacheKey);\n if (pending) return pending;\n\n const promise = createA2AClient(agentUrl, authToken)\n .then(client => {\n a2aClientCache.set(cacheKey, client);\n return client;\n })\n .finally(() => {\n pendingA2AClients.delete(cacheKey);\n });\n\n pendingA2AClients.set(cacheKey, promise);\n return promise;\n}\n\nasync function createA2AClient(\n agentUrl: string,\n authToken: string | undefined\n): Promise<InstanceType<typeof A2AClient>> {\n const fetchImpl = buildFetchImpl(authToken);\n const cardUrls = buildCardUrls(agentUrl);\n\n const context = callContextStorage.getStore();\n context?.debugLogs.push({\n type: 'info',\n message: `A2A: Discovering agent card at ${cardUrls.join(', ')}`,\n timestamp: new Date().toISOString(),\n });\n\n let client: InstanceType<typeof A2AClient> | undefined;\n let lastError: Error = new Error(`A2A agent card not found at ${cardUrls.join(', ')}`);\n for (const cardUrl of cardUrls) {\n try {\n client = await A2AClient.fromCardUrl(cardUrl, { fetchImpl });\n break;\n } catch (err: unknown) {\n lastError = err as Error;\n if (context?.got401Ref.value) break;\n }\n }\n if (!client) throw lastError;\n\n return client;\n}\n\nfunction buildFetchImpl(authToken: string | undefined) {\n // The A2A client is cached per (url, authToken, signingCacheKey). We capture\n // the signing context at client-creation time so all subsequent calls that\n // share this cached client use the same signing identity — changing identity\n // requires a different cache entry, built on a separate call that enters ALS\n // with a different context.\n const signingContext = signingContextStorage.getStore();\n\n // Innermost wrapper: enforce response body size cap from the active\n // `responseSizeLimitStorage` slot. Pass-through when no slot is set.\n const networkFetch = wrapFetchWithTransportDiagnostics(\n wrapFetchWithSizeLimit((input, init) => {\n const scopedFetch = callContextStorage.getStore()?.fetchFn;\n return scopedFetch ? scopedFetch(input, init) : fetch(input as any, init);\n })\n );\n\n // Inner fetch handles auth/header injection and 401 detection. If the\n // agent has request-signing configured, we wrap it with the AdCP signing\n // fetch so the signature covers the exact bytes we're about to send (auth\n // headers included, since the signer re-reads the final header record).\n const baseFetch = async (url: string | URL | Request, options?: RequestInit): Promise<Response> => {\n const context = callContextStorage.getStore();\n\n const existingHeaders: Record<string, string> = {};\n if (options?.headers) {\n if (options.headers instanceof Headers) {\n options.headers.forEach((value, key) => {\n existingHeaders[key] = value;\n });\n } else if (Array.isArray(options.headers)) {\n for (const [key, value] of options.headers) {\n existingHeaders[key] = value;\n }\n } else {\n Object.assign(existingHeaders, options.headers);\n }\n }\n\n // Only inject trace context headers for actual tool requests, not discovery.\n // The agent card endpoint is external/untrusted — don't leak trace IDs to it.\n const urlString = typeof url === 'string' ? url : url.toString();\n const isDiscoveryRequest = isAgentCardPath(urlString);\n const traceHeaders = isDiscoveryRequest ? {} : injectTraceHeaders();\n const requestTimeoutMs = isDiscoveryRequest\n ? resolveRequestTimeoutMs(context?.requestTimeoutMs, DEFAULT_REQUEST_TIMEOUT_MS)\n : resolveRequestTimeoutMs(context?.requestTimeoutMs);\n\n // Merge: existing < trace < custom < auth (auth always wins)\n const headers: Record<string, string> = {\n ...existingHeaders,\n ...traceHeaders,\n ...context?.customHeaders,\n ...(authToken && {\n Authorization: `Bearer ${authToken}`,\n 'x-adcp-auth': authToken,\n }),\n };\n\n context?.debugLogs.push({\n type: 'info',\n message: `A2A: Fetch to ${urlString}`,\n timestamp: new Date().toISOString(),\n hasAuth: !!authToken,\n headers: redactHeadersForDebug(headers),\n });\n\n const response = await withAbortSignal<Response>([context?.signal, options?.signal], requestTimeoutMs, signal =>\n networkFetch(url as any, { ...options, headers, signal })\n );\n\n if (response.status === 401 && context) {\n context.got401Ref.value = true;\n }\n\n return response;\n };\n\n if (!signingContext) return wrapFetchWithCapture(baseFetch);\n\n // The signing wrapper assembles headers into the signature base. We invoke\n // it first so the signer sees the caller-supplied headers; baseFetch then\n // overlays auth/trace headers afterwards — A2A's auth scheme (bearer) is\n // not among the MANDATORY_COMPONENTS and is injected by the counterparty's\n // transport layer, not signed.\n const signingFetch = buildAgentSigningFetch({\n upstream: (input, init) => baseFetch(input as any, init),\n signing: signingContext.signing,\n getCapability: signingContext.getCapability,\n });\n return wrapFetchWithCapture(signingFetch as typeof fetch);\n}\n\n/**\n * Terminal A2A task states per A2A 0.3.0 §3.4. Only these can carry the\n * AdCP-mandated artifact + DataPart envelope (per transport-errors §A2A\n * Binding); intermediate states (`working`, `submitted`, `input-required`,\n * `auth-required`) carry no completion artifact.\n */\nconst TERMINAL_A2A_STATES = new Set(['completed', 'failed', 'rejected', 'canceled']);\n\n/**\n * Detect whether a JSON-RPC response carries a spec-compliant terminal-state\n * Task with at least one artifact containing a structured DataPart payload.\n * Per AdCP transport-errors §A2A Binding, the artifact's DataPart is the\n * canonical envelope for both the success arm (`completed`) and the error\n * arms (`failed` / `rejected` / `canceled`). The criterion intentionally\n * matches the unwrapper's terminal-state extraction in\n * `unwrapA2AResponse` — keeping protocol layer and unwrapper in lockstep\n * across all terminal states, not just the error arms.\n *\n * Used to short-circuit the generic \"A2A agent returned error\" throw when\n * a non-conformant seller surfaces both a transport-level `result.error`\n * hint and the canonical artifact envelope side-by-side. The DataPart is\n * authoritative; the throw would otherwise swallow it.\n */\nfunction hasTerminalTaskWithDataArtifact(response: unknown): boolean {\n if (!response || typeof response !== 'object') return false;\n const result = (response as { result?: unknown }).result;\n if (!result || typeof result !== 'object') return false;\n const r = result as { kind?: unknown; status?: unknown; artifacts?: unknown };\n if (r.kind !== 'task') return false;\n const status = r.status as { state?: unknown } | undefined;\n if (typeof status?.state !== 'string' || !TERMINAL_A2A_STATES.has(status.state)) return false;\n return getLatestA2ADataPartFromResponse(response) !== undefined;\n}\n\n/**\n * Protocol-level session identifiers that ride on the A2A Message envelope\n * (not in the skill parameters). `contextId` binds sends to a server-side\n * conversation; `taskId` resumes an existing non-terminal task.\n *\n * Callers (buyers) typically retain these across calls on a per-conversation\n * AgentClient; see AgentClient.getContextId() / getPendingTaskId().\n */\nexport interface A2ASessionIds {\n contextId?: string;\n taskId?: string;\n}\n\nexport async function callA2ATool(\n agentUrl: string,\n toolName: string,\n parameters: Record<string, unknown>,\n authToken?: string,\n debugLogs: DebugLogEntry[] = [],\n pushNotificationConfig?: PushNotificationConfig,\n customHeaders?: Record<string, string>,\n signingContext?: AgentSigningContext,\n session?: A2ASessionIds,\n signal?: AbortSignal,\n requestTimeoutMs?: number,\n fetchFn?: typeof fetch\n): Promise<unknown> {\n return withSpan(\n 'adcp.a2a.call_tool',\n {\n 'adcp.tool': toolName,\n 'http.url': agentUrl,\n },\n async () => {\n const context: A2ACallContext = {\n customHeaders,\n debugLogs,\n got401Ref: { value: false },\n signal,\n requestTimeoutMs,\n fetchFn,\n };\n return signingContextStorage.run(signingContext, () =>\n callContextStorage.run(context, () =>\n callA2AToolImpl(\n agentUrl,\n toolName,\n parameters,\n authToken,\n debugLogs,\n pushNotificationConfig,\n context,\n session\n )\n )\n );\n }\n );\n}\n\nasync function callA2AToolImpl(\n agentUrl: string,\n toolName: string,\n parameters: Record<string, unknown>,\n authToken: string | undefined,\n debugLogs: DebugLogEntry[],\n pushNotificationConfig: PushNotificationConfig | undefined,\n context: A2ACallContext,\n session: A2ASessionIds | undefined\n): Promise<unknown> {\n try {\n const client = await getOrCreateA2AClient(\n agentUrl,\n authToken,\n context.customHeaders,\n !!context.signal || context.requestTimeoutMs !== undefined\n );\n\n const requestPayload: {\n message: {\n messageId: string;\n role: string;\n kind: string;\n parts: Array<{ kind: string; data: { skill: string; parameters: Record<string, unknown> } }>;\n contextId?: string;\n taskId?: string;\n };\n configuration?: { pushNotificationConfig: PushNotificationConfig };\n } = {\n message: {\n messageId: `msg_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,\n role: 'user',\n kind: 'message',\n parts: [\n {\n kind: 'data',\n data: {\n skill: toolName,\n parameters: parameters,\n },\n },\n ],\n ...(session?.contextId && { contextId: session.contextId }),\n ...(session?.taskId && { taskId: session.taskId }),\n },\n };\n\n if (pushNotificationConfig) {\n requestPayload.configuration = {\n pushNotificationConfig: pushNotificationConfig,\n };\n }\n\n const payloadSize = JSON.stringify(requestPayload).length;\n const redactedParameters = redactIdempotencyKeyInArgs(parameters);\n const redactedPayload = {\n ...requestPayload,\n message: {\n ...requestPayload.message,\n parts: [\n {\n kind: 'data',\n data: { skill: toolName, parameters: redactedParameters },\n },\n ],\n },\n ...(requestPayload.configuration && {\n configuration: {\n pushNotificationConfig: redactPushNotificationConfigForDebug(\n requestPayload.configuration.pushNotificationConfig\n )!,\n },\n }),\n };\n debugLogs.push({\n type: 'info',\n message: `A2A: Calling skill ${toolName} with parameters: ${JSON.stringify(\n redactedParameters\n )}. Payload size: ${payloadSize} bytes`,\n timestamp: new Date().toISOString(),\n payloadSize,\n actualPayload: redactedPayload,\n });\n\n debugLogs.push({\n type: 'info',\n message: `A2A: Sending message via sendMessage()`,\n timestamp: new Date().toISOString(),\n skill: toolName,\n });\n\n const messageResponse = await client.sendMessage(requestPayload);\n\n debugLogs.push({\n type: messageResponse?.error ? 'error' : 'success',\n message: `A2A: Response received (${messageResponse?.error ? 'error' : 'success'})`,\n timestamp: new Date().toISOString(),\n response: messageResponse,\n skill: toolName,\n });\n\n if (messageResponse?.error || messageResponse?.result?.error) {\n // adcp-client#1575: when the seller emits a spec-compliant terminal-state\n // Task carrying an `adcp_error` DataPart (per AdCP transport-errors §A2A\n // Binding), the structured artifact is canonical — even if the seller\n // also surfaced a transport-level error string. Pass the response\n // through so the upstream unwrapper extracts `adcp_error.code` instead\n // of throwing a generic message that loses the AdCP error envelope.\n if (!hasTerminalTaskWithDataArtifact(messageResponse)) {\n const errorObj = messageResponse.error || messageResponse.result?.error;\n const errorMessage = errorObj.message || JSON.stringify(errorObj);\n throw new Error(`A2A agent returned error: ${errorMessage}`);\n }\n }\n\n return messageResponse;\n } catch (error: unknown) {\n if (is401Error(error, context.got401Ref.value)) {\n // Evict this cache entry — credential may have expired or been\n // revoked. Same disambiguator as the original `getOrCreateA2AClient`\n // call: when the credential rode on customHeaders.Authorization (Basic\n // case) rather than authToken, the cache key must reflect that or we\n // evict the wrong entry.\n const signingContext = signingContextStorage.getStore();\n a2aClientCache.delete(a2aCacheKey(agentUrl, authToken, signingContext?.cacheKey, context.customHeaders));\n\n debugLogs.push({\n type: 'error',\n message: `A2A: Authentication required for ${agentUrl}`,\n timestamp: new Date().toISOString(),\n });\n\n // Re-probe to surface the WWW-Authenticate scheme on the error envelope.\n // Basic-fronted agents (Apigee/Kong/AWS API GW with a BasicAuthentication\n // policy) would otherwise leave consumers chasing OAuth metadata that\n // doesn't exist. Matches the MCP discovery throw site in\n // `SingleAgentClient.discoverMCPEndpoint`.\n const challenge = await probeAuthChallenge(agentUrl, { fetchFn: context.fetchFn });\n const oauthMetadata = await discoverOAuthMetadata(agentUrl, { fetch: context.fetchFn });\n throw new AuthenticationRequiredError(agentUrl, oauthMetadata || undefined, undefined, challenge ?? undefined);\n }\n\n throw error;\n }\n}\n"],"mappings":"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","names":["import_client","A2AClientImpl"]}
|
|
@@ -74,7 +74,7 @@ function closeA2AConnections() {
|
|
|
74
74
|
pendingA2AClients.clear();
|
|
75
75
|
}
|
|
76
76
|
const CANCEL_TIMEOUT_MS = 5e3;
|
|
77
|
-
async function cancelA2ATask(agent, taskId) {
|
|
77
|
+
async function cancelA2ATask(agent, taskId, fetchFn) {
|
|
78
78
|
if (agent.protocol !== "a2a") {
|
|
79
79
|
return;
|
|
80
80
|
}
|
|
@@ -101,7 +101,7 @@ async function cancelA2ATask(agent, taskId) {
|
|
|
101
101
|
signal: AbortSignal.timeout(CANCEL_TIMEOUT_MS)
|
|
102
102
|
};
|
|
103
103
|
if (agent.request_signing) {
|
|
104
|
-
const upstream = (input, ini) => fetch(input, ini);
|
|
104
|
+
const upstream = (input, ini) => fetchFn ? fetchFn(input, ini) : fetch(input, ini);
|
|
105
105
|
if (isInlineSigningConfig(agent.request_signing)) {
|
|
106
106
|
const signed = createSigningFetch(upstream, toSignerKey(agent.request_signing));
|
|
107
107
|
await signed(agentUrl, init);
|
|
@@ -113,12 +113,13 @@ async function cancelA2ATask(agent, taskId) {
|
|
|
113
113
|
return;
|
|
114
114
|
}
|
|
115
115
|
}
|
|
116
|
-
await fetch(agentUrl, init);
|
|
116
|
+
await (fetchFn ?? fetch)(agentUrl, init);
|
|
117
117
|
}
|
|
118
118
|
async function getOrCreateA2AClient(agentUrl, authToken, customHeaders, bypassCache = false) {
|
|
119
119
|
const signingContext = signingContextStorage.getStore();
|
|
120
|
+
const fetchFn = callContextStorage.getStore()?.fetchFn;
|
|
120
121
|
const cacheKey = a2aCacheKey(agentUrl, authToken, signingContext?.cacheKey, customHeaders);
|
|
121
|
-
if (bypassCache) {
|
|
122
|
+
if (bypassCache || fetchFn) {
|
|
122
123
|
return createA2AClient(agentUrl, authToken);
|
|
123
124
|
}
|
|
124
125
|
const cached = a2aClientCache.get(cacheKey);
|
|
@@ -160,7 +161,10 @@ async function createA2AClient(agentUrl, authToken) {
|
|
|
160
161
|
function buildFetchImpl(authToken) {
|
|
161
162
|
const signingContext = signingContextStorage.getStore();
|
|
162
163
|
const networkFetch = wrapFetchWithTransportDiagnostics(
|
|
163
|
-
wrapFetchWithSizeLimit((input, init) =>
|
|
164
|
+
wrapFetchWithSizeLimit((input, init) => {
|
|
165
|
+
const scopedFetch = callContextStorage.getStore()?.fetchFn;
|
|
166
|
+
return scopedFetch ? scopedFetch(input, init) : fetch(input, init);
|
|
167
|
+
})
|
|
164
168
|
);
|
|
165
169
|
const baseFetch = async (url, options) => {
|
|
166
170
|
const context = callContextStorage.getStore();
|
|
@@ -227,7 +231,7 @@ function hasTerminalTaskWithDataArtifact(response) {
|
|
|
227
231
|
if (typeof status?.state !== "string" || !TERMINAL_A2A_STATES.has(status.state)) return false;
|
|
228
232
|
return getLatestA2ADataPartFromResponse(response) !== void 0;
|
|
229
233
|
}
|
|
230
|
-
async function callA2ATool(agentUrl, toolName, parameters, authToken, debugLogs = [], pushNotificationConfig, customHeaders, signingContext, session, signal, requestTimeoutMs) {
|
|
234
|
+
async function callA2ATool(agentUrl, toolName, parameters, authToken, debugLogs = [], pushNotificationConfig, customHeaders, signingContext, session, signal, requestTimeoutMs, fetchFn) {
|
|
231
235
|
return withSpan(
|
|
232
236
|
"adcp.a2a.call_tool",
|
|
233
237
|
{
|
|
@@ -240,7 +244,8 @@ async function callA2ATool(agentUrl, toolName, parameters, authToken, debugLogs
|
|
|
240
244
|
debugLogs,
|
|
241
245
|
got401Ref: { value: false },
|
|
242
246
|
signal,
|
|
243
|
-
requestTimeoutMs
|
|
247
|
+
requestTimeoutMs,
|
|
248
|
+
fetchFn
|
|
244
249
|
};
|
|
245
250
|
return signingContextStorage.run(
|
|
246
251
|
signingContext,
|
|
@@ -353,8 +358,8 @@ async function callA2AToolImpl(agentUrl, toolName, parameters, authToken, debugL
|
|
|
353
358
|
message: `A2A: Authentication required for ${agentUrl}`,
|
|
354
359
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
355
360
|
});
|
|
356
|
-
const challenge = await probeAuthChallenge(agentUrl);
|
|
357
|
-
const oauthMetadata = await discoverOAuthMetadata(agentUrl);
|
|
361
|
+
const challenge = await probeAuthChallenge(agentUrl, { fetchFn: context.fetchFn });
|
|
362
|
+
const oauthMetadata = await discoverOAuthMetadata(agentUrl, { fetch: context.fetchFn });
|
|
358
363
|
throw new AuthenticationRequiredError(agentUrl, oauthMetadata || void 0, void 0, challenge ?? void 0);
|
|
359
364
|
}
|
|
360
365
|
throw error;
|