@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,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 { z } from "zod";
|
|
8
2
|
import { ADCP_CAPABILITIES, getSdkServer } from "./adcp-server.mjs";
|
|
9
3
|
import {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/lib/server/test-controller.ts"],"sourcesContent":["/**\n * Server-side comply_test_controller implementation.\n *\n * Most new code should use {@link createComplyController} from\n * `@adcp/sdk/testing` — it wraps this module with a domain-grouped\n * (`seed` / `force` / `simulate`) adapter surface, typed params, sandbox\n * gating, and built-in seed idempotency. The functions below remain\n * supported for existing integrations and for custom wrappers that need\n * access to the underlying request handler / MCP envelope helpers.\n *\n * Sellers typically call `registerTestController(server, store)` to add the\n * `comply_test_controller` tool to their MCP server. The TestControllerStore\n * interface lets sellers wire up their own state management while the SDK\n * handles request parsing, scenario dispatch, and response formatting.\n *\n * @example Basic usage — single in-memory store\n * ```typescript\n * import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\n * import { registerTestController, TestControllerError } from '@adcp/sdk';\n *\n * const accounts = new Map<string, string>();\n *\n * registerTestController(server, {\n * async forceAccountStatus(accountId, status) {\n * const prev = accounts.get(accountId);\n * if (!prev) throw new TestControllerError('NOT_FOUND', `Account ${accountId} not found`);\n * accounts.set(accountId, status);\n * return { success: true, previous_state: prev, current_state: status };\n * },\n * });\n * ```\n *\n * @example Session-backed store (factory shape)\n *\n * If your session state is persisted (Postgres/Redis/JSONB) and rehydrated into\n * a *new* object per request, closures over module-level state will silently\n * lose data between calls. Pass a factory object: `scenarios` declares the\n * static capability set (answered without invoking your factory), and\n * `createStore` runs per request so the returned store closes over the live\n * session.\n *\n * ```typescript\n * import { registerTestController, CONTROLLER_SCENARIOS, enforceMapCap } from '@adcp/sdk';\n *\n * registerTestController(server, {\n * scenarios: [CONTROLLER_SCENARIOS.FORCE_ACCOUNT_STATUS, CONTROLLER_SCENARIOS.FORCE_MEDIA_BUY_STATUS],\n * async createStore(input) {\n * const session = await loadSession((input.context as { session_id?: string })?.session_id);\n * return {\n * async forceAccountStatus(accountId, status) {\n * enforceMapCap(session.accountStatuses, accountId, 'account statuses');\n * const prev = session.accountStatuses.get(accountId) ?? 'active';\n * session.accountStatuses.set(accountId, status);\n * await saveSession(session);\n * return { success: true, previous_state: prev, current_state: status };\n * },\n * };\n * },\n * });\n * ```\n *\n * @example Custom MCP wrapper\n *\n * For wrappers that need `AsyncLocalStorage`, sandbox gating, or a custom task\n * store, bypass `registerTestController` and compose the exported building\n * blocks directly. `TOOL_INPUT_SHAPE` is the canonical Zod shape;\n * `toMcpResponse` produces the same envelope the default registration does.\n *\n * ```typescript\n * import { AsyncLocalStorage } from 'node:async_hooks';\n * import {\n * handleTestControllerRequest,\n * toMcpResponse,\n * TOOL_INPUT_SHAPE,\n * } from '@adcp/sdk';\n *\n * const sessionContext = new AsyncLocalStorage<{ sessionId: string }>();\n * const store = { async forceAccountStatus() { ... } };\n *\n * server.registerTool(\n * 'comply_test_controller',\n * { description: 'Sandbox only.', inputSchema: TOOL_INPUT_SHAPE },\n * async input => {\n * if (!sandboxEnabled()) {\n * return toMcpResponse({ success: false, error: 'FORBIDDEN', error_detail: 'Sandbox disabled' });\n * }\n * return sessionContext.run({ sessionId: (input.context as { session_id: string }).session_id }, async () => {\n * const response = await handleTestControllerRequest(store, input as Record<string, unknown>);\n * return toMcpResponse(response);\n * });\n * }\n * );\n * ```\n */\n\nimport { z } from 'zod';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { AdcpServer } from './adcp-server';\nimport { ADCP_CAPABILITIES, getSdkServer } from './adcp-server';\nimport type { GetAdCPCapabilitiesResponse } from '../types/tools.generated';\nimport type {\n ListScenariosSuccess,\n StateTransitionSuccess,\n SimulationSuccess,\n ForcedDirectiveSuccess,\n ControllerError,\n ComplyTestControllerResponse,\n ProvenanceAuditObservationsSuccess,\n ContextObject,\n ExtensionObject,\n} from '../types/tools.generated';\nimport type {\n AccountStatus,\n AudienceStatus,\n CatalogItemStatus,\n MediaBuyStatus,\n CreativeStatus,\n} from '../types/core.generated';\nimport {\n AccountStatusSchema,\n AudienceStatusSchema,\n CatalogItemStatusSchema,\n CreativeEventReasonCodeSchema,\n MediaBuyStatusSchema,\n CreativeStatusSchema,\n} from '../types/schemas.generated';\nimport type { McpToolResponse } from './responses';\nimport { toStructuredContent } from './responses';\n\n// ────────────────────────────────────────────────────────────\n// Scenario names\n// ────────────────────────────────────────────────────────────\n\n/** Scenario names advertised via `list_scenarios`.\n * `list_scenarios` itself is a discovery operation, not an advertised\n * capability, so it is excluded from the generated scenario union. */\nexport type ControllerScenario = ListScenariosSuccess['scenarios'][number];\n\n/** Scenario names accepted in `scenario` requests but not advertised via\n * `list_scenarios`. Sellers opt in by implementing the matching store method. */\nexport type SeedScenario =\n | 'seed_account'\n | 'seed_product'\n | 'seed_pricing_option'\n | 'seed_creative'\n | 'seed_plan'\n | 'seed_media_buy'\n | 'seed_creative_format'\n | 'seed_measurement_catalog'\n | 'seed_buyer_agent';\n\n/**\n * Scenario name constants for force_* and simulate_* (the advertised set).\n * Use in place of string literals for type-safe dispatch in custom wrappers,\n * tests, and factory `scenarios` declarations.\n *\n * ```ts\n * if (input.scenario === CONTROLLER_SCENARIOS.FORCE_ACCOUNT_STATUS) { ... }\n * ```\n */\nexport const CONTROLLER_SCENARIOS = {\n FORCE_CREATIVE_STATUS: 'force_creative_status',\n FORCE_ACCOUNT_STATUS: 'force_account_status',\n FORCE_MEDIA_BUY_STATUS: 'force_media_buy_status',\n FORCE_SESSION_STATUS: 'force_session_status',\n FORCE_CREATE_MEDIA_BUY_ARM: 'force_create_media_buy_arm',\n FORCE_TASK_COMPLETION: 'force_task_completion',\n FORCE_CREATIVE_PURGE: 'force_creative_purge',\n SIMULATE_DELIVERY: 'simulate_delivery',\n SIMULATE_BUDGET_SPEND: 'simulate_budget_spend',\n QUERY_PROVENANCE_AUDIT_OBSERVATIONS: 'query_provenance_audit_observations',\n QUERY_UPSTREAM_TRAFFIC: 'query_upstream_traffic',\n FORCE_UPSTREAM_UNAVAILABLE: 'force_upstream_unavailable',\n} as const satisfies Record<string, ControllerScenario>;\n\n/**\n * Seed scenario name constants. Used by seed dispatch and the\n * {@link createComplyController} domain-grouped façade. Seeds are not\n * advertised via `list_scenarios`.\n */\nexport const SEED_SCENARIOS = {\n SEED_ACCOUNT: 'seed_account',\n SEED_PRODUCT: 'seed_product',\n SEED_PRICING_OPTION: 'seed_pricing_option',\n SEED_CREATIVE: 'seed_creative',\n SEED_PLAN: 'seed_plan',\n SEED_MEDIA_BUY: 'seed_media_buy',\n SEED_CREATIVE_FORMAT: 'seed_creative_format',\n SEED_MEASUREMENT_CATALOG: 'seed_measurement_catalog',\n SEED_BUYER_AGENT: 'seed_buyer_agent',\n} as const satisfies Record<string, SeedScenario>;\n\n/**\n * Extension scenario names for async discovery storyboards. These are not\n * first-class members of the generated controller scenario enum until the\n * protocol schema lands them, but the controller contract is open for\n * extension and the dispatcher accepts string scenario names.\n *\n * Tracked upstream: adcontextprotocol/adcp#5342.\n */\nexport const DISCOVERY_ARM_SCENARIOS = {\n FORCE_GET_PRODUCTS_ARM: 'force_get_products_arm',\n FORCE_GET_SIGNALS_ARM: 'force_get_signals_arm',\n} as const;\n\n/**\n * Stable `SeedSuccess.message` strings the SDK's `dispatchSeed` emits.\n * Adopters who want to detect first-seed vs idempotent-replay can match\n * on these constants instead of grepping for the literal prose.\n *\n * Third-party sellers MAY emit any string the spec allows (the spec only\n * requires `success: true`); these constants are SDK-specific contracts\n * and not portable across implementations. For cross-implementation\n * replay detection, do not rely on `message`.\n */\nexport const SEED_MESSAGES = {\n fresh: 'Fixture seeded',\n replay: 'Fixture re-seeded (equivalent)',\n} as const;\n\n/**\n * Build-time check: every scenario in the generated union must appear in\n * {@link CONTROLLER_SCENARIOS}. When the protocol adds a new scenario and\n * this type goes non-`never`, TypeScript will reject the assignment below\n * and the build fails until the const is updated.\n */\ntype ExhaustiveScenarioCheck = Exclude<\n ControllerScenario,\n (typeof CONTROLLER_SCENARIOS)[keyof typeof CONTROLLER_SCENARIOS] | SeedScenario\n>;\nconst _scenarioExhaustivenessGuard: string extends ControllerScenario\n ? true\n : ExhaustiveScenarioCheck extends never\n ? true\n : never = true;\nvoid _scenarioExhaustivenessGuard;\n\n// ────────────────────────────────────────────────────────────\n// Store + factory interfaces\n// ────────────────────────────────────────────────────────────\n\n/**\n * Seller-side store for comply_test_controller.\n *\n * Implement the methods for each scenario you support.\n * Unimplemented methods mean that scenario is not advertised in list_scenarios.\n */\nexport interface TestControllerStore {\n /** Transition a creative to the specified status. */\n forceCreativeStatus?(\n creativeId: string,\n status: CreativeStatus,\n rejectionReason?: string\n ): Promise<StateTransitionSuccess>;\n\n /** Transition an account to the specified status. */\n forceAccountStatus?(accountId: string, status: AccountStatus): Promise<StateTransitionSuccess>;\n\n /** Transition a media buy to the specified status. */\n forceMediaBuyStatus?(\n mediaBuyId: string,\n status: MediaBuyStatus,\n rejectionReason?: string\n ): Promise<StateTransitionSuccess>;\n\n /** Transition an SI session to a terminal status. */\n forceSessionStatus?(\n sessionId: string,\n status: 'complete' | 'terminated',\n terminationReason?: string\n ): Promise<StateTransitionSuccess>;\n\n /**\n * Register a directive shaping the next `create_media_buy` call from this\n * authenticated sandbox account into the requested arm. The directive is\n * consumed on the next call. `arm: 'submitted'` requires `task_id` so the\n * seller's task envelope is deterministic (the buyer can drive\n * `tasks/get` with the registered id).\n */\n forceCreateMediaBuyArm?(params: {\n arm: 'submitted' | 'input-required';\n task_id?: string;\n message?: string;\n }): Promise<ForcedDirectiveSuccess>;\n\n /**\n * Register a directive shaping the next `get_products` call from this\n * authenticated sandbox account into the submitted async arm. Extension\n * scenario for async discovery storyboard prep (adcp#5342).\n */\n forceGetProductsArm?(params: {\n arm: 'submitted';\n task_id: string;\n message?: string;\n }): Promise<ForcedDirectiveSuccess>;\n\n /**\n * Register a directive shaping the next `get_signals` call from this\n * authenticated sandbox account into the submitted async arm. Extension\n * scenario for async discovery storyboard prep (adcp#5342).\n */\n forceGetSignalsArm?(params: { arm: 'submitted'; task_id: string; message?: string }): Promise<ForcedDirectiveSuccess>;\n\n /**\n * Transition an in-flight task to `completed` and record the supplied\n * completion payload. The seller MUST deliver `result` verbatim to the\n * buyer's `push_notification_config.url` per the AdCP 3.0 completion path.\n */\n forceTaskCompletion?(taskId: string, result: Record<string, unknown>): Promise<StateTransitionSuccess>;\n\n /** Destroy or tombstone a sandbox creative for account-level lifecycle webhook checks. */\n forceCreativePurge?(\n creativeId: string,\n params: {\n purge_kind?: 'soft' | 'hard';\n reason_code?: string;\n reason_detail?: string;\n [key: string]: unknown;\n }\n ): Promise<StateTransitionSuccess>;\n\n /**\n * Transition a synced audience to the specified matching status. Backs\n * the `impairment.coherence` audience inverse-rule traversal — storyboards\n * flip an audience offline to verify that downstream segments / activations\n * reflect the upstream state change. Advertised as `force_audience_status`\n * via `list_scenarios`.\n *\n * Extension scenario: not yet a member of `CONTROLLER_SCENARIOS` because\n * the schema cache's `ListScenariosSuccess['scenarios']` union doesn't\n * include `force_audience_status` (open-for-extension per spec). The\n * dispatcher accepts the literal string under `TOOL_INPUT_SHAPE.scenario:\n * z.string()`. Status values are validated against the spec-shipped\n * `AudienceStatusSchema` — when the spec adds offline values\n * (e.g. `suspended` for adcp#2860), codegen widens the schema and this\n * adapter automatically accepts them.\n *\n * Wire param convention: the dispatcher accepts `reason` (this issue's\n * proposed name, neutral across non-rejection transitions like\n * `processing → ready`) OR `rejection_reason` (the field name used by\n * `force_creative_status` / `force_media_buy_status`). Adapters receive\n * whichever was supplied as the single `reason` argument.\n *\n * TODO(adcp#2860): when the spec PR lands populated effects\n * (`matched_count`, `effective_match_rate`) on the `ready` transition,\n * the return shape may widen from plain `StateTransitionSuccess` to a\n * variant carrying `effects?`. Track the spec PR and update once the\n * schema cache picks it up.\n */\n forceAudienceStatus?(audienceId: string, status: AudienceStatus, reason?: string): Promise<StateTransitionSuccess>;\n\n /**\n * Transition a single catalog item to the specified review status. Backs\n * the catalog-side `impairment.coherence` traversal (item-level approval\n * flipping). Advertised as `force_catalog_item_status` via\n * `list_scenarios`.\n *\n * Extension scenario; see {@link forceAudienceStatus} for the rationale.\n * Status validated against `CatalogItemStatusSchema`. Same dual\n * `reason` / `rejection_reason` acceptance as `forceAudienceStatus`.\n *\n * Param name note: `catalog_item_id` (not `item_id`). Storyboard runner\n * bindings in `sales-catalog-driven` reference catalog items by this\n * name; the wire entity field is `item_id`, but the controller layer is\n * driven by storyboards, so we follow the binding convention.\n */\n forceCatalogItemStatus?(itemId: string, status: CatalogItemStatus, reason?: string): Promise<StateTransitionSuccess>;\n\n /** Inject synthetic delivery data for a media buy. */\n simulateDelivery?(\n mediaBuyId: string,\n params: {\n impressions?: number;\n clicks?: number;\n reported_spend?: { amount: number; currency: string };\n conversions?: number;\n [key: string]: unknown;\n }\n ): Promise<SimulationSuccess>;\n\n /** Simulate budget consumption to a specified percentage. */\n simulateBudgetSpend?(params: {\n account_id?: string;\n media_buy_id?: string;\n spend_percentage: number;\n [key: string]: unknown;\n }): Promise<SimulationSuccess>;\n\n /** Seed an account fixture. Returns when the fixture is persisted. */\n seedAccount?(accountId: string, fixture: Record<string, unknown> | undefined): Promise<void>;\n\n /** Seed a product fixture. Returns when the fixture is persisted. */\n seedProduct?(productId: string, fixture: Record<string, unknown> | undefined): Promise<void>;\n\n /** Seed a pricing option fixture (scoped to a product). */\n seedPricingOption?(\n productId: string,\n pricingOptionId: string,\n fixture: Record<string, unknown> | undefined\n ): Promise<void>;\n\n /** Seed a creative fixture. */\n seedCreative?(creativeId: string, fixture: Record<string, unknown> | undefined): Promise<void>;\n\n /** Seed a plan fixture. */\n seedPlan?(planId: string, fixture: Record<string, unknown> | undefined): Promise<void>;\n\n /** Seed a media-buy fixture. */\n seedMediaBuy?(mediaBuyId: string, fixture: Record<string, unknown> | undefined): Promise<void>;\n\n /**\n * Seed a creative-format fixture. The seller MUST expose this format ID\n * in `list_creative_formats` responses for the duration of the compliance\n * session.\n */\n seedCreativeFormat?(formatId: string, fixture: Record<string, unknown> | undefined): Promise<void>;\n\n /**\n * Seed a measurement catalog fixture. The params mirror the protocol\n * controller request (`vendor`, `metrics`, optional `fixture`) because the\n * catalog is keyed by vendor plus metric ids rather than a single id field.\n */\n seedMeasurementCatalog?(params: {\n vendor?: unknown;\n metrics?: unknown;\n fixture?: Record<string, unknown> | undefined;\n [key: string]: unknown;\n }): Promise<void>;\n\n /**\n * Seed a buyer-agent commercial relationship. The seller SHOULD scope the\n * seeded record to the current compliance session / auth principal so\n * storyboards can exercise per-agent gates without requiring a bespoke\n * bearer-prefix convention.\n */\n seedBuyerAgent?(agentUrl: string, fixture: Record<string, unknown> | undefined): Promise<void>;\n\n /** Return sandbox audit observations recorded for a creative. */\n queryProvenanceAuditObservations?(params: {\n creative_id: string;\n [key: string]: unknown;\n }): Promise<ProvenanceAuditObservationsSuccess>;\n\n /**\n * Return outbound HTTP calls the agent has made since `since_timestamp`,\n * scoped to the calling principal. Backs the `upstream_traffic` storyboard\n * validation per spec PR adcontextprotocol/adcp#3816 — runners assert that\n * the adapter actually called upstream with the storyboard-supplied\n * identifiers. The producer-side reference middleware lives at\n * `@adcp/sdk/upstream-recorder`; adopters typically delegate to\n * `recorder.query()` + `toQueryUpstreamTrafficResponse()` rather than\n * implementing this from scratch.\n *\n * Adopters opt in by implementing this method (advertised as\n * `query_upstream_traffic` via `list_scenarios`). First-class member of\n * `CONTROLLER_SCENARIOS` as of AdCP 3.1.0-beta.2 — spec PR adcp#3816\n * landed the scenario in `ListScenariosSuccess['scenarios']`.\n */\n queryUpstreamTraffic?(params: {\n since_timestamp?: string;\n endpoint_pattern?: string;\n limit?: number;\n attestation_mode?: 'raw' | 'digest';\n identifier_value_digests?: string[];\n }): Promise<UpstreamTrafficSuccessResponse>;\n\n /**\n * Mark an upstream dependency as unreachable for subsequent calls within\n * the compliance session. Backs the stale-response advisory storyboard.\n */\n forceUpstreamUnavailable?(params: { tool: string; upstream_name?: string }): Promise<StateTransitionSuccess>;\n}\n\n/**\n * Wire shape returned by `queryUpstreamTraffic` — mirrors\n * `UpstreamTrafficSuccess` in `comply-test-controller-response.json`\n * (spec PR adcontextprotocol/adcp#3816). Now available in the generated\n * types as of 3.1.0-beta.2; the local shape is preserved for adopters\n * mid-migration and for the SDK's typed `recorded_calls` widening.\n *\n * `recorded_calls` is typed as an opaque array. The spec's per-item shape is\n * a discriminated raw/digest attestation: raw items carry\n * `attestation_mode: \"raw\"`, `payload`, `payload_length`, and metadata;\n * digest items carry `attestation_mode: \"digest\"`,\n * `payload_digest_sha256`, `payload_length`, and optional\n * `identifier_match_proofs[]` for requested `identifier_value_digests`.\n * The runtime contract is intentionally not re-expressed here so adopters\n * can return their own typed `RecordedCall[]` (e.g. from\n * `@adcp/sdk/upstream-recorder`) without `exactOptionalPropertyTypes`\n * collisions on optional fields.\n * The wire-shape Ajv test in\n * `test/lib/upstream-recorder-spec-shape.test.js` enforces the runtime\n * contract; adopters validating their own shape against the spec schema\n * get the same guarantee.\n */\nexport interface UpstreamTrafficSuccessResponse {\n success: true;\n recorded_calls: ReadonlyArray<unknown>;\n total_count: number;\n truncated?: boolean;\n since_timestamp?: string;\n}\n\n/**\n * Factory shape for per-request stores. `scenarios` answers list_scenarios\n * without invoking `createStore`, so session-backed factories never run on\n * capability-discovery pings. `createStore` runs once per non-`list_scenarios`\n * request and returns a store bound to the current session.\n */\nexport interface TestControllerStoreFactory {\n /**\n * Static list of scenarios this factory's stores support. The SDK returns\n * this verbatim for `list_scenarios` — your `createStore` is skipped on\n * capability probes, so you don't have to load a session just to answer.\n */\n scenarios: readonly ControllerScenario[];\n\n /**\n * Build a {@link TestControllerStore} bound to the current request. Invoked\n * once per non-`list_scenarios` request with the raw tool input. Throw\n * {@link TestControllerError} for typed errors (`FORBIDDEN`, `NOT_FOUND`);\n * other exceptions surface as `INTERNAL_ERROR` with a scrubbed detail.\n */\n createStore(input: Record<string, unknown>): TestControllerStore | Promise<TestControllerStore>;\n}\n\n/** Either a pre-built store (simple case) or a per-request factory (session-backed case). */\nexport type TestControllerStoreOrFactory = TestControllerStore | TestControllerStoreFactory;\nexport type TestControllerErrorMetadata = {\n context?: ContextObject;\n ext?: ExtensionObject;\n};\n\nfunction isFactory(x: TestControllerStoreOrFactory): x is TestControllerStoreFactory {\n return typeof (x as TestControllerStoreFactory).createStore === 'function';\n}\n\n// ────────────────────────────────────────────────────────────\n// Error class for sellers\n// ────────────────────────────────────────────────────────────\n\n/**\n * Throw from TestControllerStore methods to return a typed controller error.\n *\n * @example\n * ```typescript\n * throw new TestControllerError('NOT_FOUND', `Account ${id} not found`);\n * throw new TestControllerError('INVALID_TRANSITION', 'Cannot pause a completed buy', 'completed');\n * throw new TestControllerError('INTERNAL_ERROR', 'JCS failed', null, {\n * context: { error_kind: 'jcs_non_finite' },\n * });\n * ```\n */\nexport class TestControllerError extends Error {\n constructor(\n public readonly code: ControllerError['error'],\n message: string,\n public readonly currentState?: string | null,\n public readonly metadata: TestControllerErrorMetadata = {}\n ) {\n super(message);\n this.name = 'TestControllerError';\n }\n}\n\n// ────────────────────────────────────────────────────────────\n// Quota guard\n// ────────────────────────────────────────────────────────────\n\n/**\n * Default cap on the number of entries a single session-scoped Map may hold\n * before {@link enforceMapCap} rejects further inserts. Tuned for compliance\n * testing workloads — raise the cap explicitly if you need a higher ceiling.\n */\nexport const SESSION_ENTRY_CAP = 1000;\n\n/** Max chars of `label` echoed into `enforceMapCap`'s error message. */\nconst MAX_LABEL_LENGTH = 64;\n\n/** Clamp `label` before interpolating into error messages — attacker-controlled\n * input could otherwise spam logs or derail structured detail parsing. */\nfunction sanitizeLabel(label: string): string {\n const trimmed = typeof label === 'string' ? label : String(label);\n return trimmed.slice(0, MAX_LABEL_LENGTH).replace(/[^\\x20-\\x7E]/g, '?');\n}\n\n/**\n * Reject new entries when `map` has reached `cap`. Use inside\n * {@link TestControllerStore} methods before `.set()` on any Map that\n * accumulates per-session state (account statuses, media-buy states,\n * simulated deliveries). Existing keys are allowed to overwrite — only\n * *net-new* keys are rejected at the cap.\n *\n * Rejects via {@link TestControllerError} with code `INVALID_STATE`, which the\n * dispatcher converts to a typed `ControllerError` response. The caller gets\n * a clear \"clear the session or reuse an existing id\" error rather than silent\n * LRU eviction that would make compliance tests nondeterministic.\n *\n * @param label - Human-readable plural noun used in the error message\n * (`\"account statuses\"`, `\"media buy states\"`). Intended to be a\n * caller-controlled string literal; defensively truncated to 64 chars and\n * stripped of non-printable characters before interpolation so user-controlled\n * input can't spam logs.\n *\n * @example\n * ```ts\n * async forceAccountStatus(accountId, status) {\n * enforceMapCap(session.accountStatuses, accountId, 'account statuses');\n * const prev = session.accountStatuses.get(accountId) ?? 'active';\n * session.accountStatuses.set(accountId, status);\n * return { success: true, previous_state: prev, current_state: status };\n * }\n * ```\n */\nexport function enforceMapCap<V>(\n map: Map<string, V>,\n key: string,\n label: string,\n cap: number = SESSION_ENTRY_CAP\n): void {\n if (!map.has(key) && map.size >= cap) {\n throw new TestControllerError(\n 'INVALID_STATE',\n `Too many ${sanitizeLabel(label)} entries (limit ${cap}). Clear the session or reuse an existing id.`\n );\n }\n}\n\n// ────────────────────────────────────────────────────────────\n// Request handler\n// ────────────────────────────────────────────────────────────\n\n/**\n * Force-transition extension scenarios for resource families whose status\n * enum exists in the spec but isn't yet a member of\n * `ListScenariosSuccess['scenarios']`. Issue #1819 — unblocks the\n * dependency-impairment storyboard cluster (adcp#2860) by giving adopters\n * a registration slot today; the value list expands automatically when the\n * spec adds offline values and codegen reruns.\n */\nconst FORCE_AUDIENCE_STATUS_SCENARIO = 'force_audience_status';\nconst FORCE_CATALOG_ITEM_STATUS_SCENARIO = 'force_catalog_item_status';\nconst CreativePurgeKindSchema = z.enum(['soft', 'hard']);\n\n/** Map store method presence to scenario names. */\nconst SCENARIO_MAP: Array<[keyof TestControllerStore, ControllerScenario]> = [\n ['forceCreativeStatus', CONTROLLER_SCENARIOS.FORCE_CREATIVE_STATUS],\n ['forceAccountStatus', CONTROLLER_SCENARIOS.FORCE_ACCOUNT_STATUS],\n ['forceMediaBuyStatus', CONTROLLER_SCENARIOS.FORCE_MEDIA_BUY_STATUS],\n ['forceSessionStatus', CONTROLLER_SCENARIOS.FORCE_SESSION_STATUS],\n ['forceCreateMediaBuyArm', CONTROLLER_SCENARIOS.FORCE_CREATE_MEDIA_BUY_ARM],\n ['forceTaskCompletion', CONTROLLER_SCENARIOS.FORCE_TASK_COMPLETION],\n ['forceCreativePurge', CONTROLLER_SCENARIOS.FORCE_CREATIVE_PURGE],\n ['simulateDelivery', CONTROLLER_SCENARIOS.SIMULATE_DELIVERY],\n ['simulateBudgetSpend', CONTROLLER_SCENARIOS.SIMULATE_BUDGET_SPEND],\n ['seedAccount', SEED_SCENARIOS.SEED_ACCOUNT],\n ['seedProduct', SEED_SCENARIOS.SEED_PRODUCT],\n ['seedPricingOption', SEED_SCENARIOS.SEED_PRICING_OPTION],\n ['seedCreative', SEED_SCENARIOS.SEED_CREATIVE],\n ['seedPlan', SEED_SCENARIOS.SEED_PLAN],\n ['seedMediaBuy', SEED_SCENARIOS.SEED_MEDIA_BUY],\n ['seedCreativeFormat', SEED_SCENARIOS.SEED_CREATIVE_FORMAT],\n ['seedMeasurementCatalog', SEED_SCENARIOS.SEED_MEASUREMENT_CATALOG],\n ['queryProvenanceAuditObservations', CONTROLLER_SCENARIOS.QUERY_PROVENANCE_AUDIT_OBSERVATIONS],\n ['queryUpstreamTraffic', CONTROLLER_SCENARIOS.QUERY_UPSTREAM_TRAFFIC],\n ['forceUpstreamUnavailable', CONTROLLER_SCENARIOS.FORCE_UPSTREAM_UNAVAILABLE],\n];\n\nfunction scenariosFromStore(store: TestControllerStore): ControllerScenario[] {\n return SCENARIO_MAP.filter(([method]) => typeof store[method] === 'function').map(([, scenario]) => scenario);\n}\n\n/**\n * All scenarios — first-class controller scenarios plus temporary extension\n * scenarios accepted by the dispatcher. Used for `list_scenarios`, which is\n * open for extension per the comply controller contract.\n */\nfunction allScenariosFromStore(store: TestControllerStore): string[] {\n const out: string[] = scenariosFromStore(store);\n if (typeof store.seedBuyerAgent === 'function') out.push(SEED_SCENARIOS.SEED_BUYER_AGENT);\n if (typeof store.forceAudienceStatus === 'function') out.push(FORCE_AUDIENCE_STATUS_SCENARIO);\n if (typeof store.forceCatalogItemStatus === 'function') out.push(FORCE_CATALOG_ITEM_STATUS_SCENARIO);\n if (typeof store.forceGetProductsArm === 'function') out.push(DISCOVERY_ARM_SCENARIOS.FORCE_GET_PRODUCTS_ARM);\n if (typeof store.forceGetSignalsArm === 'function') out.push(DISCOVERY_ARM_SCENARIOS.FORCE_GET_SIGNALS_ARM);\n return out;\n}\n\nfunction controllerError(\n code: ControllerError['error'],\n detail: string,\n currentState?: string | null,\n metadata: TestControllerErrorMetadata = {}\n): ComplyTestControllerResponse {\n // AdCP 3.1.0-beta.2+: envelope `status` is REQUIRED on every response,\n // including the error arms of a discriminated controller response.\n return {\n status: 'failed',\n success: false,\n error: code,\n error_detail: detail,\n ...(currentState !== undefined && { current_state: currentState }),\n ...(metadata.context !== undefined && { context: metadata.context }),\n ...(metadata.ext !== undefined && { ext: metadata.ext }),\n } as ComplyTestControllerResponse;\n}\n\n/**\n * AdCP 3.1.0-beta.2+: envelope `status` is REQUIRED on every response.\n * Store methods return bare success shapes (`StateTransitionSuccess`,\n * `UpstreamTrafficSuccessResponse`, etc.); wrap them with the envelope\n * `status: 'completed'` before returning to satisfy the response schema.\n *\n * No-op if the result already carries `status` (e.g. adopter explicitly\n * set it). The cast is needed because the TS interface for the store\n * methods predates the envelope-required change.\n */\nfunction wrapStoreSuccess<T extends object>(result: T): ComplyTestControllerResponse {\n if ('status' in result) return result as unknown as ComplyTestControllerResponse;\n return { status: 'completed', ...result } as unknown as ComplyTestControllerResponse;\n}\n\n// ────────────────────────────────────────────────────────────\n// Seed fixture idempotency\n// ────────────────────────────────────────────────────────────\n\n/**\n * Per-controller cache for seed-fixture equivalence checks. The handler uses\n * it to enforce the spec-level rule that re-seeding with the same ID and an\n * equivalent fixture yields `SeedSuccess` with `message: \"Fixture re-seeded\n * (equivalent)\"`, while a divergent fixture returns `INVALID_PARAMS`.\n *\n * Passed explicitly to {@link handleTestControllerRequest} so custom wrappers\n * can scope the cache to a session, tenant, or test run. Keys are\n * scenario-scoped (`seed_creative:cr-1`) to avoid cross-kind collisions.\n */\nexport interface SeedFixtureCache {\n get(key: string): Record<string, unknown> | undefined;\n set(key: string, fixture: Record<string, unknown>): void;\n has(key: string): boolean;\n}\n\n/** Build an in-memory {@link SeedFixtureCache}. Capped at {@link SESSION_ENTRY_CAP}\n * net-new keys to bound memory from a sandbox-authed caller that keeps seeding\n * fresh IDs. Existing keys are always writable so re-seeds continue to work at\n * the cap. Raise the cap explicitly for high-volume test runs. */\nexport function createSeedFixtureCache(cap: number = SESSION_ENTRY_CAP): SeedFixtureCache {\n const map = new Map<string, Record<string, unknown>>();\n return {\n get: k => map.get(k),\n set: (k, v) => {\n if (!map.has(k) && map.size >= cap) {\n throw new TestControllerError(\n 'INVALID_STATE',\n `Seed fixture cache full (limit ${cap}). Clear the session or reuse an existing id.`\n );\n }\n map.set(k, v);\n },\n has: k => map.has(k),\n };\n}\n\n/** Canonical JSON for equivalence checks. Sorts object keys recursively so\n * `{a:1,b:2}` matches `{b:2,a:1}`. Arrays remain order-sensitive. Uses a\n * seen-set to short-circuit circular references rather than stack-overflowing. */\nfunction canonicalJson(value: unknown, seen: WeakSet<object> = new WeakSet()): string {\n if (value === null || typeof value !== 'object') return JSON.stringify(value);\n if (seen.has(value as object)) return '\"__cycle__\"';\n seen.add(value as object);\n if (Array.isArray(value)) return `[${value.map(v => canonicalJson(v, seen)).join(',')}]`;\n const entries = Object.keys(value as Record<string, unknown>)\n .sort()\n .map(k => `${JSON.stringify(k)}:${canonicalJson((value as Record<string, unknown>)[k], seen)}`);\n return `{${entries.join(',')}}`;\n}\n\nfunction fixturesEquivalent(a: Record<string, unknown>, b: Record<string, unknown>): boolean {\n try {\n return canonicalJson(a) === canonicalJson(b);\n } catch {\n // BigInt, symbols, or other non-JSON-safe values — treat as divergent so\n // the caller gets a clear INVALID_PARAMS instead of an INTERNAL_ERROR.\n return false;\n }\n}\n\nfunction measurementCatalogMetricKey(metrics: unknown): string {\n if (Array.isArray(metrics)) {\n const metricIds = metrics.map(metric => {\n if (metric === null || typeof metric !== 'object') return undefined;\n const metricId = (metric as { metric_id?: unknown }).metric_id;\n return typeof metricId === 'string' && metricId.length > 0 ? metricId : undefined;\n });\n if (metricIds.every((metricId): metricId is string => metricId !== undefined)) {\n return canonicalJson([...new Set(metricIds)].sort());\n }\n }\n return canonicalJson(metrics);\n}\n\nconst BUYER_AGENT_ORDER_INSENSITIVE_FIELDS = new Set(['billing_capabilities', 'aliases', 'allowed_brands']);\n\nfunction normalizeBuyerAgentSeedFixture(fixture: Record<string, unknown>): Record<string, unknown> {\n const normalized: Record<string, unknown> = { ...fixture };\n for (const key of BUYER_AGENT_ORDER_INSENSITIVE_FIELDS) {\n const value = normalized[key];\n if (Array.isArray(value)) {\n normalized[key] = [...value].sort((a, b) => canonicalJson(a).localeCompare(canonicalJson(b)));\n }\n }\n return normalized;\n}\n\n/** Dispatch a seed scenario through the correct adapter method, honoring the\n * spec idempotency rules when a {@link SeedFixtureCache} is provided. */\n/**\n * Build the seed-cache key for a scenario. When `scope` is present (the\n * request's account / session / correlation scope), it's prefixed so two\n * sandbox accounts, sessions, or storyboard runs on one server can each seed\n * the same fixture id with divergent fixtures without colliding in the\n * process-wide `SeedFixtureCache`.\n *\n * Without scope (legacy callers, or requests with no `account` envelope),\n * the unscoped key is used — preserving the pre-#1215 behavior. Adopters\n * who relied on cross-account replay equivalence (rare, since storyboards\n * usually scope per account anyway) are unaffected when their requests\n * don't carry an `account` block.\n */\nfunction makeSeedCacheKey(unscopedKey: string, scope: string | undefined): string {\n return scope != null && scope.length > 0 ? `${scope}:${unscopedKey}` : unscopedKey;\n}\n\nfunction makeSeedCacheScope(input: Record<string, unknown>): string | undefined {\n const parts: string[] = [];\n const context = input.context;\n if (context != null && typeof context === 'object') {\n const sessionId = (context as { session_id?: unknown }).session_id;\n if (typeof sessionId === 'string' && sessionId.length > 0) parts.push(`session:${sessionId}`);\n const correlationId = (context as { correlation_id?: unknown }).correlation_id;\n if (typeof correlationId === 'string' && correlationId.length > 0) parts.push(`correlation:${correlationId}`);\n }\n\n const account =\n input.account ??\n (context != null && typeof context === 'object' ? (context as { account?: unknown }).account : undefined);\n if (account != null && typeof account === 'object') {\n const accountRef = account as { account_id?: unknown; brand?: unknown; operator?: unknown; sandbox?: unknown };\n if (typeof accountRef.account_id === 'string' && accountRef.account_id.length > 0) {\n parts.push(`account_id:${accountRef.account_id}`);\n } else if (\n accountRef.brand !== undefined ||\n accountRef.operator !== undefined ||\n accountRef.sandbox !== undefined\n ) {\n parts.push(\n `account_ref:${canonicalJson({\n ...(accountRef.brand !== undefined && { brand: accountRef.brand }),\n ...(accountRef.operator !== undefined && { operator: accountRef.operator }),\n ...(accountRef.sandbox !== undefined && { sandbox: accountRef.sandbox }),\n })}`\n );\n }\n }\n\n return parts.length > 0 ? parts.join('|') : undefined;\n}\n\nasync function dispatchSeed(\n store: TestControllerStore,\n scenario: SeedScenario,\n params: Record<string, unknown> | undefined,\n cache: SeedFixtureCache | undefined,\n scope: string | undefined\n): Promise<ComplyTestControllerResponse> {\n // Validate fixture is a plain object (not null, array, or primitive) before\n // casting — the adapter signature promises Record<string, unknown>.\n const rawFixture = params?.fixture;\n if (\n rawFixture !== undefined &&\n (typeof rawFixture !== 'object' || Array.isArray(rawFixture) || rawFixture === null)\n ) {\n return controllerError(\n 'INVALID_PARAMS',\n `${scenario} requires params.fixture to be an object (got ${Array.isArray(rawFixture) ? 'array' : typeof rawFixture})`\n );\n }\n const fixture = (rawFixture as Record<string, unknown> | undefined) ?? {};\n // Reject keys that can pollute prototypes when adapters spread the fixture\n // into plain objects. Checked only at the top level — nested uses\n // `Object.hasOwn` or similar in the adapter's own data layer.\n for (const key of Object.keys(fixture)) {\n if (key === '__proto__' || key === 'constructor' || key === 'prototype') {\n return controllerError(\n 'INVALID_PARAMS',\n `${scenario} fixture key ${JSON.stringify(key)} is reserved and rejected to prevent prototype pollution`\n );\n }\n }\n\n // Route to adapter + pick a cache key unique per (kind, id).\n type SeedDispatch = { key: string; fixture: Record<string, unknown>; invoke: () => Promise<void> };\n let dispatch: SeedDispatch | null = null;\n let missingParam: string | null = null;\n\n switch (scenario) {\n case SEED_SCENARIOS.SEED_ACCOUNT: {\n if (!params?.account_id) {\n missingParam = 'seed_account requires params.account_id';\n break;\n }\n if (!store.seedAccount) return controllerError('UNKNOWN_SCENARIO', `Scenario not supported: ${scenario}`);\n const accountId = params.account_id as string;\n dispatch = {\n key: makeSeedCacheKey(`seed_account:${accountId}`, scope),\n fixture,\n invoke: () => store.seedAccount!(accountId, fixture),\n };\n break;\n }\n case SEED_SCENARIOS.SEED_PRODUCT: {\n if (!params?.product_id) {\n missingParam = 'seed_product requires params.product_id';\n break;\n }\n if (!store.seedProduct) return controllerError('UNKNOWN_SCENARIO', `Scenario not supported: ${scenario}`);\n const productId = params.product_id as string;\n dispatch = {\n key: makeSeedCacheKey(`seed_product:${productId}`, scope),\n fixture,\n invoke: () => store.seedProduct!(productId, fixture),\n };\n break;\n }\n case SEED_SCENARIOS.SEED_PRICING_OPTION: {\n if (!params?.product_id || !params?.pricing_option_id) {\n missingParam = 'seed_pricing_option requires params.product_id and params.pricing_option_id';\n break;\n }\n if (!store.seedPricingOption) return controllerError('UNKNOWN_SCENARIO', `Scenario not supported: ${scenario}`);\n const productId = params.product_id as string;\n const pricingOptionId = params.pricing_option_id as string;\n dispatch = {\n key: makeSeedCacheKey(`seed_pricing_option:${productId}:${pricingOptionId}`, scope),\n fixture,\n invoke: () => store.seedPricingOption!(productId, pricingOptionId, fixture),\n };\n break;\n }\n case SEED_SCENARIOS.SEED_CREATIVE: {\n if (!params?.creative_id) {\n missingParam = 'seed_creative requires params.creative_id';\n break;\n }\n if (!store.seedCreative) return controllerError('UNKNOWN_SCENARIO', `Scenario not supported: ${scenario}`);\n const creativeId = params.creative_id as string;\n dispatch = {\n key: makeSeedCacheKey(`seed_creative:${creativeId}`, scope),\n fixture,\n invoke: () => store.seedCreative!(creativeId, fixture),\n };\n break;\n }\n case SEED_SCENARIOS.SEED_PLAN: {\n if (!params?.plan_id) {\n missingParam = 'seed_plan requires params.plan_id';\n break;\n }\n if (!store.seedPlan) return controllerError('UNKNOWN_SCENARIO', `Scenario not supported: ${scenario}`);\n const planId = params.plan_id as string;\n dispatch = {\n key: makeSeedCacheKey(`seed_plan:${planId}`, scope),\n fixture,\n invoke: () => store.seedPlan!(planId, fixture),\n };\n break;\n }\n case SEED_SCENARIOS.SEED_MEDIA_BUY: {\n if (!params?.media_buy_id) {\n missingParam = 'seed_media_buy requires params.media_buy_id';\n break;\n }\n if (!store.seedMediaBuy) return controllerError('UNKNOWN_SCENARIO', `Scenario not supported: ${scenario}`);\n const mediaBuyId = params.media_buy_id as string;\n dispatch = {\n key: makeSeedCacheKey(`seed_media_buy:${mediaBuyId}`, scope),\n fixture,\n invoke: () => store.seedMediaBuy!(mediaBuyId, fixture),\n };\n break;\n }\n case SEED_SCENARIOS.SEED_CREATIVE_FORMAT: {\n if (!params?.format_id) {\n missingParam = 'seed_creative_format requires params.format_id';\n break;\n }\n if (!store.seedCreativeFormat) return controllerError('UNKNOWN_SCENARIO', `Scenario not supported: ${scenario}`);\n const formatId = params.format_id as string;\n dispatch = {\n key: makeSeedCacheKey(`seed_creative_format:${formatId}`, scope),\n fixture,\n invoke: () => store.seedCreativeFormat!(formatId, fixture),\n };\n break;\n }\n case SEED_SCENARIOS.SEED_MEASUREMENT_CATALOG: {\n if (!params || !Object.hasOwn(params, 'vendor') || !Object.hasOwn(params, 'metrics')) {\n missingParam = 'seed_measurement_catalog requires params.vendor and params.metrics';\n break;\n }\n if (!store.seedMeasurementCatalog) {\n return controllerError('UNKNOWN_SCENARIO', `Scenario not supported: ${scenario}`);\n }\n const measurementCatalogFixture = { ...params, fixture };\n const measurementCatalogKey = [\n 'seed_measurement_catalog',\n canonicalJson(params.vendor ?? 'default'),\n measurementCatalogMetricKey(params.metrics),\n ].join(':');\n dispatch = {\n key: makeSeedCacheKey(measurementCatalogKey, scope),\n fixture: measurementCatalogFixture,\n invoke: () => store.seedMeasurementCatalog!(measurementCatalogFixture),\n };\n break;\n }\n case SEED_SCENARIOS.SEED_BUYER_AGENT: {\n if (!params?.agent_url) {\n missingParam = 'seed_buyer_agent requires params.agent_url';\n break;\n }\n if (!store.seedBuyerAgent) return controllerError('UNKNOWN_SCENARIO', `Scenario not supported: ${scenario}`);\n const agentUrl = params.agent_url as string;\n const { agent_url: _agentUrl, fixture: _fixture, ...directFields } = params;\n void _agentUrl;\n void _fixture;\n if (rawFixture !== undefined && Object.keys(directFields).length > 0) {\n return controllerError(\n 'INVALID_PARAMS',\n 'seed_buyer_agent accepts either direct params fields or params.fixture, not both'\n );\n }\n const buyerAgentFixture = normalizeBuyerAgentSeedFixture(rawFixture !== undefined ? fixture : directFields);\n if (Object.prototype.hasOwnProperty.call(buyerAgentFixture, 'agent_url')) {\n return controllerError(\n 'INVALID_PARAMS',\n 'seed_buyer_agent does not accept agent_url inside params.fixture; use top-level params.agent_url'\n );\n }\n for (const key of Object.keys(buyerAgentFixture)) {\n if (key === '__proto__' || key === 'constructor' || key === 'prototype') {\n return controllerError(\n 'INVALID_PARAMS',\n `${scenario} fixture key ${JSON.stringify(key)} is reserved and rejected to prevent prototype pollution`\n );\n }\n }\n dispatch = {\n key: makeSeedCacheKey(`seed_buyer_agent:${agentUrl}`, scope),\n fixture: buyerAgentFixture,\n invoke: () => store.seedBuyerAgent!(agentUrl, buyerAgentFixture),\n };\n break;\n }\n }\n\n if (missingParam) return controllerError('INVALID_PARAMS', missingParam);\n if (!dispatch) return controllerError('UNKNOWN_SCENARIO', `Scenario not supported: ${scenario}`);\n\n // Spec idempotency: same ID + equivalent fixture = existing; divergent = INVALID_PARAMS.\n // We also handle caches where has() can race with get() (TTL/LRU eviction) —\n // a missing prior entry is treated as a fresh seed rather than a crash.\n const prior = cache?.has(dispatch.key) ? cache.get(dispatch.key) : undefined;\n if (prior !== undefined) {\n if (!fixturesEquivalent(prior, dispatch.fixture)) {\n return controllerError(\n 'INVALID_PARAMS',\n `Fixture for ${sanitizeLabel(dispatch.key)} diverges from the previously seeded fixture. ` +\n 'Seed replays must carry an equivalent fixture or choose a new id.'\n );\n }\n await dispatch.invoke();\n // SeedSuccess (3.0.1+): message-only arm. The schema's `oneOf` excludes\n // `previous_state`/`current_state` from this branch — seeds are\n // pre-population, not entity transitions. {@link SEED_MESSAGES} carries\n // the SDK-specific replay-detection token.\n return wrapStoreSuccess({ success: true, message: SEED_MESSAGES.replay });\n }\n\n await dispatch.invoke();\n cache?.set(dispatch.key, dispatch.fixture);\n return wrapStoreSuccess({ success: true, message: SEED_MESSAGES.fresh });\n}\n\nasync function handleTestControllerRequestImpl(\n storeOrFactory: TestControllerStoreOrFactory,\n input: Record<string, unknown>,\n options?: { seedCache?: SeedFixtureCache }\n): Promise<ComplyTestControllerResponse> {\n const scenario = input.scenario as string | undefined;\n if (!scenario) {\n return controllerError('INVALID_PARAMS', 'Missing required field: scenario');\n }\n\n // list_scenarios is a stateless capability probe — answer it without\n // invoking the factory so session-backed factories don't need to tolerate\n // sessionless probes.\n if (scenario === 'list_scenarios') {\n const scenarios = isFactory(storeOrFactory) ? [...storeOrFactory.scenarios] : allScenariosFromStore(storeOrFactory);\n return wrapStoreSuccess({ success: true, scenarios });\n }\n\n let store: TestControllerStore;\n try {\n store = isFactory(storeOrFactory) ? await storeOrFactory.createStore(input) : storeOrFactory;\n } catch (err) {\n if (err instanceof TestControllerError) {\n return controllerError(err.code, err.message, err.currentState, err.metadata);\n }\n return controllerError(\n 'INTERNAL_ERROR',\n `Failed to resolve test controller store for scenario ${sanitizeLabel(scenario)}`\n );\n }\n\n const params = input.params as Record<string, unknown> | undefined;\n\n try {\n switch (scenario) {\n case CONTROLLER_SCENARIOS.FORCE_CREATIVE_STATUS: {\n if (!store.forceCreativeStatus) {\n return controllerError('UNKNOWN_SCENARIO', `Scenario not supported: ${scenario}`);\n }\n if (!params?.creative_id || !params?.status) {\n return controllerError(\n 'INVALID_PARAMS',\n 'force_creative_status requires params.creative_id and params.status'\n );\n }\n const creativeStatus = CreativeStatusSchema.safeParse(params.status);\n if (!creativeStatus.success) {\n return controllerError('INVALID_PARAMS', `Invalid creative status: ${params.status}`);\n }\n return wrapStoreSuccess(\n await store.forceCreativeStatus(\n params.creative_id as string,\n creativeStatus.data,\n params.rejection_reason as string | undefined\n )\n );\n }\n\n case CONTROLLER_SCENARIOS.FORCE_ACCOUNT_STATUS: {\n if (!store.forceAccountStatus) {\n return controllerError('UNKNOWN_SCENARIO', `Scenario not supported: ${scenario}`);\n }\n if (!params?.account_id || !params?.status) {\n return controllerError('INVALID_PARAMS', 'force_account_status requires params.account_id and params.status');\n }\n const accountStatus = AccountStatusSchema.safeParse(params.status);\n if (!accountStatus.success) {\n return controllerError('INVALID_PARAMS', `Invalid account status: ${params.status}`);\n }\n return wrapStoreSuccess(await store.forceAccountStatus(params.account_id as string, accountStatus.data));\n }\n\n case CONTROLLER_SCENARIOS.FORCE_MEDIA_BUY_STATUS: {\n if (!store.forceMediaBuyStatus) {\n return controllerError('UNKNOWN_SCENARIO', `Scenario not supported: ${scenario}`);\n }\n if (!params?.media_buy_id || !params?.status) {\n return controllerError(\n 'INVALID_PARAMS',\n 'force_media_buy_status requires params.media_buy_id and params.status'\n );\n }\n const mediaBuyStatus = MediaBuyStatusSchema.safeParse(params.status);\n if (!mediaBuyStatus.success) {\n return controllerError('INVALID_PARAMS', `Invalid media buy status: ${params.status}`);\n }\n return wrapStoreSuccess(\n await store.forceMediaBuyStatus(\n params.media_buy_id as string,\n mediaBuyStatus.data,\n params.rejection_reason as string | undefined\n )\n );\n }\n\n case CONTROLLER_SCENARIOS.FORCE_SESSION_STATUS: {\n if (!store.forceSessionStatus) {\n return controllerError('UNKNOWN_SCENARIO', `Scenario not supported: ${scenario}`);\n }\n if (!params?.session_id || !params?.status) {\n return controllerError('INVALID_PARAMS', 'force_session_status requires params.session_id and params.status');\n }\n const validSessionStatuses = ['complete', 'terminated'];\n if (!validSessionStatuses.includes(params.status as string)) {\n return controllerError('INVALID_PARAMS', `Invalid session status: ${params.status}`);\n }\n return wrapStoreSuccess(\n await store.forceSessionStatus(\n params.session_id as string,\n params.status as 'complete' | 'terminated',\n params.termination_reason as string | undefined\n )\n );\n }\n\n case CONTROLLER_SCENARIOS.SIMULATE_DELIVERY: {\n if (!store.simulateDelivery) {\n return controllerError('UNKNOWN_SCENARIO', `Scenario not supported: ${scenario}`);\n }\n if (!params?.media_buy_id) {\n return controllerError('INVALID_PARAMS', 'simulate_delivery requires params.media_buy_id');\n }\n // Spread params verbatim so extension fields (e.g. vendor_metric_values)\n // reach the adapter — params is spec-canonical additionalProperties:true.\n const { media_buy_id: simulateDeliveryId, ...simulateDeliveryRest } = params;\n return wrapStoreSuccess(await store.simulateDelivery(simulateDeliveryId as string, simulateDeliveryRest));\n }\n\n case CONTROLLER_SCENARIOS.SIMULATE_BUDGET_SPEND: {\n if (!store.simulateBudgetSpend) {\n return controllerError('UNKNOWN_SCENARIO', `Scenario not supported: ${scenario}`);\n }\n if (params?.spend_percentage === undefined || params?.spend_percentage === null) {\n return controllerError('INVALID_PARAMS', 'simulate_budget_spend requires params.spend_percentage');\n }\n if (!params?.account_id && !params?.media_buy_id) {\n return controllerError(\n 'INVALID_PARAMS',\n 'simulate_budget_spend requires params.account_id or params.media_buy_id'\n );\n }\n // Spread params verbatim so extension fields reach the adapter.\n return wrapStoreSuccess(\n await store.simulateBudgetSpend(\n params as {\n account_id?: string;\n media_buy_id?: string;\n spend_percentage: number;\n [key: string]: unknown;\n }\n )\n );\n }\n\n case CONTROLLER_SCENARIOS.FORCE_CREATE_MEDIA_BUY_ARM: {\n if (!store.forceCreateMediaBuyArm) {\n return controllerError('UNKNOWN_SCENARIO', `Scenario not supported: ${scenario}`);\n }\n const arm = params?.arm;\n if (arm !== 'submitted' && arm !== 'input-required') {\n return controllerError(\n 'INVALID_PARAMS',\n \"force_create_media_buy_arm requires params.arm = 'submitted' or 'input-required'\"\n );\n }\n if (arm === 'submitted' && !params?.task_id) {\n return controllerError(\n 'INVALID_PARAMS',\n \"force_create_media_buy_arm with arm='submitted' requires params.task_id\"\n );\n }\n // Spec: task_id is \"Present only when arm is 'submitted'\" — reject\n // it on the input-required arm so sellers can rely on the field's\n // presence as a discriminator.\n if (arm === 'input-required' && params?.task_id !== undefined) {\n return controllerError(\n 'INVALID_PARAMS',\n \"force_create_media_buy_arm with arm='input-required' must not include params.task_id\"\n );\n }\n return wrapStoreSuccess(\n await store.forceCreateMediaBuyArm({\n arm,\n task_id: params?.task_id as string | undefined,\n message: params?.message as string | undefined,\n })\n );\n }\n\n case DISCOVERY_ARM_SCENARIOS.FORCE_GET_PRODUCTS_ARM: {\n if (!store.forceGetProductsArm) {\n return controllerError('UNKNOWN_SCENARIO', `Scenario not supported: ${scenario}`);\n }\n const arm = params?.arm;\n if (arm !== 'submitted') {\n return controllerError('INVALID_PARAMS', \"force_get_products_arm requires params.arm = 'submitted'\");\n }\n if (!params?.task_id) {\n return controllerError(\n 'INVALID_PARAMS',\n \"force_get_products_arm with arm='submitted' requires params.task_id\"\n );\n }\n return wrapStoreSuccess(\n await store.forceGetProductsArm({\n arm,\n task_id: params.task_id as string,\n message: params?.message as string | undefined,\n })\n );\n }\n\n case DISCOVERY_ARM_SCENARIOS.FORCE_GET_SIGNALS_ARM: {\n if (!store.forceGetSignalsArm) {\n return controllerError('UNKNOWN_SCENARIO', `Scenario not supported: ${scenario}`);\n }\n const arm = params?.arm;\n if (arm !== 'submitted') {\n return controllerError('INVALID_PARAMS', \"force_get_signals_arm requires params.arm = 'submitted'\");\n }\n if (!params?.task_id) {\n return controllerError(\n 'INVALID_PARAMS',\n \"force_get_signals_arm with arm='submitted' requires params.task_id\"\n );\n }\n return wrapStoreSuccess(\n await store.forceGetSignalsArm({\n arm,\n task_id: params.task_id as string,\n message: params?.message as string | undefined,\n })\n );\n }\n\n case CONTROLLER_SCENARIOS.FORCE_TASK_COMPLETION: {\n if (!store.forceTaskCompletion) {\n return controllerError('UNKNOWN_SCENARIO', `Scenario not supported: ${scenario}`);\n }\n if (!params?.task_id) {\n return controllerError('INVALID_PARAMS', 'force_task_completion requires params.task_id');\n }\n // Reject arrays explicitly — `typeof [] === 'object'` would let an\n // array slip past the object check, but `result` is a structured\n // completion payload (validates against async-response-data.json),\n // never an array.\n if (!params?.result || typeof params.result !== 'object' || Array.isArray(params.result)) {\n return controllerError(\n 'INVALID_PARAMS',\n 'force_task_completion requires params.result (completion payload object)'\n );\n }\n return wrapStoreSuccess(\n await store.forceTaskCompletion(params.task_id as string, params.result as Record<string, unknown>)\n );\n }\n\n case CONTROLLER_SCENARIOS.FORCE_CREATIVE_PURGE: {\n if (!store.forceCreativePurge) {\n return controllerError('UNKNOWN_SCENARIO', `Scenario not supported: ${scenario}`);\n }\n if (!params?.creative_id) {\n return controllerError('INVALID_PARAMS', 'force_creative_purge requires params.creative_id');\n }\n const { creative_id: creativeId, ...purgeParams } = params;\n if (\n purgeParams.purge_kind !== undefined &&\n !CreativePurgeKindSchema.safeParse(purgeParams.purge_kind).success\n ) {\n return controllerError('INVALID_PARAMS', 'force_creative_purge params.purge_kind must be soft or hard');\n }\n if (\n purgeParams.reason_code !== undefined &&\n !CreativeEventReasonCodeSchema.safeParse(purgeParams.reason_code).success\n ) {\n return controllerError(\n 'INVALID_PARAMS',\n 'force_creative_purge params.reason_code must be a CreativeEventReasonCode'\n );\n }\n return wrapStoreSuccess(await store.forceCreativePurge(creativeId as string, purgeParams));\n }\n\n case SEED_SCENARIOS.SEED_ACCOUNT:\n case SEED_SCENARIOS.SEED_PRODUCT:\n case SEED_SCENARIOS.SEED_PRICING_OPTION:\n case SEED_SCENARIOS.SEED_CREATIVE:\n case SEED_SCENARIOS.SEED_PLAN:\n case SEED_SCENARIOS.SEED_MEDIA_BUY:\n case SEED_SCENARIOS.SEED_CREATIVE_FORMAT:\n case SEED_SCENARIOS.SEED_MEASUREMENT_CATALOG:\n case SEED_SCENARIOS.SEED_BUYER_AGENT: {\n // Seed-cache scope (issues #1215/#2156). Two sandbox accounts,\n // sessions, or storyboard correlation buckets on one server can each\n // seed the same fixture id with divergent fixtures; without a scope\n // prefix the cache treats divergent replays as INVALID_PARAMS even when\n // each scoped fixture is internally self-consistent. No resolver call here — test-controller is a\n // generic helper that doesn't know about platform.accounts.resolve,\n // and read-time misalignment is fine because the cache only governs\n // idempotency, not user-visible state.\n const scope = makeSeedCacheScope(input);\n return await dispatchSeed(store, scenario as SeedScenario, params, options?.seedCache, scope);\n }\n\n // Extension scenarios accepted by the dispatcher until the spec\n // promotes them to first-class controller scenarios.\n case FORCE_AUDIENCE_STATUS_SCENARIO: {\n if (!store.forceAudienceStatus) {\n return controllerError('UNKNOWN_SCENARIO', `Scenario not supported: ${scenario}`);\n }\n if (!params?.audience_id || !params?.status) {\n return controllerError(\n 'INVALID_PARAMS',\n 'force_audience_status requires params.audience_id and params.status'\n );\n }\n const audienceStatus = AudienceStatusSchema.safeParse(params.status);\n if (!audienceStatus.success) {\n return controllerError('INVALID_PARAMS', `Invalid audience status: ${params.status}`);\n }\n // Accept either `reason` (this issue's proposed shape, neutral across\n // transitions like processing→ready) or `rejection_reason` (the field\n // name used by force_creative_status / force_media_buy_status). The\n // adapter receives whichever was supplied — de-risks the eventual spec\n // PR (adcp#2860) picking either name.\n return wrapStoreSuccess(\n await store.forceAudienceStatus(\n params.audience_id as string,\n audienceStatus.data,\n (params.reason ?? params.rejection_reason) as string | undefined\n )\n );\n }\n\n case FORCE_CATALOG_ITEM_STATUS_SCENARIO: {\n if (!store.forceCatalogItemStatus) {\n return controllerError('UNKNOWN_SCENARIO', `Scenario not supported: ${scenario}`);\n }\n if (!params?.catalog_item_id || !params?.status) {\n return controllerError(\n 'INVALID_PARAMS',\n 'force_catalog_item_status requires params.catalog_item_id and params.status'\n );\n }\n const itemStatus = CatalogItemStatusSchema.safeParse(params.status);\n if (!itemStatus.success) {\n return controllerError('INVALID_PARAMS', `Invalid catalog item status: ${params.status}`);\n }\n // See force_audience_status above — same dual-name acceptance.\n return wrapStoreSuccess(\n await store.forceCatalogItemStatus(\n params.catalog_item_id as string,\n itemStatus.data,\n (params.reason ?? params.rejection_reason) as string | undefined\n )\n );\n }\n\n case CONTROLLER_SCENARIOS.QUERY_PROVENANCE_AUDIT_OBSERVATIONS: {\n if (!store.queryProvenanceAuditObservations) {\n return controllerError('UNKNOWN_SCENARIO', `Scenario not supported: ${scenario}`);\n }\n const queryParams = (params ?? {}) as { creative_id?: unknown; [key: string]: unknown };\n if (typeof queryParams.creative_id !== 'string' || queryParams.creative_id.length === 0) {\n return controllerError('INVALID_PARAMS', 'query_provenance_audit_observations requires params.creative_id');\n }\n return wrapStoreSuccess(\n await store.queryProvenanceAuditObservations({\n ...queryParams,\n creative_id: queryParams.creative_id,\n })\n );\n }\n\n case CONTROLLER_SCENARIOS.QUERY_UPSTREAM_TRAFFIC: {\n if (!store.queryUpstreamTraffic) {\n return controllerError('UNKNOWN_SCENARIO', `Scenario not supported: ${scenario}`);\n }\n const queryParams = (params ?? {}) as Parameters<NonNullable<TestControllerStore['queryUpstreamTraffic']>>[0];\n return wrapStoreSuccess(await store.queryUpstreamTraffic(queryParams));\n }\n\n case CONTROLLER_SCENARIOS.FORCE_UPSTREAM_UNAVAILABLE: {\n if (!store.forceUpstreamUnavailable) {\n return controllerError('UNKNOWN_SCENARIO', `Scenario not supported: ${scenario}`);\n }\n const forceParams = (params ?? {}) as { tool?: unknown; upstream_name?: unknown };\n if (typeof forceParams.tool !== 'string' || forceParams.tool.length === 0) {\n return controllerError('INVALID_PARAMS', 'force_upstream_unavailable requires params.tool');\n }\n return wrapStoreSuccess(\n await store.forceUpstreamUnavailable({\n tool: forceParams.tool,\n upstream_name: typeof forceParams.upstream_name === 'string' ? forceParams.upstream_name : undefined,\n })\n );\n }\n\n default:\n return controllerError('UNKNOWN_SCENARIO', 'Unrecognized scenario name');\n }\n } catch (err) {\n if (err instanceof TestControllerError) {\n return controllerError(err.code, err.message, err.currentState, err.metadata);\n }\n return controllerError('INTERNAL_ERROR', 'An unexpected error occurred in the test controller store');\n }\n}\n\n/**\n * Handle a `comply_test_controller` request. Exported so custom MCP wrappers\n * can compose it with their own middleware (AsyncLocalStorage, sandbox gating,\n * logging) without going through {@link registerTestController}.\n *\n * `list_scenarios` is answered from the static capability set (store method\n * presence for plain stores, `factory.scenarios` for factories) and never\n * invokes `factory.createStore`. Session-backed factories can therefore load\n * real session state unconditionally.\n *\n * `input.context` is echoed into every response branch so correlation IDs\n * round-trip per the `comply-test-controller-response.json` schema.\n */\nexport async function handleTestControllerRequest(\n storeOrFactory: TestControllerStoreOrFactory,\n input: Record<string, unknown>,\n options?: { seedCache?: SeedFixtureCache }\n): Promise<ComplyTestControllerResponse> {\n const ctx = input.context;\n const result = await handleTestControllerRequestImpl(storeOrFactory, input, options);\n if (ctx !== undefined && typeof ctx === 'object' && ctx !== null && result.context === undefined) {\n return { ...result, context: ctx } as ComplyTestControllerResponse;\n }\n return result;\n}\n\n// ────────────────────────────────────────────────────────────\n// MCP envelope helpers\n// ────────────────────────────────────────────────────────────\n\nfunction summarize(data: ComplyTestControllerResponse): string {\n if (data.success === false) return `Controller error: ${data.error}`;\n if ('scenarios' in data) return `Supported scenarios: ${data.scenarios.join(', ')}`;\n if ('previous_state' in data) {\n return `Transitioned from ${data.previous_state} to ${data.current_state}`;\n }\n if ('simulated' in data) return `Simulation complete: ${JSON.stringify(data.simulated)}`;\n if ('forced' in data) return `Directive registered: arm=${data.forced.arm}`;\n // SeedSuccess (3.0.1+): message-only arm. dispatchSeed sets the message\n // to 'Fixture seeded' / 'Fixture re-seeded (equivalent)'; third-party\n // sellers may emit other strings (or none — the spec only requires\n // success). UpstreamTrafficSuccess (3.1+): no message field — summarize\n // recorded-calls count instead.\n if ('message' in data && typeof data.message === 'string') return data.message;\n if ('recorded_calls' in data) {\n return `Recorded ${data.total_count} upstream call(s)${data.truncated ? ' (truncated)' : ''}`;\n }\n return 'Scenario succeeded';\n}\n\n/**\n * Wrap a {@link ComplyTestControllerResponse} in an MCP tool response envelope\n * (`content` + `structuredContent` + `isError`). Exported so custom wrappers\n * can reuse the same summary/error shape as {@link registerTestController}.\n */\nexport function toMcpResponse(data: ComplyTestControllerResponse): McpToolResponse & { isError?: true } {\n const isError = data.success === false;\n return {\n content: [{ type: 'text', text: summarize(data) }],\n structuredContent: toStructuredContent(data),\n ...(isError && { isError: true }),\n };\n}\n\n/**\n * Canonical Zod input schema for the `comply_test_controller` tool. Pass as\n * the `inputSchema` argument to `server.registerTool(...)` in custom wrappers\n * so the request shape stays in sync with {@link registerTestController}.\n *\n * Matches `ComplyTestControllerRequest` from the generated schema: `scenario`\n * (required), `params` (scenario-specific), and the universal `context` / `ext`\n * envelope fields. No top-level `account` or `brand` — AdCP routes account\n * and brand context through `context` on this tool.\n *\n * **Extending for vendor fields.** Custom wrappers that route sandbox gating\n * or tenant scoping on top-level fields can extend the shape locally.\n * Both `account` and `brand` are commonly added — storyboard fixtures and\n * v5-shaped wrappers often send them alongside `params`:\n *\n * ```ts\n * const MY_SHAPE = {\n * ...TOOL_INPUT_SHAPE,\n * account: z.object({ sandbox: z.boolean() }).passthrough().optional(),\n * brand: z.object({ domain: z.string() }).passthrough().optional(),\n * };\n * server.registerTool(\n * 'comply_test_controller',\n * { description: 'Sandbox only.', inputSchema: MY_SHAPE },\n * async input => {\n * if (input.account?.sandbox !== true) return toMcpResponse({ ... });\n * return toMcpResponse(await handleTestControllerRequest(store, input as Record<string, unknown>));\n * }\n * );\n * ```\n *\n * Adopters routing through `createAdcpServerFromPlatform({ complyTest })`\n * pass the same fields via `complyTest.inputSchema` (see\n * `ComplyControllerConfig.inputSchema` in `@adcp/sdk/testing`).\n *\n * This keeps the default registration protocol-compliant while giving\n * wrapper authors a documented extension point.\n */\nexport const TOOL_INPUT_SHAPE = {\n scenario: z.string().describe('Scenario to execute (e.g., list_scenarios, force_account_status)'),\n params: z.record(z.string(), z.unknown()).optional().describe('Scenario-specific parameters'),\n context: z.record(z.string(), z.unknown()).optional().describe('AdCP context object'),\n ext: z.record(z.string(), z.unknown()).optional().describe('AdCP extension object'),\n};\n\n// ────────────────────────────────────────────────────────────\n// MCP tool registration\n// ────────────────────────────────────────────────────────────\n\n/**\n * Register the `comply_test_controller` tool on an MCP server.\n *\n * Accepts a plain {@link TestControllerStore} (scenarios inferred from\n * implemented methods) or a {@link TestControllerStoreFactory} (scenarios\n * declared explicitly; `createStore` runs per request for session-backed\n * state). See the module-level examples for both patterns.\n *\n * The `server` argument takes either an `AdcpServer` from\n * `createAdcpServer()` or a raw SDK `McpServer` from\n * `createTaskCapableServer()` — the helper unwraps the opaque handle\n * when needed so tool registration reaches the underlying SDK server.\n */\nexport function registerTestController(\n server: AdcpServer | McpServer,\n storeOrFactory: TestControllerStoreOrFactory,\n options?: { seedCache?: SeedFixtureCache }\n): void {\n const mcp = getSdkServer(server as AdcpServer) ?? (server as McpServer);\n // Per-registration cache so seed idempotency holds across all requests on\n // this server instance. Callers can supply their own cache to scope by\n // session or tenant.\n const seedCache = options?.seedCache ?? createSeedFixtureCache();\n\n // Per AdCP 3.0, comply_test_controller support is declared via the\n // top-level `compliance_testing` capability block — NOT as an entry in\n // `supported_protocols`. When we've been given an AdcpServer produced by\n // createAdcpServer, auto-populate that block. The scenarios list comes\n // from the factory's static `scenarios` (factory shape) or is inferred\n // from the plain store's method presence.\n const capsBag = (server as unknown as Record<PropertyKey, unknown>)[ADCP_CAPABILITIES] as\n | GetAdCPCapabilitiesResponse\n | undefined;\n if (capsBag) {\n const incoming = isFactory(storeOrFactory) ? [...storeOrFactory.scenarios] : scenariosFromStore(storeOrFactory);\n if (incoming.length > 0) {\n // Merge with any previously-declared scenarios rather than silently\n // dropping them. A server can wire more than one controller (e.g.\n // media-buy + governance) and each call should contribute its\n // scenarios to the advertised set.\n const existing = (capsBag.compliance_testing?.scenarios ?? []) as readonly string[];\n const merged = Array.from(new Set([...existing, ...incoming]));\n capsBag.compliance_testing = { scenarios: merged } as GetAdCPCapabilitiesResponse['compliance_testing'];\n }\n }\n mcp.registerTool(\n 'comply_test_controller',\n {\n description: 'Triggers seller-side state transitions for compliance testing. Sandbox only.',\n inputSchema: TOOL_INPUT_SHAPE,\n },\n (async (input: Record<string, unknown>) => {\n const response = await handleTestControllerRequest(storeOrFactory, input, {\n seedCache,\n });\n return toMcpResponse(response);\n }) as Parameters<typeof mcp.registerTool>[2]\n );\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/lib/server/test-controller.ts"],"sourcesContent":["/**\n * Server-side comply_test_controller implementation.\n *\n * Most new code should use {@link createComplyController} from\n * `@adcp/sdk/testing` — it wraps this module with a domain-grouped\n * (`seed` / `force` / `simulate`) adapter surface, typed params, sandbox\n * gating, and built-in seed idempotency. The functions below remain\n * supported for existing integrations and for custom wrappers that need\n * access to the underlying request handler / MCP envelope helpers.\n *\n * Sellers typically call `registerTestController(server, store)` to add the\n * `comply_test_controller` tool to their MCP server. The TestControllerStore\n * interface lets sellers wire up their own state management while the SDK\n * handles request parsing, scenario dispatch, and response formatting.\n *\n * @example Basic usage — single in-memory store\n * ```typescript\n * import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\n * import { registerTestController, TestControllerError } from '@adcp/sdk';\n *\n * const accounts = new Map<string, string>();\n *\n * registerTestController(server, {\n * async forceAccountStatus(accountId, status) {\n * const prev = accounts.get(accountId);\n * if (!prev) throw new TestControllerError('NOT_FOUND', `Account ${accountId} not found`);\n * accounts.set(accountId, status);\n * return { success: true, previous_state: prev, current_state: status };\n * },\n * });\n * ```\n *\n * @example Session-backed store (factory shape)\n *\n * If your session state is persisted (Postgres/Redis/JSONB) and rehydrated into\n * a *new* object per request, closures over module-level state will silently\n * lose data between calls. Pass a factory object: `scenarios` declares the\n * static capability set (answered without invoking your factory), and\n * `createStore` runs per request so the returned store closes over the live\n * session.\n *\n * ```typescript\n * import { registerTestController, CONTROLLER_SCENARIOS, enforceMapCap } from '@adcp/sdk';\n *\n * registerTestController(server, {\n * scenarios: [CONTROLLER_SCENARIOS.FORCE_ACCOUNT_STATUS, CONTROLLER_SCENARIOS.FORCE_MEDIA_BUY_STATUS],\n * async createStore(input) {\n * const session = await loadSession((input.context as { session_id?: string })?.session_id);\n * return {\n * async forceAccountStatus(accountId, status) {\n * enforceMapCap(session.accountStatuses, accountId, 'account statuses');\n * const prev = session.accountStatuses.get(accountId) ?? 'active';\n * session.accountStatuses.set(accountId, status);\n * await saveSession(session);\n * return { success: true, previous_state: prev, current_state: status };\n * },\n * };\n * },\n * });\n * ```\n *\n * @example Custom MCP wrapper\n *\n * For wrappers that need `AsyncLocalStorage`, sandbox gating, or a custom task\n * store, bypass `registerTestController` and compose the exported building\n * blocks directly. `TOOL_INPUT_SHAPE` is the canonical Zod shape;\n * `toMcpResponse` produces the same envelope the default registration does.\n *\n * ```typescript\n * import { AsyncLocalStorage } from 'node:async_hooks';\n * import {\n * handleTestControllerRequest,\n * toMcpResponse,\n * TOOL_INPUT_SHAPE,\n * } from '@adcp/sdk';\n *\n * const sessionContext = new AsyncLocalStorage<{ sessionId: string }>();\n * const store = { async forceAccountStatus() { ... } };\n *\n * server.registerTool(\n * 'comply_test_controller',\n * { description: 'Sandbox only.', inputSchema: TOOL_INPUT_SHAPE },\n * async input => {\n * if (!sandboxEnabled()) {\n * return toMcpResponse({ success: false, error: 'FORBIDDEN', error_detail: 'Sandbox disabled' });\n * }\n * return sessionContext.run({ sessionId: (input.context as { session_id: string }).session_id }, async () => {\n * const response = await handleTestControllerRequest(store, input as Record<string, unknown>);\n * return toMcpResponse(response);\n * });\n * }\n * );\n * ```\n */\n\nimport { z } from 'zod';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { AdcpServer } from './adcp-server';\nimport { ADCP_CAPABILITIES, getSdkServer } from './adcp-server';\nimport type { GetAdCPCapabilitiesResponse } from '../types/tools.generated';\nimport type {\n ListScenariosSuccess,\n StateTransitionSuccess,\n SimulationSuccess,\n ForcedDirectiveSuccess,\n ControllerError,\n ComplyTestControllerResponse,\n ProvenanceAuditObservationsSuccess,\n ContextObject,\n ExtensionObject,\n} from '../types/tools.generated';\nimport type {\n AccountStatus,\n AudienceStatus,\n CatalogItemStatus,\n MediaBuyStatus,\n CreativeStatus,\n} from '../types/core.generated';\nimport {\n AccountStatusSchema,\n AudienceStatusSchema,\n CatalogItemStatusSchema,\n CreativeEventReasonCodeSchema,\n MediaBuyStatusSchema,\n CreativeStatusSchema,\n} from '../types/schemas.generated';\nimport type { McpToolResponse } from './responses';\nimport { toStructuredContent } from './responses';\n\n// ────────────────────────────────────────────────────────────\n// Scenario names\n// ────────────────────────────────────────────────────────────\n\n/** Scenario names advertised via `list_scenarios`.\n * `list_scenarios` itself is a discovery operation, not an advertised\n * capability, so it is excluded from the generated scenario union. */\nexport type ControllerScenario = ListScenariosSuccess['scenarios'][number];\n\n/** Scenario names accepted in `scenario` requests but not advertised via\n * `list_scenarios`. Sellers opt in by implementing the matching store method. */\nexport type SeedScenario =\n | 'seed_account'\n | 'seed_product'\n | 'seed_pricing_option'\n | 'seed_creative'\n | 'seed_plan'\n | 'seed_media_buy'\n | 'seed_creative_format'\n | 'seed_measurement_catalog'\n | 'seed_buyer_agent';\n\n/**\n * Scenario name constants for force_* and simulate_* (the advertised set).\n * Use in place of string literals for type-safe dispatch in custom wrappers,\n * tests, and factory `scenarios` declarations.\n *\n * ```ts\n * if (input.scenario === CONTROLLER_SCENARIOS.FORCE_ACCOUNT_STATUS) { ... }\n * ```\n */\nexport const CONTROLLER_SCENARIOS = {\n FORCE_CREATIVE_STATUS: 'force_creative_status',\n FORCE_ACCOUNT_STATUS: 'force_account_status',\n FORCE_MEDIA_BUY_STATUS: 'force_media_buy_status',\n FORCE_SESSION_STATUS: 'force_session_status',\n FORCE_CREATE_MEDIA_BUY_ARM: 'force_create_media_buy_arm',\n FORCE_TASK_COMPLETION: 'force_task_completion',\n FORCE_CREATIVE_PURGE: 'force_creative_purge',\n SIMULATE_DELIVERY: 'simulate_delivery',\n SIMULATE_BUDGET_SPEND: 'simulate_budget_spend',\n QUERY_PROVENANCE_AUDIT_OBSERVATIONS: 'query_provenance_audit_observations',\n QUERY_UPSTREAM_TRAFFIC: 'query_upstream_traffic',\n FORCE_UPSTREAM_UNAVAILABLE: 'force_upstream_unavailable',\n} as const satisfies Record<string, ControllerScenario>;\n\n/**\n * Seed scenario name constants. Used by seed dispatch and the\n * {@link createComplyController} domain-grouped façade. Seeds are not\n * advertised via `list_scenarios`.\n */\nexport const SEED_SCENARIOS = {\n SEED_ACCOUNT: 'seed_account',\n SEED_PRODUCT: 'seed_product',\n SEED_PRICING_OPTION: 'seed_pricing_option',\n SEED_CREATIVE: 'seed_creative',\n SEED_PLAN: 'seed_plan',\n SEED_MEDIA_BUY: 'seed_media_buy',\n SEED_CREATIVE_FORMAT: 'seed_creative_format',\n SEED_MEASUREMENT_CATALOG: 'seed_measurement_catalog',\n SEED_BUYER_AGENT: 'seed_buyer_agent',\n} as const satisfies Record<string, SeedScenario>;\n\n/**\n * Extension scenario names for async discovery storyboards. These are not\n * first-class members of the generated controller scenario enum until the\n * protocol schema lands them, but the controller contract is open for\n * extension and the dispatcher accepts string scenario names.\n *\n * Tracked upstream: adcontextprotocol/adcp#5342.\n */\nexport const DISCOVERY_ARM_SCENARIOS = {\n FORCE_GET_PRODUCTS_ARM: 'force_get_products_arm',\n FORCE_GET_SIGNALS_ARM: 'force_get_signals_arm',\n} as const;\n\n/**\n * Stable `SeedSuccess.message` strings the SDK's `dispatchSeed` emits.\n * Adopters who want to detect first-seed vs idempotent-replay can match\n * on these constants instead of grepping for the literal prose.\n *\n * Third-party sellers MAY emit any string the spec allows (the spec only\n * requires `success: true`); these constants are SDK-specific contracts\n * and not portable across implementations. For cross-implementation\n * replay detection, do not rely on `message`.\n */\nexport const SEED_MESSAGES = {\n fresh: 'Fixture seeded',\n replay: 'Fixture re-seeded (equivalent)',\n} as const;\n\n/**\n * Build-time check: every scenario in the generated union must appear in\n * {@link CONTROLLER_SCENARIOS}. When the protocol adds a new scenario and\n * this type goes non-`never`, TypeScript will reject the assignment below\n * and the build fails until the const is updated.\n */\ntype ExhaustiveScenarioCheck = Exclude<\n ControllerScenario,\n (typeof CONTROLLER_SCENARIOS)[keyof typeof CONTROLLER_SCENARIOS] | SeedScenario\n>;\nconst _scenarioExhaustivenessGuard: string extends ControllerScenario\n ? true\n : ExhaustiveScenarioCheck extends never\n ? true\n : never = true;\nvoid _scenarioExhaustivenessGuard;\n\n// ────────────────────────────────────────────────────────────\n// Store + factory interfaces\n// ────────────────────────────────────────────────────────────\n\n/**\n * Seller-side store for comply_test_controller.\n *\n * Implement the methods for each scenario you support.\n * Unimplemented methods mean that scenario is not advertised in list_scenarios.\n */\nexport interface TestControllerStore {\n /** Transition a creative to the specified status. */\n forceCreativeStatus?(\n creativeId: string,\n status: CreativeStatus,\n rejectionReason?: string\n ): Promise<StateTransitionSuccess>;\n\n /** Transition an account to the specified status. */\n forceAccountStatus?(accountId: string, status: AccountStatus): Promise<StateTransitionSuccess>;\n\n /** Transition a media buy to the specified status. */\n forceMediaBuyStatus?(\n mediaBuyId: string,\n status: MediaBuyStatus,\n rejectionReason?: string\n ): Promise<StateTransitionSuccess>;\n\n /** Transition an SI session to a terminal status. */\n forceSessionStatus?(\n sessionId: string,\n status: 'complete' | 'terminated',\n terminationReason?: string\n ): Promise<StateTransitionSuccess>;\n\n /**\n * Register a directive shaping the next `create_media_buy` call from this\n * authenticated sandbox account into the requested arm. The directive is\n * consumed on the next call. `arm: 'submitted'` requires `task_id` so the\n * seller's task envelope is deterministic (the buyer can drive\n * `tasks/get` with the registered id).\n */\n forceCreateMediaBuyArm?(params: {\n arm: 'submitted' | 'input-required';\n task_id?: string;\n message?: string;\n }): Promise<ForcedDirectiveSuccess>;\n\n /**\n * Register a directive shaping the next `get_products` call from this\n * authenticated sandbox account into the submitted async arm. Extension\n * scenario for async discovery storyboard prep (adcp#5342).\n */\n forceGetProductsArm?(params: {\n arm: 'submitted';\n task_id: string;\n message?: string;\n }): Promise<ForcedDirectiveSuccess>;\n\n /**\n * Register a directive shaping the next `get_signals` call from this\n * authenticated sandbox account into the submitted async arm. Extension\n * scenario for async discovery storyboard prep (adcp#5342).\n */\n forceGetSignalsArm?(params: { arm: 'submitted'; task_id: string; message?: string }): Promise<ForcedDirectiveSuccess>;\n\n /**\n * Transition an in-flight task to `completed` and record the supplied\n * completion payload. The seller MUST deliver `result` verbatim to the\n * buyer's `push_notification_config.url` per the AdCP 3.0 completion path.\n */\n forceTaskCompletion?(taskId: string, result: Record<string, unknown>): Promise<StateTransitionSuccess>;\n\n /** Destroy or tombstone a sandbox creative for account-level lifecycle webhook checks. */\n forceCreativePurge?(\n creativeId: string,\n params: {\n purge_kind?: 'soft' | 'hard';\n reason_code?: string;\n reason_detail?: string;\n [key: string]: unknown;\n }\n ): Promise<StateTransitionSuccess>;\n\n /**\n * Transition a synced audience to the specified matching status. Backs\n * the `impairment.coherence` audience inverse-rule traversal — storyboards\n * flip an audience offline to verify that downstream segments / activations\n * reflect the upstream state change. Advertised as `force_audience_status`\n * via `list_scenarios`.\n *\n * Extension scenario: not yet a member of `CONTROLLER_SCENARIOS` because\n * the schema cache's `ListScenariosSuccess['scenarios']` union doesn't\n * include `force_audience_status` (open-for-extension per spec). The\n * dispatcher accepts the literal string under `TOOL_INPUT_SHAPE.scenario:\n * z.string()`. Status values are validated against the spec-shipped\n * `AudienceStatusSchema` — when the spec adds offline values\n * (e.g. `suspended` for adcp#2860), codegen widens the schema and this\n * adapter automatically accepts them.\n *\n * Wire param convention: the dispatcher accepts `reason` (this issue's\n * proposed name, neutral across non-rejection transitions like\n * `processing → ready`) OR `rejection_reason` (the field name used by\n * `force_creative_status` / `force_media_buy_status`). Adapters receive\n * whichever was supplied as the single `reason` argument.\n *\n * TODO(adcp#2860): when the spec PR lands populated effects\n * (`matched_count`, `effective_match_rate`) on the `ready` transition,\n * the return shape may widen from plain `StateTransitionSuccess` to a\n * variant carrying `effects?`. Track the spec PR and update once the\n * schema cache picks it up.\n */\n forceAudienceStatus?(audienceId: string, status: AudienceStatus, reason?: string): Promise<StateTransitionSuccess>;\n\n /**\n * Transition a single catalog item to the specified review status. Backs\n * the catalog-side `impairment.coherence` traversal (item-level approval\n * flipping). Advertised as `force_catalog_item_status` via\n * `list_scenarios`.\n *\n * Extension scenario; see {@link forceAudienceStatus} for the rationale.\n * Status validated against `CatalogItemStatusSchema`. Same dual\n * `reason` / `rejection_reason` acceptance as `forceAudienceStatus`.\n *\n * Param name note: `catalog_item_id` (not `item_id`). Storyboard runner\n * bindings in `sales-catalog-driven` reference catalog items by this\n * name; the wire entity field is `item_id`, but the controller layer is\n * driven by storyboards, so we follow the binding convention.\n */\n forceCatalogItemStatus?(itemId: string, status: CatalogItemStatus, reason?: string): Promise<StateTransitionSuccess>;\n\n /** Inject synthetic delivery data for a media buy. */\n simulateDelivery?(\n mediaBuyId: string,\n params: {\n impressions?: number;\n clicks?: number;\n reported_spend?: { amount: number; currency: string };\n conversions?: number;\n [key: string]: unknown;\n }\n ): Promise<SimulationSuccess>;\n\n /** Simulate budget consumption to a specified percentage. */\n simulateBudgetSpend?(params: {\n account_id?: string;\n media_buy_id?: string;\n spend_percentage: number;\n [key: string]: unknown;\n }): Promise<SimulationSuccess>;\n\n /** Seed an account fixture. Returns when the fixture is persisted. */\n seedAccount?(accountId: string, fixture: Record<string, unknown> | undefined): Promise<void>;\n\n /** Seed a product fixture. Returns when the fixture is persisted. */\n seedProduct?(productId: string, fixture: Record<string, unknown> | undefined): Promise<void>;\n\n /** Seed a pricing option fixture (scoped to a product). */\n seedPricingOption?(\n productId: string,\n pricingOptionId: string,\n fixture: Record<string, unknown> | undefined\n ): Promise<void>;\n\n /** Seed a creative fixture. */\n seedCreative?(creativeId: string, fixture: Record<string, unknown> | undefined): Promise<void>;\n\n /** Seed a plan fixture. */\n seedPlan?(planId: string, fixture: Record<string, unknown> | undefined): Promise<void>;\n\n /** Seed a media-buy fixture. */\n seedMediaBuy?(mediaBuyId: string, fixture: Record<string, unknown> | undefined): Promise<void>;\n\n /**\n * Seed a creative-format fixture. The seller MUST expose this format ID\n * in `list_creative_formats` responses for the duration of the compliance\n * session.\n */\n seedCreativeFormat?(formatId: string, fixture: Record<string, unknown> | undefined): Promise<void>;\n\n /**\n * Seed a measurement catalog fixture. The params mirror the protocol\n * controller request (`vendor`, `metrics`, optional `fixture`) because the\n * catalog is keyed by vendor plus metric ids rather than a single id field.\n */\n seedMeasurementCatalog?(params: {\n vendor?: unknown;\n metrics?: unknown;\n fixture?: Record<string, unknown> | undefined;\n [key: string]: unknown;\n }): Promise<void>;\n\n /**\n * Seed a buyer-agent commercial relationship. The seller SHOULD scope the\n * seeded record to the current compliance session / auth principal so\n * storyboards can exercise per-agent gates without requiring a bespoke\n * bearer-prefix convention.\n */\n seedBuyerAgent?(agentUrl: string, fixture: Record<string, unknown> | undefined): Promise<void>;\n\n /** Return sandbox audit observations recorded for a creative. */\n queryProvenanceAuditObservations?(params: {\n creative_id: string;\n [key: string]: unknown;\n }): Promise<ProvenanceAuditObservationsSuccess>;\n\n /**\n * Return outbound HTTP calls the agent has made since `since_timestamp`,\n * scoped to the calling principal. Backs the `upstream_traffic` storyboard\n * validation per spec PR adcontextprotocol/adcp#3816 — runners assert that\n * the adapter actually called upstream with the storyboard-supplied\n * identifiers. The producer-side reference middleware lives at\n * `@adcp/sdk/upstream-recorder`; adopters typically delegate to\n * `recorder.query()` + `toQueryUpstreamTrafficResponse()` rather than\n * implementing this from scratch.\n *\n * Adopters opt in by implementing this method (advertised as\n * `query_upstream_traffic` via `list_scenarios`). First-class member of\n * `CONTROLLER_SCENARIOS` as of AdCP 3.1.0-beta.2 — spec PR adcp#3816\n * landed the scenario in `ListScenariosSuccess['scenarios']`.\n */\n queryUpstreamTraffic?(params: {\n since_timestamp?: string;\n endpoint_pattern?: string;\n limit?: number;\n attestation_mode?: 'raw' | 'digest';\n identifier_value_digests?: string[];\n }): Promise<UpstreamTrafficSuccessResponse>;\n\n /**\n * Mark an upstream dependency as unreachable for subsequent calls within\n * the compliance session. Backs the stale-response advisory storyboard.\n */\n forceUpstreamUnavailable?(params: { tool: string; upstream_name?: string }): Promise<StateTransitionSuccess>;\n}\n\n/**\n * Wire shape returned by `queryUpstreamTraffic` — mirrors\n * `UpstreamTrafficSuccess` in `comply-test-controller-response.json`\n * (spec PR adcontextprotocol/adcp#3816). Now available in the generated\n * types as of 3.1.0-beta.2; the local shape is preserved for adopters\n * mid-migration and for the SDK's typed `recorded_calls` widening.\n *\n * `recorded_calls` is typed as an opaque array. The spec's per-item shape is\n * a discriminated raw/digest attestation: raw items carry\n * `attestation_mode: \"raw\"`, `payload`, `payload_length`, and metadata;\n * digest items carry `attestation_mode: \"digest\"`,\n * `payload_digest_sha256`, `payload_length`, and optional\n * `identifier_match_proofs[]` for requested `identifier_value_digests`.\n * The runtime contract is intentionally not re-expressed here so adopters\n * can return their own typed `RecordedCall[]` (e.g. from\n * `@adcp/sdk/upstream-recorder`) without `exactOptionalPropertyTypes`\n * collisions on optional fields.\n * The wire-shape Ajv test in\n * `test/lib/upstream-recorder-spec-shape.test.js` enforces the runtime\n * contract; adopters validating their own shape against the spec schema\n * get the same guarantee.\n */\nexport interface UpstreamTrafficSuccessResponse {\n success: true;\n recorded_calls: ReadonlyArray<unknown>;\n total_count: number;\n truncated?: boolean;\n since_timestamp?: string;\n}\n\n/**\n * Factory shape for per-request stores. `scenarios` answers list_scenarios\n * without invoking `createStore`, so session-backed factories never run on\n * capability-discovery pings. `createStore` runs once per non-`list_scenarios`\n * request and returns a store bound to the current session.\n */\nexport interface TestControllerStoreFactory {\n /**\n * Static list of scenarios this factory's stores support. The SDK returns\n * this verbatim for `list_scenarios` — your `createStore` is skipped on\n * capability probes, so you don't have to load a session just to answer.\n */\n scenarios: readonly ControllerScenario[];\n\n /**\n * Build a {@link TestControllerStore} bound to the current request. Invoked\n * once per non-`list_scenarios` request with the raw tool input. Throw\n * {@link TestControllerError} for typed errors (`FORBIDDEN`, `NOT_FOUND`);\n * other exceptions surface as `INTERNAL_ERROR` with a scrubbed detail.\n */\n createStore(input: Record<string, unknown>): TestControllerStore | Promise<TestControllerStore>;\n}\n\n/** Either a pre-built store (simple case) or a per-request factory (session-backed case). */\nexport type TestControllerStoreOrFactory = TestControllerStore | TestControllerStoreFactory;\nexport type TestControllerErrorMetadata = {\n context?: ContextObject;\n ext?: ExtensionObject;\n};\n\nfunction isFactory(x: TestControllerStoreOrFactory): x is TestControllerStoreFactory {\n return typeof (x as TestControllerStoreFactory).createStore === 'function';\n}\n\n// ────────────────────────────────────────────────────────────\n// Error class for sellers\n// ────────────────────────────────────────────────────────────\n\n/**\n * Throw from TestControllerStore methods to return a typed controller error.\n *\n * @example\n * ```typescript\n * throw new TestControllerError('NOT_FOUND', `Account ${id} not found`);\n * throw new TestControllerError('INVALID_TRANSITION', 'Cannot pause a completed buy', 'completed');\n * throw new TestControllerError('INTERNAL_ERROR', 'JCS failed', null, {\n * context: { error_kind: 'jcs_non_finite' },\n * });\n * ```\n */\nexport class TestControllerError extends Error {\n constructor(\n public readonly code: ControllerError['error'],\n message: string,\n public readonly currentState?: string | null,\n public readonly metadata: TestControllerErrorMetadata = {}\n ) {\n super(message);\n this.name = 'TestControllerError';\n }\n}\n\n// ────────────────────────────────────────────────────────────\n// Quota guard\n// ────────────────────────────────────────────────────────────\n\n/**\n * Default cap on the number of entries a single session-scoped Map may hold\n * before {@link enforceMapCap} rejects further inserts. Tuned for compliance\n * testing workloads — raise the cap explicitly if you need a higher ceiling.\n */\nexport const SESSION_ENTRY_CAP = 1000;\n\n/** Max chars of `label` echoed into `enforceMapCap`'s error message. */\nconst MAX_LABEL_LENGTH = 64;\n\n/** Clamp `label` before interpolating into error messages — attacker-controlled\n * input could otherwise spam logs or derail structured detail parsing. */\nfunction sanitizeLabel(label: string): string {\n const trimmed = typeof label === 'string' ? label : String(label);\n return trimmed.slice(0, MAX_LABEL_LENGTH).replace(/[^\\x20-\\x7E]/g, '?');\n}\n\n/**\n * Reject new entries when `map` has reached `cap`. Use inside\n * {@link TestControllerStore} methods before `.set()` on any Map that\n * accumulates per-session state (account statuses, media-buy states,\n * simulated deliveries). Existing keys are allowed to overwrite — only\n * *net-new* keys are rejected at the cap.\n *\n * Rejects via {@link TestControllerError} with code `INVALID_STATE`, which the\n * dispatcher converts to a typed `ControllerError` response. The caller gets\n * a clear \"clear the session or reuse an existing id\" error rather than silent\n * LRU eviction that would make compliance tests nondeterministic.\n *\n * @param label - Human-readable plural noun used in the error message\n * (`\"account statuses\"`, `\"media buy states\"`). Intended to be a\n * caller-controlled string literal; defensively truncated to 64 chars and\n * stripped of non-printable characters before interpolation so user-controlled\n * input can't spam logs.\n *\n * @example\n * ```ts\n * async forceAccountStatus(accountId, status) {\n * enforceMapCap(session.accountStatuses, accountId, 'account statuses');\n * const prev = session.accountStatuses.get(accountId) ?? 'active';\n * session.accountStatuses.set(accountId, status);\n * return { success: true, previous_state: prev, current_state: status };\n * }\n * ```\n */\nexport function enforceMapCap<V>(\n map: Map<string, V>,\n key: string,\n label: string,\n cap: number = SESSION_ENTRY_CAP\n): void {\n if (!map.has(key) && map.size >= cap) {\n throw new TestControllerError(\n 'INVALID_STATE',\n `Too many ${sanitizeLabel(label)} entries (limit ${cap}). Clear the session or reuse an existing id.`\n );\n }\n}\n\n// ────────────────────────────────────────────────────────────\n// Request handler\n// ────────────────────────────────────────────────────────────\n\n/**\n * Force-transition extension scenarios for resource families whose status\n * enum exists in the spec but isn't yet a member of\n * `ListScenariosSuccess['scenarios']`. Issue #1819 — unblocks the\n * dependency-impairment storyboard cluster (adcp#2860) by giving adopters\n * a registration slot today; the value list expands automatically when the\n * spec adds offline values and codegen reruns.\n */\nconst FORCE_AUDIENCE_STATUS_SCENARIO = 'force_audience_status';\nconst FORCE_CATALOG_ITEM_STATUS_SCENARIO = 'force_catalog_item_status';\nconst CreativePurgeKindSchema = z.enum(['soft', 'hard']);\n\n/** Map store method presence to scenario names. */\nconst SCENARIO_MAP: Array<[keyof TestControllerStore, ControllerScenario]> = [\n ['forceCreativeStatus', CONTROLLER_SCENARIOS.FORCE_CREATIVE_STATUS],\n ['forceAccountStatus', CONTROLLER_SCENARIOS.FORCE_ACCOUNT_STATUS],\n ['forceMediaBuyStatus', CONTROLLER_SCENARIOS.FORCE_MEDIA_BUY_STATUS],\n ['forceSessionStatus', CONTROLLER_SCENARIOS.FORCE_SESSION_STATUS],\n ['forceCreateMediaBuyArm', CONTROLLER_SCENARIOS.FORCE_CREATE_MEDIA_BUY_ARM],\n ['forceTaskCompletion', CONTROLLER_SCENARIOS.FORCE_TASK_COMPLETION],\n ['forceCreativePurge', CONTROLLER_SCENARIOS.FORCE_CREATIVE_PURGE],\n ['simulateDelivery', CONTROLLER_SCENARIOS.SIMULATE_DELIVERY],\n ['simulateBudgetSpend', CONTROLLER_SCENARIOS.SIMULATE_BUDGET_SPEND],\n ['seedAccount', SEED_SCENARIOS.SEED_ACCOUNT],\n ['seedProduct', SEED_SCENARIOS.SEED_PRODUCT],\n ['seedPricingOption', SEED_SCENARIOS.SEED_PRICING_OPTION],\n ['seedCreative', SEED_SCENARIOS.SEED_CREATIVE],\n ['seedPlan', SEED_SCENARIOS.SEED_PLAN],\n ['seedMediaBuy', SEED_SCENARIOS.SEED_MEDIA_BUY],\n ['seedCreativeFormat', SEED_SCENARIOS.SEED_CREATIVE_FORMAT],\n ['seedMeasurementCatalog', SEED_SCENARIOS.SEED_MEASUREMENT_CATALOG],\n ['queryProvenanceAuditObservations', CONTROLLER_SCENARIOS.QUERY_PROVENANCE_AUDIT_OBSERVATIONS],\n ['queryUpstreamTraffic', CONTROLLER_SCENARIOS.QUERY_UPSTREAM_TRAFFIC],\n ['forceUpstreamUnavailable', CONTROLLER_SCENARIOS.FORCE_UPSTREAM_UNAVAILABLE],\n];\n\nfunction scenariosFromStore(store: TestControllerStore): ControllerScenario[] {\n return SCENARIO_MAP.filter(([method]) => typeof store[method] === 'function').map(([, scenario]) => scenario);\n}\n\n/**\n * All scenarios — first-class controller scenarios plus temporary extension\n * scenarios accepted by the dispatcher. Used for `list_scenarios`, which is\n * open for extension per the comply controller contract.\n */\nfunction allScenariosFromStore(store: TestControllerStore): string[] {\n const out: string[] = scenariosFromStore(store);\n if (typeof store.seedBuyerAgent === 'function') out.push(SEED_SCENARIOS.SEED_BUYER_AGENT);\n if (typeof store.forceAudienceStatus === 'function') out.push(FORCE_AUDIENCE_STATUS_SCENARIO);\n if (typeof store.forceCatalogItemStatus === 'function') out.push(FORCE_CATALOG_ITEM_STATUS_SCENARIO);\n if (typeof store.forceGetProductsArm === 'function') out.push(DISCOVERY_ARM_SCENARIOS.FORCE_GET_PRODUCTS_ARM);\n if (typeof store.forceGetSignalsArm === 'function') out.push(DISCOVERY_ARM_SCENARIOS.FORCE_GET_SIGNALS_ARM);\n return out;\n}\n\nfunction controllerError(\n code: ControllerError['error'],\n detail: string,\n currentState?: string | null,\n metadata: TestControllerErrorMetadata = {}\n): ComplyTestControllerResponse {\n // AdCP 3.1.0-beta.2+: envelope `status` is REQUIRED on every response,\n // including the error arms of a discriminated controller response.\n return {\n status: 'failed',\n success: false,\n error: code,\n error_detail: detail,\n ...(currentState !== undefined && { current_state: currentState }),\n ...(metadata.context !== undefined && { context: metadata.context }),\n ...(metadata.ext !== undefined && { ext: metadata.ext }),\n } as ComplyTestControllerResponse;\n}\n\n/**\n * AdCP 3.1.0-beta.2+: envelope `status` is REQUIRED on every response.\n * Store methods return bare success shapes (`StateTransitionSuccess`,\n * `UpstreamTrafficSuccessResponse`, etc.); wrap them with the envelope\n * `status: 'completed'` before returning to satisfy the response schema.\n *\n * No-op if the result already carries `status` (e.g. adopter explicitly\n * set it). The cast is needed because the TS interface for the store\n * methods predates the envelope-required change.\n */\nfunction wrapStoreSuccess<T extends object>(result: T): ComplyTestControllerResponse {\n if ('status' in result) return result as unknown as ComplyTestControllerResponse;\n return { status: 'completed', ...result } as unknown as ComplyTestControllerResponse;\n}\n\n// ────────────────────────────────────────────────────────────\n// Seed fixture idempotency\n// ────────────────────────────────────────────────────────────\n\n/**\n * Per-controller cache for seed-fixture equivalence checks. The handler uses\n * it to enforce the spec-level rule that re-seeding with the same ID and an\n * equivalent fixture yields `SeedSuccess` with `message: \"Fixture re-seeded\n * (equivalent)\"`, while a divergent fixture returns `INVALID_PARAMS`.\n *\n * Passed explicitly to {@link handleTestControllerRequest} so custom wrappers\n * can scope the cache to a session, tenant, or test run. Keys are\n * scenario-scoped (`seed_creative:cr-1`) to avoid cross-kind collisions.\n */\nexport interface SeedFixtureCache {\n get(key: string): Record<string, unknown> | undefined;\n set(key: string, fixture: Record<string, unknown>): void;\n has(key: string): boolean;\n}\n\n/** Build an in-memory {@link SeedFixtureCache}. Capped at {@link SESSION_ENTRY_CAP}\n * net-new keys to bound memory from a sandbox-authed caller that keeps seeding\n * fresh IDs. Existing keys are always writable so re-seeds continue to work at\n * the cap. Raise the cap explicitly for high-volume test runs. */\nexport function createSeedFixtureCache(cap: number = SESSION_ENTRY_CAP): SeedFixtureCache {\n const map = new Map<string, Record<string, unknown>>();\n return {\n get: k => map.get(k),\n set: (k, v) => {\n if (!map.has(k) && map.size >= cap) {\n throw new TestControllerError(\n 'INVALID_STATE',\n `Seed fixture cache full (limit ${cap}). Clear the session or reuse an existing id.`\n );\n }\n map.set(k, v);\n },\n has: k => map.has(k),\n };\n}\n\n/** Canonical JSON for equivalence checks. Sorts object keys recursively so\n * `{a:1,b:2}` matches `{b:2,a:1}`. Arrays remain order-sensitive. Uses a\n * seen-set to short-circuit circular references rather than stack-overflowing. */\nfunction canonicalJson(value: unknown, seen: WeakSet<object> = new WeakSet()): string {\n if (value === null || typeof value !== 'object') return JSON.stringify(value);\n if (seen.has(value as object)) return '\"__cycle__\"';\n seen.add(value as object);\n if (Array.isArray(value)) return `[${value.map(v => canonicalJson(v, seen)).join(',')}]`;\n const entries = Object.keys(value as Record<string, unknown>)\n .sort()\n .map(k => `${JSON.stringify(k)}:${canonicalJson((value as Record<string, unknown>)[k], seen)}`);\n return `{${entries.join(',')}}`;\n}\n\nfunction fixturesEquivalent(a: Record<string, unknown>, b: Record<string, unknown>): boolean {\n try {\n return canonicalJson(a) === canonicalJson(b);\n } catch {\n // BigInt, symbols, or other non-JSON-safe values — treat as divergent so\n // the caller gets a clear INVALID_PARAMS instead of an INTERNAL_ERROR.\n return false;\n }\n}\n\nfunction measurementCatalogMetricKey(metrics: unknown): string {\n if (Array.isArray(metrics)) {\n const metricIds = metrics.map(metric => {\n if (metric === null || typeof metric !== 'object') return undefined;\n const metricId = (metric as { metric_id?: unknown }).metric_id;\n return typeof metricId === 'string' && metricId.length > 0 ? metricId : undefined;\n });\n if (metricIds.every((metricId): metricId is string => metricId !== undefined)) {\n return canonicalJson([...new Set(metricIds)].sort());\n }\n }\n return canonicalJson(metrics);\n}\n\nconst BUYER_AGENT_ORDER_INSENSITIVE_FIELDS = new Set(['billing_capabilities', 'aliases', 'allowed_brands']);\n\nfunction normalizeBuyerAgentSeedFixture(fixture: Record<string, unknown>): Record<string, unknown> {\n const normalized: Record<string, unknown> = { ...fixture };\n for (const key of BUYER_AGENT_ORDER_INSENSITIVE_FIELDS) {\n const value = normalized[key];\n if (Array.isArray(value)) {\n normalized[key] = [...value].sort((a, b) => canonicalJson(a).localeCompare(canonicalJson(b)));\n }\n }\n return normalized;\n}\n\n/** Dispatch a seed scenario through the correct adapter method, honoring the\n * spec idempotency rules when a {@link SeedFixtureCache} is provided. */\n/**\n * Build the seed-cache key for a scenario. When `scope` is present (the\n * request's account / session / correlation scope), it's prefixed so two\n * sandbox accounts, sessions, or storyboard runs on one server can each seed\n * the same fixture id with divergent fixtures without colliding in the\n * process-wide `SeedFixtureCache`.\n *\n * Without scope (legacy callers, or requests with no `account` envelope),\n * the unscoped key is used — preserving the pre-#1215 behavior. Adopters\n * who relied on cross-account replay equivalence (rare, since storyboards\n * usually scope per account anyway) are unaffected when their requests\n * don't carry an `account` block.\n */\nfunction makeSeedCacheKey(unscopedKey: string, scope: string | undefined): string {\n return scope != null && scope.length > 0 ? `${scope}:${unscopedKey}` : unscopedKey;\n}\n\nfunction makeSeedCacheScope(input: Record<string, unknown>): string | undefined {\n const parts: string[] = [];\n const context = input.context;\n if (context != null && typeof context === 'object') {\n const sessionId = (context as { session_id?: unknown }).session_id;\n if (typeof sessionId === 'string' && sessionId.length > 0) parts.push(`session:${sessionId}`);\n const correlationId = (context as { correlation_id?: unknown }).correlation_id;\n if (typeof correlationId === 'string' && correlationId.length > 0) parts.push(`correlation:${correlationId}`);\n }\n\n const account =\n input.account ??\n (context != null && typeof context === 'object' ? (context as { account?: unknown }).account : undefined);\n if (account != null && typeof account === 'object') {\n const accountRef = account as { account_id?: unknown; brand?: unknown; operator?: unknown; sandbox?: unknown };\n if (typeof accountRef.account_id === 'string' && accountRef.account_id.length > 0) {\n parts.push(`account_id:${accountRef.account_id}`);\n } else if (\n accountRef.brand !== undefined ||\n accountRef.operator !== undefined ||\n accountRef.sandbox !== undefined\n ) {\n parts.push(\n `account_ref:${canonicalJson({\n ...(accountRef.brand !== undefined && { brand: accountRef.brand }),\n ...(accountRef.operator !== undefined && { operator: accountRef.operator }),\n ...(accountRef.sandbox !== undefined && { sandbox: accountRef.sandbox }),\n })}`\n );\n }\n }\n\n return parts.length > 0 ? parts.join('|') : undefined;\n}\n\nasync function dispatchSeed(\n store: TestControllerStore,\n scenario: SeedScenario,\n params: Record<string, unknown> | undefined,\n cache: SeedFixtureCache | undefined,\n scope: string | undefined\n): Promise<ComplyTestControllerResponse> {\n // Validate fixture is a plain object (not null, array, or primitive) before\n // casting — the adapter signature promises Record<string, unknown>.\n const rawFixture = params?.fixture;\n if (\n rawFixture !== undefined &&\n (typeof rawFixture !== 'object' || Array.isArray(rawFixture) || rawFixture === null)\n ) {\n return controllerError(\n 'INVALID_PARAMS',\n `${scenario} requires params.fixture to be an object (got ${Array.isArray(rawFixture) ? 'array' : typeof rawFixture})`\n );\n }\n const fixture = (rawFixture as Record<string, unknown> | undefined) ?? {};\n // Reject keys that can pollute prototypes when adapters spread the fixture\n // into plain objects. Checked only at the top level — nested uses\n // `Object.hasOwn` or similar in the adapter's own data layer.\n for (const key of Object.keys(fixture)) {\n if (key === '__proto__' || key === 'constructor' || key === 'prototype') {\n return controllerError(\n 'INVALID_PARAMS',\n `${scenario} fixture key ${JSON.stringify(key)} is reserved and rejected to prevent prototype pollution`\n );\n }\n }\n\n // Route to adapter + pick a cache key unique per (kind, id).\n type SeedDispatch = { key: string; fixture: Record<string, unknown>; invoke: () => Promise<void> };\n let dispatch: SeedDispatch | null = null;\n let missingParam: string | null = null;\n\n switch (scenario) {\n case SEED_SCENARIOS.SEED_ACCOUNT: {\n if (!params?.account_id) {\n missingParam = 'seed_account requires params.account_id';\n break;\n }\n if (!store.seedAccount) return controllerError('UNKNOWN_SCENARIO', `Scenario not supported: ${scenario}`);\n const accountId = params.account_id as string;\n dispatch = {\n key: makeSeedCacheKey(`seed_account:${accountId}`, scope),\n fixture,\n invoke: () => store.seedAccount!(accountId, fixture),\n };\n break;\n }\n case SEED_SCENARIOS.SEED_PRODUCT: {\n if (!params?.product_id) {\n missingParam = 'seed_product requires params.product_id';\n break;\n }\n if (!store.seedProduct) return controllerError('UNKNOWN_SCENARIO', `Scenario not supported: ${scenario}`);\n const productId = params.product_id as string;\n dispatch = {\n key: makeSeedCacheKey(`seed_product:${productId}`, scope),\n fixture,\n invoke: () => store.seedProduct!(productId, fixture),\n };\n break;\n }\n case SEED_SCENARIOS.SEED_PRICING_OPTION: {\n if (!params?.product_id || !params?.pricing_option_id) {\n missingParam = 'seed_pricing_option requires params.product_id and params.pricing_option_id';\n break;\n }\n if (!store.seedPricingOption) return controllerError('UNKNOWN_SCENARIO', `Scenario not supported: ${scenario}`);\n const productId = params.product_id as string;\n const pricingOptionId = params.pricing_option_id as string;\n dispatch = {\n key: makeSeedCacheKey(`seed_pricing_option:${productId}:${pricingOptionId}`, scope),\n fixture,\n invoke: () => store.seedPricingOption!(productId, pricingOptionId, fixture),\n };\n break;\n }\n case SEED_SCENARIOS.SEED_CREATIVE: {\n if (!params?.creative_id) {\n missingParam = 'seed_creative requires params.creative_id';\n break;\n }\n if (!store.seedCreative) return controllerError('UNKNOWN_SCENARIO', `Scenario not supported: ${scenario}`);\n const creativeId = params.creative_id as string;\n dispatch = {\n key: makeSeedCacheKey(`seed_creative:${creativeId}`, scope),\n fixture,\n invoke: () => store.seedCreative!(creativeId, fixture),\n };\n break;\n }\n case SEED_SCENARIOS.SEED_PLAN: {\n if (!params?.plan_id) {\n missingParam = 'seed_plan requires params.plan_id';\n break;\n }\n if (!store.seedPlan) return controllerError('UNKNOWN_SCENARIO', `Scenario not supported: ${scenario}`);\n const planId = params.plan_id as string;\n dispatch = {\n key: makeSeedCacheKey(`seed_plan:${planId}`, scope),\n fixture,\n invoke: () => store.seedPlan!(planId, fixture),\n };\n break;\n }\n case SEED_SCENARIOS.SEED_MEDIA_BUY: {\n if (!params?.media_buy_id) {\n missingParam = 'seed_media_buy requires params.media_buy_id';\n break;\n }\n if (!store.seedMediaBuy) return controllerError('UNKNOWN_SCENARIO', `Scenario not supported: ${scenario}`);\n const mediaBuyId = params.media_buy_id as string;\n dispatch = {\n key: makeSeedCacheKey(`seed_media_buy:${mediaBuyId}`, scope),\n fixture,\n invoke: () => store.seedMediaBuy!(mediaBuyId, fixture),\n };\n break;\n }\n case SEED_SCENARIOS.SEED_CREATIVE_FORMAT: {\n if (!params?.format_id) {\n missingParam = 'seed_creative_format requires params.format_id';\n break;\n }\n if (!store.seedCreativeFormat) return controllerError('UNKNOWN_SCENARIO', `Scenario not supported: ${scenario}`);\n const formatId = params.format_id as string;\n dispatch = {\n key: makeSeedCacheKey(`seed_creative_format:${formatId}`, scope),\n fixture,\n invoke: () => store.seedCreativeFormat!(formatId, fixture),\n };\n break;\n }\n case SEED_SCENARIOS.SEED_MEASUREMENT_CATALOG: {\n if (!params || !Object.hasOwn(params, 'vendor') || !Object.hasOwn(params, 'metrics')) {\n missingParam = 'seed_measurement_catalog requires params.vendor and params.metrics';\n break;\n }\n if (!store.seedMeasurementCatalog) {\n return controllerError('UNKNOWN_SCENARIO', `Scenario not supported: ${scenario}`);\n }\n const measurementCatalogFixture = { ...params, fixture };\n const measurementCatalogKey = [\n 'seed_measurement_catalog',\n canonicalJson(params.vendor ?? 'default'),\n measurementCatalogMetricKey(params.metrics),\n ].join(':');\n dispatch = {\n key: makeSeedCacheKey(measurementCatalogKey, scope),\n fixture: measurementCatalogFixture,\n invoke: () => store.seedMeasurementCatalog!(measurementCatalogFixture),\n };\n break;\n }\n case SEED_SCENARIOS.SEED_BUYER_AGENT: {\n if (!params?.agent_url) {\n missingParam = 'seed_buyer_agent requires params.agent_url';\n break;\n }\n if (!store.seedBuyerAgent) return controllerError('UNKNOWN_SCENARIO', `Scenario not supported: ${scenario}`);\n const agentUrl = params.agent_url as string;\n const { agent_url: _agentUrl, fixture: _fixture, ...directFields } = params;\n void _agentUrl;\n void _fixture;\n if (rawFixture !== undefined && Object.keys(directFields).length > 0) {\n return controllerError(\n 'INVALID_PARAMS',\n 'seed_buyer_agent accepts either direct params fields or params.fixture, not both'\n );\n }\n const buyerAgentFixture = normalizeBuyerAgentSeedFixture(rawFixture !== undefined ? fixture : directFields);\n if (Object.prototype.hasOwnProperty.call(buyerAgentFixture, 'agent_url')) {\n return controllerError(\n 'INVALID_PARAMS',\n 'seed_buyer_agent does not accept agent_url inside params.fixture; use top-level params.agent_url'\n );\n }\n for (const key of Object.keys(buyerAgentFixture)) {\n if (key === '__proto__' || key === 'constructor' || key === 'prototype') {\n return controllerError(\n 'INVALID_PARAMS',\n `${scenario} fixture key ${JSON.stringify(key)} is reserved and rejected to prevent prototype pollution`\n );\n }\n }\n dispatch = {\n key: makeSeedCacheKey(`seed_buyer_agent:${agentUrl}`, scope),\n fixture: buyerAgentFixture,\n invoke: () => store.seedBuyerAgent!(agentUrl, buyerAgentFixture),\n };\n break;\n }\n }\n\n if (missingParam) return controllerError('INVALID_PARAMS', missingParam);\n if (!dispatch) return controllerError('UNKNOWN_SCENARIO', `Scenario not supported: ${scenario}`);\n\n // Spec idempotency: same ID + equivalent fixture = existing; divergent = INVALID_PARAMS.\n // We also handle caches where has() can race with get() (TTL/LRU eviction) —\n // a missing prior entry is treated as a fresh seed rather than a crash.\n const prior = cache?.has(dispatch.key) ? cache.get(dispatch.key) : undefined;\n if (prior !== undefined) {\n if (!fixturesEquivalent(prior, dispatch.fixture)) {\n return controllerError(\n 'INVALID_PARAMS',\n `Fixture for ${sanitizeLabel(dispatch.key)} diverges from the previously seeded fixture. ` +\n 'Seed replays must carry an equivalent fixture or choose a new id.'\n );\n }\n await dispatch.invoke();\n // SeedSuccess (3.0.1+): message-only arm. The schema's `oneOf` excludes\n // `previous_state`/`current_state` from this branch — seeds are\n // pre-population, not entity transitions. {@link SEED_MESSAGES} carries\n // the SDK-specific replay-detection token.\n return wrapStoreSuccess({ success: true, message: SEED_MESSAGES.replay });\n }\n\n await dispatch.invoke();\n cache?.set(dispatch.key, dispatch.fixture);\n return wrapStoreSuccess({ success: true, message: SEED_MESSAGES.fresh });\n}\n\nasync function handleTestControllerRequestImpl(\n storeOrFactory: TestControllerStoreOrFactory,\n input: Record<string, unknown>,\n options?: { seedCache?: SeedFixtureCache }\n): Promise<ComplyTestControllerResponse> {\n const scenario = input.scenario as string | undefined;\n if (!scenario) {\n return controllerError('INVALID_PARAMS', 'Missing required field: scenario');\n }\n\n // list_scenarios is a stateless capability probe — answer it without\n // invoking the factory so session-backed factories don't need to tolerate\n // sessionless probes.\n if (scenario === 'list_scenarios') {\n const scenarios = isFactory(storeOrFactory) ? [...storeOrFactory.scenarios] : allScenariosFromStore(storeOrFactory);\n return wrapStoreSuccess({ success: true, scenarios });\n }\n\n let store: TestControllerStore;\n try {\n store = isFactory(storeOrFactory) ? await storeOrFactory.createStore(input) : storeOrFactory;\n } catch (err) {\n if (err instanceof TestControllerError) {\n return controllerError(err.code, err.message, err.currentState, err.metadata);\n }\n return controllerError(\n 'INTERNAL_ERROR',\n `Failed to resolve test controller store for scenario ${sanitizeLabel(scenario)}`\n );\n }\n\n const params = input.params as Record<string, unknown> | undefined;\n\n try {\n switch (scenario) {\n case CONTROLLER_SCENARIOS.FORCE_CREATIVE_STATUS: {\n if (!store.forceCreativeStatus) {\n return controllerError('UNKNOWN_SCENARIO', `Scenario not supported: ${scenario}`);\n }\n if (!params?.creative_id || !params?.status) {\n return controllerError(\n 'INVALID_PARAMS',\n 'force_creative_status requires params.creative_id and params.status'\n );\n }\n const creativeStatus = CreativeStatusSchema.safeParse(params.status);\n if (!creativeStatus.success) {\n return controllerError('INVALID_PARAMS', `Invalid creative status: ${params.status}`);\n }\n return wrapStoreSuccess(\n await store.forceCreativeStatus(\n params.creative_id as string,\n creativeStatus.data,\n params.rejection_reason as string | undefined\n )\n );\n }\n\n case CONTROLLER_SCENARIOS.FORCE_ACCOUNT_STATUS: {\n if (!store.forceAccountStatus) {\n return controllerError('UNKNOWN_SCENARIO', `Scenario not supported: ${scenario}`);\n }\n if (!params?.account_id || !params?.status) {\n return controllerError('INVALID_PARAMS', 'force_account_status requires params.account_id and params.status');\n }\n const accountStatus = AccountStatusSchema.safeParse(params.status);\n if (!accountStatus.success) {\n return controllerError('INVALID_PARAMS', `Invalid account status: ${params.status}`);\n }\n return wrapStoreSuccess(await store.forceAccountStatus(params.account_id as string, accountStatus.data));\n }\n\n case CONTROLLER_SCENARIOS.FORCE_MEDIA_BUY_STATUS: {\n if (!store.forceMediaBuyStatus) {\n return controllerError('UNKNOWN_SCENARIO', `Scenario not supported: ${scenario}`);\n }\n if (!params?.media_buy_id || !params?.status) {\n return controllerError(\n 'INVALID_PARAMS',\n 'force_media_buy_status requires params.media_buy_id and params.status'\n );\n }\n const mediaBuyStatus = MediaBuyStatusSchema.safeParse(params.status);\n if (!mediaBuyStatus.success) {\n return controllerError('INVALID_PARAMS', `Invalid media buy status: ${params.status}`);\n }\n return wrapStoreSuccess(\n await store.forceMediaBuyStatus(\n params.media_buy_id as string,\n mediaBuyStatus.data,\n params.rejection_reason as string | undefined\n )\n );\n }\n\n case CONTROLLER_SCENARIOS.FORCE_SESSION_STATUS: {\n if (!store.forceSessionStatus) {\n return controllerError('UNKNOWN_SCENARIO', `Scenario not supported: ${scenario}`);\n }\n if (!params?.session_id || !params?.status) {\n return controllerError('INVALID_PARAMS', 'force_session_status requires params.session_id and params.status');\n }\n const validSessionStatuses = ['complete', 'terminated'];\n if (!validSessionStatuses.includes(params.status as string)) {\n return controllerError('INVALID_PARAMS', `Invalid session status: ${params.status}`);\n }\n return wrapStoreSuccess(\n await store.forceSessionStatus(\n params.session_id as string,\n params.status as 'complete' | 'terminated',\n params.termination_reason as string | undefined\n )\n );\n }\n\n case CONTROLLER_SCENARIOS.SIMULATE_DELIVERY: {\n if (!store.simulateDelivery) {\n return controllerError('UNKNOWN_SCENARIO', `Scenario not supported: ${scenario}`);\n }\n if (!params?.media_buy_id) {\n return controllerError('INVALID_PARAMS', 'simulate_delivery requires params.media_buy_id');\n }\n // Spread params verbatim so extension fields (e.g. vendor_metric_values)\n // reach the adapter — params is spec-canonical additionalProperties:true.\n const { media_buy_id: simulateDeliveryId, ...simulateDeliveryRest } = params;\n return wrapStoreSuccess(await store.simulateDelivery(simulateDeliveryId as string, simulateDeliveryRest));\n }\n\n case CONTROLLER_SCENARIOS.SIMULATE_BUDGET_SPEND: {\n if (!store.simulateBudgetSpend) {\n return controllerError('UNKNOWN_SCENARIO', `Scenario not supported: ${scenario}`);\n }\n if (params?.spend_percentage === undefined || params?.spend_percentage === null) {\n return controllerError('INVALID_PARAMS', 'simulate_budget_spend requires params.spend_percentage');\n }\n if (!params?.account_id && !params?.media_buy_id) {\n return controllerError(\n 'INVALID_PARAMS',\n 'simulate_budget_spend requires params.account_id or params.media_buy_id'\n );\n }\n // Spread params verbatim so extension fields reach the adapter.\n return wrapStoreSuccess(\n await store.simulateBudgetSpend(\n params as {\n account_id?: string;\n media_buy_id?: string;\n spend_percentage: number;\n [key: string]: unknown;\n }\n )\n );\n }\n\n case CONTROLLER_SCENARIOS.FORCE_CREATE_MEDIA_BUY_ARM: {\n if (!store.forceCreateMediaBuyArm) {\n return controllerError('UNKNOWN_SCENARIO', `Scenario not supported: ${scenario}`);\n }\n const arm = params?.arm;\n if (arm !== 'submitted' && arm !== 'input-required') {\n return controllerError(\n 'INVALID_PARAMS',\n \"force_create_media_buy_arm requires params.arm = 'submitted' or 'input-required'\"\n );\n }\n if (arm === 'submitted' && !params?.task_id) {\n return controllerError(\n 'INVALID_PARAMS',\n \"force_create_media_buy_arm with arm='submitted' requires params.task_id\"\n );\n }\n // Spec: task_id is \"Present only when arm is 'submitted'\" — reject\n // it on the input-required arm so sellers can rely on the field's\n // presence as a discriminator.\n if (arm === 'input-required' && params?.task_id !== undefined) {\n return controllerError(\n 'INVALID_PARAMS',\n \"force_create_media_buy_arm with arm='input-required' must not include params.task_id\"\n );\n }\n return wrapStoreSuccess(\n await store.forceCreateMediaBuyArm({\n arm,\n task_id: params?.task_id as string | undefined,\n message: params?.message as string | undefined,\n })\n );\n }\n\n case DISCOVERY_ARM_SCENARIOS.FORCE_GET_PRODUCTS_ARM: {\n if (!store.forceGetProductsArm) {\n return controllerError('UNKNOWN_SCENARIO', `Scenario not supported: ${scenario}`);\n }\n const arm = params?.arm;\n if (arm !== 'submitted') {\n return controllerError('INVALID_PARAMS', \"force_get_products_arm requires params.arm = 'submitted'\");\n }\n if (!params?.task_id) {\n return controllerError(\n 'INVALID_PARAMS',\n \"force_get_products_arm with arm='submitted' requires params.task_id\"\n );\n }\n return wrapStoreSuccess(\n await store.forceGetProductsArm({\n arm,\n task_id: params.task_id as string,\n message: params?.message as string | undefined,\n })\n );\n }\n\n case DISCOVERY_ARM_SCENARIOS.FORCE_GET_SIGNALS_ARM: {\n if (!store.forceGetSignalsArm) {\n return controllerError('UNKNOWN_SCENARIO', `Scenario not supported: ${scenario}`);\n }\n const arm = params?.arm;\n if (arm !== 'submitted') {\n return controllerError('INVALID_PARAMS', \"force_get_signals_arm requires params.arm = 'submitted'\");\n }\n if (!params?.task_id) {\n return controllerError(\n 'INVALID_PARAMS',\n \"force_get_signals_arm with arm='submitted' requires params.task_id\"\n );\n }\n return wrapStoreSuccess(\n await store.forceGetSignalsArm({\n arm,\n task_id: params.task_id as string,\n message: params?.message as string | undefined,\n })\n );\n }\n\n case CONTROLLER_SCENARIOS.FORCE_TASK_COMPLETION: {\n if (!store.forceTaskCompletion) {\n return controllerError('UNKNOWN_SCENARIO', `Scenario not supported: ${scenario}`);\n }\n if (!params?.task_id) {\n return controllerError('INVALID_PARAMS', 'force_task_completion requires params.task_id');\n }\n // Reject arrays explicitly — `typeof [] === 'object'` would let an\n // array slip past the object check, but `result` is a structured\n // completion payload (validates against async-response-data.json),\n // never an array.\n if (!params?.result || typeof params.result !== 'object' || Array.isArray(params.result)) {\n return controllerError(\n 'INVALID_PARAMS',\n 'force_task_completion requires params.result (completion payload object)'\n );\n }\n return wrapStoreSuccess(\n await store.forceTaskCompletion(params.task_id as string, params.result as Record<string, unknown>)\n );\n }\n\n case CONTROLLER_SCENARIOS.FORCE_CREATIVE_PURGE: {\n if (!store.forceCreativePurge) {\n return controllerError('UNKNOWN_SCENARIO', `Scenario not supported: ${scenario}`);\n }\n if (!params?.creative_id) {\n return controllerError('INVALID_PARAMS', 'force_creative_purge requires params.creative_id');\n }\n const { creative_id: creativeId, ...purgeParams } = params;\n if (\n purgeParams.purge_kind !== undefined &&\n !CreativePurgeKindSchema.safeParse(purgeParams.purge_kind).success\n ) {\n return controllerError('INVALID_PARAMS', 'force_creative_purge params.purge_kind must be soft or hard');\n }\n if (\n purgeParams.reason_code !== undefined &&\n !CreativeEventReasonCodeSchema.safeParse(purgeParams.reason_code).success\n ) {\n return controllerError(\n 'INVALID_PARAMS',\n 'force_creative_purge params.reason_code must be a CreativeEventReasonCode'\n );\n }\n return wrapStoreSuccess(await store.forceCreativePurge(creativeId as string, purgeParams));\n }\n\n case SEED_SCENARIOS.SEED_ACCOUNT:\n case SEED_SCENARIOS.SEED_PRODUCT:\n case SEED_SCENARIOS.SEED_PRICING_OPTION:\n case SEED_SCENARIOS.SEED_CREATIVE:\n case SEED_SCENARIOS.SEED_PLAN:\n case SEED_SCENARIOS.SEED_MEDIA_BUY:\n case SEED_SCENARIOS.SEED_CREATIVE_FORMAT:\n case SEED_SCENARIOS.SEED_MEASUREMENT_CATALOG:\n case SEED_SCENARIOS.SEED_BUYER_AGENT: {\n // Seed-cache scope (issues #1215/#2156). Two sandbox accounts,\n // sessions, or storyboard correlation buckets on one server can each\n // seed the same fixture id with divergent fixtures; without a scope\n // prefix the cache treats divergent replays as INVALID_PARAMS even when\n // each scoped fixture is internally self-consistent. No resolver call here — test-controller is a\n // generic helper that doesn't know about platform.accounts.resolve,\n // and read-time misalignment is fine because the cache only governs\n // idempotency, not user-visible state.\n const scope = makeSeedCacheScope(input);\n return await dispatchSeed(store, scenario as SeedScenario, params, options?.seedCache, scope);\n }\n\n // Extension scenarios accepted by the dispatcher until the spec\n // promotes them to first-class controller scenarios.\n case FORCE_AUDIENCE_STATUS_SCENARIO: {\n if (!store.forceAudienceStatus) {\n return controllerError('UNKNOWN_SCENARIO', `Scenario not supported: ${scenario}`);\n }\n if (!params?.audience_id || !params?.status) {\n return controllerError(\n 'INVALID_PARAMS',\n 'force_audience_status requires params.audience_id and params.status'\n );\n }\n const audienceStatus = AudienceStatusSchema.safeParse(params.status);\n if (!audienceStatus.success) {\n return controllerError('INVALID_PARAMS', `Invalid audience status: ${params.status}`);\n }\n // Accept either `reason` (this issue's proposed shape, neutral across\n // transitions like processing→ready) or `rejection_reason` (the field\n // name used by force_creative_status / force_media_buy_status). The\n // adapter receives whichever was supplied — de-risks the eventual spec\n // PR (adcp#2860) picking either name.\n return wrapStoreSuccess(\n await store.forceAudienceStatus(\n params.audience_id as string,\n audienceStatus.data,\n (params.reason ?? params.rejection_reason) as string | undefined\n )\n );\n }\n\n case FORCE_CATALOG_ITEM_STATUS_SCENARIO: {\n if (!store.forceCatalogItemStatus) {\n return controllerError('UNKNOWN_SCENARIO', `Scenario not supported: ${scenario}`);\n }\n if (!params?.catalog_item_id || !params?.status) {\n return controllerError(\n 'INVALID_PARAMS',\n 'force_catalog_item_status requires params.catalog_item_id and params.status'\n );\n }\n const itemStatus = CatalogItemStatusSchema.safeParse(params.status);\n if (!itemStatus.success) {\n return controllerError('INVALID_PARAMS', `Invalid catalog item status: ${params.status}`);\n }\n // See force_audience_status above — same dual-name acceptance.\n return wrapStoreSuccess(\n await store.forceCatalogItemStatus(\n params.catalog_item_id as string,\n itemStatus.data,\n (params.reason ?? params.rejection_reason) as string | undefined\n )\n );\n }\n\n case CONTROLLER_SCENARIOS.QUERY_PROVENANCE_AUDIT_OBSERVATIONS: {\n if (!store.queryProvenanceAuditObservations) {\n return controllerError('UNKNOWN_SCENARIO', `Scenario not supported: ${scenario}`);\n }\n const queryParams = (params ?? {}) as { creative_id?: unknown; [key: string]: unknown };\n if (typeof queryParams.creative_id !== 'string' || queryParams.creative_id.length === 0) {\n return controllerError('INVALID_PARAMS', 'query_provenance_audit_observations requires params.creative_id');\n }\n return wrapStoreSuccess(\n await store.queryProvenanceAuditObservations({\n ...queryParams,\n creative_id: queryParams.creative_id,\n })\n );\n }\n\n case CONTROLLER_SCENARIOS.QUERY_UPSTREAM_TRAFFIC: {\n if (!store.queryUpstreamTraffic) {\n return controllerError('UNKNOWN_SCENARIO', `Scenario not supported: ${scenario}`);\n }\n const queryParams = (params ?? {}) as Parameters<NonNullable<TestControllerStore['queryUpstreamTraffic']>>[0];\n return wrapStoreSuccess(await store.queryUpstreamTraffic(queryParams));\n }\n\n case CONTROLLER_SCENARIOS.FORCE_UPSTREAM_UNAVAILABLE: {\n if (!store.forceUpstreamUnavailable) {\n return controllerError('UNKNOWN_SCENARIO', `Scenario not supported: ${scenario}`);\n }\n const forceParams = (params ?? {}) as { tool?: unknown; upstream_name?: unknown };\n if (typeof forceParams.tool !== 'string' || forceParams.tool.length === 0) {\n return controllerError('INVALID_PARAMS', 'force_upstream_unavailable requires params.tool');\n }\n return wrapStoreSuccess(\n await store.forceUpstreamUnavailable({\n tool: forceParams.tool,\n upstream_name: typeof forceParams.upstream_name === 'string' ? forceParams.upstream_name : undefined,\n })\n );\n }\n\n default:\n return controllerError('UNKNOWN_SCENARIO', 'Unrecognized scenario name');\n }\n } catch (err) {\n if (err instanceof TestControllerError) {\n return controllerError(err.code, err.message, err.currentState, err.metadata);\n }\n return controllerError('INTERNAL_ERROR', 'An unexpected error occurred in the test controller store');\n }\n}\n\n/**\n * Handle a `comply_test_controller` request. Exported so custom MCP wrappers\n * can compose it with their own middleware (AsyncLocalStorage, sandbox gating,\n * logging) without going through {@link registerTestController}.\n *\n * `list_scenarios` is answered from the static capability set (store method\n * presence for plain stores, `factory.scenarios` for factories) and never\n * invokes `factory.createStore`. Session-backed factories can therefore load\n * real session state unconditionally.\n *\n * `input.context` is echoed into every response branch so correlation IDs\n * round-trip per the `comply-test-controller-response.json` schema.\n */\nexport async function handleTestControllerRequest(\n storeOrFactory: TestControllerStoreOrFactory,\n input: Record<string, unknown>,\n options?: { seedCache?: SeedFixtureCache }\n): Promise<ComplyTestControllerResponse> {\n const ctx = input.context;\n const result = await handleTestControllerRequestImpl(storeOrFactory, input, options);\n if (ctx !== undefined && typeof ctx === 'object' && ctx !== null && result.context === undefined) {\n return { ...result, context: ctx } as ComplyTestControllerResponse;\n }\n return result;\n}\n\n// ────────────────────────────────────────────────────────────\n// MCP envelope helpers\n// ────────────────────────────────────────────────────────────\n\nfunction summarize(data: ComplyTestControllerResponse): string {\n if (data.success === false) return `Controller error: ${data.error}`;\n if ('scenarios' in data) return `Supported scenarios: ${data.scenarios.join(', ')}`;\n if ('previous_state' in data) {\n return `Transitioned from ${data.previous_state} to ${data.current_state}`;\n }\n if ('simulated' in data) return `Simulation complete: ${JSON.stringify(data.simulated)}`;\n if ('forced' in data) return `Directive registered: arm=${data.forced.arm}`;\n // SeedSuccess (3.0.1+): message-only arm. dispatchSeed sets the message\n // to 'Fixture seeded' / 'Fixture re-seeded (equivalent)'; third-party\n // sellers may emit other strings (or none — the spec only requires\n // success). UpstreamTrafficSuccess (3.1+): no message field — summarize\n // recorded-calls count instead.\n if ('message' in data && typeof data.message === 'string') return data.message;\n if ('recorded_calls' in data) {\n return `Recorded ${data.total_count} upstream call(s)${data.truncated ? ' (truncated)' : ''}`;\n }\n return 'Scenario succeeded';\n}\n\n/**\n * Wrap a {@link ComplyTestControllerResponse} in an MCP tool response envelope\n * (`content` + `structuredContent` + `isError`). Exported so custom wrappers\n * can reuse the same summary/error shape as {@link registerTestController}.\n */\nexport function toMcpResponse(data: ComplyTestControllerResponse): McpToolResponse & { isError?: true } {\n const isError = data.success === false;\n return {\n content: [{ type: 'text', text: summarize(data) }],\n structuredContent: toStructuredContent(data),\n ...(isError && { isError: true }),\n };\n}\n\n/**\n * Canonical Zod input schema for the `comply_test_controller` tool. Pass as\n * the `inputSchema` argument to `server.registerTool(...)` in custom wrappers\n * so the request shape stays in sync with {@link registerTestController}.\n *\n * Matches `ComplyTestControllerRequest` from the generated schema: `scenario`\n * (required), `params` (scenario-specific), and the universal `context` / `ext`\n * envelope fields. No top-level `account` or `brand` — AdCP routes account\n * and brand context through `context` on this tool.\n *\n * **Extending for vendor fields.** Custom wrappers that route sandbox gating\n * or tenant scoping on top-level fields can extend the shape locally.\n * Both `account` and `brand` are commonly added — storyboard fixtures and\n * v5-shaped wrappers often send them alongside `params`:\n *\n * ```ts\n * const MY_SHAPE = {\n * ...TOOL_INPUT_SHAPE,\n * account: z.object({ sandbox: z.boolean() }).passthrough().optional(),\n * brand: z.object({ domain: z.string() }).passthrough().optional(),\n * };\n * server.registerTool(\n * 'comply_test_controller',\n * { description: 'Sandbox only.', inputSchema: MY_SHAPE },\n * async input => {\n * if (input.account?.sandbox !== true) return toMcpResponse({ ... });\n * return toMcpResponse(await handleTestControllerRequest(store, input as Record<string, unknown>));\n * }\n * );\n * ```\n *\n * Adopters routing through `createAdcpServerFromPlatform({ complyTest })`\n * pass the same fields via `complyTest.inputSchema` (see\n * `ComplyControllerConfig.inputSchema` in `@adcp/sdk/testing`).\n *\n * This keeps the default registration protocol-compliant while giving\n * wrapper authors a documented extension point.\n */\nexport const TOOL_INPUT_SHAPE = {\n scenario: z.string().describe('Scenario to execute (e.g., list_scenarios, force_account_status)'),\n params: z.record(z.string(), z.unknown()).optional().describe('Scenario-specific parameters'),\n context: z.record(z.string(), z.unknown()).optional().describe('AdCP context object'),\n ext: z.record(z.string(), z.unknown()).optional().describe('AdCP extension object'),\n};\n\n// ────────────────────────────────────────────────────────────\n// MCP tool registration\n// ────────────────────────────────────────────────────────────\n\n/**\n * Register the `comply_test_controller` tool on an MCP server.\n *\n * Accepts a plain {@link TestControllerStore} (scenarios inferred from\n * implemented methods) or a {@link TestControllerStoreFactory} (scenarios\n * declared explicitly; `createStore` runs per request for session-backed\n * state). See the module-level examples for both patterns.\n *\n * The `server` argument takes either an `AdcpServer` from\n * `createAdcpServer()` or a raw SDK `McpServer` from\n * `createTaskCapableServer()` — the helper unwraps the opaque handle\n * when needed so tool registration reaches the underlying SDK server.\n */\nexport function registerTestController(\n server: AdcpServer | McpServer,\n storeOrFactory: TestControllerStoreOrFactory,\n options?: { seedCache?: SeedFixtureCache }\n): void {\n const mcp = getSdkServer(server as AdcpServer) ?? (server as McpServer);\n // Per-registration cache so seed idempotency holds across all requests on\n // this server instance. Callers can supply their own cache to scope by\n // session or tenant.\n const seedCache = options?.seedCache ?? createSeedFixtureCache();\n\n // Per AdCP 3.0, comply_test_controller support is declared via the\n // top-level `compliance_testing` capability block — NOT as an entry in\n // `supported_protocols`. When we've been given an AdcpServer produced by\n // createAdcpServer, auto-populate that block. The scenarios list comes\n // from the factory's static `scenarios` (factory shape) or is inferred\n // from the plain store's method presence.\n const capsBag = (server as unknown as Record<PropertyKey, unknown>)[ADCP_CAPABILITIES] as\n | GetAdCPCapabilitiesResponse\n | undefined;\n if (capsBag) {\n const incoming = isFactory(storeOrFactory) ? [...storeOrFactory.scenarios] : scenariosFromStore(storeOrFactory);\n if (incoming.length > 0) {\n // Merge with any previously-declared scenarios rather than silently\n // dropping them. A server can wire more than one controller (e.g.\n // media-buy + governance) and each call should contribute its\n // scenarios to the advertised set.\n const existing = (capsBag.compliance_testing?.scenarios ?? []) as readonly string[];\n const merged = Array.from(new Set([...existing, ...incoming]));\n capsBag.compliance_testing = { scenarios: merged } as GetAdCPCapabilitiesResponse['compliance_testing'];\n }\n }\n mcp.registerTool(\n 'comply_test_controller',\n {\n description: 'Triggers seller-side state transitions for compliance testing. Sandbox only.',\n inputSchema: TOOL_INPUT_SHAPE,\n },\n (async (input: Record<string, unknown>) => {\n const response = await handleTestControllerRequest(storeOrFactory, input, {\n seedCache,\n });\n return toMcpResponse(response);\n }) as Parameters<typeof mcp.registerTool>[2]\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
|
function createTranslationMap(adcpToUpstream) {
|
|
8
2
|
const forward = new Map(Object.entries(adcpToUpstream));
|
|
9
3
|
const reverse = /* @__PURE__ */ new Map();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/lib/server/upstream-helpers.ts"],"sourcesContent":["/**\n * Adapter translation helpers for AdCP seller agents.\n *\n * Every AdCP adapter needs the same two things:\n *\n * 1. Bidirectional key translation (AdCP wire values ↔ upstream platform values).\n * 2. A thin typed HTTP client for upstream platform APIs.\n *\n * These helpers replace the per-adapter boilerplate that appears in every\n * reference adapter (sales-social, signal-marketplace, creative-template,\n * sales-guaranteed) without encoding any domain-specific logic.\n */\n\n// ---------------------------------------------------------------------------\n// createTranslationMap\n// ---------------------------------------------------------------------------\n\n/**\n * A reversible bidirectional mapping between two sets of string keys.\n *\n * @example\n * ```ts\n * const channelMap = createTranslationMap({\n * olv: 'video',\n * ctv: 'ctv',\n * display: 'display',\n * streaming_audio: 'audio',\n * });\n *\n * channelMap.toUpstream('olv'); // 'video'\n * channelMap.toAdcp('video'); // 'olv'\n * channelMap.hasAdcp('olv'); // true\n * channelMap.hasUpstream('audio'); // true\n * ```\n *\n * Keys on the left side of the map argument are the AdCP wire values;\n * keys on the right are the upstream platform values.\n *\n * Both lookup methods return `undefined` for unknown keys — callers\n * should either guard with `has*()` or handle `undefined` explicitly.\n */\nexport interface TranslationMap<A extends string, B extends string> {\n /** Translate an AdCP wire value to the upstream platform value. */\n toUpstream(adcpKey: A): B | undefined;\n /** Translate an upstream platform value back to the AdCP wire value. */\n toAdcp(upstreamKey: B): A | undefined;\n /** True when `adcpKey` is a known AdCP-side key. */\n hasAdcp(adcpKey: string): adcpKey is A;\n /** True when `upstreamKey` is a known upstream-side key. */\n hasUpstream(upstreamKey: string): upstreamKey is B;\n}\n\n/**\n * Build a bidirectional translation map from a `{ adcpKey: upstreamKey }`\n * record. Keys on the left are AdCP wire values; values on the right are\n * upstream platform values.\n *\n * TypeScript infers literal types from the object literal, so `toUpstream`\n * and `toAdcp` return the precise union of known values rather than `string`.\n *\n * @example\n * ```ts\n * const channelMap = createTranslationMap({\n * olv: 'video',\n * ctv: 'ctv',\n * });\n * channelMap.toUpstream('olv'); // 'video' | undefined\n * channelMap.toAdcp('video'); // 'olv' | undefined\n * ```\n */\nexport function createTranslationMap<const M extends Record<string, string>>(\n adcpToUpstream: M\n): TranslationMap<keyof M & string, M[keyof M & string]> {\n type A = keyof M & string;\n type B = M[keyof M & string];\n const forward = new Map<A, B>(Object.entries(adcpToUpstream) as Array<[A, B]>);\n const reverse = new Map<B, A>();\n for (const [a, b] of forward) {\n reverse.set(b, a);\n }\n return {\n toUpstream: (k: A) => forward.get(k),\n toAdcp: (k: B) => reverse.get(k),\n hasAdcp: (k: string): k is A => forward.has(k as A),\n hasUpstream: (k: string): k is B => reverse.has(k as B),\n };\n}\n\n// ---------------------------------------------------------------------------\n// createUpstreamHttpClient\n// ---------------------------------------------------------------------------\n\n/**\n * Per-request context passed from the handler to the auth resolver.\n *\n * Adopters define their own shape — common fields include the AdCP\n * principal (`principal`), resolved tenant id (`operatorId`), or any\n * adapter-specific routing key. The SDK does not interpret this; it\n * simply forwards it to `dynamic_bearer.getToken` so the resolver can\n * pick the right credential per call.\n *\n * @example multi-tenant credential lookup\n * ```ts\n * upstream.get('/items', undefined, undefined, {\n * authContext: { operatorId: ctx.account.id }\n * });\n * ```\n *\n * @example pass-through of caller-presented credential\n * ```ts\n * upstream.get('/items', undefined, undefined, {\n * authContext: { principal: ctx.auth.principal }\n * });\n * ```\n */\nexport type AuthContext = Record<string, unknown>;\n\n/**\n * Authentication configuration for the upstream HTTP client.\n *\n * - `static_bearer` — fixed Bearer token injected into every request.\n * - `dynamic_bearer` — async token factory; called per-request. Receives\n * the optional `authContext` passed to the method call, so the same\n * resolver can return a master key for tenant-fan-out, a\n * per-operator key, or pass-through of the caller's principal.\n * OAuth client-credentials refresh is handled transparently.\n * - `api_key` — fixed key injected into a named header.\n * - `none` — no authentication header injected.\n */\nexport type UpstreamAuth =\n | { kind: 'static_bearer'; token: string }\n | { kind: 'dynamic_bearer'; getToken: (ctx?: AuthContext) => Promise<string> }\n | { kind: 'api_key'; header: string; key: string }\n | { kind: 'none' };\n\nexport interface UpstreamHttpClientOptions {\n /** Base URL of the upstream API (no trailing slash). */\n baseUrl: string;\n /**\n * Authentication strategy for outgoing requests.\n * Pass `{ kind: 'none' }` for unauthenticated internal services.\n */\n auth: UpstreamAuth;\n /**\n * Additional headers to include on every request (e.g. tenant ID,\n * API version, content-type overrides). Per-request headers passed\n * to individual method calls are merged on top and take precedence.\n */\n defaultHeaders?: Record<string, string>;\n /**\n * Override the underlying fetch implementation. Defaults to\n * `globalThis.fetch`. Adopters wiring `@adcp/sdk/upstream-recorder` pass\n * `recorder.wrapFetch(globalThis.fetch)` so outbound calls flow through\n * the recorder's per-principal scoping; production builds with the\n * recorder disabled fall back to the same global with zero overhead.\n */\n fetch?: typeof globalThis.fetch;\n}\n\n/** Result shape returned by every method on the upstream HTTP client. */\nexport interface UpstreamHttpResult<T> {\n /** HTTP status code returned by the upstream. */\n status: number;\n /**\n * Parsed response body, or `null` when the upstream returned a 404\n * or an empty body. Non-2xx responses throw — use the `status` field\n * only for within-2xx distinction (e.g. 201 Created vs 200 OK).\n */\n body: T | null;\n}\n\n/** Per-call options that don't fit naturally into positional method args. */\nexport interface UpstreamCallOptions {\n /**\n * Forwarded to `dynamic_bearer.getToken(ctx)` so handlers can pick\n * the right credential per call. Ignored by other auth kinds.\n */\n authContext?: AuthContext;\n}\n\nexport interface UpstreamHttpClient {\n get<T>(\n path: string,\n params?: Record<string, string | number | boolean | undefined>,\n headers?: Record<string, string>,\n options?: UpstreamCallOptions\n ): Promise<UpstreamHttpResult<T>>;\n post<T>(\n path: string,\n body: unknown,\n headers?: Record<string, string>,\n options?: UpstreamCallOptions\n ): Promise<UpstreamHttpResult<T>>;\n put<T>(\n path: string,\n body: unknown,\n headers?: Record<string, string>,\n options?: UpstreamCallOptions\n ): Promise<UpstreamHttpResult<T>>;\n delete<T>(\n path: string,\n headers?: Record<string, string>,\n options?: UpstreamCallOptions\n ): Promise<UpstreamHttpResult<T>>;\n}\n\nasync function resolveAuthHeader(auth: UpstreamAuth, ctx?: AuthContext): Promise<Record<string, string>> {\n switch (auth.kind) {\n case 'static_bearer':\n return { Authorization: `Bearer ${auth.token}` };\n case 'dynamic_bearer': {\n const token = await auth.getToken(ctx);\n return { Authorization: `Bearer ${token}` };\n }\n case 'api_key':\n return { [auth.header]: auth.key };\n case 'none':\n return {};\n }\n}\n\nasync function doRequest<T>(\n baseUrl: string,\n auth: UpstreamAuth,\n defaultHeaders: Record<string, string>,\n fetchImpl: typeof globalThis.fetch,\n method: string,\n path: string,\n options: {\n params?: Record<string, string | number | boolean | undefined>;\n body?: unknown;\n headers?: Record<string, string>;\n authContext?: AuthContext;\n }\n): Promise<UpstreamHttpResult<T>> {\n const authHeader = await resolveAuthHeader(auth, options.authContext);\n const mergedHeaders: Record<string, string> = {\n ...defaultHeaders,\n ...authHeader,\n ...options.headers,\n };\n\n let url = `${baseUrl}${path}`;\n if (options.params) {\n const qs = Object.entries(options.params)\n .filter(([, v]) => v !== undefined)\n .map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`)\n .join('&');\n if (qs) url = `${url}?${qs}`;\n }\n\n const init: RequestInit = { method, headers: mergedHeaders };\n if (options.body !== undefined) {\n init.body = JSON.stringify(options.body);\n mergedHeaders['Content-Type'] = 'application/json';\n }\n\n const res = await fetchImpl(url, init);\n\n // 404 → null body (not an error — resource absent is a common upstream signal)\n if (res.status === 404) return { status: 404, body: null };\n\n const text = await res.text();\n\n // Non-2xx always throws — callers must not silently swallow errors\n if (!res.ok) {\n throw new Error(`Upstream ${method} ${path} failed: ${res.status} ${text.slice(0, 200)}`);\n }\n\n // Empty body (204 No Content, etc.)\n if (!text) return { status: res.status, body: null };\n\n const body = JSON.parse(text) as T;\n return { status: res.status, body };\n}\n\n/**\n * Create a thin typed HTTP client for upstream platform APIs.\n *\n * Handles auth injection, query-string serialization, and 404→null\n * translation so adapters can focus on domain logic.\n *\n * @example\n * ```ts\n * const upstream = createUpstreamHttpClient({\n * baseUrl: process.env.UPSTREAM_URL!,\n * auth: { kind: 'static_bearer', token: process.env.UPSTREAM_TOKEN! },\n * defaultHeaders: { 'X-Tenant-Id': tenantId },\n * });\n *\n * const { body } = await upstream.get<Cohort[]>('/v2/cohorts');\n * const cohorts = body ?? [];\n * ```\n */\nexport function createUpstreamHttpClient(options: UpstreamHttpClientOptions): UpstreamHttpClient {\n const { baseUrl, auth, defaultHeaders = {}, fetch: fetchImpl = globalThis.fetch } = options;\n return {\n get: (path, params, headers, opts) =>\n doRequest(baseUrl, auth, defaultHeaders, fetchImpl, 'GET', path, {\n params,\n headers,\n authContext: opts?.authContext,\n }),\n post: (path, body, headers, opts) =>\n doRequest(baseUrl, auth, defaultHeaders, fetchImpl, 'POST', path, {\n body,\n headers,\n authContext: opts?.authContext,\n }),\n put: (path, body, headers, opts) =>\n doRequest(baseUrl, auth, defaultHeaders, fetchImpl, 'PUT', path, {\n body,\n headers,\n authContext: opts?.authContext,\n }),\n delete: (path, headers, opts) =>\n doRequest(baseUrl, auth, defaultHeaders, fetchImpl, 'DELETE', path, {\n headers,\n authContext: opts?.authContext,\n }),\n };\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/lib/server/upstream-helpers.ts"],"sourcesContent":["/**\n * Adapter translation helpers for AdCP seller agents.\n *\n * Every AdCP adapter needs the same two things:\n *\n * 1. Bidirectional key translation (AdCP wire values ↔ upstream platform values).\n * 2. A thin typed HTTP client for upstream platform APIs.\n *\n * These helpers replace the per-adapter boilerplate that appears in every\n * reference adapter (sales-social, signal-marketplace, creative-template,\n * sales-guaranteed) without encoding any domain-specific logic.\n */\n\n// ---------------------------------------------------------------------------\n// createTranslationMap\n// ---------------------------------------------------------------------------\n\n/**\n * A reversible bidirectional mapping between two sets of string keys.\n *\n * @example\n * ```ts\n * const channelMap = createTranslationMap({\n * olv: 'video',\n * ctv: 'ctv',\n * display: 'display',\n * streaming_audio: 'audio',\n * });\n *\n * channelMap.toUpstream('olv'); // 'video'\n * channelMap.toAdcp('video'); // 'olv'\n * channelMap.hasAdcp('olv'); // true\n * channelMap.hasUpstream('audio'); // true\n * ```\n *\n * Keys on the left side of the map argument are the AdCP wire values;\n * keys on the right are the upstream platform values.\n *\n * Both lookup methods return `undefined` for unknown keys — callers\n * should either guard with `has*()` or handle `undefined` explicitly.\n */\nexport interface TranslationMap<A extends string, B extends string> {\n /** Translate an AdCP wire value to the upstream platform value. */\n toUpstream(adcpKey: A): B | undefined;\n /** Translate an upstream platform value back to the AdCP wire value. */\n toAdcp(upstreamKey: B): A | undefined;\n /** True when `adcpKey` is a known AdCP-side key. */\n hasAdcp(adcpKey: string): adcpKey is A;\n /** True when `upstreamKey` is a known upstream-side key. */\n hasUpstream(upstreamKey: string): upstreamKey is B;\n}\n\n/**\n * Build a bidirectional translation map from a `{ adcpKey: upstreamKey }`\n * record. Keys on the left are AdCP wire values; values on the right are\n * upstream platform values.\n *\n * TypeScript infers literal types from the object literal, so `toUpstream`\n * and `toAdcp` return the precise union of known values rather than `string`.\n *\n * @example\n * ```ts\n * const channelMap = createTranslationMap({\n * olv: 'video',\n * ctv: 'ctv',\n * });\n * channelMap.toUpstream('olv'); // 'video' | undefined\n * channelMap.toAdcp('video'); // 'olv' | undefined\n * ```\n */\nexport function createTranslationMap<const M extends Record<string, string>>(\n adcpToUpstream: M\n): TranslationMap<keyof M & string, M[keyof M & string]> {\n type A = keyof M & string;\n type B = M[keyof M & string];\n const forward = new Map<A, B>(Object.entries(adcpToUpstream) as Array<[A, B]>);\n const reverse = new Map<B, A>();\n for (const [a, b] of forward) {\n reverse.set(b, a);\n }\n return {\n toUpstream: (k: A) => forward.get(k),\n toAdcp: (k: B) => reverse.get(k),\n hasAdcp: (k: string): k is A => forward.has(k as A),\n hasUpstream: (k: string): k is B => reverse.has(k as B),\n };\n}\n\n// ---------------------------------------------------------------------------\n// createUpstreamHttpClient\n// ---------------------------------------------------------------------------\n\n/**\n * Per-request context passed from the handler to the auth resolver.\n *\n * Adopters define their own shape — common fields include the AdCP\n * principal (`principal`), resolved tenant id (`operatorId`), or any\n * adapter-specific routing key. The SDK does not interpret this; it\n * simply forwards it to `dynamic_bearer.getToken` so the resolver can\n * pick the right credential per call.\n *\n * @example multi-tenant credential lookup\n * ```ts\n * upstream.get('/items', undefined, undefined, {\n * authContext: { operatorId: ctx.account.id }\n * });\n * ```\n *\n * @example pass-through of caller-presented credential\n * ```ts\n * upstream.get('/items', undefined, undefined, {\n * authContext: { principal: ctx.auth.principal }\n * });\n * ```\n */\nexport type AuthContext = Record<string, unknown>;\n\n/**\n * Authentication configuration for the upstream HTTP client.\n *\n * - `static_bearer` — fixed Bearer token injected into every request.\n * - `dynamic_bearer` — async token factory; called per-request. Receives\n * the optional `authContext` passed to the method call, so the same\n * resolver can return a master key for tenant-fan-out, a\n * per-operator key, or pass-through of the caller's principal.\n * OAuth client-credentials refresh is handled transparently.\n * - `api_key` — fixed key injected into a named header.\n * - `none` — no authentication header injected.\n */\nexport type UpstreamAuth =\n | { kind: 'static_bearer'; token: string }\n | { kind: 'dynamic_bearer'; getToken: (ctx?: AuthContext) => Promise<string> }\n | { kind: 'api_key'; header: string; key: string }\n | { kind: 'none' };\n\nexport interface UpstreamHttpClientOptions {\n /** Base URL of the upstream API (no trailing slash). */\n baseUrl: string;\n /**\n * Authentication strategy for outgoing requests.\n * Pass `{ kind: 'none' }` for unauthenticated internal services.\n */\n auth: UpstreamAuth;\n /**\n * Additional headers to include on every request (e.g. tenant ID,\n * API version, content-type overrides). Per-request headers passed\n * to individual method calls are merged on top and take precedence.\n */\n defaultHeaders?: Record<string, string>;\n /**\n * Override the underlying fetch implementation. Defaults to\n * `globalThis.fetch`. Adopters wiring `@adcp/sdk/upstream-recorder` pass\n * `recorder.wrapFetch(globalThis.fetch)` so outbound calls flow through\n * the recorder's per-principal scoping; production builds with the\n * recorder disabled fall back to the same global with zero overhead.\n */\n fetch?: typeof globalThis.fetch;\n}\n\n/** Result shape returned by every method on the upstream HTTP client. */\nexport interface UpstreamHttpResult<T> {\n /** HTTP status code returned by the upstream. */\n status: number;\n /**\n * Parsed response body, or `null` when the upstream returned a 404\n * or an empty body. Non-2xx responses throw — use the `status` field\n * only for within-2xx distinction (e.g. 201 Created vs 200 OK).\n */\n body: T | null;\n}\n\n/** Per-call options that don't fit naturally into positional method args. */\nexport interface UpstreamCallOptions {\n /**\n * Forwarded to `dynamic_bearer.getToken(ctx)` so handlers can pick\n * the right credential per call. Ignored by other auth kinds.\n */\n authContext?: AuthContext;\n}\n\nexport interface UpstreamHttpClient {\n get<T>(\n path: string,\n params?: Record<string, string | number | boolean | undefined>,\n headers?: Record<string, string>,\n options?: UpstreamCallOptions\n ): Promise<UpstreamHttpResult<T>>;\n post<T>(\n path: string,\n body: unknown,\n headers?: Record<string, string>,\n options?: UpstreamCallOptions\n ): Promise<UpstreamHttpResult<T>>;\n put<T>(\n path: string,\n body: unknown,\n headers?: Record<string, string>,\n options?: UpstreamCallOptions\n ): Promise<UpstreamHttpResult<T>>;\n delete<T>(\n path: string,\n headers?: Record<string, string>,\n options?: UpstreamCallOptions\n ): Promise<UpstreamHttpResult<T>>;\n}\n\nasync function resolveAuthHeader(auth: UpstreamAuth, ctx?: AuthContext): Promise<Record<string, string>> {\n switch (auth.kind) {\n case 'static_bearer':\n return { Authorization: `Bearer ${auth.token}` };\n case 'dynamic_bearer': {\n const token = await auth.getToken(ctx);\n return { Authorization: `Bearer ${token}` };\n }\n case 'api_key':\n return { [auth.header]: auth.key };\n case 'none':\n return {};\n }\n}\n\nasync function doRequest<T>(\n baseUrl: string,\n auth: UpstreamAuth,\n defaultHeaders: Record<string, string>,\n fetchImpl: typeof globalThis.fetch,\n method: string,\n path: string,\n options: {\n params?: Record<string, string | number | boolean | undefined>;\n body?: unknown;\n headers?: Record<string, string>;\n authContext?: AuthContext;\n }\n): Promise<UpstreamHttpResult<T>> {\n const authHeader = await resolveAuthHeader(auth, options.authContext);\n const mergedHeaders: Record<string, string> = {\n ...defaultHeaders,\n ...authHeader,\n ...options.headers,\n };\n\n let url = `${baseUrl}${path}`;\n if (options.params) {\n const qs = Object.entries(options.params)\n .filter(([, v]) => v !== undefined)\n .map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`)\n .join('&');\n if (qs) url = `${url}?${qs}`;\n }\n\n const init: RequestInit = { method, headers: mergedHeaders };\n if (options.body !== undefined) {\n init.body = JSON.stringify(options.body);\n mergedHeaders['Content-Type'] = 'application/json';\n }\n\n const res = await fetchImpl(url, init);\n\n // 404 → null body (not an error — resource absent is a common upstream signal)\n if (res.status === 404) return { status: 404, body: null };\n\n const text = await res.text();\n\n // Non-2xx always throws — callers must not silently swallow errors\n if (!res.ok) {\n throw new Error(`Upstream ${method} ${path} failed: ${res.status} ${text.slice(0, 200)}`);\n }\n\n // Empty body (204 No Content, etc.)\n if (!text) return { status: res.status, body: null };\n\n const body = JSON.parse(text) as T;\n return { status: res.status, body };\n}\n\n/**\n * Create a thin typed HTTP client for upstream platform APIs.\n *\n * Handles auth injection, query-string serialization, and 404→null\n * translation so adapters can focus on domain logic.\n *\n * @example\n * ```ts\n * const upstream = createUpstreamHttpClient({\n * baseUrl: process.env.UPSTREAM_URL!,\n * auth: { kind: 'static_bearer', token: process.env.UPSTREAM_TOKEN! },\n * defaultHeaders: { 'X-Tenant-Id': tenantId },\n * });\n *\n * const { body } = await upstream.get<Cohort[]>('/v2/cohorts');\n * const cohorts = body ?? [];\n * ```\n */\nexport function createUpstreamHttpClient(options: UpstreamHttpClientOptions): UpstreamHttpClient {\n const { baseUrl, auth, defaultHeaders = {}, fetch: fetchImpl = globalThis.fetch } = options;\n return {\n get: (path, params, headers, opts) =>\n doRequest(baseUrl, auth, defaultHeaders, fetchImpl, 'GET', path, {\n params,\n headers,\n authContext: opts?.authContext,\n }),\n post: (path, body, headers, opts) =>\n doRequest(baseUrl, auth, defaultHeaders, fetchImpl, 'POST', path, {\n body,\n headers,\n authContext: opts?.authContext,\n }),\n put: (path, body, headers, opts) =>\n doRequest(baseUrl, auth, defaultHeaders, fetchImpl, 'PUT', path, {\n body,\n headers,\n authContext: opts?.authContext,\n }),\n delete: (path, headers, opts) =>\n doRequest(baseUrl, auth, defaultHeaders, fetchImpl, 'DELETE', path, {\n headers,\n authContext: opts?.authContext,\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 { signWebhook } from "../signing/signer.mjs";
|
|
8
2
|
import { signWebhookAsync } from "../signing/signer-async.mjs";
|
|
9
3
|
import { createPinAndBindFetch } from "./pin-and-bind-fetch.mjs";
|