@adcp/sdk 12.0.0 → 12.0.2
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/dist/lib/adapters/content-standards-adapter.mjs +0 -6
- package/dist/lib/adapters/content-standards-adapter.mjs.map +1 -1
- package/dist/lib/adapters/derived-account-store.mjs +0 -6
- package/dist/lib/adapters/derived-account-store.mjs.map +1 -1
- package/dist/lib/adapters/governance-adapter.mjs +0 -6
- package/dist/lib/adapters/governance-adapter.mjs.map +1 -1
- package/dist/lib/adapters/implicit-account-store.mjs +0 -6
- package/dist/lib/adapters/implicit-account-store.mjs.map +1 -1
- package/dist/lib/adapters/index.mjs +0 -6
- package/dist/lib/adapters/index.mjs.map +1 -1
- package/dist/lib/adapters/legacy/v2-5/create_media_buy.mjs +0 -6
- package/dist/lib/adapters/legacy/v2-5/create_media_buy.mjs.map +1 -1
- package/dist/lib/adapters/legacy/v2-5/get_products.mjs +0 -6
- package/dist/lib/adapters/legacy/v2-5/get_products.mjs.map +1 -1
- package/dist/lib/adapters/legacy/v2-5/index.mjs +0 -6
- package/dist/lib/adapters/legacy/v2-5/index.mjs.map +1 -1
- package/dist/lib/adapters/legacy/v2-5/list_creative_formats.mjs +0 -6
- package/dist/lib/adapters/legacy/v2-5/list_creative_formats.mjs.map +1 -1
- package/dist/lib/adapters/legacy/v2-5/preview_creative.mjs +0 -6
- package/dist/lib/adapters/legacy/v2-5/preview_creative.mjs.map +1 -1
- package/dist/lib/adapters/legacy/v2-5/sync_creatives.mjs +0 -6
- package/dist/lib/adapters/legacy/v2-5/sync_creatives.mjs.map +1 -1
- package/dist/lib/adapters/legacy/v2-5/types.mjs +0 -6
- package/dist/lib/adapters/legacy/v2-5/update_media_buy.mjs +0 -6
- package/dist/lib/adapters/legacy/v2-5/update_media_buy.mjs.map +1 -1
- package/dist/lib/adapters/oauth-passthrough-resolver.mjs +0 -6
- package/dist/lib/adapters/oauth-passthrough-resolver.mjs.map +1 -1
- package/dist/lib/adapters/property-list-adapter.mjs +0 -6
- package/dist/lib/adapters/property-list-adapter.mjs.map +1 -1
- package/dist/lib/adapters/roster-account-store.mjs +0 -6
- package/dist/lib/adapters/roster-account-store.mjs.map +1 -1
- package/dist/lib/adapters/si-session-manager.mjs +0 -6
- package/dist/lib/adapters/si-session-manager.mjs.map +1 -1
- package/dist/lib/adapters/version/3.0/brand-fields.mjs +0 -6
- package/dist/lib/adapters/version/3.0/brand-fields.mjs.map +1 -1
- package/dist/lib/adapters/version/3.0/get-products.mjs +0 -6
- package/dist/lib/adapters/version/3.0/get-products.mjs.map +1 -1
- package/dist/lib/adapters/version/3.0/index.mjs +0 -6
- package/dist/lib/adapters/version/3.0/index.mjs.map +1 -1
- package/dist/lib/adapters/version/3.0/sync-accounts.mjs +0 -6
- package/dist/lib/adapters/version/3.0/sync-accounts.mjs.map +1 -1
- package/dist/lib/adapters/version/index.mjs +0 -6
- package/dist/lib/adapters/version/index.mjs.map +1 -1
- package/dist/lib/adapters/version/types.mjs +0 -6
- package/dist/lib/advanced.mjs +0 -6
- package/dist/lib/advanced.mjs.map +1 -1
- package/dist/lib/agents/index.generated.mjs +0 -6
- package/dist/lib/agents/index.generated.mjs.map +1 -1
- package/dist/lib/auth/index.mjs +0 -6
- package/dist/lib/auth/index.mjs.map +1 -1
- package/dist/lib/auth/oauth/CLIFlowHandler.mjs +0 -6
- package/dist/lib/auth/oauth/CLIFlowHandler.mjs.map +1 -1
- package/dist/lib/auth/oauth/ClientCredentialsFlow.mjs +0 -6
- package/dist/lib/auth/oauth/ClientCredentialsFlow.mjs.map +1 -1
- package/dist/lib/auth/oauth/MCPOAuthProvider.mjs +0 -6
- package/dist/lib/auth/oauth/MCPOAuthProvider.mjs.map +1 -1
- package/dist/lib/auth/oauth/NonInteractiveFlowHandler.mjs +0 -6
- package/dist/lib/auth/oauth/NonInteractiveFlowHandler.mjs.map +1 -1
- package/dist/lib/auth/oauth/authorization-required.mjs +0 -6
- package/dist/lib/auth/oauth/authorization-required.mjs.map +1 -1
- package/dist/lib/auth/oauth/diagnose.mjs +0 -6
- package/dist/lib/auth/oauth/diagnose.mjs.map +1 -1
- package/dist/lib/auth/oauth/diagnostics.mjs +0 -6
- package/dist/lib/auth/oauth/diagnostics.mjs.map +1 -1
- package/dist/lib/auth/oauth/discovery.mjs +0 -6
- package/dist/lib/auth/oauth/discovery.mjs.map +1 -1
- package/dist/lib/auth/oauth/file-storage.mjs +0 -6
- package/dist/lib/auth/oauth/file-storage.mjs.map +1 -1
- package/dist/lib/auth/oauth/index.mjs +0 -6
- package/dist/lib/auth/oauth/index.mjs.map +1 -1
- package/dist/lib/auth/oauth/secret-resolver.mjs +0 -6
- package/dist/lib/auth/oauth/secret-resolver.mjs.map +1 -1
- package/dist/lib/auth/oauth/storage-registry.mjs +0 -6
- package/dist/lib/auth/oauth/storage-registry.mjs.map +1 -1
- package/dist/lib/auth/oauth/types.mjs +0 -6
- package/dist/lib/auth/oauth/types.mjs.map +1 -1
- package/dist/lib/auth/oauth/web-flow.mjs +0 -6
- package/dist/lib/auth/oauth/web-flow.mjs.map +1 -1
- package/dist/lib/brand/index.mjs +0 -6
- package/dist/lib/brand/index.mjs.map +1 -1
- package/dist/lib/canonical-references/index.mjs +6 -7
- package/dist/lib/canonical-references/index.mjs.map +1 -1
- package/dist/lib/client/index.mjs +0 -6
- package/dist/lib/client/index.mjs.map +1 -1
- package/dist/lib/compliance/index.mjs +0 -6
- package/dist/lib/compliance/index.mjs.map +1 -1
- package/dist/lib/compliance-fixtures/index.mjs +0 -6
- package/dist/lib/compliance-fixtures/index.mjs.map +1 -1
- package/dist/lib/compliance-fixtures/test-authorization-server.mjs +0 -6
- package/dist/lib/compliance-fixtures/test-authorization-server.mjs.map +1 -1
- package/dist/lib/conformance/index.mjs +0 -6
- package/dist/lib/conformance/index.mjs.map +1 -1
- package/dist/lib/conformance/invariants/uniformError.mjs +0 -6
- package/dist/lib/conformance/invariants/uniformError.mjs.map +1 -1
- package/dist/lib/conformance/invariants/uniformErrorComparator.mjs +0 -6
- package/dist/lib/conformance/invariants/uniformErrorComparator.mjs.map +1 -1
- package/dist/lib/conformance/oracle.mjs +0 -6
- package/dist/lib/conformance/oracle.mjs.map +1 -1
- package/dist/lib/conformance/runConformance.mjs +0 -6
- package/dist/lib/conformance/runConformance.mjs.map +1 -1
- package/dist/lib/conformance/runners.mjs +0 -6
- package/dist/lib/conformance/runners.mjs.map +1 -1
- package/dist/lib/conformance/schemaArbitrary.mjs +0 -6
- package/dist/lib/conformance/schemaArbitrary.mjs.map +1 -1
- package/dist/lib/conformance/schemaLoader.mjs +5 -6
- package/dist/lib/conformance/schemaLoader.mjs.map +1 -1
- package/dist/lib/conformance/seeder.mjs +0 -6
- package/dist/lib/conformance/seeder.mjs.map +1 -1
- package/dist/lib/conformance/types.mjs +0 -6
- package/dist/lib/conformance/types.mjs.map +1 -1
- package/dist/lib/conformance/webhook.mjs +0 -6
- package/dist/lib/conformance/webhook.mjs.map +1 -1
- package/dist/lib/core/ADCPMultiAgentClient.mjs +0 -6
- package/dist/lib/core/ADCPMultiAgentClient.mjs.map +1 -1
- package/dist/lib/core/AgentClient.mjs +0 -6
- package/dist/lib/core/AgentClient.mjs.map +1 -1
- package/dist/lib/core/AsyncHandler.mjs +0 -6
- package/dist/lib/core/AsyncHandler.mjs.map +1 -1
- package/dist/lib/core/ConfigurationManager.mjs +0 -6
- package/dist/lib/core/ConfigurationManager.mjs.map +1 -1
- package/dist/lib/core/ConversationTypes.mjs +0 -6
- package/dist/lib/core/CreativeAgentClient.mjs +0 -6
- package/dist/lib/core/CreativeAgentClient.mjs.map +1 -1
- package/dist/lib/core/GovernanceMiddleware.mjs +0 -6
- package/dist/lib/core/GovernanceMiddleware.mjs.map +1 -1
- package/dist/lib/core/GovernanceTypes.mjs +0 -6
- package/dist/lib/core/GovernanceTypes.mjs.map +1 -1
- package/dist/lib/core/ProtocolResponseParser.mjs +0 -6
- package/dist/lib/core/ProtocolResponseParser.mjs.map +1 -1
- package/dist/lib/core/ResponseValidator.mjs +0 -6
- package/dist/lib/core/ResponseValidator.mjs.map +1 -1
- package/dist/lib/core/SingleAgentClient.mjs +0 -6
- package/dist/lib/core/SingleAgentClient.mjs.map +1 -1
- package/dist/lib/core/TaskEventTypes.mjs +0 -6
- package/dist/lib/core/TaskEventTypes.mjs.map +1 -1
- package/dist/lib/core/TaskExecutor.mjs +0 -6
- package/dist/lib/core/TaskExecutor.mjs.map +1 -1
- package/dist/lib/core/match.mjs +0 -6
- package/dist/lib/core/match.mjs.map +1 -1
- package/dist/lib/core/task-status.mjs +0 -6
- package/dist/lib/core/task-status.mjs.map +1 -1
- package/dist/lib/core/webhook-url.mjs +0 -6
- package/dist/lib/core/webhook-url.mjs.map +1 -1
- package/dist/lib/discovery/adagents-redirects.mjs +0 -6
- package/dist/lib/discovery/adagents-redirects.mjs.map +1 -1
- package/dist/lib/discovery/agent-directory.mjs +0 -6
- package/dist/lib/discovery/agent-directory.mjs.map +1 -1
- package/dist/lib/discovery/inline-publisher-properties.mjs +0 -6
- package/dist/lib/discovery/inline-publisher-properties.mjs.map +1 -1
- package/dist/lib/discovery/network-consistency-checker.mjs +0 -6
- package/dist/lib/discovery/network-consistency-checker.mjs.map +1 -1
- package/dist/lib/discovery/property-crawler.mjs +0 -6
- package/dist/lib/discovery/property-crawler.mjs.map +1 -1
- package/dist/lib/discovery/property-index.mjs +0 -6
- package/dist/lib/discovery/property-index.mjs.map +1 -1
- package/dist/lib/discovery/publisher-property-selector.mjs +0 -6
- package/dist/lib/discovery/publisher-property-selector.mjs.map +1 -1
- package/dist/lib/discovery/resolve-agent-properties.mjs +0 -6
- package/dist/lib/discovery/resolve-agent-properties.mjs.map +1 -1
- package/dist/lib/discovery/types.mjs +0 -6
- package/dist/lib/discovery/validate-adagents.mjs +0 -6
- package/dist/lib/discovery/validate-adagents.mjs.map +1 -1
- package/dist/lib/enums.mjs +0 -6
- package/dist/lib/enums.mjs.map +1 -1
- package/dist/lib/errors/index.mjs +0 -6
- package/dist/lib/errors/index.mjs.map +1 -1
- package/dist/lib/express-mcp/index.mjs +0 -6
- package/dist/lib/express-mcp/index.mjs.map +1 -1
- package/dist/lib/governance/index.mjs +0 -6
- package/dist/lib/governance/index.mjs.map +1 -1
- package/dist/lib/handlers/types.mjs +0 -6
- package/dist/lib/handlers/types.mjs.map +1 -1
- package/dist/lib/index.d.mts +1 -1
- package/dist/lib/index.d.ts +1 -1
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/index.mjs +0 -6
- package/dist/lib/index.mjs.map +1 -1
- package/dist/lib/media-buy/available-actions.mjs +0 -6
- package/dist/lib/media-buy/available-actions.mjs.map +1 -1
- package/dist/lib/media-buy/index.mjs +0 -6
- package/dist/lib/media-buy/index.mjs.map +1 -1
- package/dist/lib/media-buy/preflight.mjs +0 -6
- package/dist/lib/media-buy/preflight.mjs.map +1 -1
- package/dist/lib/media-buy/property-policy.mjs +0 -6
- package/dist/lib/media-buy/property-policy.mjs.map +1 -1
- package/dist/lib/media-buy/types.mjs +0 -6
- package/dist/lib/media-buy/types.mjs.map +1 -1
- package/dist/lib/media-buy/update-fields.generated.mjs +0 -6
- package/dist/lib/media-buy/update-fields.generated.mjs.map +1 -1
- package/dist/lib/mock-server/creative-ad-server/seed-data.mjs +0 -6
- package/dist/lib/mock-server/creative-ad-server/seed-data.mjs.map +1 -1
- package/dist/lib/mock-server/creative-ad-server/server.mjs +0 -6
- package/dist/lib/mock-server/creative-ad-server/server.mjs.map +1 -1
- package/dist/lib/mock-server/creative-template/seed-data.mjs +0 -6
- package/dist/lib/mock-server/creative-template/seed-data.mjs.map +1 -1
- package/dist/lib/mock-server/creative-template/server.mjs +0 -6
- package/dist/lib/mock-server/creative-template/server.mjs.map +1 -1
- package/dist/lib/mock-server/index.mjs +0 -6
- package/dist/lib/mock-server/index.mjs.map +1 -1
- package/dist/lib/mock-server/sales-guaranteed/recipe.mjs +0 -6
- package/dist/lib/mock-server/sales-guaranteed/recipe.mjs.map +1 -1
- package/dist/lib/mock-server/sales-guaranteed/seed-data.mjs +0 -6
- package/dist/lib/mock-server/sales-guaranteed/seed-data.mjs.map +1 -1
- package/dist/lib/mock-server/sales-guaranteed/server.mjs +0 -6
- package/dist/lib/mock-server/sales-guaranteed/server.mjs.map +1 -1
- package/dist/lib/mock-server/sales-non-guaranteed/recipe.mjs +0 -6
- package/dist/lib/mock-server/sales-non-guaranteed/recipe.mjs.map +1 -1
- package/dist/lib/mock-server/sales-non-guaranteed/seed-data.mjs +0 -6
- package/dist/lib/mock-server/sales-non-guaranteed/seed-data.mjs.map +1 -1
- package/dist/lib/mock-server/sales-non-guaranteed/server.mjs +0 -6
- package/dist/lib/mock-server/sales-non-guaranteed/server.mjs.map +1 -1
- package/dist/lib/mock-server/sales-social/seed-data.mjs +0 -6
- package/dist/lib/mock-server/sales-social/seed-data.mjs.map +1 -1
- package/dist/lib/mock-server/sales-social/server.mjs +0 -6
- package/dist/lib/mock-server/sales-social/server.mjs.map +1 -1
- package/dist/lib/mock-server/scenario.mjs +0 -6
- package/dist/lib/mock-server/scenario.mjs.map +1 -1
- package/dist/lib/mock-server/signal-marketplace/seed-data.mjs +0 -6
- package/dist/lib/mock-server/signal-marketplace/seed-data.mjs.map +1 -1
- package/dist/lib/mock-server/signal-marketplace/server.mjs +0 -6
- package/dist/lib/mock-server/signal-marketplace/server.mjs.map +1 -1
- package/dist/lib/mock-server/sponsored-intelligence/seed-data.mjs +0 -6
- package/dist/lib/mock-server/sponsored-intelligence/seed-data.mjs.map +1 -1
- package/dist/lib/mock-server/sponsored-intelligence/server.mjs +0 -6
- package/dist/lib/mock-server/sponsored-intelligence/server.mjs.map +1 -1
- package/dist/lib/net/address-guards.mjs +0 -6
- package/dist/lib/net/address-guards.mjs.map +1 -1
- package/dist/lib/net/index.mjs +0 -6
- package/dist/lib/net/index.mjs.map +1 -1
- package/dist/lib/net/ssrf-fetch.mjs +0 -6
- package/dist/lib/net/ssrf-fetch.mjs.map +1 -1
- package/dist/lib/observability/index.mjs +0 -6
- package/dist/lib/observability/index.mjs.map +1 -1
- package/dist/lib/observability/tracing.mjs +6 -7
- package/dist/lib/observability/tracing.mjs.map +1 -1
- package/dist/lib/protocols/a2a.mjs +0 -6
- package/dist/lib/protocols/a2a.mjs.map +1 -1
- package/dist/lib/protocols/abort.mjs +0 -6
- package/dist/lib/protocols/abort.mjs.map +1 -1
- package/dist/lib/protocols/index.mjs +0 -6
- package/dist/lib/protocols/index.mjs.map +1 -1
- package/dist/lib/protocols/mcp-modern.mjs +0 -6
- package/dist/lib/protocols/mcp-modern.mjs.map +1 -1
- package/dist/lib/protocols/mcp-tasks.mjs +0 -6
- package/dist/lib/protocols/mcp-tasks.mjs.map +1 -1
- package/dist/lib/protocols/mcp.mjs +0 -6
- package/dist/lib/protocols/mcp.mjs.map +1 -1
- package/dist/lib/protocols/rawResponseCapture.mjs +0 -6
- package/dist/lib/protocols/rawResponseCapture.mjs.map +1 -1
- package/dist/lib/protocols/responseSizeLimit.mjs +0 -6
- package/dist/lib/protocols/responseSizeLimit.mjs.map +1 -1
- package/dist/lib/protocols/transportDiagnostics.mjs +0 -6
- package/dist/lib/protocols/transportDiagnostics.mjs.map +1 -1
- package/dist/lib/registry/cursor-store.mjs +0 -6
- package/dist/lib/registry/cursor-store.mjs.map +1 -1
- package/dist/lib/registry/feed-stream.mjs +0 -6
- package/dist/lib/registry/feed-stream.mjs.map +1 -1
- package/dist/lib/registry/index.mjs +0 -6
- package/dist/lib/registry/index.mjs.map +1 -1
- package/dist/lib/registry/property-registry.mjs +0 -6
- package/dist/lib/registry/property-registry.mjs.map +1 -1
- package/dist/lib/registry/sync.mjs +0 -6
- package/dist/lib/registry/sync.mjs.map +1 -1
- package/dist/lib/registry/types.generated.mjs +0 -6
- package/dist/lib/registry/types.mjs +0 -6
- package/dist/lib/schemas/index.mjs +0 -6
- package/dist/lib/schemas/index.mjs.map +1 -1
- package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
- package/dist/lib/server/a2a-adapter.mjs +0 -6
- package/dist/lib/server/a2a-adapter.mjs.map +1 -1
- package/dist/lib/server/account-mode.mjs +0 -6
- package/dist/lib/server/account-mode.mjs.map +1 -1
- package/dist/lib/server/adcp-server.mjs +0 -6
- package/dist/lib/server/adcp-server.mjs.map +1 -1
- package/dist/lib/server/auth-introspection.mjs +0 -6
- package/dist/lib/server/auth-introspection.mjs.map +1 -1
- package/dist/lib/server/auth-signature.mjs +0 -6
- package/dist/lib/server/auth-signature.mjs.map +1 -1
- package/dist/lib/server/auth.mjs +0 -6
- package/dist/lib/server/auth.mjs.map +1 -1
- package/dist/lib/server/create-adcp-server.d.mts +13 -0
- package/dist/lib/server/create-adcp-server.d.ts +13 -0
- package/dist/lib/server/create-adcp-server.d.ts.map +1 -1
- package/dist/lib/server/create-adcp-server.js +3 -2
- package/dist/lib/server/create-adcp-server.js.map +1 -1
- package/dist/lib/server/create-adcp-server.mjs +3 -8
- package/dist/lib/server/create-adcp-server.mjs.map +1 -1
- package/dist/lib/server/credential-policy.mjs +0 -6
- package/dist/lib/server/credential-policy.mjs.map +1 -1
- package/dist/lib/server/ctx-metadata/backends/memory.mjs +0 -6
- package/dist/lib/server/ctx-metadata/backends/memory.mjs.map +1 -1
- package/dist/lib/server/ctx-metadata/backends/pg.mjs +0 -6
- package/dist/lib/server/ctx-metadata/backends/pg.mjs.map +1 -1
- package/dist/lib/server/ctx-metadata/backends/redis.mjs +0 -6
- package/dist/lib/server/ctx-metadata/backends/redis.mjs.map +1 -1
- package/dist/lib/server/ctx-metadata/index.mjs +0 -6
- package/dist/lib/server/ctx-metadata/index.mjs.map +1 -1
- package/dist/lib/server/ctx-metadata/store.mjs +0 -6
- package/dist/lib/server/ctx-metadata/store.mjs.map +1 -1
- package/dist/lib/server/ctx-metadata/wire-shape.mjs +0 -6
- package/dist/lib/server/ctx-metadata/wire-shape.mjs.map +1 -1
- package/dist/lib/server/decisioning/account.mjs +0 -6
- package/dist/lib/server/decisioning/account.mjs.map +1 -1
- package/dist/lib/server/decisioning/admin-router.mjs +0 -6
- package/dist/lib/server/decisioning/admin-router.mjs.map +1 -1
- package/dist/lib/server/decisioning/assembly-helpers.mjs +0 -6
- package/dist/lib/server/decisioning/assembly-helpers.mjs.map +1 -1
- package/dist/lib/server/decisioning/async-outcome.mjs +0 -6
- package/dist/lib/server/decisioning/async-outcome.mjs.map +1 -1
- package/dist/lib/server/decisioning/buyer-agent.mjs +0 -6
- package/dist/lib/server/decisioning/buyer-agent.mjs.map +1 -1
- package/dist/lib/server/decisioning/capabilities.mjs +0 -6
- package/dist/lib/server/decisioning/capabilities.mjs.map +1 -1
- package/dist/lib/server/decisioning/compose.mjs +0 -6
- package/dist/lib/server/decisioning/compose.mjs.map +1 -1
- package/dist/lib/server/decisioning/context.mjs +0 -6
- package/dist/lib/server/decisioning/errors-typed.mjs +0 -6
- package/dist/lib/server/decisioning/errors-typed.mjs.map +1 -1
- package/dist/lib/server/decisioning/helpers.mjs +0 -6
- package/dist/lib/server/decisioning/helpers.mjs.map +1 -1
- package/dist/lib/server/decisioning/index.mjs +0 -6
- package/dist/lib/server/decisioning/index.mjs.map +1 -1
- package/dist/lib/server/decisioning/list-helpers.mjs +0 -6
- package/dist/lib/server/decisioning/list-helpers.mjs.map +1 -1
- package/dist/lib/server/decisioning/manifest-helpers.mjs +0 -6
- package/dist/lib/server/decisioning/manifest-helpers.mjs.map +1 -1
- package/dist/lib/server/decisioning/pagination.mjs +0 -6
- package/dist/lib/server/decisioning/platform-helpers.mjs +0 -6
- package/dist/lib/server/decisioning/platform-helpers.mjs.map +1 -1
- package/dist/lib/server/decisioning/platform.mjs +0 -6
- package/dist/lib/server/decisioning/proposal/dispatch.mjs +0 -6
- package/dist/lib/server/decisioning/proposal/dispatch.mjs.map +1 -1
- package/dist/lib/server/decisioning/proposal/index.mjs +0 -6
- package/dist/lib/server/decisioning/proposal/index.mjs.map +1 -1
- package/dist/lib/server/decisioning/proposal/lifecycle.mjs +0 -6
- package/dist/lib/server/decisioning/proposal/lifecycle.mjs.map +1 -1
- package/dist/lib/server/decisioning/proposal/mock-manager.mjs +0 -6
- package/dist/lib/server/decisioning/proposal/mock-manager.mjs.map +1 -1
- package/dist/lib/server/decisioning/proposal/store.mjs +0 -6
- package/dist/lib/server/decisioning/proposal/store.mjs.map +1 -1
- package/dist/lib/server/decisioning/proposal/types.mjs +0 -6
- package/dist/lib/server/decisioning/proposal/types.mjs.map +1 -1
- package/dist/lib/server/decisioning/resolve-presets.mjs +0 -6
- package/dist/lib/server/decisioning/resolve-presets.mjs.map +1 -1
- package/dist/lib/server/decisioning/runtime/entity-hydration.generated.mjs +0 -6
- package/dist/lib/server/decisioning/runtime/entity-hydration.generated.mjs.map +1 -1
- package/dist/lib/server/decisioning/runtime/from-platform.mjs +0 -6
- package/dist/lib/server/decisioning/runtime/from-platform.mjs.map +1 -1
- package/dist/lib/server/decisioning/runtime/observed-modes.mjs +0 -6
- package/dist/lib/server/decisioning/runtime/observed-modes.mjs.map +1 -1
- package/dist/lib/server/decisioning/runtime/postgres-task-registry.mjs +0 -6
- package/dist/lib/server/decisioning/runtime/postgres-task-registry.mjs.map +1 -1
- package/dist/lib/server/decisioning/runtime/protocol-for-tool.mjs +0 -6
- package/dist/lib/server/decisioning/runtime/protocol-for-tool.mjs.map +1 -1
- package/dist/lib/server/decisioning/runtime/task-registry.mjs +0 -6
- package/dist/lib/server/decisioning/runtime/task-registry.mjs.map +1 -1
- package/dist/lib/server/decisioning/runtime/to-context.mjs +0 -6
- package/dist/lib/server/decisioning/runtime/to-context.mjs.map +1 -1
- package/dist/lib/server/decisioning/runtime/validate-platform.mjs +0 -6
- package/dist/lib/server/decisioning/runtime/validate-platform.mjs.map +1 -1
- package/dist/lib/server/decisioning/specialisms/audiences.mjs +0 -6
- package/dist/lib/server/decisioning/specialisms/brand-rights.mjs +0 -6
- package/dist/lib/server/decisioning/specialisms/campaign-governance.mjs +0 -6
- package/dist/lib/server/decisioning/specialisms/content-standards.mjs +0 -6
- package/dist/lib/server/decisioning/specialisms/creative-ad-server.mjs +0 -6
- package/dist/lib/server/decisioning/specialisms/creative.mjs +0 -6
- package/dist/lib/server/decisioning/specialisms/lists.mjs +0 -6
- package/dist/lib/server/decisioning/specialisms/sales.mjs +0 -6
- package/dist/lib/server/decisioning/specialisms/signals.mjs +0 -6
- package/dist/lib/server/decisioning/specialisms/sponsored-intelligence.mjs +0 -6
- package/dist/lib/server/decisioning/start-time.mjs +0 -6
- package/dist/lib/server/decisioning/start-time.mjs.map +1 -1
- package/dist/lib/server/decisioning/status-changes.mjs +0 -6
- package/dist/lib/server/decisioning/status-changes.mjs.map +1 -1
- package/dist/lib/server/decisioning/status-mappers.mjs +0 -6
- package/dist/lib/server/decisioning/status-mappers.mjs.map +1 -1
- package/dist/lib/server/decisioning/tenant-registry.mjs +0 -6
- package/dist/lib/server/decisioning/tenant-registry.mjs.map +1 -1
- package/dist/lib/server/decisioning/tenant-store.mjs +0 -6
- package/dist/lib/server/decisioning/tenant-store.mjs.map +1 -1
- package/dist/lib/server/decisioning/validate-specialisms.mjs +0 -6
- package/dist/lib/server/decisioning/validate-specialisms.mjs.map +1 -1
- package/dist/lib/server/dynamic-registry.mjs +0 -6
- package/dist/lib/server/dynamic-registry.mjs.map +1 -1
- package/dist/lib/server/envelope-allowlist.mjs +0 -6
- package/dist/lib/server/envelope-allowlist.mjs.map +1 -1
- package/dist/lib/server/error-arm-tools.mjs +5 -6
- package/dist/lib/server/error-arm-tools.mjs.map +1 -1
- package/dist/lib/server/errors.mjs +0 -6
- package/dist/lib/server/errors.mjs.map +1 -1
- package/dist/lib/server/example-tld-guard.mjs +0 -6
- package/dist/lib/server/example-tld-guard.mjs.map +1 -1
- package/dist/lib/server/express-adapter.mjs +0 -6
- package/dist/lib/server/express-adapter.mjs.map +1 -1
- package/dist/lib/server/governance.mjs +0 -6
- package/dist/lib/server/governance.mjs.map +1 -1
- package/dist/lib/server/idempotency/backends/lazy.mjs +0 -6
- package/dist/lib/server/idempotency/backends/lazy.mjs.map +1 -1
- package/dist/lib/server/idempotency/backends/memory.mjs +0 -6
- package/dist/lib/server/idempotency/backends/memory.mjs.map +1 -1
- package/dist/lib/server/idempotency/backends/pg.mjs +0 -6
- package/dist/lib/server/idempotency/backends/pg.mjs.map +1 -1
- package/dist/lib/server/idempotency/backends/redis.mjs +0 -6
- package/dist/lib/server/idempotency/backends/redis.mjs.map +1 -1
- package/dist/lib/server/idempotency/index.mjs +0 -6
- package/dist/lib/server/idempotency/index.mjs.map +1 -1
- package/dist/lib/server/idempotency/store.mjs +0 -6
- package/dist/lib/server/idempotency/store.mjs.map +1 -1
- package/dist/lib/server/index.d.mts +1 -1
- package/dist/lib/server/index.d.ts +1 -1
- package/dist/lib/server/index.d.ts.map +1 -1
- package/dist/lib/server/index.js.map +1 -1
- package/dist/lib/server/index.mjs +0 -6
- package/dist/lib/server/index.mjs.map +1 -1
- package/dist/lib/server/legacy/v5/index.d.mts +1 -1
- package/dist/lib/server/legacy/v5/index.d.ts +1 -1
- package/dist/lib/server/legacy/v5/index.d.ts.map +1 -1
- package/dist/lib/server/legacy/v5/index.js.map +1 -1
- package/dist/lib/server/legacy/v5/index.mjs +0 -6
- package/dist/lib/server/legacy/v5/index.mjs.map +1 -1
- package/dist/lib/server/mcp-modern-server.mjs +0 -6
- package/dist/lib/server/mcp-modern-server.mjs.map +1 -1
- package/dist/lib/server/media-buy-actions.mjs +0 -6
- package/dist/lib/server/media-buy-actions.mjs.map +1 -1
- package/dist/lib/server/media-buy-helpers.mjs +0 -6
- package/dist/lib/server/media-buy-helpers.mjs.map +1 -1
- package/dist/lib/server/media-buy-store.mjs +0 -6
- package/dist/lib/server/media-buy-store.mjs.map +1 -1
- package/dist/lib/server/normalize-errors.mjs +0 -6
- package/dist/lib/server/normalize-errors.mjs.map +1 -1
- package/dist/lib/server/operational-platform.mjs +0 -6
- package/dist/lib/server/operational-platform.mjs.map +1 -1
- package/dist/lib/server/pick-safe-details.mjs +0 -6
- package/dist/lib/server/pick-safe-details.mjs.map +1 -1
- package/dist/lib/server/pin-and-bind-fetch.mjs +0 -6
- package/dist/lib/server/pin-and-bind-fetch.mjs.map +1 -1
- package/dist/lib/server/postgres-state-store.mjs +0 -6
- package/dist/lib/server/postgres-state-store.mjs.map +1 -1
- package/dist/lib/server/postgres-task-store.mjs +0 -6
- package/dist/lib/server/postgres-task-store.mjs.map +1 -1
- package/dist/lib/server/product-defaults.mjs +0 -6
- package/dist/lib/server/product-defaults.mjs.map +1 -1
- package/dist/lib/server/redact.mjs +0 -6
- package/dist/lib/server/redact.mjs.map +1 -1
- package/dist/lib/server/responses.mjs +0 -6
- package/dist/lib/server/responses.mjs.map +1 -1
- package/dist/lib/server/serve.mjs +0 -6
- package/dist/lib/server/serve.mjs.map +1 -1
- package/dist/lib/server/socket-mode/conformance-client.mjs +0 -6
- package/dist/lib/server/socket-mode/conformance-client.mjs.map +1 -1
- package/dist/lib/server/socket-mode/index.mjs +0 -6
- package/dist/lib/server/socket-mode/index.mjs.map +1 -1
- package/dist/lib/server/socket-mode/ws-transport.mjs +0 -6
- package/dist/lib/server/socket-mode/ws-transport.mjs.map +1 -1
- package/dist/lib/server/state-machine.mjs +0 -6
- package/dist/lib/server/state-machine.mjs.map +1 -1
- package/dist/lib/server/state-store.mjs +0 -6
- package/dist/lib/server/state-store.mjs.map +1 -1
- package/dist/lib/server/structured-serialize.mjs +0 -6
- package/dist/lib/server/structured-serialize.mjs.map +1 -1
- package/dist/lib/server/targeting-helpers.mjs +0 -6
- package/dist/lib/server/targeting-helpers.mjs.map +1 -1
- package/dist/lib/server/tasks.mjs +0 -6
- package/dist/lib/server/tasks.mjs.map +1 -1
- package/dist/lib/server/test-controller-bridge.mjs +0 -6
- package/dist/lib/server/test-controller-bridge.mjs.map +1 -1
- package/dist/lib/server/test-controller.mjs +0 -6
- package/dist/lib/server/test-controller.mjs.map +1 -1
- package/dist/lib/server/upstream-helpers.mjs +0 -6
- package/dist/lib/server/upstream-helpers.mjs.map +1 -1
- package/dist/lib/server/webhook-emitter.mjs +0 -6
- package/dist/lib/server/webhook-emitter.mjs.map +1 -1
- package/dist/lib/server/wire-safe.mjs +0 -6
- package/dist/lib/server/wire-safe.mjs.map +1 -1
- package/dist/lib/server/wire-spec-fields.generated.mjs +0 -6
- package/dist/lib/server/wire-spec-fields.generated.mjs.map +1 -1
- package/dist/lib/server/wrap-envelope.mjs +0 -6
- package/dist/lib/server/wrap-envelope.mjs.map +1 -1
- package/dist/lib/signing/agent-context.mjs +0 -6
- package/dist/lib/signing/agent-context.mjs.map +1 -1
- package/dist/lib/signing/agent-fetch.mjs +0 -6
- package/dist/lib/signing/agent-fetch.mjs.map +1 -1
- package/dist/lib/signing/agent-resolver/canonicalize.mjs +0 -6
- package/dist/lib/signing/agent-resolver/canonicalize.mjs.map +1 -1
- package/dist/lib/signing/agent-resolver/capabilities-types.mjs +0 -6
- package/dist/lib/signing/agent-resolver/capabilities-types.mjs.map +1 -1
- package/dist/lib/signing/agent-resolver/consistency.mjs +0 -6
- package/dist/lib/signing/agent-resolver/consistency.mjs.map +1 -1
- package/dist/lib/signing/agent-resolver/errors.mjs +0 -6
- package/dist/lib/signing/agent-resolver/errors.mjs.map +1 -1
- package/dist/lib/signing/agent-resolver/etld.mjs +0 -6
- package/dist/lib/signing/agent-resolver/etld.mjs.map +1 -1
- package/dist/lib/signing/agent-resolver/fetch-helpers.mjs +0 -6
- package/dist/lib/signing/agent-resolver/fetch-helpers.mjs.map +1 -1
- package/dist/lib/signing/agent-resolver/index.mjs +0 -6
- package/dist/lib/signing/agent-resolver/index.mjs.map +1 -1
- package/dist/lib/signing/agent-resolver/jwks-set.mjs +0 -6
- package/dist/lib/signing/agent-resolver/jwks-set.mjs.map +1 -1
- package/dist/lib/signing/agent-resolver/resolve-agent.mjs +0 -6
- package/dist/lib/signing/agent-resolver/resolve-agent.mjs.map +1 -1
- package/dist/lib/signing/agent-resolver/select-agent.mjs +0 -6
- package/dist/lib/signing/agent-resolver/select-agent.mjs.map +1 -1
- package/dist/lib/signing/agent-resolver/strict-json.mjs +0 -6
- package/dist/lib/signing/agent-resolver/strict-json.mjs.map +1 -1
- package/dist/lib/signing/brand-jwks.mjs +0 -6
- package/dist/lib/signing/brand-jwks.mjs.map +1 -1
- package/dist/lib/signing/canonicalize.mjs +0 -6
- package/dist/lib/signing/canonicalize.mjs.map +1 -1
- package/dist/lib/signing/capability-cache.mjs +0 -6
- package/dist/lib/signing/capability-cache.mjs.map +1 -1
- package/dist/lib/signing/capability-priming.mjs +0 -6
- package/dist/lib/signing/capability-priming.mjs.map +1 -1
- package/dist/lib/signing/client.mjs +0 -6
- package/dist/lib/signing/client.mjs.map +1 -1
- package/dist/lib/signing/content-digest.mjs +0 -6
- package/dist/lib/signing/content-digest.mjs.map +1 -1
- package/dist/lib/signing/crypto.mjs +0 -6
- package/dist/lib/signing/crypto.mjs.map +1 -1
- package/dist/lib/signing/ecdsa-encoding.mjs +0 -6
- package/dist/lib/signing/ecdsa-encoding.mjs.map +1 -1
- package/dist/lib/signing/errors.mjs +0 -6
- package/dist/lib/signing/errors.mjs.map +1 -1
- package/dist/lib/signing/fetch-async.mjs +0 -6
- package/dist/lib/signing/fetch-async.mjs.map +1 -1
- package/dist/lib/signing/fetch.mjs +0 -6
- package/dist/lib/signing/fetch.mjs.map +1 -1
- package/dist/lib/signing/index.mjs +0 -6
- package/dist/lib/signing/index.mjs.map +1 -1
- package/dist/lib/signing/jwks-helpers.mjs +0 -6
- package/dist/lib/signing/jwks-helpers.mjs.map +1 -1
- package/dist/lib/signing/jwks-https.mjs +0 -6
- package/dist/lib/signing/jwks-https.mjs.map +1 -1
- package/dist/lib/signing/jwks.mjs +0 -6
- package/dist/lib/signing/jwks.mjs.map +1 -1
- package/dist/lib/signing/middleware.mjs +0 -6
- package/dist/lib/signing/middleware.mjs.map +1 -1
- package/dist/lib/signing/parser.mjs +0 -6
- package/dist/lib/signing/parser.mjs.map +1 -1
- package/dist/lib/signing/postgres-replay-store.mjs +0 -6
- package/dist/lib/signing/postgres-replay-store.mjs.map +1 -1
- package/dist/lib/signing/protocol-response.mjs +0 -6
- package/dist/lib/signing/protocol-response.mjs.map +1 -1
- package/dist/lib/signing/provider.mjs +0 -6
- package/dist/lib/signing/redis-replay-store.mjs +0 -6
- package/dist/lib/signing/redis-replay-store.mjs.map +1 -1
- package/dist/lib/signing/replay.mjs +0 -6
- package/dist/lib/signing/replay.mjs.map +1 -1
- package/dist/lib/signing/request-context.mjs +0 -6
- package/dist/lib/signing/request-context.mjs.map +1 -1
- package/dist/lib/signing/revocation-https.mjs +0 -6
- package/dist/lib/signing/revocation-https.mjs.map +1 -1
- package/dist/lib/signing/revocation.mjs +0 -6
- package/dist/lib/signing/revocation.mjs.map +1 -1
- package/dist/lib/signing/server.mjs +0 -6
- package/dist/lib/signing/server.mjs.map +1 -1
- package/dist/lib/signing/signer-async.mjs +0 -6
- package/dist/lib/signing/signer-async.mjs.map +1 -1
- package/dist/lib/signing/signer.mjs +0 -6
- package/dist/lib/signing/signer.mjs.map +1 -1
- package/dist/lib/signing/testing.mjs +0 -6
- package/dist/lib/signing/testing.mjs.map +1 -1
- package/dist/lib/signing/types.mjs +0 -6
- package/dist/lib/signing/types.mjs.map +1 -1
- package/dist/lib/signing/verifier.mjs +0 -6
- package/dist/lib/signing/verifier.mjs.map +1 -1
- package/dist/lib/signing/webhook-auth-detection.mjs +0 -6
- package/dist/lib/signing/webhook-auth-detection.mjs.map +1 -1
- package/dist/lib/signing/webhook-verifier.mjs +0 -6
- package/dist/lib/signing/webhook-verifier.mjs.map +1 -1
- package/dist/lib/storage/MemoryStorage.mjs +0 -6
- package/dist/lib/storage/MemoryStorage.mjs.map +1 -1
- package/dist/lib/storage/interfaces.mjs +0 -6
- package/dist/lib/substitution/encoder/SubstitutionEncoder.mjs +0 -6
- package/dist/lib/substitution/encoder/SubstitutionEncoder.mjs.map +1 -1
- package/dist/lib/substitution/encoder/index.mjs +0 -6
- package/dist/lib/substitution/encoder/index.mjs.map +1 -1
- package/dist/lib/substitution/index.mjs +0 -6
- package/dist/lib/substitution/index.mjs.map +1 -1
- package/dist/lib/substitution/observer/SubstitutionObserver.mjs +0 -6
- package/dist/lib/substitution/observer/SubstitutionObserver.mjs.map +1 -1
- package/dist/lib/substitution/observer/alignment.mjs +0 -6
- package/dist/lib/substitution/observer/alignment.mjs.map +1 -1
- package/dist/lib/substitution/observer/assertions.mjs +0 -6
- package/dist/lib/substitution/observer/assertions.mjs.map +1 -1
- package/dist/lib/substitution/observer/html-parser.mjs +0 -6
- package/dist/lib/substitution/observer/html-parser.mjs.map +1 -1
- package/dist/lib/substitution/observer/index.mjs +0 -6
- package/dist/lib/substitution/observer/index.mjs.map +1 -1
- package/dist/lib/substitution/observer/ssrf.mjs +0 -6
- package/dist/lib/substitution/observer/ssrf.mjs.map +1 -1
- package/dist/lib/substitution/rfc3986.mjs +0 -6
- package/dist/lib/substitution/rfc3986.mjs.map +1 -1
- package/dist/lib/substitution/translate.mjs +0 -6
- package/dist/lib/substitution/translate.mjs.map +1 -1
- package/dist/lib/substitution/types.mjs +0 -6
- package/dist/lib/substitution/vectors.mjs +0 -6
- package/dist/lib/substitution/vectors.mjs.map +1 -1
- package/dist/lib/testing/agent-tester.mjs +0 -6
- package/dist/lib/testing/agent-tester.mjs.map +1 -1
- package/dist/lib/testing/client.mjs +0 -6
- package/dist/lib/testing/client.mjs.map +1 -1
- package/dist/lib/testing/compliance/briefs.mjs +0 -6
- package/dist/lib/testing/compliance/briefs.mjs.map +1 -1
- package/dist/lib/testing/compliance/comply.mjs +0 -6
- package/dist/lib/testing/compliance/comply.mjs.map +1 -1
- package/dist/lib/testing/compliance/index.mjs +0 -6
- package/dist/lib/testing/compliance/index.mjs.map +1 -1
- package/dist/lib/testing/compliance/spec-conformance.mjs +0 -6
- package/dist/lib/testing/compliance/spec-conformance.mjs.map +1 -1
- package/dist/lib/testing/compliance/storyboard-tracks.mjs +0 -6
- package/dist/lib/testing/compliance/storyboard-tracks.mjs.map +1 -1
- package/dist/lib/testing/compliance/summary.mjs +0 -6
- package/dist/lib/testing/compliance/summary.mjs.map +1 -1
- package/dist/lib/testing/compliance/types.mjs +0 -6
- package/dist/lib/testing/comply-controller.mjs +0 -6
- package/dist/lib/testing/comply-controller.mjs.map +1 -1
- package/dist/lib/testing/controller-assertions.mjs +0 -6
- package/dist/lib/testing/controller-assertions.mjs.map +1 -1
- package/dist/lib/testing/formatter.mjs +0 -6
- package/dist/lib/testing/formatter.mjs.map +1 -1
- package/dist/lib/testing/index.mjs +0 -6
- package/dist/lib/testing/index.mjs.map +1 -1
- package/dist/lib/testing/local-agent-runner.mjs +0 -6
- package/dist/lib/testing/local-agent-runner.mjs.map +1 -1
- package/dist/lib/testing/orchestrator.mjs +0 -6
- package/dist/lib/testing/orchestrator.mjs.map +1 -1
- package/dist/lib/testing/personas/index.mjs +0 -6
- package/dist/lib/testing/personas/index.mjs.map +1 -1
- package/dist/lib/testing/scenarios/brand-rights.mjs +0 -6
- package/dist/lib/testing/scenarios/brand-rights.mjs.map +1 -1
- package/dist/lib/testing/scenarios/capabilities.mjs +0 -6
- package/dist/lib/testing/scenarios/capabilities.mjs.map +1 -1
- package/dist/lib/testing/scenarios/creative.mjs +0 -6
- package/dist/lib/testing/scenarios/creative.mjs.map +1 -1
- package/dist/lib/testing/scenarios/deterministic.mjs +0 -6
- package/dist/lib/testing/scenarios/deterministic.mjs.map +1 -1
- package/dist/lib/testing/scenarios/discovery.mjs +0 -6
- package/dist/lib/testing/scenarios/discovery.mjs.map +1 -1
- package/dist/lib/testing/scenarios/edge-cases.mjs +0 -6
- package/dist/lib/testing/scenarios/edge-cases.mjs.map +1 -1
- package/dist/lib/testing/scenarios/error-compliance.mjs +0 -6
- package/dist/lib/testing/scenarios/error-compliance.mjs.map +1 -1
- package/dist/lib/testing/scenarios/governance.mjs +0 -6
- package/dist/lib/testing/scenarios/governance.mjs.map +1 -1
- package/dist/lib/testing/scenarios/health.mjs +0 -6
- package/dist/lib/testing/scenarios/health.mjs.map +1 -1
- package/dist/lib/testing/scenarios/index.mjs +0 -6
- package/dist/lib/testing/scenarios/index.mjs.map +1 -1
- package/dist/lib/testing/scenarios/media-buy.mjs +0 -6
- package/dist/lib/testing/scenarios/media-buy.mjs.map +1 -1
- package/dist/lib/testing/scenarios/schema-compliance.mjs +0 -6
- package/dist/lib/testing/scenarios/schema-compliance.mjs.map +1 -1
- package/dist/lib/testing/scenarios/signals.mjs +0 -6
- package/dist/lib/testing/scenarios/signals.mjs.map +1 -1
- package/dist/lib/testing/scenarios/sponsored-intelligence.mjs +0 -6
- package/dist/lib/testing/scenarios/sponsored-intelligence.mjs.map +1 -1
- package/dist/lib/testing/scenarios/trusted-match.mjs +0 -6
- package/dist/lib/testing/scenarios/trusted-match.mjs.map +1 -1
- package/dist/lib/testing/seed-merge.mjs +0 -6
- package/dist/lib/testing/seed-merge.mjs.map +1 -1
- package/dist/lib/testing/storyboard/agent-routing.mjs +0 -6
- package/dist/lib/testing/storyboard/agent-routing.mjs.map +1 -1
- package/dist/lib/testing/storyboard/assertions.mjs +0 -6
- package/dist/lib/testing/storyboard/assertions.mjs.map +1 -1
- package/dist/lib/testing/storyboard/canonical-format-satisfaction.mjs +0 -6
- package/dist/lib/testing/storyboard/canonical-format-satisfaction.mjs.map +1 -1
- package/dist/lib/testing/storyboard/compliance.mjs +5 -6
- package/dist/lib/testing/storyboard/compliance.mjs.map +1 -1
- package/dist/lib/testing/storyboard/context.mjs +0 -6
- package/dist/lib/testing/storyboard/context.mjs.map +1 -1
- package/dist/lib/testing/storyboard/default-invariants.mjs +0 -6
- package/dist/lib/testing/storyboard/default-invariants.mjs.map +1 -1
- package/dist/lib/testing/storyboard/index.mjs +0 -6
- package/dist/lib/testing/storyboard/index.mjs.map +1 -1
- package/dist/lib/testing/storyboard/junit.mjs +0 -6
- package/dist/lib/testing/storyboard/junit.mjs.map +1 -1
- package/dist/lib/testing/storyboard/loader.mjs +0 -6
- package/dist/lib/testing/storyboard/loader.mjs.map +1 -1
- package/dist/lib/testing/storyboard/parallel-dispatch.mjs +0 -6
- package/dist/lib/testing/storyboard/parallel-dispatch.mjs.map +1 -1
- package/dist/lib/testing/storyboard/path.mjs +0 -6
- package/dist/lib/testing/storyboard/path.mjs.map +1 -1
- package/dist/lib/testing/storyboard/probes.mjs +0 -6
- package/dist/lib/testing/storyboard/probes.mjs.map +1 -1
- package/dist/lib/testing/storyboard/rate-limit-trip.mjs +0 -6
- package/dist/lib/testing/storyboard/rate-limit-trip.mjs.map +1 -1
- package/dist/lib/testing/storyboard/rejection-hints.mjs +0 -6
- package/dist/lib/testing/storyboard/rejection-hints.mjs.map +1 -1
- package/dist/lib/testing/storyboard/request-builder.mjs +0 -6
- package/dist/lib/testing/storyboard/request-builder.mjs.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/builder.mjs +0 -6
- package/dist/lib/testing/storyboard/request-signing/builder.mjs.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/grader.mjs +0 -6
- package/dist/lib/testing/storyboard/request-signing/grader.mjs.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/index.mjs +0 -6
- package/dist/lib/testing/storyboard/request-signing/index.mjs.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/probe-dispatch.mjs +0 -6
- package/dist/lib/testing/storyboard/request-signing/probe-dispatch.mjs.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/probe.mjs +0 -6
- package/dist/lib/testing/storyboard/request-signing/probe.mjs.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/synthesize.mjs +0 -6
- package/dist/lib/testing/storyboard/request-signing/synthesize.mjs.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/test-kit.mjs +0 -6
- package/dist/lib/testing/storyboard/request-signing/test-kit.mjs.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/types.mjs +0 -6
- package/dist/lib/testing/storyboard/request-signing/types.mjs.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/vector-loader.mjs +0 -6
- package/dist/lib/testing/storyboard/request-signing/vector-loader.mjs.map +1 -1
- package/dist/lib/testing/storyboard/runner.mjs +0 -6
- package/dist/lib/testing/storyboard/runner.mjs.map +1 -1
- package/dist/lib/testing/storyboard/sandbox-entities.mjs +0 -6
- package/dist/lib/testing/storyboard/sandbox-entities.mjs.map +1 -1
- package/dist/lib/testing/storyboard/seeding.mjs +0 -6
- package/dist/lib/testing/storyboard/seeding.mjs.map +1 -1
- package/dist/lib/testing/storyboard/shape-drift-hints.mjs +0 -6
- package/dist/lib/testing/storyboard/shape-drift-hints.mjs.map +1 -1
- package/dist/lib/testing/storyboard/signer-grader/grader.mjs +0 -6
- package/dist/lib/testing/storyboard/signer-grader/grader.mjs.map +1 -1
- package/dist/lib/testing/storyboard/signer-grader/index.mjs +0 -6
- package/dist/lib/testing/storyboard/signer-grader/index.mjs.map +1 -1
- package/dist/lib/testing/storyboard/signer-grader/types.mjs +0 -6
- package/dist/lib/testing/storyboard/strict-validation-hints.mjs +0 -6
- package/dist/lib/testing/storyboard/strict-validation-hints.mjs.map +1 -1
- package/dist/lib/testing/storyboard/task-map.mjs +0 -6
- package/dist/lib/testing/storyboard/task-map.mjs.map +1 -1
- package/dist/lib/testing/storyboard/test-kit.mjs +0 -6
- package/dist/lib/testing/storyboard/test-kit.mjs.map +1 -1
- package/dist/lib/testing/storyboard/types.mjs +0 -6
- package/dist/lib/testing/storyboard/types.mjs.map +1 -1
- package/dist/lib/testing/storyboard/validations.mjs +0 -6
- package/dist/lib/testing/storyboard/validations.mjs.map +1 -1
- package/dist/lib/testing/storyboard/webhook-assertions.mjs +0 -6
- package/dist/lib/testing/storyboard/webhook-assertions.mjs.map +1 -1
- package/dist/lib/testing/storyboard/webhook-receiver.mjs +0 -6
- package/dist/lib/testing/storyboard/webhook-receiver.mjs.map +1 -1
- package/dist/lib/testing/stubs/governance-agent-stub.mjs +0 -6
- package/dist/lib/testing/stubs/governance-agent-stub.mjs.map +1 -1
- package/dist/lib/testing/stubs/index.mjs +0 -6
- package/dist/lib/testing/stubs/index.mjs.map +1 -1
- package/dist/lib/testing/test-controller.mjs +0 -6
- package/dist/lib/testing/test-controller.mjs.map +1 -1
- package/dist/lib/testing/test-helpers.mjs +0 -6
- package/dist/lib/testing/test-helpers.mjs.map +1 -1
- package/dist/lib/testing/types.mjs +0 -6
- package/dist/lib/types/adcp.mjs +0 -6
- package/dist/lib/types/adcp.mjs.map +1 -1
- package/dist/lib/types/asset-instances.mjs +0 -6
- package/dist/lib/types/compat.mjs +0 -6
- package/dist/lib/types/compat.mjs.map +1 -1
- package/dist/lib/types/core.generated.mjs +0 -6
- package/dist/lib/types/enums.generated.mjs +0 -6
- package/dist/lib/types/enums.generated.mjs.map +1 -1
- package/dist/lib/types/error-codes.mjs +0 -6
- package/dist/lib/types/error-codes.mjs.map +1 -1
- package/dist/lib/types/error-details.aliases.mjs +0 -6
- package/dist/lib/types/error-details.aliases.mjs.map +1 -1
- package/dist/lib/types/format-asset-slots.mjs +0 -6
- package/dist/lib/types/forward-compat-error-codes.mjs +0 -6
- package/dist/lib/types/forward-compat-error-codes.mjs.map +1 -1
- package/dist/lib/types/index.mjs +0 -6
- package/dist/lib/types/index.mjs.map +1 -1
- package/dist/lib/types/inline-enums.aliases.mjs +0 -6
- package/dist/lib/types/inline-enums.aliases.mjs.map +1 -1
- package/dist/lib/types/inline-enums.generated.mjs +0 -6
- package/dist/lib/types/inline-enums.generated.mjs.map +1 -1
- package/dist/lib/types/manifest.generated.mjs +0 -6
- package/dist/lib/types/manifest.generated.mjs.map +1 -1
- package/dist/lib/types/schemas.generated.mjs +0 -6
- package/dist/lib/types/schemas.generated.mjs.map +1 -1
- package/dist/lib/types/server-payload-aliases.mjs +0 -6
- package/dist/lib/types/server-payload.mjs +0 -6
- package/dist/lib/types/sync-rows.mjs +0 -6
- package/dist/lib/types/tools.generated.mjs +0 -6
- package/dist/lib/types/v2-5/index.mjs +0 -6
- package/dist/lib/types/v2-5/index.mjs.map +1 -1
- package/dist/lib/types/v2-5/tools.generated.mjs +0 -6
- package/dist/lib/types/v3-1-beta/index.mjs +0 -6
- package/dist/lib/types/v3-1-beta/index.mjs.map +1 -1
- package/dist/lib/types/v3-1-beta/tools.generated.mjs +0 -6
- package/dist/lib/types/wellknown-schemas.generated.mjs +0 -6
- package/dist/lib/types/wellknown-schemas.generated.mjs.map +1 -1
- package/dist/lib/upstream-recorder/constants.mjs +0 -6
- package/dist/lib/upstream-recorder/constants.mjs.map +1 -1
- package/dist/lib/upstream-recorder/index.mjs +0 -6
- package/dist/lib/upstream-recorder/index.mjs.map +1 -1
- package/dist/lib/upstream-recorder/recorder.mjs +0 -6
- package/dist/lib/upstream-recorder/recorder.mjs.map +1 -1
- package/dist/lib/upstream-recorder/types.mjs +0 -6
- package/dist/lib/upstream-recorder/types.mjs.map +1 -1
- package/dist/lib/utils/a2a-artifacts.mjs +0 -6
- package/dist/lib/utils/a2a-artifacts.mjs.map +1 -1
- package/dist/lib/utils/a2a-discovery.mjs +0 -6
- package/dist/lib/utils/a2a-discovery.mjs.map +1 -1
- package/dist/lib/utils/activation-key-builders.mjs +0 -6
- package/dist/lib/utils/activation-key-builders.mjs.map +1 -1
- package/dist/lib/utils/adcp-version-config.mjs +0 -6
- package/dist/lib/utils/adcp-version-config.mjs.map +1 -1
- package/dist/lib/utils/asset-builders.mjs +0 -6
- package/dist/lib/utils/asset-builders.mjs.map +1 -1
- package/dist/lib/utils/build-creative-return-builders.mjs +0 -6
- package/dist/lib/utils/build-creative-return-builders.mjs.map +1 -1
- package/dist/lib/utils/buyer-retry-policy.mjs +0 -6
- package/dist/lib/utils/buyer-retry-policy.mjs.map +1 -1
- package/dist/lib/utils/capabilities.mjs +0 -6
- package/dist/lib/utils/capabilities.mjs.map +1 -1
- package/dist/lib/utils/capability-rollups.mjs +0 -6
- package/dist/lib/utils/capability-rollups.mjs.map +1 -1
- package/dist/lib/utils/creative-adapter.mjs +0 -6
- package/dist/lib/utils/creative-adapter.mjs.map +1 -1
- package/dist/lib/utils/creative-delivery.mjs +0 -6
- package/dist/lib/utils/creative-delivery.mjs.map +1 -1
- package/dist/lib/utils/deprecation.mjs +0 -6
- package/dist/lib/utils/deprecation.mjs.map +1 -1
- package/dist/lib/utils/envelope-status-compat.mjs +0 -6
- package/dist/lib/utils/envelope-status-compat.mjs.map +1 -1
- package/dist/lib/utils/error-extraction.mjs +0 -6
- package/dist/lib/utils/error-extraction.mjs.map +1 -1
- package/dist/lib/utils/extract-result.mjs +0 -6
- package/dist/lib/utils/extract-result.mjs.map +1 -1
- package/dist/lib/utils/format-asset-slot-builders.mjs +0 -6
- package/dist/lib/utils/format-asset-slot-builders.mjs.map +1 -1
- package/dist/lib/utils/format-assets.mjs +0 -6
- package/dist/lib/utils/format-assets.mjs.map +1 -1
- package/dist/lib/utils/format-render-builders.mjs +0 -6
- package/dist/lib/utils/format-render-builders.mjs.map +1 -1
- package/dist/lib/utils/format-renders.mjs +0 -6
- package/dist/lib/utils/format-renders.mjs.map +1 -1
- package/dist/lib/utils/get-products-cache-scope.mjs +0 -6
- package/dist/lib/utils/get-products-cache-scope.mjs.map +1 -1
- package/dist/lib/utils/glob.mjs +0 -6
- package/dist/lib/utils/glob.mjs.map +1 -1
- package/dist/lib/utils/global-async-local-storage.mjs +0 -6
- package/dist/lib/utils/global-async-local-storage.mjs.map +1 -1
- package/dist/lib/utils/idempotency.mjs +0 -6
- package/dist/lib/utils/idempotency.mjs.map +1 -1
- package/dist/lib/utils/index.mjs +0 -6
- package/dist/lib/utils/index.mjs.map +1 -1
- package/dist/lib/utils/jcs.mjs +0 -6
- package/dist/lib/utils/jcs.mjs.map +1 -1
- package/dist/lib/utils/json-depth.mjs +0 -6
- package/dist/lib/utils/json-depth.mjs.map +1 -1
- package/dist/lib/utils/logger.mjs +0 -6
- package/dist/lib/utils/logger.mjs.map +1 -1
- package/dist/lib/utils/media-buy-delivery-notification-builders.mjs +0 -6
- package/dist/lib/utils/media-buy-delivery-notification-builders.mjs.map +1 -1
- package/dist/lib/utils/media-buy-status.mjs +0 -6
- package/dist/lib/utils/media-buy-status.mjs.map +1 -1
- package/dist/lib/utils/pagination.mjs +0 -6
- package/dist/lib/utils/pagination.mjs.map +1 -1
- package/dist/lib/utils/preview-creative-builders.mjs +0 -6
- package/dist/lib/utils/preview-creative-builders.mjs.map +1 -1
- package/dist/lib/utils/preview-normalizer.mjs +0 -6
- package/dist/lib/utils/preview-normalizer.mjs.map +1 -1
- package/dist/lib/utils/preview-utils.mjs +0 -6
- package/dist/lib/utils/preview-utils.mjs.map +1 -1
- package/dist/lib/utils/pricing-adapter.mjs +0 -6
- package/dist/lib/utils/pricing-adapter.mjs.map +1 -1
- package/dist/lib/utils/probe-policy.mjs +0 -6
- package/dist/lib/utils/probe-policy.mjs.map +1 -1
- package/dist/lib/utils/protocol-detection.mjs +0 -6
- package/dist/lib/utils/protocol-detection.mjs.map +1 -1
- package/dist/lib/utils/redact-secrets.mjs +0 -6
- package/dist/lib/utils/redact-secrets.mjs.map +1 -1
- package/dist/lib/utils/redis-default-prefix-warn.mjs +0 -6
- package/dist/lib/utils/redis-default-prefix-warn.mjs.map +1 -1
- package/dist/lib/utils/render-builders.mjs +0 -6
- package/dist/lib/utils/render-builders.mjs.map +1 -1
- package/dist/lib/utils/request-normalizer.mjs +0 -6
- package/dist/lib/utils/request-normalizer.mjs.map +1 -1
- package/dist/lib/utils/response-schemas.mjs +0 -6
- package/dist/lib/utils/response-schemas.mjs.map +1 -1
- package/dist/lib/utils/response-unwrapper.mjs +0 -6
- package/dist/lib/utils/response-unwrapper.mjs.map +1 -1
- package/dist/lib/utils/retry.mjs +0 -6
- package/dist/lib/utils/retry.mjs.map +1 -1
- package/dist/lib/utils/signal-discovery-helpers.mjs +0 -6
- package/dist/lib/utils/signal-discovery-helpers.mjs.map +1 -1
- package/dist/lib/utils/signal-id-builders.mjs +0 -6
- package/dist/lib/utils/signal-id-builders.mjs.map +1 -1
- package/dist/lib/utils/sync-creatives-adapter.mjs +0 -6
- package/dist/lib/utils/sync-creatives-adapter.mjs.map +1 -1
- package/dist/lib/utils/task-state.mjs +0 -6
- package/dist/lib/utils/task-state.mjs.map +1 -1
- package/dist/lib/utils/tool-request-schemas.mjs +0 -6
- package/dist/lib/utils/tool-request-schemas.mjs.map +1 -1
- package/dist/lib/utils/typeGuards.mjs +0 -6
- package/dist/lib/utils/typeGuards.mjs.map +1 -1
- package/dist/lib/utils/union-errors.mjs +0 -6
- package/dist/lib/utils/union-errors.mjs.map +1 -1
- package/dist/lib/utils/validate-user-agent.mjs +0 -6
- package/dist/lib/utils/validate-user-agent.mjs.map +1 -1
- package/dist/lib/v2/format-schema/fetch.mjs +0 -6
- package/dist/lib/v2/format-schema/fetch.mjs.map +1 -1
- package/dist/lib/v2/format-schema/index.mjs +0 -6
- package/dist/lib/v2/format-schema/index.mjs.map +1 -1
- package/dist/lib/v2/format-schema/regex-safety.mjs +0 -6
- package/dist/lib/v2/format-schema/regex-safety.mjs.map +1 -1
- package/dist/lib/v2/format-schema/resolver.mjs +7 -8
- package/dist/lib/v2/format-schema/resolver.mjs.map +1 -1
- package/dist/lib/v2/format-schema/sandbox-refs.mjs +0 -6
- package/dist/lib/v2/format-schema/sandbox-refs.mjs.map +1 -1
- package/dist/lib/v2/projection/augment-response.mjs +0 -6
- package/dist/lib/v2/projection/augment-response.mjs.map +1 -1
- package/dist/lib/v2/projection/builders.mjs +0 -6
- package/dist/lib/v2/projection/builders.mjs.map +1 -1
- package/dist/lib/v2/projection/cache-versions.mjs +0 -6
- package/dist/lib/v2/projection/cache-versions.mjs.map +1 -1
- package/dist/lib/v2/projection/canonical-properties.mjs +5 -6
- package/dist/lib/v2/projection/canonical-properties.mjs.map +1 -1
- package/dist/lib/v2/projection/catalog.mjs +5 -6
- package/dist/lib/v2/projection/catalog.mjs.map +1 -1
- package/dist/lib/v2/projection/constants.mjs +0 -6
- package/dist/lib/v2/projection/constants.mjs.map +1 -1
- package/dist/lib/v2/projection/index.mjs +0 -6
- package/dist/lib/v2/projection/index.mjs.map +1 -1
- package/dist/lib/v2/projection/registry.mjs +5 -6
- package/dist/lib/v2/projection/registry.mjs.map +1 -1
- package/dist/lib/v2/projection/types.mjs +0 -6
- package/dist/lib/v2/projection/v1-to-v2.mjs +0 -6
- package/dist/lib/v2/projection/v1-to-v2.mjs.map +1 -1
- package/dist/lib/v2/projection/v2-to-v1.mjs +0 -6
- package/dist/lib/v2/projection/v2-to-v1.mjs.map +1 -1
- package/dist/lib/v2/projection/write-side.mjs +0 -6
- package/dist/lib/v2/projection/write-side.mjs.map +1 -1
- package/dist/lib/v2/publisher-catalog/formats.mjs +0 -6
- package/dist/lib/v2/publisher-catalog/formats.mjs.map +1 -1
- package/dist/lib/v2/publisher-catalog/index.mjs +0 -6
- package/dist/lib/v2/publisher-catalog/index.mjs.map +1 -1
- package/dist/lib/validation/client-hooks.mjs +0 -6
- package/dist/lib/validation/client-hooks.mjs.map +1 -1
- package/dist/lib/validation/hints.mjs +0 -6
- package/dist/lib/validation/hints.mjs.map +1 -1
- package/dist/lib/validation/index.mjs +0 -6
- package/dist/lib/validation/index.mjs.map +1 -1
- package/dist/lib/validation/schema-errors.mjs +0 -6
- package/dist/lib/validation/schema-errors.mjs.map +1 -1
- package/dist/lib/validation/schema-loader.mjs +5 -6
- package/dist/lib/validation/schema-loader.mjs.map +1 -1
- package/dist/lib/validation/schema-validator.mjs +0 -6
- package/dist/lib/validation/schema-validator.mjs.map +1 -1
- package/dist/lib/validation/sync-creatives.mjs +0 -6
- package/dist/lib/validation/sync-creatives.mjs.map +1 -1
- package/dist/lib/version.d.mts +3 -3
- package/dist/lib/version.d.ts +3 -3
- package/dist/lib/version.js +3 -3
- package/dist/lib/version.js.map +1 -1
- package/dist/lib/version.mjs +3 -9
- package/dist/lib/version.mjs.map +1 -1
- package/dist/lib/webhooks/index.mjs +0 -6
- package/dist/lib/webhooks/index.mjs.map +1 -1
- package/dist/lib/wholesale-feed-sync/index.mjs +0 -6
- package/dist/lib/wholesale-feed-sync/index.mjs.map +1 -1
- package/dist/lib/wholesale-feed-sync/sync.mjs +0 -6
- package/dist/lib/wholesale-feed-sync/sync.mjs.map +1 -1
- package/dist/lib/wholesale-feed-sync/types.mjs +0 -6
- package/dist/lib/wholesale-feed-sync/webhook-notification.mjs +0 -6
- package/dist/lib/wholesale-feed-sync/webhook-notification.mjs.map +1 -1
- package/package.json +2 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/lib/server/a2a-adapter.ts"],"sourcesContent":["/**\n * A2A transport adapter for `AdcpServer`.\n *\n * Peer of `serve()` / `createExpressAdapter()`: same `AdcpServer` handle,\n * different wire transport. MCP and A2A share the dispatcher, idempotency\n * store, state store, resolveAccount, and governance — everything the\n * framework pipeline owns is transport-agnostic.\n *\n * **Scope (v0)**: `message/send`, `tasks/get`, `tasks/cancel`, and\n * `GET /.well-known/agent-card.json`. Streaming (`message/stream`),\n * push notifications, and mid-flight `input-required` interrupts are\n * explicit \"not yet\" — see `docs/guides/BUILD-AN-AGENT.md`.\n *\n * **Handler-return → A2A `Task.state` mapping:**\n *\n * | Handler returned… | A2A `Task.state` | Artifact payload |\n * |-------------------------------------|-------------------|--------------------------------------------------|\n * | Success arm | `completed` | DataPart with the typed AdCP response |\n * | Submitted arm (`status:'submitted'`)| `completed` [^1] | DataPart with AdCP response + `metadata.adcp_task_id` |\n * | Error arm (`errors:[]`) | `failed` | DataPart with the AdCP Error arm payload |\n * | `adcpError()` envelope | `failed` | DataPart with `adcp_error` |\n *\n * [^1]: A2A `submitted` is the INITIAL lifecycle state (before\n * `working`); A2A `completed` marks the transport call as done.\n * When the AdCP handler returns a Submitted arm the HTTP call itself\n * has completed — the AdCP-level async work is queued and buyers\n * resume it via `adcp_task_id` on `artifact.metadata`. The DataPart's\n * `data` still carries `status: 'submitted'` from the AdCP response,\n * so a buyer reading the artifact payload sees the ad-tech state\n * directly; the A2A Task.state only mirrors whether the transport\n * call itself terminated.\n *\n * **Message shape.** A client addresses a tool by sending a `Message`\n * with a single `DataPart`: `{ kind: 'data', data: { skill, input } }`.\n * `skill` must match a registered AdCP tool name (e.g. `get_products`);\n * `input` becomes the tool arguments before AdCP schema validation\n * runs. The older client convention `{ skill, parameters }` (used by\n * `src/lib/protocols/a2a.ts`) is also accepted — `data.parameters` is\n * treated as an alias for `data.input`; new callers should prefer\n * `input` since A2A `DataPart.data` is free-form and `input` matches\n * the AdCP tool's typed request shape.\n *\n * **Two lifecycles, one response.** A2A's `Task.state` tracks the\n * TRANSPORT call (did the HTTP request complete?). AdCP's `status`\n * inside the artifact tracks the WORK (submitted / completed /\n * failed). Don't conflate them: a `completed` A2A task can carry a\n * `submitted` AdCP response, meaning the call returned successfully\n * but the ad-tech operation itself is still queued. Buyers resume the\n * AdCP work via the `adcp_task_id` on the artifact's `metadata`, not\n * by re-polling the A2A Task.\n *\n * @preview v0 surface — field semantics may shift while the ecosystem\n * converges on AdCP-over-A2A conventions. Pinning a minor version is\n * recommended.\n */\n\nimport {\n type AgentExecutor,\n type ExecutionEventBus,\n type RequestContext,\n type TaskStore,\n type User,\n type UnauthenticatedUser,\n DefaultRequestHandler,\n InMemoryTaskStore as SdkInMemoryTaskStore,\n DefaultExecutionEventBusManager,\n} from '@a2a-js/sdk/server';\nimport { jsonRpcHandler, agentCardHandler } from '@a2a-js/sdk/server/express';\nimport type {\n AgentCard,\n AgentCapabilities,\n AgentProvider,\n AgentSkill,\n Artifact,\n DataPart,\n Message,\n SecurityScheme,\n Task,\n TaskArtifactUpdateEvent,\n TaskStatusUpdateEvent,\n} from '@a2a-js/sdk';\nimport type { Request, RequestHandler } from 'express';\nimport { randomUUID } from 'node:crypto';\nimport { getSdkServer, listRegisteredToolNames, type AdcpAuthInfo, type AdcpServer } from './adcp-server';\nimport type { McpToolResponse } from './responses';\nimport type { AdcpLogger } from './create-adcp-server';\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/**\n * Agent-card identity fields the adapter can't derive automatically.\n * Auto-seeded fields (`capabilities`, `skills`, `defaultInputModes`,\n * `defaultOutputModes`, `protocolVersion`, `additionalInterfaces`) may\n * be overridden by passing them here; the merged card is validated\n * against A2A's required-field set at boot.\n */\nexport interface A2AAgentCardOverrides {\n /** Human-readable agent name (required). */\n name: string;\n /** Human-readable description (required). */\n description: string;\n /** Agent URL — the endpoint A2A clients connect to (required). */\n url: string;\n /** Agent version (required). */\n version: string;\n\n provider?: AgentProvider;\n documentationUrl?: string;\n iconUrl?: string;\n securitySchemes?: { [k: string]: SecurityScheme };\n security?: { [k: string]: string[] }[];\n preferredTransport?: string;\n\n /**\n * Override the auto-generated capabilities. The adapter sets\n * `streaming: false` and `pushNotifications: false` by default (v0\n * ships neither). Set `streaming: true` if you wire a downstream\n * extension; the adapter still won't emit `TaskStatusUpdateEvent`s\n * on the stream path in v0.\n */\n capabilities?: AgentCapabilities;\n\n /**\n * Override the auto-generated skills list. When omitted the adapter\n * derives one `AgentSkill` per registered AdCP tool from the server's\n * capability object. Supply this to add descriptions, examples, tags,\n * or per-skill input/output modes the SDK can't infer. Framework-private\n * tools such as `comply_test_controller` are still filtered from the public\n * A2A agent card.\n */\n skills?: AgentSkill[];\n\n defaultInputModes?: string[];\n defaultOutputModes?: string[];\n protocolVersion?: string;\n}\n\n/**\n * Options for {@link createA2AAdapter}.\n *\n * **Auth posture.** `authenticate(req)` runs BEFORE the tool handler\n * sees the request. Return an `AdcpAuthInfo` to let the pipeline\n * proceed with that principal; return `null` (or throw) to reject.\n * A rejection currently surfaces as a generic JSON-RPC `-32000`\n * server error — the `@a2a-js/sdk` doesn't yet expose a typed\n * authentication-failed code for the `UserBuilder` path. Production\n * deployments SHOULD wire upstream middleware (e.g. `express-jwt`) to\n * reject with a proper HTTP 401 / WWW-Authenticate challenge before\n * the request reaches `jsonRpcHandler`. The `authenticate` option\n * here is a last-line-of-defense guard, not the primary auth surface.\n *\n * **Agent-card `securitySchemes`.** The `agentCard.securitySchemes`\n * you provide is served verbatim at `/.well-known/agent-card.json` —\n * only put non-secret discovery data there (token endpoint, scopes,\n * OIDC issuer URL). Never paste client secrets, private JWKS, or\n * internal URLs into the card. The SDK doesn't schema-validate\n * `securitySchemes` at boot (v0 check is required-field presence\n * only), so a hand-crafted malformed entry will ship as-written.\n *\n * Omitting `authenticate` makes the adapter anonymous — handlers see\n * `ctx.authInfo === undefined`, matching `serve({ authenticate: undefined })`.\n */\nexport interface A2AAdapterOptions {\n /** AdCP server whose registered tools this adapter exposes over A2A. */\n server: AdcpServer;\n\n /**\n * Authenticate an inbound A2A request. Transport-level auth runs\n * before `AdcpServer.invoke()` so the framework pipeline sees a\n * verified `authInfo`. Return `null` (or throw) to reject.\n */\n authenticate?: (req: Request) => Promise<AdcpAuthInfo | null>;\n\n /** Seller-supplied agent-card identity fields. Required. */\n agentCard: A2AAgentCardOverrides;\n\n /**\n * A2A task store. Defaults to the SDK's `InMemoryTaskStore`.\n * Persistent deployments should supply a durable implementation\n * (e.g. a Postgres-backed `TaskStore`).\n */\n taskStore?: TaskStore;\n\n /** Optional logger. Falls back to `console`. */\n logger?: AdcpLogger;\n}\n\n/** Minimal Express app surface the adapter's `mount()` helper needs. */\nexport interface ExpressAppLike {\n use(path: string, ...handlers: RequestHandler[]): unknown;\n}\n\n/** Options for {@link A2AAdapter.mount}. */\nexport interface A2AMountOptions {\n /**\n * URL-path prefix for the JSON-RPC endpoint. Defaults to the pathname\n * of the agent card's `url` field (so `url: 'https://host/a2a'` mounts\n * JSON-RPC at `/a2a`). Override to mount under a different path.\n */\n basePath?: string;\n /**\n * When true (default), also mounts the agent card at the origin root\n * (`/.well-known/agent-card.json`) so simple discovery probes targeting\n * the host itself find the card. Some deployments disable this when an\n * upstream proxy owns origin-root routes — set `false` to mount only\n * at `{basePath}/.well-known/agent-card.json`.\n */\n wellKnownAtRoot?: boolean;\n}\n\n/**\n * Value returned by {@link createA2AAdapter}.\n *\n * For almost every seller, `adapter.mount(app)` is the right entry\n * point — it wires all four routes (JSON-RPC at the agent-card's\n * path, the agent card at both `{basePath}/.well-known/agent-card.json`\n * for A2A discovery and `/.well-known/agent-card.json` for origin-root\n * probes) with one call.\n *\n * The `jsonRpcHandler` and `agentCardHandler` fields stay exposed for\n * deployments that need finer control (mounting behind a custom auth\n * layer, serving the card from a CDN, testing).\n */\nexport interface A2AAdapter {\n /** The A2A JSON-RPC middleware (`message/send`, `tasks/get`, `tasks/cancel`). */\n jsonRpcHandler: RequestHandler;\n /** The agent-card discovery GET middleware. */\n agentCardHandler: RequestHandler;\n /** Returns the merged, validated agent card. */\n getAgentCard(): Promise<AgentCard>;\n /**\n * Wire all A2A routes onto an Express-compatible app in one call.\n * Eliminates the \"card mounted at only one location\" footgun: the\n * A2A SDK derives `${agentCard.url}/.well-known/agent-card.json` for\n * discovery, while many clients also probe origin root — this helper\n * satisfies both. See {@link A2AMountOptions} to override paths.\n */\n mount(app: ExpressAppLike, options?: A2AMountOptions): void;\n}\n\n// ---------------------------------------------------------------------------\n// Internals\n// ---------------------------------------------------------------------------\n\n/**\n * Our `User` carries the full AdCP auth payload, not just the two\n * getters A2A's minimal `User` requires. The executor reads this back\n * out of `RequestContext.context.user`.\n */\ninterface A2AAdcpUser extends User {\n readonly adcpAuthInfo?: AdcpAuthInfo;\n}\n\nfunction buildAuthenticatedUser(authInfo: AdcpAuthInfo): A2AAdcpUser {\n const clientId = authInfo.clientId;\n return {\n get isAuthenticated() {\n return true;\n },\n get userName() {\n return clientId;\n },\n adcpAuthInfo: authInfo,\n };\n}\n\nfunction buildAnonymousUser(): UnauthenticatedUser {\n return {\n get isAuthenticated() {\n return false as const;\n },\n get userName() {\n return 'anonymous';\n },\n };\n}\n\nfunction getAdcpAuthInfo(context: RequestContext['context']): AdcpAuthInfo | undefined {\n const user = context?.user as A2AAdcpUser | undefined;\n return user?.adcpAuthInfo;\n}\n\n/**\n * Extract the `{ skill, input }` pair from the inbound Message's parts.\n *\n * Convention: the client sends a single DataPart with\n * `{ skill: '<tool_name>', input: { ...args } }`. Reject anything else\n * — text-only payloads, files, multiple data parts — so buyers get a\n * deterministic error instead of silently-wrong routing.\n */\ninterface ExtractedInvocation {\n skill: string;\n input: Record<string, unknown>;\n}\n\nfunction extractInvocation(message: Message): ExtractedInvocation {\n if (!Array.isArray(message.parts) || message.parts.length === 0) {\n throw new A2AInvocationError('Message must carry at least one part with a `data` kind.');\n }\n const dataParts = message.parts.filter((p): p is DataPart => p?.kind === 'data');\n if (dataParts.length === 0) {\n throw new A2AInvocationError(\n \"Message must include a DataPart with { skill, input } — text-only messages aren't routable to AdCP tools.\"\n );\n }\n if (dataParts.length > 1) {\n throw new A2AInvocationError(\n 'Message must include exactly one DataPart — multi-part invocations are not supported in v0.'\n );\n }\n const firstDataPart = dataParts[0]!;\n const rawData = firstDataPart.data;\n // Guard before destructuring — a client sending `{ kind: 'data', data: null }`\n // or `data: \"string\"` would otherwise TypeError on payload.skill and surface\n // as a generic HANDLER_THREW instead of INVALID_INVOCATION.\n if (rawData == null || typeof rawData !== 'object' || Array.isArray(rawData)) {\n throw new A2AInvocationError('DataPart `data` must be an object containing { skill, input }.');\n }\n const payload = rawData as Record<string, unknown>;\n const skill = payload.skill;\n // Accept `parameters` as an alias for `input` — the in-tree A2A\n // client (`src/lib/protocols/a2a.ts`) shipped first and uses\n // `parameters`, so downstream agents already emit that key. Going\n // forward prefer `input`: A2A `DataPart.data` is free-form JSON and\n // `input` matches the AdCP tool's typed request shape. The alias\n // stays for ecosystem compatibility; no deprecation timer in v0.\n const input = payload.input ?? payload.parameters;\n if (typeof skill !== 'string' || skill.length === 0) {\n throw new A2AInvocationError('DataPart must include a non-empty string `skill` field naming the AdCP tool.');\n }\n if (input != null && (typeof input !== 'object' || Array.isArray(input))) {\n throw new A2AInvocationError('DataPart `input` (or legacy `parameters`) must be an object (or omitted).');\n }\n return { skill, input: (input as Record<string, unknown>) ?? {} };\n}\n\n/** Thrown when an incoming Message doesn't match the AdCP-over-A2A convention. */\nexport class A2AInvocationError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'A2AInvocationError';\n }\n}\n\n// ---------------------------------------------------------------------------\n// Executor — translates A2A execute() into adcpServer.invoke() + events\n// ---------------------------------------------------------------------------\n\n/**\n * Classify a framework-produced `McpToolResponse` so the executor knows\n * what A2A `Task.state` to publish.\n */\ntype ClassifiedResult =\n | { kind: 'success'; data: Record<string, unknown> }\n | { kind: 'submitted'; adcpTaskId: string; data: Record<string, unknown> }\n | { kind: 'error_arm'; data: Record<string, unknown> }\n | { kind: 'adcp_error'; data: Record<string, unknown> };\n\nfunction classifyResponse(res: McpToolResponse): ClassifiedResult {\n const structured = (res.structuredContent ?? {}) as Record<string, unknown>;\n if (res.isError === true) {\n if (structured.adcp_error && typeof structured.adcp_error === 'object') {\n return { kind: 'adcp_error', data: structured };\n }\n return { kind: 'error_arm', data: structured };\n }\n if (structured.status === 'submitted' && typeof structured.task_id === 'string') {\n return { kind: 'submitted', adcpTaskId: structured.task_id, data: structured };\n }\n return { kind: 'success', data: structured };\n}\n\nclass AdcpA2AAgentExecutor implements AgentExecutor {\n // Cooperative-cancel flag. `DefaultRequestHandler.cancelTask` only\n // calls our executor's `cancelTask` while an execute() is in-flight\n // (eventBus still open); post-completion cancels are handled by the\n // SDK directly against the task store. So this set only holds\n // taskIds that currently have a pending `cancelTask`, and execute()\n // always clears its entry at the end — no unbounded growth.\n private readonly canceled = new Set<string>();\n\n // A2A `Task.id` → original `contextId`. Populated when execute()\n // starts so `cancelTask` can emit a well-formed status-update event\n // against the same contextId instead of guessing an empty string.\n // Cleared alongside the canceled flag in execute()'s finally block.\n private readonly taskContextIds = new Map<string, string>();\n\n constructor(\n private readonly server: AdcpServer,\n private readonly logger: AdcpLogger\n ) {}\n\n async execute(requestContext: RequestContext, eventBus: ExecutionEventBus): Promise<void> {\n const { taskId, contextId, userMessage } = requestContext;\n const authInfo = getAdcpAuthInfo(requestContext.context);\n\n this.taskContextIds.set(taskId, contextId);\n try {\n // Register the task with the ResultManager by publishing a Task\n // event first — subsequent status-update / artifact-update events\n // only resolve if the manager has seen the task. `working` is the\n // initial state; we replace it with completed / submitted / failed\n // once the handler returns.\n this.publishInitialTask(eventBus, taskId, contextId, userMessage);\n\n let invocation: ExtractedInvocation;\n try {\n invocation = extractInvocation(userMessage);\n } catch (err) {\n const message = err instanceof Error ? err.message : 'Invalid A2A invocation';\n this.emitFailure(eventBus, taskId, contextId, {\n reason: 'INVALID_INVOCATION',\n message,\n });\n return;\n }\n\n let response: McpToolResponse | undefined;\n const toolNames = a2aSkillToServerToolNames(invocation.skill);\n let invokedToolName: string | undefined;\n try {\n for (const toolName of toolNames) {\n try {\n invokedToolName = toolName;\n response = await this.server.invoke({\n // Platform servers register `tasks_get`; older/custom A2A\n // fixtures may still register the slash name directly.\n toolName,\n args: invocation.input,\n ...(authInfo && { authInfo }),\n });\n break;\n } catch (err) {\n if (toolName === 'tasks_get' && invocation.skill === 'tasks/get' && isToolNotRegistered(err, toolName)) {\n continue;\n }\n throw err;\n }\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n this.logger.error('A2A adapter: handler invocation threw', {\n toolName: invokedToolName ?? invocation.skill,\n requestedToolName: invocation.skill,\n error: message,\n });\n this.emitFailure(eventBus, taskId, contextId, {\n reason: 'HANDLER_THREW',\n message,\n });\n return;\n }\n if (response === undefined) {\n this.emitFailure(eventBus, taskId, contextId, {\n reason: 'HANDLER_THREW',\n message: `No handler registered for ${invocation.skill}`,\n });\n return;\n }\n\n if (this.canceled.has(taskId)) {\n this.publishStatus(eventBus, taskId, contextId, 'canceled', true);\n return;\n }\n\n const classified = classifyResponse(response);\n this.publishArtifact(eventBus, taskId, contextId, classified);\n // A2A Task.state maps the TRANSPORT call lifecycle, not the AdCP\n // work. A `submitted` AdCP arm means the HTTP call itself\n // completed — the ad-tech work is queued, resumed via\n // `adcp_task_id` on the artifact metadata. Emitting A2A\n // `state: 'submitted'` with `final: true` would be a\n // non-conformant transition per A2A 0.3.0 (`submitted` is the\n // INITIAL state before `working`, never terminal). Buyers read\n // the AdCP-level status from the artifact's `data.status` field.\n this.publishStatus(\n eventBus,\n taskId,\n contextId,\n classified.kind === 'success' || classified.kind === 'submitted' ? 'completed' : 'failed',\n true\n );\n } finally {\n // Clean up per-task state regardless of path — the executor is\n // long-lived (one per adapter instance), so any leak compounds.\n this.canceled.delete(taskId);\n this.taskContextIds.delete(taskId);\n eventBus.finished();\n }\n }\n\n async cancelTask(taskId: string, eventBus: ExecutionEventBus): Promise<void> {\n this.canceled.add(taskId);\n // `DefaultRequestHandler.cancelTask` only reaches us when execute()\n // is still in-flight (eventBus still open). Publish the canceled\n // status so the SDK's secondary `_processEvents` loop terminates;\n // execute() will ALSO see the flag and short-circuit before\n // publishing a success/failure status. The A2A event bus is\n // idempotent on duplicate status publishes — whichever lands first\n // wins the taskStore write.\n const contextId = this.taskContextIds.get(taskId) ?? '';\n this.publishStatus(eventBus, taskId, contextId, 'canceled', true);\n // Do NOT call eventBus.finished() here — execute()'s finally block\n // owns the finished() signal, and calling it twice risks closing\n // the queue mid-event-flush on the primary processEvents loop.\n }\n\n private publishInitialTask(\n eventBus: ExecutionEventBus,\n taskId: string,\n contextId: string,\n userMessage: Message\n ): void {\n const task: Task = {\n kind: 'task',\n id: taskId,\n contextId,\n status: {\n state: 'working',\n timestamp: new Date().toISOString(),\n },\n history: [userMessage],\n };\n eventBus.publish(task);\n }\n\n private publishStatus(\n eventBus: ExecutionEventBus,\n taskId: string,\n contextId: string,\n state: TaskStatusUpdateEvent['status']['state'],\n final: boolean\n ): void {\n const event: TaskStatusUpdateEvent = {\n kind: 'status-update',\n taskId,\n contextId,\n status: {\n state,\n timestamp: new Date().toISOString(),\n },\n final,\n };\n eventBus.publish(event);\n }\n\n private publishArtifact(\n eventBus: ExecutionEventBus,\n taskId: string,\n contextId: string,\n classified: ClassifiedResult\n ): void {\n const artifactName =\n classified.kind === 'success' ? 'result' : classified.kind === 'submitted' ? 'submitted' : 'error';\n // DataPart `data` is the AdCP tool's typed response — no\n // transport-level fields injected here so the payload still\n // validates against the tool's AdCP response schema. AdCP\n // transport metadata (`adcp_task_id` pointing at the async handle)\n // rides on `artifact.metadata` per A2A 0.3.0's extension\n // convention.\n const artifact: Artifact = {\n artifactId: randomUUID(),\n name: artifactName,\n parts: [{ kind: 'data', data: classified.data }],\n ...(classified.kind === 'submitted' && {\n metadata: { adcp_task_id: classified.adcpTaskId },\n }),\n };\n const event: TaskArtifactUpdateEvent = {\n kind: 'artifact-update',\n taskId,\n contextId,\n artifact,\n append: false,\n lastChunk: true,\n };\n eventBus.publish(event);\n }\n\n private emitFailure(\n eventBus: ExecutionEventBus,\n taskId: string,\n contextId: string,\n payload: { reason: string; message: string }\n ): void {\n const artifact: Artifact = {\n artifactId: randomUUID(),\n name: 'error',\n parts: [{ kind: 'data', data: payload }],\n };\n eventBus.publish({\n kind: 'artifact-update',\n taskId,\n contextId,\n artifact,\n append: false,\n lastChunk: true,\n } satisfies TaskArtifactUpdateEvent);\n this.publishStatus(eventBus, taskId, contextId, 'failed', true);\n }\n}\n\nfunction isToolNotRegistered(err: unknown, toolName: string): boolean {\n return err instanceof Error && err.message.includes(`tool \"${toolName}\" is not registered`);\n}\n\nfunction a2aSkillToServerToolNames(skill: string): string[] {\n return skill === 'tasks/get' ? ['tasks_get', 'tasks/get'] : [skill];\n}\n\n// ---------------------------------------------------------------------------\n// Agent card\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_MODES = ['application/json'] as const;\nconst DEFAULT_PROTOCOL_VERSION = '0.3.0';\nconst PUBLIC_AGENT_CARD_EXCLUDED_SKILLS = new Set(['get_adcp_capabilities', 'comply_test_controller']);\n\n/**\n * Derive one `AgentSkill` per registered AdCP tool. Skills without\n * seller-supplied descriptions get a generic one pointing at the\n * AdCP tool name — enough to pass A2A registry validation; sellers\n * are expected to enrich via `agentCard.skills` in production.\n */\nfunction deriveSkills(toolNames: string[]): AgentSkill[] {\n const publicNames = new Set(toolNames.map(toA2ASkillName));\n return [...publicNames].map(name => ({\n id: name,\n name,\n description: `AdCP tool: ${name}. Send { skill: \"${name}\", input: { ... } } as a DataPart.`,\n tags: ['adcp'],\n }));\n}\n\nfunction toA2ASkillName(toolName: string): string {\n return toolName === 'tasks_get' ? 'tasks/get' : toolName;\n}\n\nfunction listRegisteredTools(server: AdcpServer): string[] {\n const sdk = getSdkServer(server);\n if (!sdk) return [];\n return listRegisteredToolNames(sdk).filter(name => !PUBLIC_AGENT_CARD_EXCLUDED_SKILLS.has(name));\n}\n\nfunction filterPublicAgentCardSkills(skills: AgentSkill[]): AgentSkill[] {\n return skills.filter(skill => {\n const id = typeof skill.id === 'string' ? skill.id : undefined;\n const name = typeof skill.name === 'string' ? skill.name : undefined;\n return !(\n (id != null && PUBLIC_AGENT_CARD_EXCLUDED_SKILLS.has(id)) ||\n (name != null && PUBLIC_AGENT_CARD_EXCLUDED_SKILLS.has(name))\n );\n });\n}\n\nfunction buildAgentCard(server: AdcpServer, overrides: A2AAgentCardOverrides): AgentCard {\n const tools = listRegisteredTools(server);\n const skills = filterPublicAgentCardSkills(overrides.skills ?? deriveSkills(tools));\n\n const card: AgentCard = {\n name: overrides.name,\n description: overrides.description,\n url: overrides.url,\n version: overrides.version,\n protocolVersion: overrides.protocolVersion ?? DEFAULT_PROTOCOL_VERSION,\n defaultInputModes: overrides.defaultInputModes ?? [...DEFAULT_MODES],\n defaultOutputModes: overrides.defaultOutputModes ?? [...DEFAULT_MODES],\n capabilities: overrides.capabilities ?? {\n streaming: false,\n pushNotifications: false,\n },\n skills,\n ...(overrides.provider && { provider: overrides.provider }),\n ...(overrides.documentationUrl && { documentationUrl: overrides.documentationUrl }),\n ...(overrides.iconUrl && { iconUrl: overrides.iconUrl }),\n ...(overrides.securitySchemes && { securitySchemes: overrides.securitySchemes }),\n ...(overrides.security && { security: overrides.security }),\n ...(overrides.preferredTransport && { preferredTransport: overrides.preferredTransport }),\n };\n\n validateAgentCard(card);\n return card;\n}\n\n/**\n * Fail loud at adapter construction when the merged card misses\n * A2A-required fields. The SDK would reject the discovery response\n * at runtime anyway — better to catch it at boot so the agent never\n * binds a port with an unserviceable card.\n */\nfunction validateAgentCard(card: AgentCard): void {\n const missing: string[] = [];\n if (!card.name) missing.push('name');\n if (!card.description) missing.push('description');\n if (!card.url) missing.push('url');\n if (!card.version) missing.push('version');\n if (!card.protocolVersion) missing.push('protocolVersion');\n if (!card.capabilities) missing.push('capabilities');\n if (!Array.isArray(card.defaultInputModes) || card.defaultInputModes.length === 0) {\n missing.push('defaultInputModes');\n }\n if (!Array.isArray(card.defaultOutputModes) || card.defaultOutputModes.length === 0) {\n missing.push('defaultOutputModes');\n }\n if (!Array.isArray(card.skills)) missing.push('skills');\n if (missing.length > 0) {\n throw new Error(\n `createA2AAdapter: agent card is missing required fields — ${missing.join(', ')}. ` +\n `Supply them via options.agentCard so A2A discovery doesn't fail at runtime.`\n );\n }\n if (Array.isArray(card.skills) && card.skills.length === 0) {\n throw new Error(\n 'createA2AAdapter: agent card has no skills — register AdCP handlers on the server (or supply options.agentCard.skills) before creating the adapter.'\n );\n }\n}\n\n// ---------------------------------------------------------------------------\n// Public entry point\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_LOGGER: AdcpLogger = {\n debug: (m, d) => console.debug(m, d ?? ''),\n info: (m, d) => console.info(m, d ?? ''),\n warn: (m, d) => console.warn(m, d ?? ''),\n error: (m, d) => console.error(m, d ?? ''),\n};\n\n/**\n * Create an A2A transport adapter around an `AdcpServer`.\n *\n * @example\n * ```ts\n * const adcp = createAdcpServer({ mediaBuy: { getProducts: async () => ({ products: [] }) } });\n * const a2a = createA2AAdapter({\n * server: adcp,\n * agentCard: {\n * name: 'Acme SSP',\n * description: 'Guaranteed + non-guaranteed display inventory',\n * url: 'https://ssp.acme.com/a2a',\n * version: '1.0.0',\n * provider: { organization: 'Acme', url: 'https://acme.com' },\n * securitySchemes: { bearer: { type: 'http', scheme: 'bearer' } },\n * },\n * async authenticate(req) {\n * const token = extractBearer(req);\n * return token ? { token, clientId: 'buyer_123', scopes: [] } : null;\n * },\n * });\n *\n * app.use('/a2a', a2a.jsonRpcHandler);\n * app.use('/.well-known/agent-card.json', a2a.agentCardHandler);\n * ```\n *\n * @preview — see the module docstring.\n */\nexport function createA2AAdapter(options: A2AAdapterOptions): A2AAdapter {\n const logger = options.logger ?? DEFAULT_LOGGER;\n const card = buildAgentCard(options.server, options.agentCard);\n const taskStore = options.taskStore ?? new SdkInMemoryTaskStore();\n const executor = new AdcpA2AAgentExecutor(options.server, logger);\n const eventBusManager = new DefaultExecutionEventBusManager();\n const requestHandler = new DefaultRequestHandler(card, taskStore, executor, eventBusManager);\n\n const userBuilder = async (req: Request): Promise<User> => {\n if (!options.authenticate) return buildAnonymousUser();\n const authInfo = await options.authenticate(req);\n if (authInfo == null) {\n // Throwing an A2AError with an authentication code would give the\n // SDK's JSON-RPC envelope the right shape, but the SDK keeps\n // `A2AError` internal — surfacing as a thrown Error yields a\n // generic -32000 server error, which is still closer to the\n // right signal than silently continuing anonymously. Most\n // deployments should reject before the UserBuilder via upstream\n // middleware (e.g. `express-jwt`); auth via the UserBuilder is\n // the fallback path.\n throw new Error('A2A authentication failed');\n }\n return buildAuthenticatedUser(authInfo);\n };\n\n const jsonRpc = jsonRpcHandler({ requestHandler, userBuilder });\n const agentCardMiddleware = agentCardHandler({ agentCardProvider: requestHandler });\n\n // Derive the default basePath from the agent-card URL's pathname so\n // `mount(app)` \"just works\" for the common case where the URL the\n // seller advertised and the URL their app serves are aligned.\n // Falls back to `/a2a` when the URL has no path (empty or `/`).\n const defaultBasePath = (() => {\n try {\n const parsed = new URL(card.url);\n const pathname = parsed.pathname.replace(/\\/+$/, '');\n return pathname.length > 0 ? pathname : '/a2a';\n } catch {\n // `agentCard.url` might be a relative path or otherwise unparseable\n // — fall back to the conventional A2A mount point.\n return '/a2a';\n }\n })();\n\n const mount = (app: ExpressAppLike, mountOptions: A2AMountOptions = {}): void => {\n const basePath = mountOptions.basePath ?? defaultBasePath;\n const wellKnownAtRoot = mountOptions.wellKnownAtRoot ?? true;\n // A2A SDK clients derive `${agentCard.url}/.well-known/agent-card.json`\n // for discovery, so mount there first.\n app.use(`${basePath}/.well-known/agent-card.json`, agentCardMiddleware);\n if (wellKnownAtRoot) {\n // Origin-root is where simple \"what agent lives at this host?\"\n // probes look. Serving both locations matches what deployments\n // already hand-roll; the helper just bakes it in.\n app.use('/.well-known/agent-card.json', agentCardMiddleware);\n }\n app.use(basePath, jsonRpc);\n };\n\n return {\n jsonRpcHandler: jsonRpc,\n agentCardHandler: agentCardMiddleware,\n async getAgentCard() {\n return card;\n },\n mount,\n };\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/lib/server/a2a-adapter.ts"],"sourcesContent":["/**\n * A2A transport adapter for `AdcpServer`.\n *\n * Peer of `serve()` / `createExpressAdapter()`: same `AdcpServer` handle,\n * different wire transport. MCP and A2A share the dispatcher, idempotency\n * store, state store, resolveAccount, and governance — everything the\n * framework pipeline owns is transport-agnostic.\n *\n * **Scope (v0)**: `message/send`, `tasks/get`, `tasks/cancel`, and\n * `GET /.well-known/agent-card.json`. Streaming (`message/stream`),\n * push notifications, and mid-flight `input-required` interrupts are\n * explicit \"not yet\" — see `docs/guides/BUILD-AN-AGENT.md`.\n *\n * **Handler-return → A2A `Task.state` mapping:**\n *\n * | Handler returned… | A2A `Task.state` | Artifact payload |\n * |-------------------------------------|-------------------|--------------------------------------------------|\n * | Success arm | `completed` | DataPart with the typed AdCP response |\n * | Submitted arm (`status:'submitted'`)| `completed` [^1] | DataPart with AdCP response + `metadata.adcp_task_id` |\n * | Error arm (`errors:[]`) | `failed` | DataPart with the AdCP Error arm payload |\n * | `adcpError()` envelope | `failed` | DataPart with `adcp_error` |\n *\n * [^1]: A2A `submitted` is the INITIAL lifecycle state (before\n * `working`); A2A `completed` marks the transport call as done.\n * When the AdCP handler returns a Submitted arm the HTTP call itself\n * has completed — the AdCP-level async work is queued and buyers\n * resume it via `adcp_task_id` on `artifact.metadata`. The DataPart's\n * `data` still carries `status: 'submitted'` from the AdCP response,\n * so a buyer reading the artifact payload sees the ad-tech state\n * directly; the A2A Task.state only mirrors whether the transport\n * call itself terminated.\n *\n * **Message shape.** A client addresses a tool by sending a `Message`\n * with a single `DataPart`: `{ kind: 'data', data: { skill, input } }`.\n * `skill` must match a registered AdCP tool name (e.g. `get_products`);\n * `input` becomes the tool arguments before AdCP schema validation\n * runs. The older client convention `{ skill, parameters }` (used by\n * `src/lib/protocols/a2a.ts`) is also accepted — `data.parameters` is\n * treated as an alias for `data.input`; new callers should prefer\n * `input` since A2A `DataPart.data` is free-form and `input` matches\n * the AdCP tool's typed request shape.\n *\n * **Two lifecycles, one response.** A2A's `Task.state` tracks the\n * TRANSPORT call (did the HTTP request complete?). AdCP's `status`\n * inside the artifact tracks the WORK (submitted / completed /\n * failed). Don't conflate them: a `completed` A2A task can carry a\n * `submitted` AdCP response, meaning the call returned successfully\n * but the ad-tech operation itself is still queued. Buyers resume the\n * AdCP work via the `adcp_task_id` on the artifact's `metadata`, not\n * by re-polling the A2A Task.\n *\n * @preview v0 surface — field semantics may shift while the ecosystem\n * converges on AdCP-over-A2A conventions. Pinning a minor version is\n * recommended.\n */\n\nimport {\n type AgentExecutor,\n type ExecutionEventBus,\n type RequestContext,\n type TaskStore,\n type User,\n type UnauthenticatedUser,\n DefaultRequestHandler,\n InMemoryTaskStore as SdkInMemoryTaskStore,\n DefaultExecutionEventBusManager,\n} from '@a2a-js/sdk/server';\nimport { jsonRpcHandler, agentCardHandler } from '@a2a-js/sdk/server/express';\nimport type {\n AgentCard,\n AgentCapabilities,\n AgentProvider,\n AgentSkill,\n Artifact,\n DataPart,\n Message,\n SecurityScheme,\n Task,\n TaskArtifactUpdateEvent,\n TaskStatusUpdateEvent,\n} from '@a2a-js/sdk';\nimport type { Request, RequestHandler } from 'express';\nimport { randomUUID } from 'node:crypto';\nimport { getSdkServer, listRegisteredToolNames, type AdcpAuthInfo, type AdcpServer } from './adcp-server';\nimport type { McpToolResponse } from './responses';\nimport type { AdcpLogger } from './create-adcp-server';\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/**\n * Agent-card identity fields the adapter can't derive automatically.\n * Auto-seeded fields (`capabilities`, `skills`, `defaultInputModes`,\n * `defaultOutputModes`, `protocolVersion`, `additionalInterfaces`) may\n * be overridden by passing them here; the merged card is validated\n * against A2A's required-field set at boot.\n */\nexport interface A2AAgentCardOverrides {\n /** Human-readable agent name (required). */\n name: string;\n /** Human-readable description (required). */\n description: string;\n /** Agent URL — the endpoint A2A clients connect to (required). */\n url: string;\n /** Agent version (required). */\n version: string;\n\n provider?: AgentProvider;\n documentationUrl?: string;\n iconUrl?: string;\n securitySchemes?: { [k: string]: SecurityScheme };\n security?: { [k: string]: string[] }[];\n preferredTransport?: string;\n\n /**\n * Override the auto-generated capabilities. The adapter sets\n * `streaming: false` and `pushNotifications: false` by default (v0\n * ships neither). Set `streaming: true` if you wire a downstream\n * extension; the adapter still won't emit `TaskStatusUpdateEvent`s\n * on the stream path in v0.\n */\n capabilities?: AgentCapabilities;\n\n /**\n * Override the auto-generated skills list. When omitted the adapter\n * derives one `AgentSkill` per registered AdCP tool from the server's\n * capability object. Supply this to add descriptions, examples, tags,\n * or per-skill input/output modes the SDK can't infer. Framework-private\n * tools such as `comply_test_controller` are still filtered from the public\n * A2A agent card.\n */\n skills?: AgentSkill[];\n\n defaultInputModes?: string[];\n defaultOutputModes?: string[];\n protocolVersion?: string;\n}\n\n/**\n * Options for {@link createA2AAdapter}.\n *\n * **Auth posture.** `authenticate(req)` runs BEFORE the tool handler\n * sees the request. Return an `AdcpAuthInfo` to let the pipeline\n * proceed with that principal; return `null` (or throw) to reject.\n * A rejection currently surfaces as a generic JSON-RPC `-32000`\n * server error — the `@a2a-js/sdk` doesn't yet expose a typed\n * authentication-failed code for the `UserBuilder` path. Production\n * deployments SHOULD wire upstream middleware (e.g. `express-jwt`) to\n * reject with a proper HTTP 401 / WWW-Authenticate challenge before\n * the request reaches `jsonRpcHandler`. The `authenticate` option\n * here is a last-line-of-defense guard, not the primary auth surface.\n *\n * **Agent-card `securitySchemes`.** The `agentCard.securitySchemes`\n * you provide is served verbatim at `/.well-known/agent-card.json` —\n * only put non-secret discovery data there (token endpoint, scopes,\n * OIDC issuer URL). Never paste client secrets, private JWKS, or\n * internal URLs into the card. The SDK doesn't schema-validate\n * `securitySchemes` at boot (v0 check is required-field presence\n * only), so a hand-crafted malformed entry will ship as-written.\n *\n * Omitting `authenticate` makes the adapter anonymous — handlers see\n * `ctx.authInfo === undefined`, matching `serve({ authenticate: undefined })`.\n */\nexport interface A2AAdapterOptions {\n /** AdCP server whose registered tools this adapter exposes over A2A. */\n server: AdcpServer;\n\n /**\n * Authenticate an inbound A2A request. Transport-level auth runs\n * before `AdcpServer.invoke()` so the framework pipeline sees a\n * verified `authInfo`. Return `null` (or throw) to reject.\n */\n authenticate?: (req: Request) => Promise<AdcpAuthInfo | null>;\n\n /** Seller-supplied agent-card identity fields. Required. */\n agentCard: A2AAgentCardOverrides;\n\n /**\n * A2A task store. Defaults to the SDK's `InMemoryTaskStore`.\n * Persistent deployments should supply a durable implementation\n * (e.g. a Postgres-backed `TaskStore`).\n */\n taskStore?: TaskStore;\n\n /** Optional logger. Falls back to `console`. */\n logger?: AdcpLogger;\n}\n\n/** Minimal Express app surface the adapter's `mount()` helper needs. */\nexport interface ExpressAppLike {\n use(path: string, ...handlers: RequestHandler[]): unknown;\n}\n\n/** Options for {@link A2AAdapter.mount}. */\nexport interface A2AMountOptions {\n /**\n * URL-path prefix for the JSON-RPC endpoint. Defaults to the pathname\n * of the agent card's `url` field (so `url: 'https://host/a2a'` mounts\n * JSON-RPC at `/a2a`). Override to mount under a different path.\n */\n basePath?: string;\n /**\n * When true (default), also mounts the agent card at the origin root\n * (`/.well-known/agent-card.json`) so simple discovery probes targeting\n * the host itself find the card. Some deployments disable this when an\n * upstream proxy owns origin-root routes — set `false` to mount only\n * at `{basePath}/.well-known/agent-card.json`.\n */\n wellKnownAtRoot?: boolean;\n}\n\n/**\n * Value returned by {@link createA2AAdapter}.\n *\n * For almost every seller, `adapter.mount(app)` is the right entry\n * point — it wires all four routes (JSON-RPC at the agent-card's\n * path, the agent card at both `{basePath}/.well-known/agent-card.json`\n * for A2A discovery and `/.well-known/agent-card.json` for origin-root\n * probes) with one call.\n *\n * The `jsonRpcHandler` and `agentCardHandler` fields stay exposed for\n * deployments that need finer control (mounting behind a custom auth\n * layer, serving the card from a CDN, testing).\n */\nexport interface A2AAdapter {\n /** The A2A JSON-RPC middleware (`message/send`, `tasks/get`, `tasks/cancel`). */\n jsonRpcHandler: RequestHandler;\n /** The agent-card discovery GET middleware. */\n agentCardHandler: RequestHandler;\n /** Returns the merged, validated agent card. */\n getAgentCard(): Promise<AgentCard>;\n /**\n * Wire all A2A routes onto an Express-compatible app in one call.\n * Eliminates the \"card mounted at only one location\" footgun: the\n * A2A SDK derives `${agentCard.url}/.well-known/agent-card.json` for\n * discovery, while many clients also probe origin root — this helper\n * satisfies both. See {@link A2AMountOptions} to override paths.\n */\n mount(app: ExpressAppLike, options?: A2AMountOptions): void;\n}\n\n// ---------------------------------------------------------------------------\n// Internals\n// ---------------------------------------------------------------------------\n\n/**\n * Our `User` carries the full AdCP auth payload, not just the two\n * getters A2A's minimal `User` requires. The executor reads this back\n * out of `RequestContext.context.user`.\n */\ninterface A2AAdcpUser extends User {\n readonly adcpAuthInfo?: AdcpAuthInfo;\n}\n\nfunction buildAuthenticatedUser(authInfo: AdcpAuthInfo): A2AAdcpUser {\n const clientId = authInfo.clientId;\n return {\n get isAuthenticated() {\n return true;\n },\n get userName() {\n return clientId;\n },\n adcpAuthInfo: authInfo,\n };\n}\n\nfunction buildAnonymousUser(): UnauthenticatedUser {\n return {\n get isAuthenticated() {\n return false as const;\n },\n get userName() {\n return 'anonymous';\n },\n };\n}\n\nfunction getAdcpAuthInfo(context: RequestContext['context']): AdcpAuthInfo | undefined {\n const user = context?.user as A2AAdcpUser | undefined;\n return user?.adcpAuthInfo;\n}\n\n/**\n * Extract the `{ skill, input }` pair from the inbound Message's parts.\n *\n * Convention: the client sends a single DataPart with\n * `{ skill: '<tool_name>', input: { ...args } }`. Reject anything else\n * — text-only payloads, files, multiple data parts — so buyers get a\n * deterministic error instead of silently-wrong routing.\n */\ninterface ExtractedInvocation {\n skill: string;\n input: Record<string, unknown>;\n}\n\nfunction extractInvocation(message: Message): ExtractedInvocation {\n if (!Array.isArray(message.parts) || message.parts.length === 0) {\n throw new A2AInvocationError('Message must carry at least one part with a `data` kind.');\n }\n const dataParts = message.parts.filter((p): p is DataPart => p?.kind === 'data');\n if (dataParts.length === 0) {\n throw new A2AInvocationError(\n \"Message must include a DataPart with { skill, input } — text-only messages aren't routable to AdCP tools.\"\n );\n }\n if (dataParts.length > 1) {\n throw new A2AInvocationError(\n 'Message must include exactly one DataPart — multi-part invocations are not supported in v0.'\n );\n }\n const firstDataPart = dataParts[0]!;\n const rawData = firstDataPart.data;\n // Guard before destructuring — a client sending `{ kind: 'data', data: null }`\n // or `data: \"string\"` would otherwise TypeError on payload.skill and surface\n // as a generic HANDLER_THREW instead of INVALID_INVOCATION.\n if (rawData == null || typeof rawData !== 'object' || Array.isArray(rawData)) {\n throw new A2AInvocationError('DataPart `data` must be an object containing { skill, input }.');\n }\n const payload = rawData as Record<string, unknown>;\n const skill = payload.skill;\n // Accept `parameters` as an alias for `input` — the in-tree A2A\n // client (`src/lib/protocols/a2a.ts`) shipped first and uses\n // `parameters`, so downstream agents already emit that key. Going\n // forward prefer `input`: A2A `DataPart.data` is free-form JSON and\n // `input` matches the AdCP tool's typed request shape. The alias\n // stays for ecosystem compatibility; no deprecation timer in v0.\n const input = payload.input ?? payload.parameters;\n if (typeof skill !== 'string' || skill.length === 0) {\n throw new A2AInvocationError('DataPart must include a non-empty string `skill` field naming the AdCP tool.');\n }\n if (input != null && (typeof input !== 'object' || Array.isArray(input))) {\n throw new A2AInvocationError('DataPart `input` (or legacy `parameters`) must be an object (or omitted).');\n }\n return { skill, input: (input as Record<string, unknown>) ?? {} };\n}\n\n/** Thrown when an incoming Message doesn't match the AdCP-over-A2A convention. */\nexport class A2AInvocationError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'A2AInvocationError';\n }\n}\n\n// ---------------------------------------------------------------------------\n// Executor — translates A2A execute() into adcpServer.invoke() + events\n// ---------------------------------------------------------------------------\n\n/**\n * Classify a framework-produced `McpToolResponse` so the executor knows\n * what A2A `Task.state` to publish.\n */\ntype ClassifiedResult =\n | { kind: 'success'; data: Record<string, unknown> }\n | { kind: 'submitted'; adcpTaskId: string; data: Record<string, unknown> }\n | { kind: 'error_arm'; data: Record<string, unknown> }\n | { kind: 'adcp_error'; data: Record<string, unknown> };\n\nfunction classifyResponse(res: McpToolResponse): ClassifiedResult {\n const structured = (res.structuredContent ?? {}) as Record<string, unknown>;\n if (res.isError === true) {\n if (structured.adcp_error && typeof structured.adcp_error === 'object') {\n return { kind: 'adcp_error', data: structured };\n }\n return { kind: 'error_arm', data: structured };\n }\n if (structured.status === 'submitted' && typeof structured.task_id === 'string') {\n return { kind: 'submitted', adcpTaskId: structured.task_id, data: structured };\n }\n return { kind: 'success', data: structured };\n}\n\nclass AdcpA2AAgentExecutor implements AgentExecutor {\n // Cooperative-cancel flag. `DefaultRequestHandler.cancelTask` only\n // calls our executor's `cancelTask` while an execute() is in-flight\n // (eventBus still open); post-completion cancels are handled by the\n // SDK directly against the task store. So this set only holds\n // taskIds that currently have a pending `cancelTask`, and execute()\n // always clears its entry at the end — no unbounded growth.\n private readonly canceled = new Set<string>();\n\n // A2A `Task.id` → original `contextId`. Populated when execute()\n // starts so `cancelTask` can emit a well-formed status-update event\n // against the same contextId instead of guessing an empty string.\n // Cleared alongside the canceled flag in execute()'s finally block.\n private readonly taskContextIds = new Map<string, string>();\n\n constructor(\n private readonly server: AdcpServer,\n private readonly logger: AdcpLogger\n ) {}\n\n async execute(requestContext: RequestContext, eventBus: ExecutionEventBus): Promise<void> {\n const { taskId, contextId, userMessage } = requestContext;\n const authInfo = getAdcpAuthInfo(requestContext.context);\n\n this.taskContextIds.set(taskId, contextId);\n try {\n // Register the task with the ResultManager by publishing a Task\n // event first — subsequent status-update / artifact-update events\n // only resolve if the manager has seen the task. `working` is the\n // initial state; we replace it with completed / submitted / failed\n // once the handler returns.\n this.publishInitialTask(eventBus, taskId, contextId, userMessage);\n\n let invocation: ExtractedInvocation;\n try {\n invocation = extractInvocation(userMessage);\n } catch (err) {\n const message = err instanceof Error ? err.message : 'Invalid A2A invocation';\n this.emitFailure(eventBus, taskId, contextId, {\n reason: 'INVALID_INVOCATION',\n message,\n });\n return;\n }\n\n let response: McpToolResponse | undefined;\n const toolNames = a2aSkillToServerToolNames(invocation.skill);\n let invokedToolName: string | undefined;\n try {\n for (const toolName of toolNames) {\n try {\n invokedToolName = toolName;\n response = await this.server.invoke({\n // Platform servers register `tasks_get`; older/custom A2A\n // fixtures may still register the slash name directly.\n toolName,\n args: invocation.input,\n ...(authInfo && { authInfo }),\n });\n break;\n } catch (err) {\n if (toolName === 'tasks_get' && invocation.skill === 'tasks/get' && isToolNotRegistered(err, toolName)) {\n continue;\n }\n throw err;\n }\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n this.logger.error('A2A adapter: handler invocation threw', {\n toolName: invokedToolName ?? invocation.skill,\n requestedToolName: invocation.skill,\n error: message,\n });\n this.emitFailure(eventBus, taskId, contextId, {\n reason: 'HANDLER_THREW',\n message,\n });\n return;\n }\n if (response === undefined) {\n this.emitFailure(eventBus, taskId, contextId, {\n reason: 'HANDLER_THREW',\n message: `No handler registered for ${invocation.skill}`,\n });\n return;\n }\n\n if (this.canceled.has(taskId)) {\n this.publishStatus(eventBus, taskId, contextId, 'canceled', true);\n return;\n }\n\n const classified = classifyResponse(response);\n this.publishArtifact(eventBus, taskId, contextId, classified);\n // A2A Task.state maps the TRANSPORT call lifecycle, not the AdCP\n // work. A `submitted` AdCP arm means the HTTP call itself\n // completed — the ad-tech work is queued, resumed via\n // `adcp_task_id` on the artifact metadata. Emitting A2A\n // `state: 'submitted'` with `final: true` would be a\n // non-conformant transition per A2A 0.3.0 (`submitted` is the\n // INITIAL state before `working`, never terminal). Buyers read\n // the AdCP-level status from the artifact's `data.status` field.\n this.publishStatus(\n eventBus,\n taskId,\n contextId,\n classified.kind === 'success' || classified.kind === 'submitted' ? 'completed' : 'failed',\n true\n );\n } finally {\n // Clean up per-task state regardless of path — the executor is\n // long-lived (one per adapter instance), so any leak compounds.\n this.canceled.delete(taskId);\n this.taskContextIds.delete(taskId);\n eventBus.finished();\n }\n }\n\n async cancelTask(taskId: string, eventBus: ExecutionEventBus): Promise<void> {\n this.canceled.add(taskId);\n // `DefaultRequestHandler.cancelTask` only reaches us when execute()\n // is still in-flight (eventBus still open). Publish the canceled\n // status so the SDK's secondary `_processEvents` loop terminates;\n // execute() will ALSO see the flag and short-circuit before\n // publishing a success/failure status. The A2A event bus is\n // idempotent on duplicate status publishes — whichever lands first\n // wins the taskStore write.\n const contextId = this.taskContextIds.get(taskId) ?? '';\n this.publishStatus(eventBus, taskId, contextId, 'canceled', true);\n // Do NOT call eventBus.finished() here — execute()'s finally block\n // owns the finished() signal, and calling it twice risks closing\n // the queue mid-event-flush on the primary processEvents loop.\n }\n\n private publishInitialTask(\n eventBus: ExecutionEventBus,\n taskId: string,\n contextId: string,\n userMessage: Message\n ): void {\n const task: Task = {\n kind: 'task',\n id: taskId,\n contextId,\n status: {\n state: 'working',\n timestamp: new Date().toISOString(),\n },\n history: [userMessage],\n };\n eventBus.publish(task);\n }\n\n private publishStatus(\n eventBus: ExecutionEventBus,\n taskId: string,\n contextId: string,\n state: TaskStatusUpdateEvent['status']['state'],\n final: boolean\n ): void {\n const event: TaskStatusUpdateEvent = {\n kind: 'status-update',\n taskId,\n contextId,\n status: {\n state,\n timestamp: new Date().toISOString(),\n },\n final,\n };\n eventBus.publish(event);\n }\n\n private publishArtifact(\n eventBus: ExecutionEventBus,\n taskId: string,\n contextId: string,\n classified: ClassifiedResult\n ): void {\n const artifactName =\n classified.kind === 'success' ? 'result' : classified.kind === 'submitted' ? 'submitted' : 'error';\n // DataPart `data` is the AdCP tool's typed response — no\n // transport-level fields injected here so the payload still\n // validates against the tool's AdCP response schema. AdCP\n // transport metadata (`adcp_task_id` pointing at the async handle)\n // rides on `artifact.metadata` per A2A 0.3.0's extension\n // convention.\n const artifact: Artifact = {\n artifactId: randomUUID(),\n name: artifactName,\n parts: [{ kind: 'data', data: classified.data }],\n ...(classified.kind === 'submitted' && {\n metadata: { adcp_task_id: classified.adcpTaskId },\n }),\n };\n const event: TaskArtifactUpdateEvent = {\n kind: 'artifact-update',\n taskId,\n contextId,\n artifact,\n append: false,\n lastChunk: true,\n };\n eventBus.publish(event);\n }\n\n private emitFailure(\n eventBus: ExecutionEventBus,\n taskId: string,\n contextId: string,\n payload: { reason: string; message: string }\n ): void {\n const artifact: Artifact = {\n artifactId: randomUUID(),\n name: 'error',\n parts: [{ kind: 'data', data: payload }],\n };\n eventBus.publish({\n kind: 'artifact-update',\n taskId,\n contextId,\n artifact,\n append: false,\n lastChunk: true,\n } satisfies TaskArtifactUpdateEvent);\n this.publishStatus(eventBus, taskId, contextId, 'failed', true);\n }\n}\n\nfunction isToolNotRegistered(err: unknown, toolName: string): boolean {\n return err instanceof Error && err.message.includes(`tool \"${toolName}\" is not registered`);\n}\n\nfunction a2aSkillToServerToolNames(skill: string): string[] {\n return skill === 'tasks/get' ? ['tasks_get', 'tasks/get'] : [skill];\n}\n\n// ---------------------------------------------------------------------------\n// Agent card\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_MODES = ['application/json'] as const;\nconst DEFAULT_PROTOCOL_VERSION = '0.3.0';\nconst PUBLIC_AGENT_CARD_EXCLUDED_SKILLS = new Set(['get_adcp_capabilities', 'comply_test_controller']);\n\n/**\n * Derive one `AgentSkill` per registered AdCP tool. Skills without\n * seller-supplied descriptions get a generic one pointing at the\n * AdCP tool name — enough to pass A2A registry validation; sellers\n * are expected to enrich via `agentCard.skills` in production.\n */\nfunction deriveSkills(toolNames: string[]): AgentSkill[] {\n const publicNames = new Set(toolNames.map(toA2ASkillName));\n return [...publicNames].map(name => ({\n id: name,\n name,\n description: `AdCP tool: ${name}. Send { skill: \"${name}\", input: { ... } } as a DataPart.`,\n tags: ['adcp'],\n }));\n}\n\nfunction toA2ASkillName(toolName: string): string {\n return toolName === 'tasks_get' ? 'tasks/get' : toolName;\n}\n\nfunction listRegisteredTools(server: AdcpServer): string[] {\n const sdk = getSdkServer(server);\n if (!sdk) return [];\n return listRegisteredToolNames(sdk).filter(name => !PUBLIC_AGENT_CARD_EXCLUDED_SKILLS.has(name));\n}\n\nfunction filterPublicAgentCardSkills(skills: AgentSkill[]): AgentSkill[] {\n return skills.filter(skill => {\n const id = typeof skill.id === 'string' ? skill.id : undefined;\n const name = typeof skill.name === 'string' ? skill.name : undefined;\n return !(\n (id != null && PUBLIC_AGENT_CARD_EXCLUDED_SKILLS.has(id)) ||\n (name != null && PUBLIC_AGENT_CARD_EXCLUDED_SKILLS.has(name))\n );\n });\n}\n\nfunction buildAgentCard(server: AdcpServer, overrides: A2AAgentCardOverrides): AgentCard {\n const tools = listRegisteredTools(server);\n const skills = filterPublicAgentCardSkills(overrides.skills ?? deriveSkills(tools));\n\n const card: AgentCard = {\n name: overrides.name,\n description: overrides.description,\n url: overrides.url,\n version: overrides.version,\n protocolVersion: overrides.protocolVersion ?? DEFAULT_PROTOCOL_VERSION,\n defaultInputModes: overrides.defaultInputModes ?? [...DEFAULT_MODES],\n defaultOutputModes: overrides.defaultOutputModes ?? [...DEFAULT_MODES],\n capabilities: overrides.capabilities ?? {\n streaming: false,\n pushNotifications: false,\n },\n skills,\n ...(overrides.provider && { provider: overrides.provider }),\n ...(overrides.documentationUrl && { documentationUrl: overrides.documentationUrl }),\n ...(overrides.iconUrl && { iconUrl: overrides.iconUrl }),\n ...(overrides.securitySchemes && { securitySchemes: overrides.securitySchemes }),\n ...(overrides.security && { security: overrides.security }),\n ...(overrides.preferredTransport && { preferredTransport: overrides.preferredTransport }),\n };\n\n validateAgentCard(card);\n return card;\n}\n\n/**\n * Fail loud at adapter construction when the merged card misses\n * A2A-required fields. The SDK would reject the discovery response\n * at runtime anyway — better to catch it at boot so the agent never\n * binds a port with an unserviceable card.\n */\nfunction validateAgentCard(card: AgentCard): void {\n const missing: string[] = [];\n if (!card.name) missing.push('name');\n if (!card.description) missing.push('description');\n if (!card.url) missing.push('url');\n if (!card.version) missing.push('version');\n if (!card.protocolVersion) missing.push('protocolVersion');\n if (!card.capabilities) missing.push('capabilities');\n if (!Array.isArray(card.defaultInputModes) || card.defaultInputModes.length === 0) {\n missing.push('defaultInputModes');\n }\n if (!Array.isArray(card.defaultOutputModes) || card.defaultOutputModes.length === 0) {\n missing.push('defaultOutputModes');\n }\n if (!Array.isArray(card.skills)) missing.push('skills');\n if (missing.length > 0) {\n throw new Error(\n `createA2AAdapter: agent card is missing required fields — ${missing.join(', ')}. ` +\n `Supply them via options.agentCard so A2A discovery doesn't fail at runtime.`\n );\n }\n if (Array.isArray(card.skills) && card.skills.length === 0) {\n throw new Error(\n 'createA2AAdapter: agent card has no skills — register AdCP handlers on the server (or supply options.agentCard.skills) before creating the adapter.'\n );\n }\n}\n\n// ---------------------------------------------------------------------------\n// Public entry point\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_LOGGER: AdcpLogger = {\n debug: (m, d) => console.debug(m, d ?? ''),\n info: (m, d) => console.info(m, d ?? ''),\n warn: (m, d) => console.warn(m, d ?? ''),\n error: (m, d) => console.error(m, d ?? ''),\n};\n\n/**\n * Create an A2A transport adapter around an `AdcpServer`.\n *\n * @example\n * ```ts\n * const adcp = createAdcpServer({ mediaBuy: { getProducts: async () => ({ products: [] }) } });\n * const a2a = createA2AAdapter({\n * server: adcp,\n * agentCard: {\n * name: 'Acme SSP',\n * description: 'Guaranteed + non-guaranteed display inventory',\n * url: 'https://ssp.acme.com/a2a',\n * version: '1.0.0',\n * provider: { organization: 'Acme', url: 'https://acme.com' },\n * securitySchemes: { bearer: { type: 'http', scheme: 'bearer' } },\n * },\n * async authenticate(req) {\n * const token = extractBearer(req);\n * return token ? { token, clientId: 'buyer_123', scopes: [] } : null;\n * },\n * });\n *\n * app.use('/a2a', a2a.jsonRpcHandler);\n * app.use('/.well-known/agent-card.json', a2a.agentCardHandler);\n * ```\n *\n * @preview — see the module docstring.\n */\nexport function createA2AAdapter(options: A2AAdapterOptions): A2AAdapter {\n const logger = options.logger ?? DEFAULT_LOGGER;\n const card = buildAgentCard(options.server, options.agentCard);\n const taskStore = options.taskStore ?? new SdkInMemoryTaskStore();\n const executor = new AdcpA2AAgentExecutor(options.server, logger);\n const eventBusManager = new DefaultExecutionEventBusManager();\n const requestHandler = new DefaultRequestHandler(card, taskStore, executor, eventBusManager);\n\n const userBuilder = async (req: Request): Promise<User> => {\n if (!options.authenticate) return buildAnonymousUser();\n const authInfo = await options.authenticate(req);\n if (authInfo == null) {\n // Throwing an A2AError with an authentication code would give the\n // SDK's JSON-RPC envelope the right shape, but the SDK keeps\n // `A2AError` internal — surfacing as a thrown Error yields a\n // generic -32000 server error, which is still closer to the\n // right signal than silently continuing anonymously. Most\n // deployments should reject before the UserBuilder via upstream\n // middleware (e.g. `express-jwt`); auth via the UserBuilder is\n // the fallback path.\n throw new Error('A2A authentication failed');\n }\n return buildAuthenticatedUser(authInfo);\n };\n\n const jsonRpc = jsonRpcHandler({ requestHandler, userBuilder });\n const agentCardMiddleware = agentCardHandler({ agentCardProvider: requestHandler });\n\n // Derive the default basePath from the agent-card URL's pathname so\n // `mount(app)` \"just works\" for the common case where the URL the\n // seller advertised and the URL their app serves are aligned.\n // Falls back to `/a2a` when the URL has no path (empty or `/`).\n const defaultBasePath = (() => {\n try {\n const parsed = new URL(card.url);\n const pathname = parsed.pathname.replace(/\\/+$/, '');\n return pathname.length > 0 ? pathname : '/a2a';\n } catch {\n // `agentCard.url` might be a relative path or otherwise unparseable\n // — fall back to the conventional A2A mount point.\n return '/a2a';\n }\n })();\n\n const mount = (app: ExpressAppLike, mountOptions: A2AMountOptions = {}): void => {\n const basePath = mountOptions.basePath ?? defaultBasePath;\n const wellKnownAtRoot = mountOptions.wellKnownAtRoot ?? true;\n // A2A SDK clients derive `${agentCard.url}/.well-known/agent-card.json`\n // for discovery, so mount there first.\n app.use(`${basePath}/.well-known/agent-card.json`, agentCardMiddleware);\n if (wellKnownAtRoot) {\n // Origin-root is where simple \"what agent lives at this host?\"\n // probes look. Serving both locations matches what deployments\n // already hand-roll; the helper just bakes it in.\n app.use('/.well-known/agent-card.json', agentCardMiddleware);\n }\n app.use(basePath, jsonRpc);\n };\n\n return {\n jsonRpcHandler: jsonRpc,\n agentCardHandler: agentCardMiddleware,\n async getAgentCard() {\n return card;\n },\n mount,\n };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
import { fileURLToPath as __adcpFileURLToPath } from 'node:url';
|
|
2
|
-
import { dirname as __adcpDirname } from 'node:path';
|
|
3
|
-
import { createRequire as __adcpCreateRequire } from 'node:module';
|
|
4
|
-
const __filename = __adcpFileURLToPath(import.meta.url);
|
|
5
|
-
const __dirname = __adcpDirname(__filename);
|
|
6
|
-
const require = __adcpCreateRequire(import.meta.url);
|
|
7
1
|
import { AdcpError } from "./decisioning/async-outcome.mjs";
|
|
8
2
|
function getAccountMode(account) {
|
|
9
3
|
if (account == null || typeof account !== "object") return "live";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/lib/server/account-mode.ts"],"sourcesContent":["/**\n * Account-mode primitives for sandbox-authority enforcement of\n * `comply_test_controller` and other test-only surfaces.\n *\n * The hard rule: under no circumstances should the comply test\n * controller (or any test-only surface) operate on a `live`-mode\n * account. The flag must live on the resolved account, not on a\n * process-level env var — env vars are operator-error-prone proxies\n * for what is fundamentally an authority decision per principal.\n *\n * This module ships the type + helpers; auto-wiring into\n * `createAdcpServerFromPlatform`'s `complyTest` path lands in a\n * follow-up alongside mock-mode routing (#1435 Phase 2).\n *\n * @public\n */\n\nimport { AdcpError } from './decisioning/async-outcome';\n\n/**\n * Three operationally distinct account modes:\n *\n * - `live`: production traffic. Adopter's upstream is truth. Test-only\n * surfaces (comply controller, force_*, simulate_*) are denied.\n * - `sandbox`: adopter's own test account. Their code path runs against\n * their test infrastructure. Test-only surfaces are allowed.\n * - `mock`: SDK-routed-to-mock-server. Adopter's code is bypassed; the\n * SDK forwards to the mock upstream backend. Test-only surfaces are\n * allowed.\n *\n * Default when unspecified: `live`. A missing or unknown `mode` reads\n * as production, fail-closed for any test-only dispatch.\n *\n * See `docs/proposals/lifecycle-state-and-sandbox-authority.md` for the\n * full three-mode design and Phase 1/2/3 rollout.\n */\nexport type AccountMode = 'live' | 'sandbox' | 'mock';\n\n/**\n * Reads `mode` off any account-shaped value, with back-compat for\n * the legacy `sandbox: boolean` field. Returns the explicit mode if\n * present; otherwise infers `'sandbox'` from `sandbox === true`;\n * otherwise `'live'`.\n *\n * Adopters that have not yet migrated to the `mode` field continue to\n * work — `account.sandbox === true` reads as sandbox mode through this\n * helper. New code should prefer `mode` directly.\n *\n * Prototype-pollution defense: both `mode` and `sandbox` are read via\n * `Object.hasOwn` rather than bare property access. Bare access traverses\n * the prototype chain, so an attacker who reaches a `__proto__`-via-merge\n * sink upstream (reachable in MCP envelope handling and similar deep-merge\n * sites) could stamp `Object.prototype.mode = 'sandbox'` and silently\n * downgrade every account's gate to a no-op. The own-property check makes\n * the gate immune to that class of attack regardless of upstream hardening.\n */\nexport function getAccountMode(account: unknown): AccountMode {\n if (account == null || typeof account !== 'object') return 'live';\n if (Object.hasOwn(account, 'mode')) {\n const mode = (account as { mode?: unknown }).mode;\n if (mode === 'live' || mode === 'sandbox' || mode === 'mock') return mode;\n }\n // Back-compat: legacy `sandbox: true` flag reads as `sandbox` mode.\n if (Object.hasOwn(account, 'sandbox') && (account as { sandbox?: unknown }).sandbox === true) {\n return 'sandbox';\n }\n return 'live';\n}\n\n/**\n * Predicate: is the account in a non-production mode that admits\n * test-only surfaces (comply controller, force_*, simulate_*)?\n *\n * Returns `true` for `mode === 'sandbox' | 'mock'` (or legacy\n * `sandbox: true`); `false` for `mode === 'live'` or any account\n * shape that doesn't carry the field.\n */\nexport function isSandboxOrMockAccount(account: unknown): boolean {\n const mode = getAccountMode(account);\n return mode === 'sandbox' || mode === 'mock';\n}\n\n/**\n * Throws an `AdcpError('PERMISSION_DENIED')` if the account is not in\n * a non-production mode. Use to gate dispatch of test-only surfaces.\n *\n * Fail-closed semantics:\n * - `account === undefined` (no resolved account): throws.\n * - `account.mode === 'live'` or unspecified + no `sandbox: true`:\n * throws.\n * - `account.mode === 'sandbox' | 'mock'` (or legacy `sandbox: true`):\n * no-op, dispatch proceeds.\n *\n * The `details` payload carries `{ scope: 'sandbox-gate', tool? }` so\n * dashboards can distinguish gate-rejections from other permission\n * denials.\n *\n * **Resolver discipline.** The strength of this gate depends entirely on\n * how the adopter's `AccountStore.resolve` constructs its return value.\n * Resolvers MUST NOT spread untrusted input (request body, headers,\n * `ctx_metadata`, query params) into the resolved account — doing so lets\n * a buyer self-promote to `mode: 'sandbox'` and unlock test-only surfaces\n * on a live principal. Source `mode` (and `sandbox`) from a trusted store\n * keyed by the authenticated principal; never from request data.\n *\n * **opts.message must be a static string literal.** The message is echoed\n * on the wire inside the error envelope. Interpolating user-controlled\n * values into it creates a reflection sink (PII leakage, log injection,\n * downstream HTML rendering). Pick from a fixed set of messages keyed by\n * `tool` if you need variants.\n *\n * @param account The resolved account (typically `ctx.account` inside\n * a tool dispatch). Pass `undefined` if no account resolved — the\n * helper fails closed.\n * @param opts.tool Optional tool name to surface in the error details\n * (e.g., `'comply_test_controller'`).\n * @param opts.message Optional override for the user-facing message.\n * MUST be a static string literal — see \"opts.message\" note above.\n *\n * @example\n * import { assertSandboxAccount } from '@adcp/sdk/server';\n *\n * sandboxGate: input => {\n * const account = await resolveAccount(input);\n * assertSandboxAccount(account, { tool: 'comply_test_controller' });\n * return true;\n * }\n */\nexport function assertSandboxAccount(account: unknown, opts: { tool?: string; message?: string } = {}): void {\n if (isSandboxOrMockAccount(account)) return;\n throw new AdcpError('PERMISSION_DENIED', {\n message: opts.message ?? 'Test-only surface requires a sandbox or mock account; resolved account is in live mode.',\n details: {\n scope: 'sandbox-gate',\n reason: 'sandbox-or-mock-required',\n ...(opts.tool && { tool: opts.tool }),\n },\n });\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../../src/lib/server/account-mode.ts"],"sourcesContent":["/**\n * Account-mode primitives for sandbox-authority enforcement of\n * `comply_test_controller` and other test-only surfaces.\n *\n * The hard rule: under no circumstances should the comply test\n * controller (or any test-only surface) operate on a `live`-mode\n * account. The flag must live on the resolved account, not on a\n * process-level env var — env vars are operator-error-prone proxies\n * for what is fundamentally an authority decision per principal.\n *\n * This module ships the type + helpers; auto-wiring into\n * `createAdcpServerFromPlatform`'s `complyTest` path lands in a\n * follow-up alongside mock-mode routing (#1435 Phase 2).\n *\n * @public\n */\n\nimport { AdcpError } from './decisioning/async-outcome';\n\n/**\n * Three operationally distinct account modes:\n *\n * - `live`: production traffic. Adopter's upstream is truth. Test-only\n * surfaces (comply controller, force_*, simulate_*) are denied.\n * - `sandbox`: adopter's own test account. Their code path runs against\n * their test infrastructure. Test-only surfaces are allowed.\n * - `mock`: SDK-routed-to-mock-server. Adopter's code is bypassed; the\n * SDK forwards to the mock upstream backend. Test-only surfaces are\n * allowed.\n *\n * Default when unspecified: `live`. A missing or unknown `mode` reads\n * as production, fail-closed for any test-only dispatch.\n *\n * See `docs/proposals/lifecycle-state-and-sandbox-authority.md` for the\n * full three-mode design and Phase 1/2/3 rollout.\n */\nexport type AccountMode = 'live' | 'sandbox' | 'mock';\n\n/**\n * Reads `mode` off any account-shaped value, with back-compat for\n * the legacy `sandbox: boolean` field. Returns the explicit mode if\n * present; otherwise infers `'sandbox'` from `sandbox === true`;\n * otherwise `'live'`.\n *\n * Adopters that have not yet migrated to the `mode` field continue to\n * work — `account.sandbox === true` reads as sandbox mode through this\n * helper. New code should prefer `mode` directly.\n *\n * Prototype-pollution defense: both `mode` and `sandbox` are read via\n * `Object.hasOwn` rather than bare property access. Bare access traverses\n * the prototype chain, so an attacker who reaches a `__proto__`-via-merge\n * sink upstream (reachable in MCP envelope handling and similar deep-merge\n * sites) could stamp `Object.prototype.mode = 'sandbox'` and silently\n * downgrade every account's gate to a no-op. The own-property check makes\n * the gate immune to that class of attack regardless of upstream hardening.\n */\nexport function getAccountMode(account: unknown): AccountMode {\n if (account == null || typeof account !== 'object') return 'live';\n if (Object.hasOwn(account, 'mode')) {\n const mode = (account as { mode?: unknown }).mode;\n if (mode === 'live' || mode === 'sandbox' || mode === 'mock') return mode;\n }\n // Back-compat: legacy `sandbox: true` flag reads as `sandbox` mode.\n if (Object.hasOwn(account, 'sandbox') && (account as { sandbox?: unknown }).sandbox === true) {\n return 'sandbox';\n }\n return 'live';\n}\n\n/**\n * Predicate: is the account in a non-production mode that admits\n * test-only surfaces (comply controller, force_*, simulate_*)?\n *\n * Returns `true` for `mode === 'sandbox' | 'mock'` (or legacy\n * `sandbox: true`); `false` for `mode === 'live'` or any account\n * shape that doesn't carry the field.\n */\nexport function isSandboxOrMockAccount(account: unknown): boolean {\n const mode = getAccountMode(account);\n return mode === 'sandbox' || mode === 'mock';\n}\n\n/**\n * Throws an `AdcpError('PERMISSION_DENIED')` if the account is not in\n * a non-production mode. Use to gate dispatch of test-only surfaces.\n *\n * Fail-closed semantics:\n * - `account === undefined` (no resolved account): throws.\n * - `account.mode === 'live'` or unspecified + no `sandbox: true`:\n * throws.\n * - `account.mode === 'sandbox' | 'mock'` (or legacy `sandbox: true`):\n * no-op, dispatch proceeds.\n *\n * The `details` payload carries `{ scope: 'sandbox-gate', tool? }` so\n * dashboards can distinguish gate-rejections from other permission\n * denials.\n *\n * **Resolver discipline.** The strength of this gate depends entirely on\n * how the adopter's `AccountStore.resolve` constructs its return value.\n * Resolvers MUST NOT spread untrusted input (request body, headers,\n * `ctx_metadata`, query params) into the resolved account — doing so lets\n * a buyer self-promote to `mode: 'sandbox'` and unlock test-only surfaces\n * on a live principal. Source `mode` (and `sandbox`) from a trusted store\n * keyed by the authenticated principal; never from request data.\n *\n * **opts.message must be a static string literal.** The message is echoed\n * on the wire inside the error envelope. Interpolating user-controlled\n * values into it creates a reflection sink (PII leakage, log injection,\n * downstream HTML rendering). Pick from a fixed set of messages keyed by\n * `tool` if you need variants.\n *\n * @param account The resolved account (typically `ctx.account` inside\n * a tool dispatch). Pass `undefined` if no account resolved — the\n * helper fails closed.\n * @param opts.tool Optional tool name to surface in the error details\n * (e.g., `'comply_test_controller'`).\n * @param opts.message Optional override for the user-facing message.\n * MUST be a static string literal — see \"opts.message\" note above.\n *\n * @example\n * import { assertSandboxAccount } from '@adcp/sdk/server';\n *\n * sandboxGate: input => {\n * const account = await resolveAccount(input);\n * assertSandboxAccount(account, { tool: 'comply_test_controller' });\n * return true;\n * }\n */\nexport function assertSandboxAccount(account: unknown, opts: { tool?: string; message?: string } = {}): void {\n if (isSandboxOrMockAccount(account)) return;\n throw new AdcpError('PERMISSION_DENIED', {\n message: opts.message ?? 'Test-only surface requires a sandbox or mock account; resolved account is in live mode.',\n details: {\n scope: 'sandbox-gate',\n reason: 'sandbox-or-mock-required',\n ...(opts.tool && { tool: opts.tool }),\n },\n });\n}\n"],"mappings":"AAiBA,SAAS,iBAAiB;AAuCnB,SAAS,eAAe,SAA+B;AAC5D,MAAI,WAAW,QAAQ,OAAO,YAAY,SAAU,QAAO;AAC3D,MAAI,OAAO,OAAO,SAAS,MAAM,GAAG;AAClC,UAAM,OAAQ,QAA+B;AAC7C,QAAI,SAAS,UAAU,SAAS,aAAa,SAAS,OAAQ,QAAO;AAAA,EACvE;AAEA,MAAI,OAAO,OAAO,SAAS,SAAS,KAAM,QAAkC,YAAY,MAAM;AAC5F,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAUO,SAAS,uBAAuB,SAA2B;AAChE,QAAM,OAAO,eAAe,OAAO;AACnC,SAAO,SAAS,aAAa,SAAS;AACxC;AAgDO,SAAS,qBAAqB,SAAkB,OAA4C,CAAC,GAAS;AAC3G,MAAI,uBAAuB,OAAO,EAAG;AACrC,QAAM,IAAI,UAAU,qBAAqB;AAAA,IACvC,SAAS,KAAK,WAAW;AAAA,IACzB,SAAS;AAAA,MACP,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,GAAI,KAAK,QAAQ,EAAE,MAAM,KAAK,KAAK;AAAA,IACrC;AAAA,EACF,CAAC;AACH;","names":[]}
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
import { fileURLToPath as __adcpFileURLToPath } from 'node:url';
|
|
2
|
-
import { dirname as __adcpDirname } from 'node:path';
|
|
3
|
-
import { createRequire as __adcpCreateRequire } from 'node:module';
|
|
4
|
-
const __filename = __adcpFileURLToPath(import.meta.url);
|
|
5
|
-
const __dirname = __adcpDirname(__filename);
|
|
6
|
-
const require = __adcpCreateRequire(import.meta.url);
|
|
7
1
|
import { ADCP_VERSION } from "../version.mjs";
|
|
8
2
|
const ADCP_SERVER_BRAND = /* @__PURE__ */ Symbol("ADCP_SERVER_BRAND");
|
|
9
3
|
const ADCP_SDK_SERVER = /* @__PURE__ */ Symbol.for("@adcp/client.sdkServer");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/lib/server/adcp-server.ts"],"sourcesContent":["/**\n * Opaque AdCP server handle.\n *\n * `createAdcpServer()` returns a value typed as `AdcpServer` rather than the\n * underlying `@modelcontextprotocol/sdk` `McpServer`. Two reasons:\n *\n * 1. **Dual-package hazard.** Re-exporting the SDK's `McpServer` type from\n * our public API forces downstream consumers through a specific SDK\n * resolution path (ESM or CJS). When a TypeScript ESM consumer\n * imports `@adcp/sdk` (CJS) and separately imports the SDK (ESM),\n * the two `McpServer` types are structurally identical but distinct\n * — the SDK's private `_serverInfo` field breaks assignment\n * compatibility between them. Owning the type on our side\n * eliminates the hazard: `AdcpServer` resolves the same way for\n * every consumer because it comes from a single package.\n *\n * 2. **Test ergonomics.** Downstream test harnesses have been reaching\n * into `(server as any)._requestHandlers` and\n * `server.server._requestHandlers` to dispatch tool calls in-process.\n * `dispatchTestRequest()` moves that private-field access into a\n * single encapsulated helper so consumers don't have to repeat it.\n *\n * To register additional tools or resources, configure them through\n * `createAdcpServer()`'s domain-grouped handler config. The opaque\n * surface intentionally doesn't expose `registerTool()` — keeping it off the\n * public API lets the framework own tool registration conventions\n * (idempotency, governance, validation, response shape).\n */\n\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';\nimport type { McpToolResponse } from './responses';\nimport { ADCP_VERSION } from '../version';\n\n/**\n * Structural shape of an MCP transport the server can connect to.\n *\n * Matches `@modelcontextprotocol/sdk/shared/transport.js` Transport without\n * importing it — keeping the SDK type off our public API surface.\n * `StreamableHTTPServerTransport`, `StdioServerTransport`, and\n * `InMemoryTransport` from the SDK all satisfy this shape.\n *\n * `message` / callback parameters are typed with `any` (not `unknown`) so\n * SDK-specific transports with narrower message types (JSONRPCMessage) are\n * assignable bi-directionally without forcing downstream consumers to\n * widen their own transport types.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport interface AdcpServerTransport {\n start(): Promise<void>;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n send(message: any, options?: any): Promise<void>;\n close(): Promise<void>;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n onmessage?: (message: any, extra?: any) => void;\n onclose?: () => void;\n onerror?: (error: Error) => void;\n sessionId?: string;\n setProtocolVersion?: (version: string) => void;\n}\n\n/**\n * Request envelope accepted by `AdcpServer.dispatchTestRequest()`.\n *\n * Mirrors the JSON-RPC method/params shape so tests can target the same\n * methods the transport would — `tools/call`, `tools/list`, etc.\n */\nexport interface AdcpTestRequest {\n method: string;\n params?: Record<string, unknown>;\n}\n\n/**\n * Typed variant of {@link AdcpTestRequest} for `tools/call`. Used by the\n * `dispatchTestRequest` overload that returns a typed `CallToolResult`.\n */\nexport interface AdcpTestToolsCallRequest {\n method: 'tools/call';\n params: { name: string; arguments?: Record<string, unknown> };\n}\n\n/**\n * Response returned by `AdcpServer.dispatchTestRequest()`.\n *\n * The shape depends on the `method` — for `tools/call` this is the\n * `CallToolResult` (content + structuredContent + optional isError);\n * for other methods it's the method-specific result. Left as `unknown`\n * so callers narrow with type guards or schema validation relevant to\n * the specific method they're dispatching.\n */\nexport type AdcpTestResponse = unknown;\n\n/**\n * Auth principal visible to handlers and `resolveAccount`. Mirrors the\n * shape `serve()` produces from its `authenticate` hook, and what the\n * A2A adapter's `authenticate` callback should return.\n */\nexport interface AdcpAuthInfo {\n token: string;\n clientId: string;\n scopes: string[];\n expiresAt?: number;\n extra?: Record<string, unknown>;\n}\n\n/**\n * Optional per-call overrides for `dispatchTestRequest()`. Lets tests\n * simulate transport-level state — most importantly the `authInfo` that\n * `serve()` populates from its `authenticate` hook — without spinning up\n * a real HTTP transport.\n */\nexport interface AdcpTestRequestExtras {\n authInfo?: AdcpAuthInfo;\n sessionId?: string;\n}\n\n/**\n * Arguments accepted by `AdcpServer.invoke()`. A transport adapter (MCP\n * `serve()`, `createA2AAdapter()`, etc.) authenticates the incoming\n * request, maps it to one of the registered AdCP tool names, then calls\n * this surface to run the framework pipeline (idempotency, account\n * resolution, validation, governance, response-union narrowing).\n */\nexport interface AdcpInvokeOptions {\n /** AdCP tool name (`get_products`, `create_media_buy`, ...). */\n toolName: string;\n /** Tool arguments as received from the transport, pre-schema-validation. */\n args: Record<string, unknown>;\n /**\n * Auth principal the transport produced from its `authenticate` hook.\n * Handlers and `resolveAccount` see it as `ctx.authInfo`. Transports\n * MUST verify the principal before calling `invoke()` — `invoke()`\n * does NOT re-check the token.\n */\n authInfo?: AdcpAuthInfo;\n /** Abort signal for cancellation; defaults to a fresh controller. */\n signal?: AbortSignal;\n}\n\n/**\n * Test-harness hooks attached to every `AdcpServer`. Namespaced under\n * `compliance` so production code paths don't accidentally reach for\n * them — `reset()` drops cached state and is intended for storyboard\n * runners that exercise many scenarios in one process.\n */\nexport interface AdcpServerComplianceApi {\n /**\n * Drop session state and the idempotency cache so subsequent\n * storyboards don't inherit plans, media buys, or cached replies from\n * earlier runs. Conformance storyboards share brand domains across\n * test kits — without this hook, a $10K governance plan seeded by\n * `media_buy_seller/governance_denied` would intercept a $50K buy in\n * `sales_guaranteed`.\n *\n * Refuses to run by default unless BOTH guardrails hold:\n * - `NODE_ENV !== 'production'` (opt out with `allowProduction: true`)\n * - The configured stores are the in-memory SDK defaults\n * (`InMemoryStateStore` + memory idempotency backend). Opt out\n * with `force: true` when you've wired a disposable test\n * Postgres cluster and know the flush is safe.\n *\n * The two flags are independent on purpose: `force` lets you flush\n * a non-memory store you've verified is a test DB, WITHOUT also\n * opening the door to running against a production `NODE_ENV`.\n * Same the other way: `allowProduction` lets you run in a prod-\n * labeled CI environment against memory stores, without bypassing\n * the store-shape safety check.\n */\n reset(options?: { force?: boolean; allowProduction?: boolean }): Promise<void>;\n}\n\n/**\n * Phantom brand for {@link AdcpServer}. Makes the type nominal — a plain\n * object that has the same methods structurally cannot be assigned to\n * `AdcpServer` because it lacks this symbol-keyed property. Closes the\n * door on `(plainObject as AdcpServer).registerTool(...)` patterns that\n * tried to reach for an MCP-SDK method the framework intentionally\n * doesn't expose.\n *\n * The brand is `never`-typed and never set on the runtime object, so\n * `Object.getOwnPropertySymbols(server)` won't expose it — only the\n * wrapper produced by `createAdcpServer()` carries the type-level\n * signature, via the `as AdcpServerInternal` cast in `wrapMcpServer()`.\n *\n * Must be a real (non-`declare`) const so the d.ts emit retains it. An\n * `export declare const X: unique symbol` in a `.ts` source file does\n * not survive emit — the binding gets dropped from the published d.ts\n * while consumers (the public `AdcpServer` interface) keep referencing\n * it, which produces `TS2304: Cannot find name 'ADCP_SERVER_BRAND'` on\n * any adopter `tsc --noEmit` against `@adcp/sdk/server` (issue #1236).\n */\nexport const ADCP_SERVER_BRAND: unique symbol = Symbol('ADCP_SERVER_BRAND');\n\n/**\n * Opaque handle returned by `createAdcpServer()`.\n *\n * Pass to `serve()` to mount on an HTTP transport, or use\n * `dispatchTestRequest()` from a test harness to exercise handlers\n * in-process without opening a socket.\n */\nexport interface AdcpServer {\n /**\n * Phantom brand — type-level only, never present at runtime. See\n * {@link ADCP_SERVER_BRAND}. Tagged `never` so `(x as AdcpServer)`\n * casts from structurally-similar objects fail; a real `AdcpServer`\n * is only obtainable via `createAdcpServer()`.\n */\n readonly [ADCP_SERVER_BRAND]?: never;\n\n /**\n * Connect the server to an MCP transport.\n *\n * Takes ownership of the transport — any callbacks previously\n * registered on it are replaced. Matches the semantics of the SDK's\n * `McpServer.connect()`.\n */\n connect(transport: AdcpServerTransport): Promise<void>;\n\n /** Close the server and release resources. */\n close(): Promise<void>;\n\n /**\n * Test-harness hooks — see {@link AdcpServerComplianceApi}. Not\n * intended for production call sites; production code paths should\n * use `close()` to release resources.\n */\n readonly compliance: AdcpServerComplianceApi;\n\n /**\n * Invoke a registered handler in-process and return its response.\n *\n * Bypasses the transport layer — no sockets, no JSON-RPC framing.\n * Intended for test harnesses only; production callers should go\n * through a transport so protocol invariants (request ID linking,\n * task progress, notifications) are enforced.\n *\n * Throws when no handler is registered for the requested method,\n * or when a `tools/call` request targets an unknown tool. Arguments\n * default to `{}` when `params.arguments` is omitted — a validation\n * error from the tool's input schema (not from dispatch) is the\n * expected failure mode for a missing required field.\n *\n * **Never mount this behind an HTTP route.** `extras.authInfo` is\n * written directly onto the MCP handler's `extra` without any check\n * against `serve({ authenticate })` — exposing it externally lets a\n * caller impersonate any principal. This API is for in-process test\n * harnesses and storyboard runners only.\n *\n * @example\n * ```typescript\n * const result = await server.dispatchTestRequest({\n * method: 'tools/call',\n * params: {\n * name: 'get_products',\n * arguments: { brief: 'premium sports inventory' },\n * },\n * });\n * ```\n */\n dispatchTestRequest(request: AdcpTestToolsCallRequest, extras?: AdcpTestRequestExtras): Promise<CallToolResult>;\n dispatchTestRequest(request: AdcpTestRequest, extras?: AdcpTestRequestExtras): Promise<AdcpTestResponse>;\n\n /**\n * Production-safe tool invocation surface for transport adapters.\n *\n * Runs the full framework pipeline against a registered tool:\n * request schema validation → account resolution → idempotency →\n * handler dispatch → response narrowing → response validation.\n * Returns the same `McpToolResponse` envelope the MCP transport\n * observes — a transport adapter layers its own protocol framing on\n * top (JSON-RPC result for MCP, A2A `Task` artifact for A2A, etc.).\n *\n * **Auth is the caller's responsibility.** `invoke()` forwards the\n * provided `authInfo` verbatim to handlers and `resolveAccount`\n * without re-verifying. Mount it only behind a transport that has\n * already authenticated the principal (e.g. `serve({ authenticate })`\n * for MCP, `createA2AAdapter({ authenticate })` for A2A). Do not call\n * it directly from an HTTP handler — that path skips auth.\n *\n * For in-process tests that want to synthesize an `authInfo` without\n * running a transport, reach for {@link dispatchTestRequest} instead\n * — it takes the same principal shape but is explicitly marked\n * test-only in its docstring.\n *\n * Throws when `toolName` is not registered; schema errors round-trip\n * as structured `VALIDATION_ERROR` envelopes inside the return value.\n */\n invoke(options: AdcpInvokeOptions): Promise<McpToolResponse>;\n\n /**\n * Returns the AdCP protocol version this server is configured to speak.\n *\n * Defaults to {@link ADCP_VERSION} (the GA version the SDK ships against)\n * unless overridden via `createAdcpServer({ adcpVersion })`. This is the\n * protocol version, **not** the publisher's app version (`config.version`).\n *\n * Plumbing surface — Stage 2 of the multi-version refactor exposes the\n * configured value but does not yet vary validator/schema selection by\n * version. Wire-shape adapters key off this getter in subsequent stages.\n */\n getAdcpVersion(): string;\n}\n\n/**\n * Symbol-keyed accessor for the underlying SDK `McpServer`. Used by\n * `serve()` to reach through the opaque wrapper when SDK-specific\n * operations are needed. Not part of the public API — typed on a\n * separate internal interface so downstream consumers can't discover\n * it through the `AdcpServer` type.\n *\n * @internal\n */\nexport const ADCP_SDK_SERVER: unique symbol = Symbol.for('@adcp/client.sdkServer');\n\n/**\n * Symbol-keyed accessor for the state store backing an `AdcpServer`.\n * Test harnesses and helpers like\n * `@adcp/sdk/compliance-fixtures`'s `seedComplianceFixtures` need\n * to reach the store without widening the public `AdcpServer`\n * interface (handlers already get `ctx.store`; production code paths\n * don't need a second accessor).\n *\n * @internal\n */\nexport const ADCP_STATE_STORE: unique symbol = Symbol.for('@adcp/client.stateStore');\n\n/**\n * Symbol-keyed accessor for the capabilities object `createAdcpServer`\n * assembles and the auto-wired `get_adcp_capabilities` handler reads from.\n * Mutating this object mutates what subsequent capability-discovery calls\n * return — `registerTestController` uses this to add a\n * `compliance_testing` capability block after registration so the server\n * advertises its controller scenarios even though `registerTestController`\n * runs after `createAdcpServer`.\n *\n * @internal\n */\nexport const ADCP_CAPABILITIES: unique symbol = Symbol.for('@adcp/client.capabilities');\n\n/** Per-request tool visibility policy consumed by transport adapters. @internal */\nexport const ADCP_TOOL_VISIBILITY: unique symbol = Symbol.for('@adcp/client.toolVisibility');\n\n/** @internal */\nexport type AdcpToolVisibilityResolver = (options: {\n toolName: string;\n authInfo?: AdcpAuthInfo;\n input?: Readonly<Record<string, unknown>>;\n}) => boolean | Promise<boolean>;\n\n/** @internal */\nexport interface AdcpServerInternal extends AdcpServer {\n readonly [ADCP_SDK_SERVER]: McpServer;\n [ADCP_TOOL_VISIBILITY]?: AdcpToolVisibilityResolver;\n}\n\n/**\n * Return the underlying `McpServer` for a wrapped `AdcpServer`, or\n * `undefined` if the value is not an `AdcpServer` produced by\n * `createAdcpServer()`.\n *\n * @internal\n */\nexport function getSdkServer(server: AdcpServer | McpServer): McpServer | undefined {\n const candidate = (server as unknown as Partial<AdcpServerInternal>)[ADCP_SDK_SERVER];\n return candidate;\n}\n\n/** Attach a transport-independent per-request tool visibility policy. @internal */\nexport function setToolVisibilityResolver(server: AdcpServer, resolver: AdcpToolVisibilityResolver): void {\n (server as AdcpServerInternal)[ADCP_TOOL_VISIBILITY] = resolver;\n}\n\n/** Resolve whether a registered tool may be exposed to this request. @internal */\nexport async function isRegisteredToolVisible(\n server: AdcpServer,\n options: Parameters<AdcpToolVisibilityResolver>[0]\n): Promise<boolean> {\n const resolver = (server as AdcpServerInternal)[ADCP_TOOL_VISIBILITY];\n return resolver ? resolver(options) : true;\n}\n\n/**\n * True when `value` is an `AdcpServer` produced by `createAdcpServer()`.\n *\n * @internal\n */\nexport function isAdcpServer(value: unknown): value is AdcpServerInternal {\n return (\n value != null &&\n typeof value === 'object' &&\n ADCP_SDK_SERVER in (value as Record<PropertyKey, unknown>) &&\n typeof (value as AdcpServer).dispatchTestRequest === 'function'\n );\n}\n\n// ---------------------------------------------------------------------------\n// Private-field access — centralized\n// ---------------------------------------------------------------------------\n//\n// The MCP SDK marks `_requestHandlers` and `_registeredTools` as private.\n// Dispatching a request in-process from a test needs them. Doing it once\n// here means downstream harnesses don't each repeat the (server as any)\n// dance, which is the whole point of exposing `dispatchTestRequest()`.\n//\n// If/when the SDK ships a public in-process dispatch API, swap these two\n// helpers for that API. The AdcpServer surface stays the same.\n\ninterface RegisteredTool {\n title?: string;\n description?: string;\n inputSchema?: unknown;\n outputSchema?: unknown;\n annotations?: unknown;\n _meta?: Record<string, unknown>;\n enabled?: boolean;\n handler: (args: unknown, extra: unknown) => unknown | Promise<unknown>;\n}\n\ninterface McpServerPrivates {\n _registeredTools?: Record<string, RegisteredTool | undefined>;\n server?: {\n _requestHandlers?: Map<\n string,\n (request: { method: string; params?: unknown }, extra: unknown) => unknown | Promise<unknown>\n >;\n _serverInfo?: { name: string; version: string; [key: string]: unknown };\n _instructions?: string;\n };\n}\n\n/** Tool metadata needed to mirror the registered AdCP surface into another official MCP transport. @internal */\nexport interface RegisteredToolDefinition {\n name: string;\n title?: string;\n description?: string;\n inputSchema?: unknown;\n outputSchema?: unknown;\n annotations?: unknown;\n _meta?: Record<string, unknown>;\n}\n\n/** @internal */\ntype McpRequestHandler = (request: { method: string; params?: unknown }, extra: unknown) => unknown | Promise<unknown>;\n\n/**\n * Write the instructions string onto the low-level SDK server backing\n * `mcpServer`. Called by the async-function-form instructions hook to\n * inject the resolved string just before the MCP `initialize` response.\n *\n * @internal\n * @remarks Pokes an SDK private field — see the \"Private-field access\" note above.\n */\nexport function setSdkServerInstructions(mcpServer: McpServer, value: string | undefined): void {\n const priv = mcpServer as unknown as McpServerPrivates;\n if (priv.server) {\n priv.server._instructions = value;\n }\n}\n\n/**\n * Wrap the `initialize` request handler on `mcpServer` so that `wrapper`\n * runs (and can await async work) before the original handler responds.\n * The wrapper receives the original handler as its first argument and must\n * call it to complete the handshake.\n *\n * The wrapper is invoked once per MCP `initialize` call on this server\n * instance. Under `serve({ reuseAgent: false })` the server is created\n * fresh per request, so the wrapper fires once per session. Under\n * `reuseAgent: true` the `ADCP_INSTRUCTIONS_FN` check in `serve.ts`\n * blocks the combination before the wrapper can be called a second time.\n *\n * @internal\n * @remarks Pokes an SDK private field — see the \"Private-field access\" note above.\n */\nexport function wrapInitializeHandler(\n mcpServer: McpServer,\n wrapper: (\n origHandler: McpRequestHandler,\n req: { method: string; params?: unknown },\n extra: unknown\n ) => unknown | Promise<unknown>\n): void {\n const priv = mcpServer as unknown as McpServerPrivates;\n const handlers = priv.server?._requestHandlers;\n if (!handlers) return;\n const orig = handlers.get('initialize');\n if (!orig) return;\n handlers.set('initialize', (req, extra) => wrapper(orig, req, extra));\n}\n\nfunction getRegisteredTool(server: McpServer, name: string): RegisteredTool | undefined {\n return (server as unknown as McpServerPrivates)._registeredTools?.[name];\n}\n\n/**\n * Wrap a registered MCP tool handler without changing the tool definition\n * advertised by the SDK server.\n *\n * @internal\n * @remarks Pokes an SDK private field — see the \"Private-field access\" note above.\n */\nexport function wrapRegisteredToolHandler(\n server: McpServer,\n name: string,\n wrapper: (orig: RegisteredTool['handler'], args: unknown, extra: unknown) => unknown | Promise<unknown>\n): boolean {\n const tool = getRegisteredTool(server, name);\n if (!tool) return false;\n const orig = tool.handler;\n tool.handler = (args, extra) => wrapper(orig, args, extra);\n return true;\n}\n\n/**\n * Enumerate the tool names registered on the underlying SDK server.\n * Used by transport adapters that need to derive discovery metadata\n * (agent cards, capability listings) from the registered surface\n * without reaching into private SDK fields at every call site.\n *\n * @internal\n */\nexport function listRegisteredToolNames(server: McpServer): string[] {\n const registered = (server as unknown as McpServerPrivates)._registeredTools ?? {};\n return Object.keys(registered);\n}\n\n/**\n * Return enabled registered-tool metadata for the modern MCP server adapter.\n * Handler functions stay private; the adapter dispatches through\n * {@link AdcpServer.invoke} so the framework pipeline remains authoritative.\n *\n * @internal\n */\nexport function listRegisteredToolDefinitions(server: McpServer): RegisteredToolDefinition[] {\n const registered = (server as unknown as McpServerPrivates)._registeredTools ?? {};\n return Object.entries(registered).flatMap(([name, tool]) => {\n if (!tool || tool.enabled === false) return [];\n return [\n {\n name,\n ...(tool.title !== undefined && { title: tool.title }),\n ...(tool.description !== undefined && { description: tool.description }),\n ...(tool.inputSchema !== undefined && { inputSchema: tool.inputSchema }),\n ...(tool.outputSchema !== undefined && { outputSchema: tool.outputSchema }),\n ...(tool.annotations !== undefined && { annotations: tool.annotations }),\n ...(tool._meta !== undefined && { _meta: tool._meta }),\n },\n ];\n });\n}\n\n/** Return the SDK server identity advertised during MCP discovery. @internal */\nexport function getSdkServerInfo(server: McpServer): { name: string; version: string; [key: string]: unknown } {\n return (\n (server as unknown as McpServerPrivates).server?._serverInfo ?? {\n name: 'AdCP Server',\n version: 'unknown',\n }\n );\n}\n\n/** Return static or already-resolved server instructions. @internal */\nexport function getSdkServerInstructions(server: McpServer): string | undefined {\n return (server as unknown as McpServerPrivates).server?._instructions;\n}\n\nfunction getRequestHandler(\n server: McpServer,\n method: string\n): ((request: { method: string; params?: unknown }, extra: unknown) => unknown | Promise<unknown>) | undefined {\n return (server as unknown as McpServerPrivates).server?._requestHandlers?.get(method);\n}\n\n/**\n * Wrap an SDK request handler such as `tools/list` while preserving the\n * original handler's request/extra contract.\n *\n * @internal\n * @remarks Pokes an SDK private field — see the \"Private-field access\" note above.\n */\nexport function wrapSdkRequestHandler(\n server: McpServer,\n method: string,\n wrapper: (\n orig: McpRequestHandler,\n req: { method: string; params?: unknown },\n extra: unknown\n ) => unknown | Promise<unknown>\n): boolean {\n const handlers = (server as unknown as McpServerPrivates).server?._requestHandlers;\n const orig = handlers?.get(method);\n if (!handlers || !orig) return false;\n handlers.set(method, (req, extra) => wrapper(orig, req, extra));\n return true;\n}\n\n/**\n * Wrap an SDK `McpServer` into the opaque `AdcpServer` handle returned by\n * `createAdcpServer()`. Consumers should prefer `createAdcpServer()` and\n * treat this as an internal utility; it's exported for framework code\n * that needs to produce an `AdcpServer` around a hand-built server.\n *\n * Idempotent — passing an already-wrapped `AdcpServer` returns it\n * unchanged rather than nesting wrappers (which would leave\n * `ADCP_SDK_SERVER` pointing at the outer wrapper instead of the SDK\n * handle and break `getSdkServer()` round-trips).\n *\n * @internal\n */\nexport function wrapMcpServer(\n inner: McpServer | AdcpServerInternal,\n compliance?: AdcpServerComplianceApi,\n adcpVersion: string = ADCP_VERSION\n): AdcpServerInternal {\n if (isAdcpServer(inner)) return inner;\n const mcp = inner as McpServer;\n const resolvedCompliance: AdcpServerComplianceApi = compliance ?? {\n async reset() {\n throw new Error(\n 'AdcpServer.compliance.reset: no-op handle returned by `wrapMcpServer` without a compliance implementation. ' +\n 'Use `createAdcpServer()` to get a server whose `compliance.reset()` flushes the state and idempotency stores.'\n );\n },\n };\n const dispatch = async (request: AdcpTestRequest, extras?: AdcpTestRequestExtras): Promise<AdcpTestResponse> => {\n const extra: { signal: AbortSignal; authInfo?: AdcpTestRequestExtras['authInfo']; sessionId?: string } = {\n signal: new AbortController().signal,\n };\n if (extras?.authInfo) extra.authInfo = extras.authInfo;\n if (extras?.sessionId) extra.sessionId = extras.sessionId;\n\n if (request.method === 'tools/call') {\n const params = (request.params ?? {}) as { name?: string; arguments?: Record<string, unknown> };\n if (typeof params.name !== 'string') {\n throw new Error(\"dispatchTestRequest: 'tools/call' requires params.name (string)\");\n }\n const tool = getRegisteredTool(mcp, params.name);\n if (!tool) {\n throw new Error(`dispatchTestRequest: tool \"${params.name}\" is not registered`);\n }\n return tool.handler(params.arguments ?? {}, extra);\n }\n\n const handler = getRequestHandler(mcp, request.method);\n if (!handler) {\n throw new Error(`dispatchTestRequest: no handler registered for method \"${request.method}\"`);\n }\n return handler({ method: request.method, params: request.params ?? {} }, extra);\n };\n const invoke = async (options: AdcpInvokeOptions): Promise<McpToolResponse> => {\n const tool = getRegisteredTool(mcp, options.toolName);\n if (!tool) {\n throw new Error(`AdcpServer.invoke: tool \"${options.toolName}\" is not registered`);\n }\n const extra: { signal: AbortSignal; authInfo?: AdcpAuthInfo } = {\n signal: options.signal ?? new AbortController().signal,\n };\n if (options.authInfo) extra.authInfo = options.authInfo;\n return (await tool.handler(options.args, extra)) as McpToolResponse;\n };\n const wrapper: AdcpServerInternal = {\n [ADCP_SDK_SERVER]: mcp,\n connect(transport) {\n // The SDK's Transport interface is structurally compatible with\n // AdcpServerTransport — the cast is boundary-crossing, not lossy.\n return mcp.connect(transport as unknown as Parameters<McpServer['connect']>[0]);\n },\n close() {\n return mcp.close();\n },\n compliance: resolvedCompliance,\n // Satisfies both overloads (typed tools/call + generic fallback) — the\n // runtime dispatcher is a single function that narrows by method.\n dispatchTestRequest: dispatch as AdcpServerInternal['dispatchTestRequest'],\n invoke,\n getAdcpVersion: () => adcpVersion,\n };\n return wrapper;\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/lib/server/adcp-server.ts"],"sourcesContent":["/**\n * Opaque AdCP server handle.\n *\n * `createAdcpServer()` returns a value typed as `AdcpServer` rather than the\n * underlying `@modelcontextprotocol/sdk` `McpServer`. Two reasons:\n *\n * 1. **Dual-package hazard.** Re-exporting the SDK's `McpServer` type from\n * our public API forces downstream consumers through a specific SDK\n * resolution path (ESM or CJS). When a TypeScript ESM consumer\n * imports `@adcp/sdk` (CJS) and separately imports the SDK (ESM),\n * the two `McpServer` types are structurally identical but distinct\n * — the SDK's private `_serverInfo` field breaks assignment\n * compatibility between them. Owning the type on our side\n * eliminates the hazard: `AdcpServer` resolves the same way for\n * every consumer because it comes from a single package.\n *\n * 2. **Test ergonomics.** Downstream test harnesses have been reaching\n * into `(server as any)._requestHandlers` and\n * `server.server._requestHandlers` to dispatch tool calls in-process.\n * `dispatchTestRequest()` moves that private-field access into a\n * single encapsulated helper so consumers don't have to repeat it.\n *\n * To register additional tools or resources, configure them through\n * `createAdcpServer()`'s domain-grouped handler config. The opaque\n * surface intentionally doesn't expose `registerTool()` — keeping it off the\n * public API lets the framework own tool registration conventions\n * (idempotency, governance, validation, response shape).\n */\n\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';\nimport type { McpToolResponse } from './responses';\nimport { ADCP_VERSION } from '../version';\n\n/**\n * Structural shape of an MCP transport the server can connect to.\n *\n * Matches `@modelcontextprotocol/sdk/shared/transport.js` Transport without\n * importing it — keeping the SDK type off our public API surface.\n * `StreamableHTTPServerTransport`, `StdioServerTransport`, and\n * `InMemoryTransport` from the SDK all satisfy this shape.\n *\n * `message` / callback parameters are typed with `any` (not `unknown`) so\n * SDK-specific transports with narrower message types (JSONRPCMessage) are\n * assignable bi-directionally without forcing downstream consumers to\n * widen their own transport types.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport interface AdcpServerTransport {\n start(): Promise<void>;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n send(message: any, options?: any): Promise<void>;\n close(): Promise<void>;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n onmessage?: (message: any, extra?: any) => void;\n onclose?: () => void;\n onerror?: (error: Error) => void;\n sessionId?: string;\n setProtocolVersion?: (version: string) => void;\n}\n\n/**\n * Request envelope accepted by `AdcpServer.dispatchTestRequest()`.\n *\n * Mirrors the JSON-RPC method/params shape so tests can target the same\n * methods the transport would — `tools/call`, `tools/list`, etc.\n */\nexport interface AdcpTestRequest {\n method: string;\n params?: Record<string, unknown>;\n}\n\n/**\n * Typed variant of {@link AdcpTestRequest} for `tools/call`. Used by the\n * `dispatchTestRequest` overload that returns a typed `CallToolResult`.\n */\nexport interface AdcpTestToolsCallRequest {\n method: 'tools/call';\n params: { name: string; arguments?: Record<string, unknown> };\n}\n\n/**\n * Response returned by `AdcpServer.dispatchTestRequest()`.\n *\n * The shape depends on the `method` — for `tools/call` this is the\n * `CallToolResult` (content + structuredContent + optional isError);\n * for other methods it's the method-specific result. Left as `unknown`\n * so callers narrow with type guards or schema validation relevant to\n * the specific method they're dispatching.\n */\nexport type AdcpTestResponse = unknown;\n\n/**\n * Auth principal visible to handlers and `resolveAccount`. Mirrors the\n * shape `serve()` produces from its `authenticate` hook, and what the\n * A2A adapter's `authenticate` callback should return.\n */\nexport interface AdcpAuthInfo {\n token: string;\n clientId: string;\n scopes: string[];\n expiresAt?: number;\n extra?: Record<string, unknown>;\n}\n\n/**\n * Optional per-call overrides for `dispatchTestRequest()`. Lets tests\n * simulate transport-level state — most importantly the `authInfo` that\n * `serve()` populates from its `authenticate` hook — without spinning up\n * a real HTTP transport.\n */\nexport interface AdcpTestRequestExtras {\n authInfo?: AdcpAuthInfo;\n sessionId?: string;\n}\n\n/**\n * Arguments accepted by `AdcpServer.invoke()`. A transport adapter (MCP\n * `serve()`, `createA2AAdapter()`, etc.) authenticates the incoming\n * request, maps it to one of the registered AdCP tool names, then calls\n * this surface to run the framework pipeline (idempotency, account\n * resolution, validation, governance, response-union narrowing).\n */\nexport interface AdcpInvokeOptions {\n /** AdCP tool name (`get_products`, `create_media_buy`, ...). */\n toolName: string;\n /** Tool arguments as received from the transport, pre-schema-validation. */\n args: Record<string, unknown>;\n /**\n * Auth principal the transport produced from its `authenticate` hook.\n * Handlers and `resolveAccount` see it as `ctx.authInfo`. Transports\n * MUST verify the principal before calling `invoke()` — `invoke()`\n * does NOT re-check the token.\n */\n authInfo?: AdcpAuthInfo;\n /** Abort signal for cancellation; defaults to a fresh controller. */\n signal?: AbortSignal;\n}\n\n/**\n * Test-harness hooks attached to every `AdcpServer`. Namespaced under\n * `compliance` so production code paths don't accidentally reach for\n * them — `reset()` drops cached state and is intended for storyboard\n * runners that exercise many scenarios in one process.\n */\nexport interface AdcpServerComplianceApi {\n /**\n * Drop session state and the idempotency cache so subsequent\n * storyboards don't inherit plans, media buys, or cached replies from\n * earlier runs. Conformance storyboards share brand domains across\n * test kits — without this hook, a $10K governance plan seeded by\n * `media_buy_seller/governance_denied` would intercept a $50K buy in\n * `sales_guaranteed`.\n *\n * Refuses to run by default unless BOTH guardrails hold:\n * - `NODE_ENV !== 'production'` (opt out with `allowProduction: true`)\n * - The configured stores are the in-memory SDK defaults\n * (`InMemoryStateStore` + memory idempotency backend). Opt out\n * with `force: true` when you've wired a disposable test\n * Postgres cluster and know the flush is safe.\n *\n * The two flags are independent on purpose: `force` lets you flush\n * a non-memory store you've verified is a test DB, WITHOUT also\n * opening the door to running against a production `NODE_ENV`.\n * Same the other way: `allowProduction` lets you run in a prod-\n * labeled CI environment against memory stores, without bypassing\n * the store-shape safety check.\n */\n reset(options?: { force?: boolean; allowProduction?: boolean }): Promise<void>;\n}\n\n/**\n * Phantom brand for {@link AdcpServer}. Makes the type nominal — a plain\n * object that has the same methods structurally cannot be assigned to\n * `AdcpServer` because it lacks this symbol-keyed property. Closes the\n * door on `(plainObject as AdcpServer).registerTool(...)` patterns that\n * tried to reach for an MCP-SDK method the framework intentionally\n * doesn't expose.\n *\n * The brand is `never`-typed and never set on the runtime object, so\n * `Object.getOwnPropertySymbols(server)` won't expose it — only the\n * wrapper produced by `createAdcpServer()` carries the type-level\n * signature, via the `as AdcpServerInternal` cast in `wrapMcpServer()`.\n *\n * Must be a real (non-`declare`) const so the d.ts emit retains it. An\n * `export declare const X: unique symbol` in a `.ts` source file does\n * not survive emit — the binding gets dropped from the published d.ts\n * while consumers (the public `AdcpServer` interface) keep referencing\n * it, which produces `TS2304: Cannot find name 'ADCP_SERVER_BRAND'` on\n * any adopter `tsc --noEmit` against `@adcp/sdk/server` (issue #1236).\n */\nexport const ADCP_SERVER_BRAND: unique symbol = Symbol('ADCP_SERVER_BRAND');\n\n/**\n * Opaque handle returned by `createAdcpServer()`.\n *\n * Pass to `serve()` to mount on an HTTP transport, or use\n * `dispatchTestRequest()` from a test harness to exercise handlers\n * in-process without opening a socket.\n */\nexport interface AdcpServer {\n /**\n * Phantom brand — type-level only, never present at runtime. See\n * {@link ADCP_SERVER_BRAND}. Tagged `never` so `(x as AdcpServer)`\n * casts from structurally-similar objects fail; a real `AdcpServer`\n * is only obtainable via `createAdcpServer()`.\n */\n readonly [ADCP_SERVER_BRAND]?: never;\n\n /**\n * Connect the server to an MCP transport.\n *\n * Takes ownership of the transport — any callbacks previously\n * registered on it are replaced. Matches the semantics of the SDK's\n * `McpServer.connect()`.\n */\n connect(transport: AdcpServerTransport): Promise<void>;\n\n /** Close the server and release resources. */\n close(): Promise<void>;\n\n /**\n * Test-harness hooks — see {@link AdcpServerComplianceApi}. Not\n * intended for production call sites; production code paths should\n * use `close()` to release resources.\n */\n readonly compliance: AdcpServerComplianceApi;\n\n /**\n * Invoke a registered handler in-process and return its response.\n *\n * Bypasses the transport layer — no sockets, no JSON-RPC framing.\n * Intended for test harnesses only; production callers should go\n * through a transport so protocol invariants (request ID linking,\n * task progress, notifications) are enforced.\n *\n * Throws when no handler is registered for the requested method,\n * or when a `tools/call` request targets an unknown tool. Arguments\n * default to `{}` when `params.arguments` is omitted — a validation\n * error from the tool's input schema (not from dispatch) is the\n * expected failure mode for a missing required field.\n *\n * **Never mount this behind an HTTP route.** `extras.authInfo` is\n * written directly onto the MCP handler's `extra` without any check\n * against `serve({ authenticate })` — exposing it externally lets a\n * caller impersonate any principal. This API is for in-process test\n * harnesses and storyboard runners only.\n *\n * @example\n * ```typescript\n * const result = await server.dispatchTestRequest({\n * method: 'tools/call',\n * params: {\n * name: 'get_products',\n * arguments: { brief: 'premium sports inventory' },\n * },\n * });\n * ```\n */\n dispatchTestRequest(request: AdcpTestToolsCallRequest, extras?: AdcpTestRequestExtras): Promise<CallToolResult>;\n dispatchTestRequest(request: AdcpTestRequest, extras?: AdcpTestRequestExtras): Promise<AdcpTestResponse>;\n\n /**\n * Production-safe tool invocation surface for transport adapters.\n *\n * Runs the full framework pipeline against a registered tool:\n * request schema validation → account resolution → idempotency →\n * handler dispatch → response narrowing → response validation.\n * Returns the same `McpToolResponse` envelope the MCP transport\n * observes — a transport adapter layers its own protocol framing on\n * top (JSON-RPC result for MCP, A2A `Task` artifact for A2A, etc.).\n *\n * **Auth is the caller's responsibility.** `invoke()` forwards the\n * provided `authInfo` verbatim to handlers and `resolveAccount`\n * without re-verifying. Mount it only behind a transport that has\n * already authenticated the principal (e.g. `serve({ authenticate })`\n * for MCP, `createA2AAdapter({ authenticate })` for A2A). Do not call\n * it directly from an HTTP handler — that path skips auth.\n *\n * For in-process tests that want to synthesize an `authInfo` without\n * running a transport, reach for {@link dispatchTestRequest} instead\n * — it takes the same principal shape but is explicitly marked\n * test-only in its docstring.\n *\n * Throws when `toolName` is not registered; schema errors round-trip\n * as structured `VALIDATION_ERROR` envelopes inside the return value.\n */\n invoke(options: AdcpInvokeOptions): Promise<McpToolResponse>;\n\n /**\n * Returns the AdCP protocol version this server is configured to speak.\n *\n * Defaults to {@link ADCP_VERSION} (the GA version the SDK ships against)\n * unless overridden via `createAdcpServer({ adcpVersion })`. This is the\n * protocol version, **not** the publisher's app version (`config.version`).\n *\n * Plumbing surface — Stage 2 of the multi-version refactor exposes the\n * configured value but does not yet vary validator/schema selection by\n * version. Wire-shape adapters key off this getter in subsequent stages.\n */\n getAdcpVersion(): string;\n}\n\n/**\n * Symbol-keyed accessor for the underlying SDK `McpServer`. Used by\n * `serve()` to reach through the opaque wrapper when SDK-specific\n * operations are needed. Not part of the public API — typed on a\n * separate internal interface so downstream consumers can't discover\n * it through the `AdcpServer` type.\n *\n * @internal\n */\nexport const ADCP_SDK_SERVER: unique symbol = Symbol.for('@adcp/client.sdkServer');\n\n/**\n * Symbol-keyed accessor for the state store backing an `AdcpServer`.\n * Test harnesses and helpers like\n * `@adcp/sdk/compliance-fixtures`'s `seedComplianceFixtures` need\n * to reach the store without widening the public `AdcpServer`\n * interface (handlers already get `ctx.store`; production code paths\n * don't need a second accessor).\n *\n * @internal\n */\nexport const ADCP_STATE_STORE: unique symbol = Symbol.for('@adcp/client.stateStore');\n\n/**\n * Symbol-keyed accessor for the capabilities object `createAdcpServer`\n * assembles and the auto-wired `get_adcp_capabilities` handler reads from.\n * Mutating this object mutates what subsequent capability-discovery calls\n * return — `registerTestController` uses this to add a\n * `compliance_testing` capability block after registration so the server\n * advertises its controller scenarios even though `registerTestController`\n * runs after `createAdcpServer`.\n *\n * @internal\n */\nexport const ADCP_CAPABILITIES: unique symbol = Symbol.for('@adcp/client.capabilities');\n\n/** Per-request tool visibility policy consumed by transport adapters. @internal */\nexport const ADCP_TOOL_VISIBILITY: unique symbol = Symbol.for('@adcp/client.toolVisibility');\n\n/** @internal */\nexport type AdcpToolVisibilityResolver = (options: {\n toolName: string;\n authInfo?: AdcpAuthInfo;\n input?: Readonly<Record<string, unknown>>;\n}) => boolean | Promise<boolean>;\n\n/** @internal */\nexport interface AdcpServerInternal extends AdcpServer {\n readonly [ADCP_SDK_SERVER]: McpServer;\n [ADCP_TOOL_VISIBILITY]?: AdcpToolVisibilityResolver;\n}\n\n/**\n * Return the underlying `McpServer` for a wrapped `AdcpServer`, or\n * `undefined` if the value is not an `AdcpServer` produced by\n * `createAdcpServer()`.\n *\n * @internal\n */\nexport function getSdkServer(server: AdcpServer | McpServer): McpServer | undefined {\n const candidate = (server as unknown as Partial<AdcpServerInternal>)[ADCP_SDK_SERVER];\n return candidate;\n}\n\n/** Attach a transport-independent per-request tool visibility policy. @internal */\nexport function setToolVisibilityResolver(server: AdcpServer, resolver: AdcpToolVisibilityResolver): void {\n (server as AdcpServerInternal)[ADCP_TOOL_VISIBILITY] = resolver;\n}\n\n/** Resolve whether a registered tool may be exposed to this request. @internal */\nexport async function isRegisteredToolVisible(\n server: AdcpServer,\n options: Parameters<AdcpToolVisibilityResolver>[0]\n): Promise<boolean> {\n const resolver = (server as AdcpServerInternal)[ADCP_TOOL_VISIBILITY];\n return resolver ? resolver(options) : true;\n}\n\n/**\n * True when `value` is an `AdcpServer` produced by `createAdcpServer()`.\n *\n * @internal\n */\nexport function isAdcpServer(value: unknown): value is AdcpServerInternal {\n return (\n value != null &&\n typeof value === 'object' &&\n ADCP_SDK_SERVER in (value as Record<PropertyKey, unknown>) &&\n typeof (value as AdcpServer).dispatchTestRequest === 'function'\n );\n}\n\n// ---------------------------------------------------------------------------\n// Private-field access — centralized\n// ---------------------------------------------------------------------------\n//\n// The MCP SDK marks `_requestHandlers` and `_registeredTools` as private.\n// Dispatching a request in-process from a test needs them. Doing it once\n// here means downstream harnesses don't each repeat the (server as any)\n// dance, which is the whole point of exposing `dispatchTestRequest()`.\n//\n// If/when the SDK ships a public in-process dispatch API, swap these two\n// helpers for that API. The AdcpServer surface stays the same.\n\ninterface RegisteredTool {\n title?: string;\n description?: string;\n inputSchema?: unknown;\n outputSchema?: unknown;\n annotations?: unknown;\n _meta?: Record<string, unknown>;\n enabled?: boolean;\n handler: (args: unknown, extra: unknown) => unknown | Promise<unknown>;\n}\n\ninterface McpServerPrivates {\n _registeredTools?: Record<string, RegisteredTool | undefined>;\n server?: {\n _requestHandlers?: Map<\n string,\n (request: { method: string; params?: unknown }, extra: unknown) => unknown | Promise<unknown>\n >;\n _serverInfo?: { name: string; version: string; [key: string]: unknown };\n _instructions?: string;\n };\n}\n\n/** Tool metadata needed to mirror the registered AdCP surface into another official MCP transport. @internal */\nexport interface RegisteredToolDefinition {\n name: string;\n title?: string;\n description?: string;\n inputSchema?: unknown;\n outputSchema?: unknown;\n annotations?: unknown;\n _meta?: Record<string, unknown>;\n}\n\n/** @internal */\ntype McpRequestHandler = (request: { method: string; params?: unknown }, extra: unknown) => unknown | Promise<unknown>;\n\n/**\n * Write the instructions string onto the low-level SDK server backing\n * `mcpServer`. Called by the async-function-form instructions hook to\n * inject the resolved string just before the MCP `initialize` response.\n *\n * @internal\n * @remarks Pokes an SDK private field — see the \"Private-field access\" note above.\n */\nexport function setSdkServerInstructions(mcpServer: McpServer, value: string | undefined): void {\n const priv = mcpServer as unknown as McpServerPrivates;\n if (priv.server) {\n priv.server._instructions = value;\n }\n}\n\n/**\n * Wrap the `initialize` request handler on `mcpServer` so that `wrapper`\n * runs (and can await async work) before the original handler responds.\n * The wrapper receives the original handler as its first argument and must\n * call it to complete the handshake.\n *\n * The wrapper is invoked once per MCP `initialize` call on this server\n * instance. Under `serve({ reuseAgent: false })` the server is created\n * fresh per request, so the wrapper fires once per session. Under\n * `reuseAgent: true` the `ADCP_INSTRUCTIONS_FN` check in `serve.ts`\n * blocks the combination before the wrapper can be called a second time.\n *\n * @internal\n * @remarks Pokes an SDK private field — see the \"Private-field access\" note above.\n */\nexport function wrapInitializeHandler(\n mcpServer: McpServer,\n wrapper: (\n origHandler: McpRequestHandler,\n req: { method: string; params?: unknown },\n extra: unknown\n ) => unknown | Promise<unknown>\n): void {\n const priv = mcpServer as unknown as McpServerPrivates;\n const handlers = priv.server?._requestHandlers;\n if (!handlers) return;\n const orig = handlers.get('initialize');\n if (!orig) return;\n handlers.set('initialize', (req, extra) => wrapper(orig, req, extra));\n}\n\nfunction getRegisteredTool(server: McpServer, name: string): RegisteredTool | undefined {\n return (server as unknown as McpServerPrivates)._registeredTools?.[name];\n}\n\n/**\n * Wrap a registered MCP tool handler without changing the tool definition\n * advertised by the SDK server.\n *\n * @internal\n * @remarks Pokes an SDK private field — see the \"Private-field access\" note above.\n */\nexport function wrapRegisteredToolHandler(\n server: McpServer,\n name: string,\n wrapper: (orig: RegisteredTool['handler'], args: unknown, extra: unknown) => unknown | Promise<unknown>\n): boolean {\n const tool = getRegisteredTool(server, name);\n if (!tool) return false;\n const orig = tool.handler;\n tool.handler = (args, extra) => wrapper(orig, args, extra);\n return true;\n}\n\n/**\n * Enumerate the tool names registered on the underlying SDK server.\n * Used by transport adapters that need to derive discovery metadata\n * (agent cards, capability listings) from the registered surface\n * without reaching into private SDK fields at every call site.\n *\n * @internal\n */\nexport function listRegisteredToolNames(server: McpServer): string[] {\n const registered = (server as unknown as McpServerPrivates)._registeredTools ?? {};\n return Object.keys(registered);\n}\n\n/**\n * Return enabled registered-tool metadata for the modern MCP server adapter.\n * Handler functions stay private; the adapter dispatches through\n * {@link AdcpServer.invoke} so the framework pipeline remains authoritative.\n *\n * @internal\n */\nexport function listRegisteredToolDefinitions(server: McpServer): RegisteredToolDefinition[] {\n const registered = (server as unknown as McpServerPrivates)._registeredTools ?? {};\n return Object.entries(registered).flatMap(([name, tool]) => {\n if (!tool || tool.enabled === false) return [];\n return [\n {\n name,\n ...(tool.title !== undefined && { title: tool.title }),\n ...(tool.description !== undefined && { description: tool.description }),\n ...(tool.inputSchema !== undefined && { inputSchema: tool.inputSchema }),\n ...(tool.outputSchema !== undefined && { outputSchema: tool.outputSchema }),\n ...(tool.annotations !== undefined && { annotations: tool.annotations }),\n ...(tool._meta !== undefined && { _meta: tool._meta }),\n },\n ];\n });\n}\n\n/** Return the SDK server identity advertised during MCP discovery. @internal */\nexport function getSdkServerInfo(server: McpServer): { name: string; version: string; [key: string]: unknown } {\n return (\n (server as unknown as McpServerPrivates).server?._serverInfo ?? {\n name: 'AdCP Server',\n version: 'unknown',\n }\n );\n}\n\n/** Return static or already-resolved server instructions. @internal */\nexport function getSdkServerInstructions(server: McpServer): string | undefined {\n return (server as unknown as McpServerPrivates).server?._instructions;\n}\n\nfunction getRequestHandler(\n server: McpServer,\n method: string\n): ((request: { method: string; params?: unknown }, extra: unknown) => unknown | Promise<unknown>) | undefined {\n return (server as unknown as McpServerPrivates).server?._requestHandlers?.get(method);\n}\n\n/**\n * Wrap an SDK request handler such as `tools/list` while preserving the\n * original handler's request/extra contract.\n *\n * @internal\n * @remarks Pokes an SDK private field — see the \"Private-field access\" note above.\n */\nexport function wrapSdkRequestHandler(\n server: McpServer,\n method: string,\n wrapper: (\n orig: McpRequestHandler,\n req: { method: string; params?: unknown },\n extra: unknown\n ) => unknown | Promise<unknown>\n): boolean {\n const handlers = (server as unknown as McpServerPrivates).server?._requestHandlers;\n const orig = handlers?.get(method);\n if (!handlers || !orig) return false;\n handlers.set(method, (req, extra) => wrapper(orig, req, extra));\n return true;\n}\n\n/**\n * Wrap an SDK `McpServer` into the opaque `AdcpServer` handle returned by\n * `createAdcpServer()`. Consumers should prefer `createAdcpServer()` and\n * treat this as an internal utility; it's exported for framework code\n * that needs to produce an `AdcpServer` around a hand-built server.\n *\n * Idempotent — passing an already-wrapped `AdcpServer` returns it\n * unchanged rather than nesting wrappers (which would leave\n * `ADCP_SDK_SERVER` pointing at the outer wrapper instead of the SDK\n * handle and break `getSdkServer()` round-trips).\n *\n * @internal\n */\nexport function wrapMcpServer(\n inner: McpServer | AdcpServerInternal,\n compliance?: AdcpServerComplianceApi,\n adcpVersion: string = ADCP_VERSION\n): AdcpServerInternal {\n if (isAdcpServer(inner)) return inner;\n const mcp = inner as McpServer;\n const resolvedCompliance: AdcpServerComplianceApi = compliance ?? {\n async reset() {\n throw new Error(\n 'AdcpServer.compliance.reset: no-op handle returned by `wrapMcpServer` without a compliance implementation. ' +\n 'Use `createAdcpServer()` to get a server whose `compliance.reset()` flushes the state and idempotency stores.'\n );\n },\n };\n const dispatch = async (request: AdcpTestRequest, extras?: AdcpTestRequestExtras): Promise<AdcpTestResponse> => {\n const extra: { signal: AbortSignal; authInfo?: AdcpTestRequestExtras['authInfo']; sessionId?: string } = {\n signal: new AbortController().signal,\n };\n if (extras?.authInfo) extra.authInfo = extras.authInfo;\n if (extras?.sessionId) extra.sessionId = extras.sessionId;\n\n if (request.method === 'tools/call') {\n const params = (request.params ?? {}) as { name?: string; arguments?: Record<string, unknown> };\n if (typeof params.name !== 'string') {\n throw new Error(\"dispatchTestRequest: 'tools/call' requires params.name (string)\");\n }\n const tool = getRegisteredTool(mcp, params.name);\n if (!tool) {\n throw new Error(`dispatchTestRequest: tool \"${params.name}\" is not registered`);\n }\n return tool.handler(params.arguments ?? {}, extra);\n }\n\n const handler = getRequestHandler(mcp, request.method);\n if (!handler) {\n throw new Error(`dispatchTestRequest: no handler registered for method \"${request.method}\"`);\n }\n return handler({ method: request.method, params: request.params ?? {} }, extra);\n };\n const invoke = async (options: AdcpInvokeOptions): Promise<McpToolResponse> => {\n const tool = getRegisteredTool(mcp, options.toolName);\n if (!tool) {\n throw new Error(`AdcpServer.invoke: tool \"${options.toolName}\" is not registered`);\n }\n const extra: { signal: AbortSignal; authInfo?: AdcpAuthInfo } = {\n signal: options.signal ?? new AbortController().signal,\n };\n if (options.authInfo) extra.authInfo = options.authInfo;\n return (await tool.handler(options.args, extra)) as McpToolResponse;\n };\n const wrapper: AdcpServerInternal = {\n [ADCP_SDK_SERVER]: mcp,\n connect(transport) {\n // The SDK's Transport interface is structurally compatible with\n // AdcpServerTransport — the cast is boundary-crossing, not lossy.\n return mcp.connect(transport as unknown as Parameters<McpServer['connect']>[0]);\n },\n close() {\n return mcp.close();\n },\n compliance: resolvedCompliance,\n // Satisfies both overloads (typed tools/call + generic fallback) — the\n // runtime dispatcher is a single function that narrows by method.\n dispatchTestRequest: dispatch as AdcpServerInternal['dispatchTestRequest'],\n invoke,\n getAdcpVersion: () => adcpVersion,\n };\n return wrapper;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
import { fileURLToPath as __adcpFileURLToPath } from 'node:url';
|
|
2
|
-
import { dirname as __adcpDirname } from 'node:path';
|
|
3
|
-
import { createRequire as __adcpCreateRequire } from 'node:module';
|
|
4
|
-
const __filename = __adcpFileURLToPath(import.meta.url);
|
|
5
|
-
const __dirname = __adcpDirname(__filename);
|
|
6
|
-
const require = __adcpCreateRequire(import.meta.url);
|
|
7
1
|
import { createHash } from "crypto";
|
|
8
2
|
import { AuthError, extractBearerToken } from "./auth.mjs";
|
|
9
3
|
function verifyIntrospection(options) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/lib/server/auth-introspection.ts"],"sourcesContent":["/**\n * RFC 7662 OAuth 2.0 Token Introspection authenticator.\n *\n * Use case: the agent is NOT the identity issuer. Instead it proxies an\n * upstream OAuth provider (Snap, Meta, TikTok, Google, …) — the Bearer\n * token a buyer presents is the upstream platform's access token, not a\n * JWT the adapter minted. {@link verifyBearer} doesn't apply there; the\n * only way to validate the token is to ask the upstream: POST the token\n * to its RFC 7662 introspection endpoint and check `active: true`.\n *\n * Matches the {@link Authenticator} contract of {@link verifyBearer}:\n * returns `null` when no bearer header is present (so {@link anyOf} can\n * fall through), throws {@link AuthError} when a token was presented\n * and rejected. Public error messages are sanitized — the upstream\n * response body never crosses the wire, only a generic \"Token\n * validation failed.\" / \"Insufficient scope.\" message on 401.\n *\n * **Positive-response caching is opt-in.** Introspection endpoints are\n * often rate-limited and always round-trip at least one TLS handshake\n * worth of latency — a small in-process LRU keyed on the token's\n * SHA-256 hash amortizes the cost across closely-spaced requests from\n * the same buyer. Negative responses are NOT cached by default; a\n * stolen-then-revoked token must be able to fail the next request. Set\n * `cache.negativeTtlSeconds` to cache `active: false` too (mitigates\n * introspection-amplification DoS, trades against revocation latency).\n */\nimport { createHash } from 'crypto';\nimport type { IncomingMessage } from 'http';\nimport { AuthError, extractBearerToken, type AuthPrincipal, type AuthResult, type Authenticator } from './auth';\n\n/** RFC 7662 §2.2 introspection response. Fields are all optional except `active`. */\nexport interface IntrospectionResponse {\n active: boolean;\n scope?: string;\n client_id?: string;\n username?: string;\n token_type?: string;\n exp?: number;\n iat?: number;\n nbf?: number;\n sub?: string;\n aud?: string | string[];\n iss?: string;\n jti?: string;\n [extension: string]: unknown;\n}\n\nexport interface IntrospectionCacheOptions {\n /** Max cached entries (positive + negative combined). Default 1024. */\n max?: number;\n /**\n * Seconds to cache `active: true` responses. Default 60. Must be <=\n * the token's remaining lifetime — the helper caps the effective\n * TTL at `exp - now` so the cache can't extend a token past its\n * issued expiry.\n */\n ttlSeconds?: number;\n /**\n * Seconds to cache `active: false` responses. Default 0 (disabled).\n * Caching negatives mitigates introspection-amplification DoS from an\n * attacker replaying a stolen-then-revoked token, but delays propagation\n * of a fresh revocation by up to this many seconds. Keep short (≤5s).\n */\n negativeTtlSeconds?: number;\n}\n\nexport interface VerifyIntrospectionOptions {\n /** Absolute URL of the upstream introspection endpoint (RFC 7662 §2). */\n introspectionUrl: string;\n /**\n * Client credentials for the introspection call. Introspection endpoints\n * ALWAYS require client authentication — without credentials the endpoint\n * would be an open oracle for \"is this token valid.\" Basic auth is the\n * default (RFC 7662 §2.1); `clientAuth: 'body'` sends `client_id` +\n * `client_secret` as form params instead (some IdPs require this).\n */\n clientId: string;\n clientSecret: string;\n clientAuth?: 'basic' | 'body';\n /**\n * Required scopes — the introspection response's `scope` claim (space\n * delimited per RFC 7662) must contain ALL of these. Useful when the\n * agent grants a subset of what the upstream IdP issues.\n */\n requiredScopes?: string[];\n /**\n * Optional constraint on the introspection response's `aud` claim.\n * When set, the helper rejects a token whose `aud` doesn't contain\n * this value. Note: many upstream platforms (Snap, Meta) don't set\n * `aud` on access tokens — their tokens are bound to the client_id,\n * not the resource URL. Leave undefined in that case.\n */\n audience?: string;\n /**\n * Optional cache. Disabled by default (every request round-trips to\n * the introspection endpoint). Enable for hot paths.\n */\n cache?: IntrospectionCacheOptions;\n /**\n * Timeout for the introspection request in milliseconds. Default\n * 2000ms. On timeout the authenticator throws an {@link AuthError}\n * (fail-closed — a slow upstream can't bypass authentication).\n */\n timeoutMs?: number;\n /**\n * `token_type_hint` sent with the introspection request (RFC 7662 §2.1).\n * Default `'access_token'`. Most IdPs ignore the hint but setting it\n * correctly is the spec-conformant move.\n */\n tokenTypeHint?: 'access_token' | 'refresh_token';\n}\n\ninterface CacheEntry {\n principal: AuthResult;\n expiresAt: number; // ms since epoch\n}\n\n/** Build a token-introspection authenticator. See {@link VerifyIntrospectionOptions}. */\nexport function verifyIntrospection(options: VerifyIntrospectionOptions): Authenticator {\n // Resolved defaults + sanity-checks at construction so misconfig fails loud at boot.\n let introspectionUrl: URL;\n try {\n introspectionUrl = new URL(options.introspectionUrl);\n } catch {\n throw new Error(`verifyIntrospection: \\`introspectionUrl\\` is not a valid URL: ${options.introspectionUrl}`);\n }\n if (introspectionUrl.protocol !== 'https:' && !isLoopbackHost(introspectionUrl.hostname)) {\n throw new Error(\n `verifyIntrospection: \\`introspectionUrl\\` must use https:// (got ${introspectionUrl.protocol}). ` +\n 'Sending bearer tokens over plain HTTP to remote hosts is a credential-exposure bug.'\n );\n }\n if (!options.clientId || !options.clientSecret) {\n throw new Error('verifyIntrospection: `clientId` and `clientSecret` are required (RFC 7662 mandates client auth).');\n }\n const clientAuth = options.clientAuth ?? 'basic';\n const timeoutMs = options.timeoutMs ?? 2000;\n const tokenTypeHint = options.tokenTypeHint ?? 'access_token';\n const requiredScopes = options.requiredScopes ?? [];\n\n const cache = options.cache ? createIntrospectionCache(options.cache) : undefined;\n\n // RFC 6749 §2.3.1 mandates application/x-www-form-urlencoded on the\n // clientId and clientSecret BEFORE base64'ing for Basic auth. That's\n // what `formUrlEncode` does (encodeURIComponent + `!*'()` escapes).\n // Some legacy ASes compare the decoded Basic-auth password directly\n // against the stored secret without percent-decoding — if that's\n // biting you, set `clientAuth: 'body'` to send creds in the form body\n // instead, which sidesteps the encoding negotiation entirely.\n const basicAuthHeader =\n clientAuth === 'basic'\n ? `Basic ${Buffer.from(`${formUrlEncode(options.clientId)}:${formUrlEncode(options.clientSecret)}`).toString('base64')}`\n : undefined;\n\n return async (req: IncomingMessage): Promise<AuthResult> => {\n const token = extractBearerToken(req);\n if (!token) return null;\n\n // Cache lookup keyed on SHA-256(token) so the cache never stores\n // the raw bearer in process memory.\n const cacheKey = cache ? sha256(token) : undefined;\n if (cache && cacheKey) {\n const hit = cache.get(cacheKey);\n if (hit) {\n if (hit.principal === null) {\n throw new AuthError('Token validation failed.');\n }\n return { ...hit.principal, token };\n }\n }\n\n let response: IntrospectionResponse;\n try {\n response = await introspect({\n introspectionUrl,\n token,\n tokenTypeHint,\n clientAuth,\n basicAuthHeader,\n clientId: options.clientId,\n clientSecret: options.clientSecret,\n timeoutMs,\n });\n } catch (err) {\n // Network / HTTP errors — fail closed but DON'T cache. The next\n // request retries. Public message is generic; cause is logged.\n throw new AuthError('Token validation failed.', { cause: err });\n }\n\n // Inactive token. Cache the negative result if enabled — mitigates\n // replay-amplification of a revoked token. Cause message is generic.\n if (!response.active) {\n if (cache && cacheKey && cache.negativeTtlMs > 0) {\n cache.set(cacheKey, { principal: null, expiresAt: Date.now() + cache.negativeTtlMs });\n }\n throw new AuthError('Token validation failed.');\n }\n\n // Audience check — only when the caller specified a required audience.\n // Many upstream IdPs don't populate `aud` on access tokens, so the\n // default \"no audience required\" is correct for the adapter case.\n if (options.audience !== undefined) {\n const aud = response.aud;\n const audList = typeof aud === 'string' ? [aud] : Array.isArray(aud) ? aud : [];\n if (!audList.includes(options.audience)) {\n throw new AuthError('Token validation failed.');\n }\n }\n\n const scopes = parseScope(response.scope);\n if (requiredScopes.length > 0) {\n const missing = requiredScopes.filter(s => !scopes.includes(s));\n if (missing.length > 0) {\n throw new AuthError('Insufficient scope.');\n }\n }\n\n const principal: AuthPrincipal = {\n principal:\n typeof response.sub === 'string'\n ? response.sub\n : typeof response.client_id === 'string'\n ? response.client_id\n : 'unknown',\n token,\n scopes,\n claims: response,\n };\n if (typeof response.exp === 'number') principal.expiresAt = response.exp;\n\n if (cache && cacheKey) {\n // Cap the positive TTL at the token's own remaining lifetime —\n // never let the cache extend a token past its issued `exp`. When\n // the IdP omits `exp`, fall back to the configured TTL.\n const nowSec = Math.floor(Date.now() / 1000);\n const configuredTtlMs = cache.positiveTtlMs;\n const effectiveTtlMs =\n typeof response.exp === 'number'\n ? Math.min(configuredTtlMs, Math.max(0, (response.exp - nowSec) * 1000))\n : configuredTtlMs;\n if (effectiveTtlMs > 0) {\n // Deep-clone the principal for the cache entry so callers that\n // mutate the returned principal can't poison the cached copy.\n // `claims` is cloned with structuredClone — a shallow spread would\n // leave nested objects aliased, and a malicious or buggy caller\n // that set `returned.claims.scope = 'admin'` would upgrade every\n // subsequent cached lookup.\n const cachedPrincipal: AuthPrincipal = {\n principal: principal.principal,\n scopes: [...scopes],\n claims: principal.claims ? structuredClone(principal.claims) : undefined,\n };\n if (principal.expiresAt !== undefined) cachedPrincipal.expiresAt = principal.expiresAt;\n cache.set(cacheKey, {\n principal: cachedPrincipal,\n expiresAt: Date.now() + effectiveTtlMs,\n });\n }\n }\n\n // Return a fresh object to callers — the cached copy is sealed behind\n // the `cachedPrincipal` closure. If the caller mutates the returned\n // `scopes` array or `claims` object, the cache entry stays clean\n // because it was cloned at `set` time (see block above).\n return principal;\n };\n}\n\n/**\n * Check whether a hostname is a loopback address. Exact-match for the\n * three literal forms (`localhost`, `127.0.0.1`, `[::1]`) so attacker\n * tricks like `localhost.evil.com` don't slip past — URL parser sets\n * `.hostname` to the whole string, not the left-most label.\n */\nfunction isLoopbackHost(hostname: string): boolean {\n // URL parses IPv6 `[::1]` as hostname `::1` (brackets stripped). The\n // 127.0.0.0/8 range is loopback per RFC 6890 — accept anything in\n // that range since Node's own fetch routes it to lo0.\n if (hostname === 'localhost' || hostname === '::1') return true;\n const m = /^127\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$/.exec(hostname);\n if (!m) return false;\n return m.slice(1).every(octet => {\n const n = Number(octet);\n return n >= 0 && n <= 255;\n });\n}\n\n/**\n * application/x-www-form-urlencoded encoding per RFC 6749 §2.3.1 and\n * Appendix B. Differs from `encodeURIComponent` in that `!*'()` MUST be\n * percent-encoded (those are reserved in the form-urlencoded grammar).\n * Rare in real-world client credentials but spec-correct and avoids\n * per-IdP quirks when a secret happens to include one of those chars.\n */\nfunction formUrlEncode(s: string): string {\n return encodeURIComponent(s).replace(/[!*'()]/g, c => '%' + c.charCodeAt(0).toString(16).toUpperCase());\n}\n\n// ---------------------------------------------------------------------------\n// Introspection transport\n// ---------------------------------------------------------------------------\n\nasync function introspect(args: {\n introspectionUrl: URL;\n token: string;\n tokenTypeHint: 'access_token' | 'refresh_token';\n clientAuth: 'basic' | 'body';\n basicAuthHeader: string | undefined;\n clientId: string;\n clientSecret: string;\n timeoutMs: number;\n}): Promise<IntrospectionResponse> {\n const body = new URLSearchParams();\n body.set('token', args.token);\n body.set('token_type_hint', args.tokenTypeHint);\n if (args.clientAuth === 'body') {\n body.set('client_id', args.clientId);\n body.set('client_secret', args.clientSecret);\n }\n\n const headers: Record<string, string> = {\n 'content-type': 'application/x-www-form-urlencoded',\n accept: 'application/json',\n };\n if (args.basicAuthHeader) headers.authorization = args.basicAuthHeader;\n\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), args.timeoutMs);\n try {\n const res = await fetch(args.introspectionUrl, {\n method: 'POST',\n headers,\n body,\n signal: controller.signal,\n });\n if (!res.ok) {\n // 4xx/5xx — don't treat as `active: false`; that would mask a\n // misconfigured client credential as a revoked token.\n const statusText = res.statusText || 'no status text';\n throw new Error(`introspection endpoint returned HTTP ${res.status} (${statusText})`);\n }\n const ct = res.headers.get('content-type') || '';\n if (!ct.includes('application/json')) {\n throw new Error(`introspection endpoint returned non-JSON content-type: ${ct}`);\n }\n const json = (await res.json()) as IntrospectionResponse;\n if (typeof json?.active !== 'boolean') {\n throw new Error('introspection response missing required `active` field');\n }\n return json;\n } finally {\n clearTimeout(timer);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction sha256(input: string): string {\n return createHash('sha256').update(input).digest('hex');\n}\n\nfunction parseScope(scope: unknown): string[] {\n if (typeof scope !== 'string' || scope.length === 0) return [];\n return scope.split(/\\s+/).filter(Boolean);\n}\n\n// ---------------------------------------------------------------------------\n// Cache\n// ---------------------------------------------------------------------------\n\ninterface IntrospectionCacheHandle {\n get(key: string): CacheEntry | undefined;\n set(key: string, entry: CacheEntry): void;\n readonly positiveTtlMs: number;\n readonly negativeTtlMs: number;\n}\n\n/**\n * Small insertion-ordered LRU. Map iteration order is insertion order; to\n * bump an entry on access, delete + re-set. Keeps the implementation\n * dependency-free.\n */\nfunction createIntrospectionCache(options: IntrospectionCacheOptions): IntrospectionCacheHandle {\n const max = options.max ?? 1024;\n const positiveTtlMs = (options.ttlSeconds ?? 60) * 1000;\n const negativeTtlMs = (options.negativeTtlSeconds ?? 0) * 1000;\n const store = new Map<string, CacheEntry>();\n\n return {\n positiveTtlMs,\n negativeTtlMs,\n get(key) {\n const entry = store.get(key);\n if (!entry) return undefined;\n if (entry.expiresAt <= Date.now()) {\n store.delete(key);\n return undefined;\n }\n // Bump LRU: re-insert at the tail.\n store.delete(key);\n store.set(key, entry);\n return entry;\n },\n set(key, entry) {\n if (store.has(key)) store.delete(key);\n store.set(key, entry);\n while (store.size > max) {\n const oldest = store.keys().next().value;\n if (oldest === undefined) break;\n store.delete(oldest);\n }\n },\n };\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/lib/server/auth-introspection.ts"],"sourcesContent":["/**\n * RFC 7662 OAuth 2.0 Token Introspection authenticator.\n *\n * Use case: the agent is NOT the identity issuer. Instead it proxies an\n * upstream OAuth provider (Snap, Meta, TikTok, Google, …) — the Bearer\n * token a buyer presents is the upstream platform's access token, not a\n * JWT the adapter minted. {@link verifyBearer} doesn't apply there; the\n * only way to validate the token is to ask the upstream: POST the token\n * to its RFC 7662 introspection endpoint and check `active: true`.\n *\n * Matches the {@link Authenticator} contract of {@link verifyBearer}:\n * returns `null` when no bearer header is present (so {@link anyOf} can\n * fall through), throws {@link AuthError} when a token was presented\n * and rejected. Public error messages are sanitized — the upstream\n * response body never crosses the wire, only a generic \"Token\n * validation failed.\" / \"Insufficient scope.\" message on 401.\n *\n * **Positive-response caching is opt-in.** Introspection endpoints are\n * often rate-limited and always round-trip at least one TLS handshake\n * worth of latency — a small in-process LRU keyed on the token's\n * SHA-256 hash amortizes the cost across closely-spaced requests from\n * the same buyer. Negative responses are NOT cached by default; a\n * stolen-then-revoked token must be able to fail the next request. Set\n * `cache.negativeTtlSeconds` to cache `active: false` too (mitigates\n * introspection-amplification DoS, trades against revocation latency).\n */\nimport { createHash } from 'crypto';\nimport type { IncomingMessage } from 'http';\nimport { AuthError, extractBearerToken, type AuthPrincipal, type AuthResult, type Authenticator } from './auth';\n\n/** RFC 7662 §2.2 introspection response. Fields are all optional except `active`. */\nexport interface IntrospectionResponse {\n active: boolean;\n scope?: string;\n client_id?: string;\n username?: string;\n token_type?: string;\n exp?: number;\n iat?: number;\n nbf?: number;\n sub?: string;\n aud?: string | string[];\n iss?: string;\n jti?: string;\n [extension: string]: unknown;\n}\n\nexport interface IntrospectionCacheOptions {\n /** Max cached entries (positive + negative combined). Default 1024. */\n max?: number;\n /**\n * Seconds to cache `active: true` responses. Default 60. Must be <=\n * the token's remaining lifetime — the helper caps the effective\n * TTL at `exp - now` so the cache can't extend a token past its\n * issued expiry.\n */\n ttlSeconds?: number;\n /**\n * Seconds to cache `active: false` responses. Default 0 (disabled).\n * Caching negatives mitigates introspection-amplification DoS from an\n * attacker replaying a stolen-then-revoked token, but delays propagation\n * of a fresh revocation by up to this many seconds. Keep short (≤5s).\n */\n negativeTtlSeconds?: number;\n}\n\nexport interface VerifyIntrospectionOptions {\n /** Absolute URL of the upstream introspection endpoint (RFC 7662 §2). */\n introspectionUrl: string;\n /**\n * Client credentials for the introspection call. Introspection endpoints\n * ALWAYS require client authentication — without credentials the endpoint\n * would be an open oracle for \"is this token valid.\" Basic auth is the\n * default (RFC 7662 §2.1); `clientAuth: 'body'` sends `client_id` +\n * `client_secret` as form params instead (some IdPs require this).\n */\n clientId: string;\n clientSecret: string;\n clientAuth?: 'basic' | 'body';\n /**\n * Required scopes — the introspection response's `scope` claim (space\n * delimited per RFC 7662) must contain ALL of these. Useful when the\n * agent grants a subset of what the upstream IdP issues.\n */\n requiredScopes?: string[];\n /**\n * Optional constraint on the introspection response's `aud` claim.\n * When set, the helper rejects a token whose `aud` doesn't contain\n * this value. Note: many upstream platforms (Snap, Meta) don't set\n * `aud` on access tokens — their tokens are bound to the client_id,\n * not the resource URL. Leave undefined in that case.\n */\n audience?: string;\n /**\n * Optional cache. Disabled by default (every request round-trips to\n * the introspection endpoint). Enable for hot paths.\n */\n cache?: IntrospectionCacheOptions;\n /**\n * Timeout for the introspection request in milliseconds. Default\n * 2000ms. On timeout the authenticator throws an {@link AuthError}\n * (fail-closed — a slow upstream can't bypass authentication).\n */\n timeoutMs?: number;\n /**\n * `token_type_hint` sent with the introspection request (RFC 7662 §2.1).\n * Default `'access_token'`. Most IdPs ignore the hint but setting it\n * correctly is the spec-conformant move.\n */\n tokenTypeHint?: 'access_token' | 'refresh_token';\n}\n\ninterface CacheEntry {\n principal: AuthResult;\n expiresAt: number; // ms since epoch\n}\n\n/** Build a token-introspection authenticator. See {@link VerifyIntrospectionOptions}. */\nexport function verifyIntrospection(options: VerifyIntrospectionOptions): Authenticator {\n // Resolved defaults + sanity-checks at construction so misconfig fails loud at boot.\n let introspectionUrl: URL;\n try {\n introspectionUrl = new URL(options.introspectionUrl);\n } catch {\n throw new Error(`verifyIntrospection: \\`introspectionUrl\\` is not a valid URL: ${options.introspectionUrl}`);\n }\n if (introspectionUrl.protocol !== 'https:' && !isLoopbackHost(introspectionUrl.hostname)) {\n throw new Error(\n `verifyIntrospection: \\`introspectionUrl\\` must use https:// (got ${introspectionUrl.protocol}). ` +\n 'Sending bearer tokens over plain HTTP to remote hosts is a credential-exposure bug.'\n );\n }\n if (!options.clientId || !options.clientSecret) {\n throw new Error('verifyIntrospection: `clientId` and `clientSecret` are required (RFC 7662 mandates client auth).');\n }\n const clientAuth = options.clientAuth ?? 'basic';\n const timeoutMs = options.timeoutMs ?? 2000;\n const tokenTypeHint = options.tokenTypeHint ?? 'access_token';\n const requiredScopes = options.requiredScopes ?? [];\n\n const cache = options.cache ? createIntrospectionCache(options.cache) : undefined;\n\n // RFC 6749 §2.3.1 mandates application/x-www-form-urlencoded on the\n // clientId and clientSecret BEFORE base64'ing for Basic auth. That's\n // what `formUrlEncode` does (encodeURIComponent + `!*'()` escapes).\n // Some legacy ASes compare the decoded Basic-auth password directly\n // against the stored secret without percent-decoding — if that's\n // biting you, set `clientAuth: 'body'` to send creds in the form body\n // instead, which sidesteps the encoding negotiation entirely.\n const basicAuthHeader =\n clientAuth === 'basic'\n ? `Basic ${Buffer.from(`${formUrlEncode(options.clientId)}:${formUrlEncode(options.clientSecret)}`).toString('base64')}`\n : undefined;\n\n return async (req: IncomingMessage): Promise<AuthResult> => {\n const token = extractBearerToken(req);\n if (!token) return null;\n\n // Cache lookup keyed on SHA-256(token) so the cache never stores\n // the raw bearer in process memory.\n const cacheKey = cache ? sha256(token) : undefined;\n if (cache && cacheKey) {\n const hit = cache.get(cacheKey);\n if (hit) {\n if (hit.principal === null) {\n throw new AuthError('Token validation failed.');\n }\n return { ...hit.principal, token };\n }\n }\n\n let response: IntrospectionResponse;\n try {\n response = await introspect({\n introspectionUrl,\n token,\n tokenTypeHint,\n clientAuth,\n basicAuthHeader,\n clientId: options.clientId,\n clientSecret: options.clientSecret,\n timeoutMs,\n });\n } catch (err) {\n // Network / HTTP errors — fail closed but DON'T cache. The next\n // request retries. Public message is generic; cause is logged.\n throw new AuthError('Token validation failed.', { cause: err });\n }\n\n // Inactive token. Cache the negative result if enabled — mitigates\n // replay-amplification of a revoked token. Cause message is generic.\n if (!response.active) {\n if (cache && cacheKey && cache.negativeTtlMs > 0) {\n cache.set(cacheKey, { principal: null, expiresAt: Date.now() + cache.negativeTtlMs });\n }\n throw new AuthError('Token validation failed.');\n }\n\n // Audience check — only when the caller specified a required audience.\n // Many upstream IdPs don't populate `aud` on access tokens, so the\n // default \"no audience required\" is correct for the adapter case.\n if (options.audience !== undefined) {\n const aud = response.aud;\n const audList = typeof aud === 'string' ? [aud] : Array.isArray(aud) ? aud : [];\n if (!audList.includes(options.audience)) {\n throw new AuthError('Token validation failed.');\n }\n }\n\n const scopes = parseScope(response.scope);\n if (requiredScopes.length > 0) {\n const missing = requiredScopes.filter(s => !scopes.includes(s));\n if (missing.length > 0) {\n throw new AuthError('Insufficient scope.');\n }\n }\n\n const principal: AuthPrincipal = {\n principal:\n typeof response.sub === 'string'\n ? response.sub\n : typeof response.client_id === 'string'\n ? response.client_id\n : 'unknown',\n token,\n scopes,\n claims: response,\n };\n if (typeof response.exp === 'number') principal.expiresAt = response.exp;\n\n if (cache && cacheKey) {\n // Cap the positive TTL at the token's own remaining lifetime —\n // never let the cache extend a token past its issued `exp`. When\n // the IdP omits `exp`, fall back to the configured TTL.\n const nowSec = Math.floor(Date.now() / 1000);\n const configuredTtlMs = cache.positiveTtlMs;\n const effectiveTtlMs =\n typeof response.exp === 'number'\n ? Math.min(configuredTtlMs, Math.max(0, (response.exp - nowSec) * 1000))\n : configuredTtlMs;\n if (effectiveTtlMs > 0) {\n // Deep-clone the principal for the cache entry so callers that\n // mutate the returned principal can't poison the cached copy.\n // `claims` is cloned with structuredClone — a shallow spread would\n // leave nested objects aliased, and a malicious or buggy caller\n // that set `returned.claims.scope = 'admin'` would upgrade every\n // subsequent cached lookup.\n const cachedPrincipal: AuthPrincipal = {\n principal: principal.principal,\n scopes: [...scopes],\n claims: principal.claims ? structuredClone(principal.claims) : undefined,\n };\n if (principal.expiresAt !== undefined) cachedPrincipal.expiresAt = principal.expiresAt;\n cache.set(cacheKey, {\n principal: cachedPrincipal,\n expiresAt: Date.now() + effectiveTtlMs,\n });\n }\n }\n\n // Return a fresh object to callers — the cached copy is sealed behind\n // the `cachedPrincipal` closure. If the caller mutates the returned\n // `scopes` array or `claims` object, the cache entry stays clean\n // because it was cloned at `set` time (see block above).\n return principal;\n };\n}\n\n/**\n * Check whether a hostname is a loopback address. Exact-match for the\n * three literal forms (`localhost`, `127.0.0.1`, `[::1]`) so attacker\n * tricks like `localhost.evil.com` don't slip past — URL parser sets\n * `.hostname` to the whole string, not the left-most label.\n */\nfunction isLoopbackHost(hostname: string): boolean {\n // URL parses IPv6 `[::1]` as hostname `::1` (brackets stripped). The\n // 127.0.0.0/8 range is loopback per RFC 6890 — accept anything in\n // that range since Node's own fetch routes it to lo0.\n if (hostname === 'localhost' || hostname === '::1') return true;\n const m = /^127\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$/.exec(hostname);\n if (!m) return false;\n return m.slice(1).every(octet => {\n const n = Number(octet);\n return n >= 0 && n <= 255;\n });\n}\n\n/**\n * application/x-www-form-urlencoded encoding per RFC 6749 §2.3.1 and\n * Appendix B. Differs from `encodeURIComponent` in that `!*'()` MUST be\n * percent-encoded (those are reserved in the form-urlencoded grammar).\n * Rare in real-world client credentials but spec-correct and avoids\n * per-IdP quirks when a secret happens to include one of those chars.\n */\nfunction formUrlEncode(s: string): string {\n return encodeURIComponent(s).replace(/[!*'()]/g, c => '%' + c.charCodeAt(0).toString(16).toUpperCase());\n}\n\n// ---------------------------------------------------------------------------\n// Introspection transport\n// ---------------------------------------------------------------------------\n\nasync function introspect(args: {\n introspectionUrl: URL;\n token: string;\n tokenTypeHint: 'access_token' | 'refresh_token';\n clientAuth: 'basic' | 'body';\n basicAuthHeader: string | undefined;\n clientId: string;\n clientSecret: string;\n timeoutMs: number;\n}): Promise<IntrospectionResponse> {\n const body = new URLSearchParams();\n body.set('token', args.token);\n body.set('token_type_hint', args.tokenTypeHint);\n if (args.clientAuth === 'body') {\n body.set('client_id', args.clientId);\n body.set('client_secret', args.clientSecret);\n }\n\n const headers: Record<string, string> = {\n 'content-type': 'application/x-www-form-urlencoded',\n accept: 'application/json',\n };\n if (args.basicAuthHeader) headers.authorization = args.basicAuthHeader;\n\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), args.timeoutMs);\n try {\n const res = await fetch(args.introspectionUrl, {\n method: 'POST',\n headers,\n body,\n signal: controller.signal,\n });\n if (!res.ok) {\n // 4xx/5xx — don't treat as `active: false`; that would mask a\n // misconfigured client credential as a revoked token.\n const statusText = res.statusText || 'no status text';\n throw new Error(`introspection endpoint returned HTTP ${res.status} (${statusText})`);\n }\n const ct = res.headers.get('content-type') || '';\n if (!ct.includes('application/json')) {\n throw new Error(`introspection endpoint returned non-JSON content-type: ${ct}`);\n }\n const json = (await res.json()) as IntrospectionResponse;\n if (typeof json?.active !== 'boolean') {\n throw new Error('introspection response missing required `active` field');\n }\n return json;\n } finally {\n clearTimeout(timer);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction sha256(input: string): string {\n return createHash('sha256').update(input).digest('hex');\n}\n\nfunction parseScope(scope: unknown): string[] {\n if (typeof scope !== 'string' || scope.length === 0) return [];\n return scope.split(/\\s+/).filter(Boolean);\n}\n\n// ---------------------------------------------------------------------------\n// Cache\n// ---------------------------------------------------------------------------\n\ninterface IntrospectionCacheHandle {\n get(key: string): CacheEntry | undefined;\n set(key: string, entry: CacheEntry): void;\n readonly positiveTtlMs: number;\n readonly negativeTtlMs: number;\n}\n\n/**\n * Small insertion-ordered LRU. Map iteration order is insertion order; to\n * bump an entry on access, delete + re-set. Keeps the implementation\n * dependency-free.\n */\nfunction createIntrospectionCache(options: IntrospectionCacheOptions): IntrospectionCacheHandle {\n const max = options.max ?? 1024;\n const positiveTtlMs = (options.ttlSeconds ?? 60) * 1000;\n const negativeTtlMs = (options.negativeTtlSeconds ?? 0) * 1000;\n const store = new Map<string, CacheEntry>();\n\n return {\n positiveTtlMs,\n negativeTtlMs,\n get(key) {\n const entry = store.get(key);\n if (!entry) return undefined;\n if (entry.expiresAt <= Date.now()) {\n store.delete(key);\n return undefined;\n }\n // Bump LRU: re-insert at the tail.\n store.delete(key);\n store.set(key, entry);\n return entry;\n },\n set(key, entry) {\n if (store.has(key)) store.delete(key);\n store.set(key, entry);\n while (store.size > max) {\n const oldest = store.keys().next().value;\n if (oldest === undefined) break;\n store.delete(oldest);\n }\n },\n };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
import { fileURLToPath as __adcpFileURLToPath } from 'node:url';
|
|
2
|
-
import { dirname as __adcpDirname } from 'node:path';
|
|
3
|
-
import { createRequire as __adcpCreateRequire } from 'node:module';
|
|
4
|
-
const __filename = __adcpFileURLToPath(import.meta.url);
|
|
5
|
-
const __dirname = __adcpDirname(__filename);
|
|
6
|
-
const require = __adcpCreateRequire(import.meta.url);
|
|
7
1
|
import { RequestSignatureError } from "../signing/errors.mjs";
|
|
8
2
|
import { InMemoryReplayStore } from "../signing/replay.mjs";
|
|
9
3
|
import { InMemoryRevocationStore } from "../signing/revocation.mjs";
|