@adcp/sdk 12.0.0 → 12.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/adapters/content-standards-adapter.mjs +0 -6
- package/dist/lib/adapters/content-standards-adapter.mjs.map +1 -1
- package/dist/lib/adapters/derived-account-store.mjs +0 -6
- package/dist/lib/adapters/derived-account-store.mjs.map +1 -1
- package/dist/lib/adapters/governance-adapter.mjs +0 -6
- package/dist/lib/adapters/governance-adapter.mjs.map +1 -1
- package/dist/lib/adapters/implicit-account-store.mjs +0 -6
- package/dist/lib/adapters/implicit-account-store.mjs.map +1 -1
- package/dist/lib/adapters/index.mjs +0 -6
- package/dist/lib/adapters/index.mjs.map +1 -1
- package/dist/lib/adapters/legacy/v2-5/create_media_buy.mjs +0 -6
- package/dist/lib/adapters/legacy/v2-5/create_media_buy.mjs.map +1 -1
- package/dist/lib/adapters/legacy/v2-5/get_products.mjs +0 -6
- package/dist/lib/adapters/legacy/v2-5/get_products.mjs.map +1 -1
- package/dist/lib/adapters/legacy/v2-5/index.mjs +0 -6
- package/dist/lib/adapters/legacy/v2-5/index.mjs.map +1 -1
- package/dist/lib/adapters/legacy/v2-5/list_creative_formats.mjs +0 -6
- package/dist/lib/adapters/legacy/v2-5/list_creative_formats.mjs.map +1 -1
- package/dist/lib/adapters/legacy/v2-5/preview_creative.mjs +0 -6
- package/dist/lib/adapters/legacy/v2-5/preview_creative.mjs.map +1 -1
- package/dist/lib/adapters/legacy/v2-5/sync_creatives.mjs +0 -6
- package/dist/lib/adapters/legacy/v2-5/sync_creatives.mjs.map +1 -1
- package/dist/lib/adapters/legacy/v2-5/types.mjs +0 -6
- package/dist/lib/adapters/legacy/v2-5/update_media_buy.mjs +0 -6
- package/dist/lib/adapters/legacy/v2-5/update_media_buy.mjs.map +1 -1
- package/dist/lib/adapters/oauth-passthrough-resolver.mjs +0 -6
- package/dist/lib/adapters/oauth-passthrough-resolver.mjs.map +1 -1
- package/dist/lib/adapters/property-list-adapter.mjs +0 -6
- package/dist/lib/adapters/property-list-adapter.mjs.map +1 -1
- package/dist/lib/adapters/roster-account-store.mjs +0 -6
- package/dist/lib/adapters/roster-account-store.mjs.map +1 -1
- package/dist/lib/adapters/si-session-manager.mjs +0 -6
- package/dist/lib/adapters/si-session-manager.mjs.map +1 -1
- package/dist/lib/adapters/version/3.0/brand-fields.mjs +0 -6
- package/dist/lib/adapters/version/3.0/brand-fields.mjs.map +1 -1
- package/dist/lib/adapters/version/3.0/get-products.mjs +0 -6
- package/dist/lib/adapters/version/3.0/get-products.mjs.map +1 -1
- package/dist/lib/adapters/version/3.0/index.mjs +0 -6
- package/dist/lib/adapters/version/3.0/index.mjs.map +1 -1
- package/dist/lib/adapters/version/3.0/sync-accounts.mjs +0 -6
- package/dist/lib/adapters/version/3.0/sync-accounts.mjs.map +1 -1
- package/dist/lib/adapters/version/index.mjs +0 -6
- package/dist/lib/adapters/version/index.mjs.map +1 -1
- package/dist/lib/adapters/version/types.mjs +0 -6
- package/dist/lib/advanced.mjs +0 -6
- package/dist/lib/advanced.mjs.map +1 -1
- package/dist/lib/agents/index.generated.mjs +0 -6
- package/dist/lib/agents/index.generated.mjs.map +1 -1
- package/dist/lib/auth/index.mjs +0 -6
- package/dist/lib/auth/index.mjs.map +1 -1
- package/dist/lib/auth/oauth/CLIFlowHandler.mjs +0 -6
- package/dist/lib/auth/oauth/CLIFlowHandler.mjs.map +1 -1
- package/dist/lib/auth/oauth/ClientCredentialsFlow.mjs +0 -6
- package/dist/lib/auth/oauth/ClientCredentialsFlow.mjs.map +1 -1
- package/dist/lib/auth/oauth/MCPOAuthProvider.mjs +0 -6
- package/dist/lib/auth/oauth/MCPOAuthProvider.mjs.map +1 -1
- package/dist/lib/auth/oauth/NonInteractiveFlowHandler.mjs +0 -6
- package/dist/lib/auth/oauth/NonInteractiveFlowHandler.mjs.map +1 -1
- package/dist/lib/auth/oauth/authorization-required.mjs +0 -6
- package/dist/lib/auth/oauth/authorization-required.mjs.map +1 -1
- package/dist/lib/auth/oauth/diagnose.mjs +0 -6
- package/dist/lib/auth/oauth/diagnose.mjs.map +1 -1
- package/dist/lib/auth/oauth/diagnostics.mjs +0 -6
- package/dist/lib/auth/oauth/diagnostics.mjs.map +1 -1
- package/dist/lib/auth/oauth/discovery.mjs +0 -6
- package/dist/lib/auth/oauth/discovery.mjs.map +1 -1
- package/dist/lib/auth/oauth/file-storage.mjs +0 -6
- package/dist/lib/auth/oauth/file-storage.mjs.map +1 -1
- package/dist/lib/auth/oauth/index.mjs +0 -6
- package/dist/lib/auth/oauth/index.mjs.map +1 -1
- package/dist/lib/auth/oauth/secret-resolver.mjs +0 -6
- package/dist/lib/auth/oauth/secret-resolver.mjs.map +1 -1
- package/dist/lib/auth/oauth/storage-registry.mjs +0 -6
- package/dist/lib/auth/oauth/storage-registry.mjs.map +1 -1
- package/dist/lib/auth/oauth/types.mjs +0 -6
- package/dist/lib/auth/oauth/types.mjs.map +1 -1
- package/dist/lib/auth/oauth/web-flow.mjs +0 -6
- package/dist/lib/auth/oauth/web-flow.mjs.map +1 -1
- package/dist/lib/brand/index.mjs +0 -6
- package/dist/lib/brand/index.mjs.map +1 -1
- package/dist/lib/canonical-references/index.mjs +6 -7
- package/dist/lib/canonical-references/index.mjs.map +1 -1
- package/dist/lib/client/index.mjs +0 -6
- package/dist/lib/client/index.mjs.map +1 -1
- package/dist/lib/compliance/index.mjs +0 -6
- package/dist/lib/compliance/index.mjs.map +1 -1
- package/dist/lib/compliance-fixtures/index.mjs +0 -6
- package/dist/lib/compliance-fixtures/index.mjs.map +1 -1
- package/dist/lib/compliance-fixtures/test-authorization-server.mjs +0 -6
- package/dist/lib/compliance-fixtures/test-authorization-server.mjs.map +1 -1
- package/dist/lib/conformance/index.mjs +0 -6
- package/dist/lib/conformance/index.mjs.map +1 -1
- package/dist/lib/conformance/invariants/uniformError.mjs +0 -6
- package/dist/lib/conformance/invariants/uniformError.mjs.map +1 -1
- package/dist/lib/conformance/invariants/uniformErrorComparator.mjs +0 -6
- package/dist/lib/conformance/invariants/uniformErrorComparator.mjs.map +1 -1
- package/dist/lib/conformance/oracle.mjs +0 -6
- package/dist/lib/conformance/oracle.mjs.map +1 -1
- package/dist/lib/conformance/runConformance.mjs +0 -6
- package/dist/lib/conformance/runConformance.mjs.map +1 -1
- package/dist/lib/conformance/runners.mjs +0 -6
- package/dist/lib/conformance/runners.mjs.map +1 -1
- package/dist/lib/conformance/schemaArbitrary.mjs +0 -6
- package/dist/lib/conformance/schemaArbitrary.mjs.map +1 -1
- package/dist/lib/conformance/schemaLoader.mjs +5 -6
- package/dist/lib/conformance/schemaLoader.mjs.map +1 -1
- package/dist/lib/conformance/seeder.mjs +0 -6
- package/dist/lib/conformance/seeder.mjs.map +1 -1
- package/dist/lib/conformance/types.mjs +0 -6
- package/dist/lib/conformance/types.mjs.map +1 -1
- package/dist/lib/conformance/webhook.mjs +0 -6
- package/dist/lib/conformance/webhook.mjs.map +1 -1
- package/dist/lib/core/ADCPMultiAgentClient.mjs +0 -6
- package/dist/lib/core/ADCPMultiAgentClient.mjs.map +1 -1
- package/dist/lib/core/AgentClient.mjs +0 -6
- package/dist/lib/core/AgentClient.mjs.map +1 -1
- package/dist/lib/core/AsyncHandler.mjs +0 -6
- package/dist/lib/core/AsyncHandler.mjs.map +1 -1
- package/dist/lib/core/ConfigurationManager.mjs +0 -6
- package/dist/lib/core/ConfigurationManager.mjs.map +1 -1
- package/dist/lib/core/ConversationTypes.mjs +0 -6
- package/dist/lib/core/CreativeAgentClient.mjs +0 -6
- package/dist/lib/core/CreativeAgentClient.mjs.map +1 -1
- package/dist/lib/core/GovernanceMiddleware.mjs +0 -6
- package/dist/lib/core/GovernanceMiddleware.mjs.map +1 -1
- package/dist/lib/core/GovernanceTypes.mjs +0 -6
- package/dist/lib/core/GovernanceTypes.mjs.map +1 -1
- package/dist/lib/core/ProtocolResponseParser.mjs +0 -6
- package/dist/lib/core/ProtocolResponseParser.mjs.map +1 -1
- package/dist/lib/core/ResponseValidator.mjs +0 -6
- package/dist/lib/core/ResponseValidator.mjs.map +1 -1
- package/dist/lib/core/SingleAgentClient.mjs +0 -6
- package/dist/lib/core/SingleAgentClient.mjs.map +1 -1
- package/dist/lib/core/TaskEventTypes.mjs +0 -6
- package/dist/lib/core/TaskEventTypes.mjs.map +1 -1
- package/dist/lib/core/TaskExecutor.mjs +0 -6
- package/dist/lib/core/TaskExecutor.mjs.map +1 -1
- package/dist/lib/core/match.mjs +0 -6
- package/dist/lib/core/match.mjs.map +1 -1
- package/dist/lib/core/task-status.mjs +0 -6
- package/dist/lib/core/task-status.mjs.map +1 -1
- package/dist/lib/core/webhook-url.mjs +0 -6
- package/dist/lib/core/webhook-url.mjs.map +1 -1
- package/dist/lib/discovery/adagents-redirects.mjs +0 -6
- package/dist/lib/discovery/adagents-redirects.mjs.map +1 -1
- package/dist/lib/discovery/agent-directory.mjs +0 -6
- package/dist/lib/discovery/agent-directory.mjs.map +1 -1
- package/dist/lib/discovery/inline-publisher-properties.mjs +0 -6
- package/dist/lib/discovery/inline-publisher-properties.mjs.map +1 -1
- package/dist/lib/discovery/network-consistency-checker.mjs +0 -6
- package/dist/lib/discovery/network-consistency-checker.mjs.map +1 -1
- package/dist/lib/discovery/property-crawler.mjs +0 -6
- package/dist/lib/discovery/property-crawler.mjs.map +1 -1
- package/dist/lib/discovery/property-index.mjs +0 -6
- package/dist/lib/discovery/property-index.mjs.map +1 -1
- package/dist/lib/discovery/publisher-property-selector.mjs +0 -6
- package/dist/lib/discovery/publisher-property-selector.mjs.map +1 -1
- package/dist/lib/discovery/resolve-agent-properties.mjs +0 -6
- package/dist/lib/discovery/resolve-agent-properties.mjs.map +1 -1
- package/dist/lib/discovery/types.mjs +0 -6
- package/dist/lib/discovery/validate-adagents.mjs +0 -6
- package/dist/lib/discovery/validate-adagents.mjs.map +1 -1
- package/dist/lib/enums.mjs +0 -6
- package/dist/lib/enums.mjs.map +1 -1
- package/dist/lib/errors/index.mjs +0 -6
- package/dist/lib/errors/index.mjs.map +1 -1
- package/dist/lib/express-mcp/index.mjs +0 -6
- package/dist/lib/express-mcp/index.mjs.map +1 -1
- package/dist/lib/governance/index.mjs +0 -6
- package/dist/lib/governance/index.mjs.map +1 -1
- package/dist/lib/handlers/types.mjs +0 -6
- package/dist/lib/handlers/types.mjs.map +1 -1
- package/dist/lib/index.d.mts +1 -1
- package/dist/lib/index.d.ts +1 -1
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/index.mjs +0 -6
- package/dist/lib/index.mjs.map +1 -1
- package/dist/lib/media-buy/available-actions.mjs +0 -6
- package/dist/lib/media-buy/available-actions.mjs.map +1 -1
- package/dist/lib/media-buy/index.mjs +0 -6
- package/dist/lib/media-buy/index.mjs.map +1 -1
- package/dist/lib/media-buy/preflight.mjs +0 -6
- package/dist/lib/media-buy/preflight.mjs.map +1 -1
- package/dist/lib/media-buy/property-policy.mjs +0 -6
- package/dist/lib/media-buy/property-policy.mjs.map +1 -1
- package/dist/lib/media-buy/types.mjs +0 -6
- package/dist/lib/media-buy/types.mjs.map +1 -1
- package/dist/lib/media-buy/update-fields.generated.mjs +0 -6
- package/dist/lib/media-buy/update-fields.generated.mjs.map +1 -1
- package/dist/lib/mock-server/creative-ad-server/seed-data.mjs +0 -6
- package/dist/lib/mock-server/creative-ad-server/seed-data.mjs.map +1 -1
- package/dist/lib/mock-server/creative-ad-server/server.mjs +0 -6
- package/dist/lib/mock-server/creative-ad-server/server.mjs.map +1 -1
- package/dist/lib/mock-server/creative-template/seed-data.mjs +0 -6
- package/dist/lib/mock-server/creative-template/seed-data.mjs.map +1 -1
- package/dist/lib/mock-server/creative-template/server.mjs +0 -6
- package/dist/lib/mock-server/creative-template/server.mjs.map +1 -1
- package/dist/lib/mock-server/index.mjs +0 -6
- package/dist/lib/mock-server/index.mjs.map +1 -1
- package/dist/lib/mock-server/sales-guaranteed/recipe.mjs +0 -6
- package/dist/lib/mock-server/sales-guaranteed/recipe.mjs.map +1 -1
- package/dist/lib/mock-server/sales-guaranteed/seed-data.mjs +0 -6
- package/dist/lib/mock-server/sales-guaranteed/seed-data.mjs.map +1 -1
- package/dist/lib/mock-server/sales-guaranteed/server.mjs +0 -6
- package/dist/lib/mock-server/sales-guaranteed/server.mjs.map +1 -1
- package/dist/lib/mock-server/sales-non-guaranteed/recipe.mjs +0 -6
- package/dist/lib/mock-server/sales-non-guaranteed/recipe.mjs.map +1 -1
- package/dist/lib/mock-server/sales-non-guaranteed/seed-data.mjs +0 -6
- package/dist/lib/mock-server/sales-non-guaranteed/seed-data.mjs.map +1 -1
- package/dist/lib/mock-server/sales-non-guaranteed/server.mjs +0 -6
- package/dist/lib/mock-server/sales-non-guaranteed/server.mjs.map +1 -1
- package/dist/lib/mock-server/sales-social/seed-data.mjs +0 -6
- package/dist/lib/mock-server/sales-social/seed-data.mjs.map +1 -1
- package/dist/lib/mock-server/sales-social/server.mjs +0 -6
- package/dist/lib/mock-server/sales-social/server.mjs.map +1 -1
- package/dist/lib/mock-server/scenario.mjs +0 -6
- package/dist/lib/mock-server/scenario.mjs.map +1 -1
- package/dist/lib/mock-server/signal-marketplace/seed-data.mjs +0 -6
- package/dist/lib/mock-server/signal-marketplace/seed-data.mjs.map +1 -1
- package/dist/lib/mock-server/signal-marketplace/server.mjs +0 -6
- package/dist/lib/mock-server/signal-marketplace/server.mjs.map +1 -1
- package/dist/lib/mock-server/sponsored-intelligence/seed-data.mjs +0 -6
- package/dist/lib/mock-server/sponsored-intelligence/seed-data.mjs.map +1 -1
- package/dist/lib/mock-server/sponsored-intelligence/server.mjs +0 -6
- package/dist/lib/mock-server/sponsored-intelligence/server.mjs.map +1 -1
- package/dist/lib/net/address-guards.mjs +0 -6
- package/dist/lib/net/address-guards.mjs.map +1 -1
- package/dist/lib/net/index.mjs +0 -6
- package/dist/lib/net/index.mjs.map +1 -1
- package/dist/lib/net/ssrf-fetch.mjs +0 -6
- package/dist/lib/net/ssrf-fetch.mjs.map +1 -1
- package/dist/lib/observability/index.mjs +0 -6
- package/dist/lib/observability/index.mjs.map +1 -1
- package/dist/lib/observability/tracing.mjs +6 -7
- package/dist/lib/observability/tracing.mjs.map +1 -1
- package/dist/lib/protocols/a2a.mjs +0 -6
- package/dist/lib/protocols/a2a.mjs.map +1 -1
- package/dist/lib/protocols/abort.mjs +0 -6
- package/dist/lib/protocols/abort.mjs.map +1 -1
- package/dist/lib/protocols/index.mjs +0 -6
- package/dist/lib/protocols/index.mjs.map +1 -1
- package/dist/lib/protocols/mcp-modern.mjs +0 -6
- package/dist/lib/protocols/mcp-modern.mjs.map +1 -1
- package/dist/lib/protocols/mcp-tasks.mjs +0 -6
- package/dist/lib/protocols/mcp-tasks.mjs.map +1 -1
- package/dist/lib/protocols/mcp.mjs +0 -6
- package/dist/lib/protocols/mcp.mjs.map +1 -1
- package/dist/lib/protocols/rawResponseCapture.mjs +0 -6
- package/dist/lib/protocols/rawResponseCapture.mjs.map +1 -1
- package/dist/lib/protocols/responseSizeLimit.mjs +0 -6
- package/dist/lib/protocols/responseSizeLimit.mjs.map +1 -1
- package/dist/lib/protocols/transportDiagnostics.mjs +0 -6
- package/dist/lib/protocols/transportDiagnostics.mjs.map +1 -1
- package/dist/lib/registry/cursor-store.mjs +0 -6
- package/dist/lib/registry/cursor-store.mjs.map +1 -1
- package/dist/lib/registry/feed-stream.mjs +0 -6
- package/dist/lib/registry/feed-stream.mjs.map +1 -1
- package/dist/lib/registry/index.mjs +0 -6
- package/dist/lib/registry/index.mjs.map +1 -1
- package/dist/lib/registry/property-registry.mjs +0 -6
- package/dist/lib/registry/property-registry.mjs.map +1 -1
- package/dist/lib/registry/sync.mjs +0 -6
- package/dist/lib/registry/sync.mjs.map +1 -1
- package/dist/lib/registry/types.generated.mjs +0 -6
- package/dist/lib/registry/types.mjs +0 -6
- package/dist/lib/schemas/index.mjs +0 -6
- package/dist/lib/schemas/index.mjs.map +1 -1
- package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
- package/dist/lib/server/a2a-adapter.mjs +0 -6
- package/dist/lib/server/a2a-adapter.mjs.map +1 -1
- package/dist/lib/server/account-mode.mjs +0 -6
- package/dist/lib/server/account-mode.mjs.map +1 -1
- package/dist/lib/server/adcp-server.mjs +0 -6
- package/dist/lib/server/adcp-server.mjs.map +1 -1
- package/dist/lib/server/auth-introspection.mjs +0 -6
- package/dist/lib/server/auth-introspection.mjs.map +1 -1
- package/dist/lib/server/auth-signature.mjs +0 -6
- package/dist/lib/server/auth-signature.mjs.map +1 -1
- package/dist/lib/server/auth.mjs +0 -6
- package/dist/lib/server/auth.mjs.map +1 -1
- package/dist/lib/server/create-adcp-server.d.mts +13 -0
- package/dist/lib/server/create-adcp-server.d.ts +13 -0
- package/dist/lib/server/create-adcp-server.d.ts.map +1 -1
- package/dist/lib/server/create-adcp-server.js +3 -2
- package/dist/lib/server/create-adcp-server.js.map +1 -1
- package/dist/lib/server/create-adcp-server.mjs +3 -8
- package/dist/lib/server/create-adcp-server.mjs.map +1 -1
- package/dist/lib/server/credential-policy.mjs +0 -6
- package/dist/lib/server/credential-policy.mjs.map +1 -1
- package/dist/lib/server/ctx-metadata/backends/memory.mjs +0 -6
- package/dist/lib/server/ctx-metadata/backends/memory.mjs.map +1 -1
- package/dist/lib/server/ctx-metadata/backends/pg.mjs +0 -6
- package/dist/lib/server/ctx-metadata/backends/pg.mjs.map +1 -1
- package/dist/lib/server/ctx-metadata/backends/redis.mjs +0 -6
- package/dist/lib/server/ctx-metadata/backends/redis.mjs.map +1 -1
- package/dist/lib/server/ctx-metadata/index.mjs +0 -6
- package/dist/lib/server/ctx-metadata/index.mjs.map +1 -1
- package/dist/lib/server/ctx-metadata/store.mjs +0 -6
- package/dist/lib/server/ctx-metadata/store.mjs.map +1 -1
- package/dist/lib/server/ctx-metadata/wire-shape.mjs +0 -6
- package/dist/lib/server/ctx-metadata/wire-shape.mjs.map +1 -1
- package/dist/lib/server/decisioning/account.mjs +0 -6
- package/dist/lib/server/decisioning/account.mjs.map +1 -1
- package/dist/lib/server/decisioning/admin-router.mjs +0 -6
- package/dist/lib/server/decisioning/admin-router.mjs.map +1 -1
- package/dist/lib/server/decisioning/assembly-helpers.mjs +0 -6
- package/dist/lib/server/decisioning/assembly-helpers.mjs.map +1 -1
- package/dist/lib/server/decisioning/async-outcome.mjs +0 -6
- package/dist/lib/server/decisioning/async-outcome.mjs.map +1 -1
- package/dist/lib/server/decisioning/buyer-agent.mjs +0 -6
- package/dist/lib/server/decisioning/buyer-agent.mjs.map +1 -1
- package/dist/lib/server/decisioning/capabilities.mjs +0 -6
- package/dist/lib/server/decisioning/capabilities.mjs.map +1 -1
- package/dist/lib/server/decisioning/compose.mjs +0 -6
- package/dist/lib/server/decisioning/compose.mjs.map +1 -1
- package/dist/lib/server/decisioning/context.mjs +0 -6
- package/dist/lib/server/decisioning/errors-typed.mjs +0 -6
- package/dist/lib/server/decisioning/errors-typed.mjs.map +1 -1
- package/dist/lib/server/decisioning/helpers.mjs +0 -6
- package/dist/lib/server/decisioning/helpers.mjs.map +1 -1
- package/dist/lib/server/decisioning/index.mjs +0 -6
- package/dist/lib/server/decisioning/index.mjs.map +1 -1
- package/dist/lib/server/decisioning/list-helpers.mjs +0 -6
- package/dist/lib/server/decisioning/list-helpers.mjs.map +1 -1
- package/dist/lib/server/decisioning/manifest-helpers.mjs +0 -6
- package/dist/lib/server/decisioning/manifest-helpers.mjs.map +1 -1
- package/dist/lib/server/decisioning/pagination.mjs +0 -6
- package/dist/lib/server/decisioning/platform-helpers.mjs +0 -6
- package/dist/lib/server/decisioning/platform-helpers.mjs.map +1 -1
- package/dist/lib/server/decisioning/platform.mjs +0 -6
- package/dist/lib/server/decisioning/proposal/dispatch.mjs +0 -6
- package/dist/lib/server/decisioning/proposal/dispatch.mjs.map +1 -1
- package/dist/lib/server/decisioning/proposal/index.mjs +0 -6
- package/dist/lib/server/decisioning/proposal/index.mjs.map +1 -1
- package/dist/lib/server/decisioning/proposal/lifecycle.mjs +0 -6
- package/dist/lib/server/decisioning/proposal/lifecycle.mjs.map +1 -1
- package/dist/lib/server/decisioning/proposal/mock-manager.mjs +0 -6
- package/dist/lib/server/decisioning/proposal/mock-manager.mjs.map +1 -1
- package/dist/lib/server/decisioning/proposal/store.mjs +0 -6
- package/dist/lib/server/decisioning/proposal/store.mjs.map +1 -1
- package/dist/lib/server/decisioning/proposal/types.mjs +0 -6
- package/dist/lib/server/decisioning/proposal/types.mjs.map +1 -1
- package/dist/lib/server/decisioning/resolve-presets.mjs +0 -6
- package/dist/lib/server/decisioning/resolve-presets.mjs.map +1 -1
- package/dist/lib/server/decisioning/runtime/entity-hydration.generated.mjs +0 -6
- package/dist/lib/server/decisioning/runtime/entity-hydration.generated.mjs.map +1 -1
- package/dist/lib/server/decisioning/runtime/from-platform.mjs +0 -6
- package/dist/lib/server/decisioning/runtime/from-platform.mjs.map +1 -1
- package/dist/lib/server/decisioning/runtime/observed-modes.mjs +0 -6
- package/dist/lib/server/decisioning/runtime/observed-modes.mjs.map +1 -1
- package/dist/lib/server/decisioning/runtime/postgres-task-registry.mjs +0 -6
- package/dist/lib/server/decisioning/runtime/postgres-task-registry.mjs.map +1 -1
- package/dist/lib/server/decisioning/runtime/protocol-for-tool.mjs +0 -6
- package/dist/lib/server/decisioning/runtime/protocol-for-tool.mjs.map +1 -1
- package/dist/lib/server/decisioning/runtime/task-registry.mjs +0 -6
- package/dist/lib/server/decisioning/runtime/task-registry.mjs.map +1 -1
- package/dist/lib/server/decisioning/runtime/to-context.mjs +0 -6
- package/dist/lib/server/decisioning/runtime/to-context.mjs.map +1 -1
- package/dist/lib/server/decisioning/runtime/validate-platform.mjs +0 -6
- package/dist/lib/server/decisioning/runtime/validate-platform.mjs.map +1 -1
- package/dist/lib/server/decisioning/specialisms/audiences.mjs +0 -6
- package/dist/lib/server/decisioning/specialisms/brand-rights.mjs +0 -6
- package/dist/lib/server/decisioning/specialisms/campaign-governance.mjs +0 -6
- package/dist/lib/server/decisioning/specialisms/content-standards.mjs +0 -6
- package/dist/lib/server/decisioning/specialisms/creative-ad-server.mjs +0 -6
- package/dist/lib/server/decisioning/specialisms/creative.mjs +0 -6
- package/dist/lib/server/decisioning/specialisms/lists.mjs +0 -6
- package/dist/lib/server/decisioning/specialisms/sales.mjs +0 -6
- package/dist/lib/server/decisioning/specialisms/signals.mjs +0 -6
- package/dist/lib/server/decisioning/specialisms/sponsored-intelligence.mjs +0 -6
- package/dist/lib/server/decisioning/start-time.mjs +0 -6
- package/dist/lib/server/decisioning/start-time.mjs.map +1 -1
- package/dist/lib/server/decisioning/status-changes.mjs +0 -6
- package/dist/lib/server/decisioning/status-changes.mjs.map +1 -1
- package/dist/lib/server/decisioning/status-mappers.mjs +0 -6
- package/dist/lib/server/decisioning/status-mappers.mjs.map +1 -1
- package/dist/lib/server/decisioning/tenant-registry.mjs +0 -6
- package/dist/lib/server/decisioning/tenant-registry.mjs.map +1 -1
- package/dist/lib/server/decisioning/tenant-store.mjs +0 -6
- package/dist/lib/server/decisioning/tenant-store.mjs.map +1 -1
- package/dist/lib/server/decisioning/validate-specialisms.mjs +0 -6
- package/dist/lib/server/decisioning/validate-specialisms.mjs.map +1 -1
- package/dist/lib/server/dynamic-registry.mjs +0 -6
- package/dist/lib/server/dynamic-registry.mjs.map +1 -1
- package/dist/lib/server/envelope-allowlist.mjs +0 -6
- package/dist/lib/server/envelope-allowlist.mjs.map +1 -1
- package/dist/lib/server/error-arm-tools.mjs +5 -6
- package/dist/lib/server/error-arm-tools.mjs.map +1 -1
- package/dist/lib/server/errors.mjs +0 -6
- package/dist/lib/server/errors.mjs.map +1 -1
- package/dist/lib/server/example-tld-guard.mjs +0 -6
- package/dist/lib/server/example-tld-guard.mjs.map +1 -1
- package/dist/lib/server/express-adapter.mjs +0 -6
- package/dist/lib/server/express-adapter.mjs.map +1 -1
- package/dist/lib/server/governance.mjs +0 -6
- package/dist/lib/server/governance.mjs.map +1 -1
- package/dist/lib/server/idempotency/backends/lazy.mjs +0 -6
- package/dist/lib/server/idempotency/backends/lazy.mjs.map +1 -1
- package/dist/lib/server/idempotency/backends/memory.mjs +0 -6
- package/dist/lib/server/idempotency/backends/memory.mjs.map +1 -1
- package/dist/lib/server/idempotency/backends/pg.mjs +0 -6
- package/dist/lib/server/idempotency/backends/pg.mjs.map +1 -1
- package/dist/lib/server/idempotency/backends/redis.mjs +0 -6
- package/dist/lib/server/idempotency/backends/redis.mjs.map +1 -1
- package/dist/lib/server/idempotency/index.mjs +0 -6
- package/dist/lib/server/idempotency/index.mjs.map +1 -1
- package/dist/lib/server/idempotency/store.mjs +0 -6
- package/dist/lib/server/idempotency/store.mjs.map +1 -1
- package/dist/lib/server/index.d.mts +1 -1
- package/dist/lib/server/index.d.ts +1 -1
- package/dist/lib/server/index.d.ts.map +1 -1
- package/dist/lib/server/index.js.map +1 -1
- package/dist/lib/server/index.mjs +0 -6
- package/dist/lib/server/index.mjs.map +1 -1
- package/dist/lib/server/legacy/v5/index.d.mts +1 -1
- package/dist/lib/server/legacy/v5/index.d.ts +1 -1
- package/dist/lib/server/legacy/v5/index.d.ts.map +1 -1
- package/dist/lib/server/legacy/v5/index.js.map +1 -1
- package/dist/lib/server/legacy/v5/index.mjs +0 -6
- package/dist/lib/server/legacy/v5/index.mjs.map +1 -1
- package/dist/lib/server/mcp-modern-server.mjs +0 -6
- package/dist/lib/server/mcp-modern-server.mjs.map +1 -1
- package/dist/lib/server/media-buy-actions.mjs +0 -6
- package/dist/lib/server/media-buy-actions.mjs.map +1 -1
- package/dist/lib/server/media-buy-helpers.mjs +0 -6
- package/dist/lib/server/media-buy-helpers.mjs.map +1 -1
- package/dist/lib/server/media-buy-store.mjs +0 -6
- package/dist/lib/server/media-buy-store.mjs.map +1 -1
- package/dist/lib/server/normalize-errors.mjs +0 -6
- package/dist/lib/server/normalize-errors.mjs.map +1 -1
- package/dist/lib/server/operational-platform.mjs +0 -6
- package/dist/lib/server/operational-platform.mjs.map +1 -1
- package/dist/lib/server/pick-safe-details.mjs +0 -6
- package/dist/lib/server/pick-safe-details.mjs.map +1 -1
- package/dist/lib/server/pin-and-bind-fetch.mjs +0 -6
- package/dist/lib/server/pin-and-bind-fetch.mjs.map +1 -1
- package/dist/lib/server/postgres-state-store.mjs +0 -6
- package/dist/lib/server/postgres-state-store.mjs.map +1 -1
- package/dist/lib/server/postgres-task-store.mjs +0 -6
- package/dist/lib/server/postgres-task-store.mjs.map +1 -1
- package/dist/lib/server/product-defaults.mjs +0 -6
- package/dist/lib/server/product-defaults.mjs.map +1 -1
- package/dist/lib/server/redact.mjs +0 -6
- package/dist/lib/server/redact.mjs.map +1 -1
- package/dist/lib/server/responses.mjs +0 -6
- package/dist/lib/server/responses.mjs.map +1 -1
- package/dist/lib/server/serve.mjs +0 -6
- package/dist/lib/server/serve.mjs.map +1 -1
- package/dist/lib/server/socket-mode/conformance-client.mjs +0 -6
- package/dist/lib/server/socket-mode/conformance-client.mjs.map +1 -1
- package/dist/lib/server/socket-mode/index.mjs +0 -6
- package/dist/lib/server/socket-mode/index.mjs.map +1 -1
- package/dist/lib/server/socket-mode/ws-transport.mjs +0 -6
- package/dist/lib/server/socket-mode/ws-transport.mjs.map +1 -1
- package/dist/lib/server/state-machine.mjs +0 -6
- package/dist/lib/server/state-machine.mjs.map +1 -1
- package/dist/lib/server/state-store.mjs +0 -6
- package/dist/lib/server/state-store.mjs.map +1 -1
- package/dist/lib/server/structured-serialize.mjs +0 -6
- package/dist/lib/server/structured-serialize.mjs.map +1 -1
- package/dist/lib/server/targeting-helpers.mjs +0 -6
- package/dist/lib/server/targeting-helpers.mjs.map +1 -1
- package/dist/lib/server/tasks.mjs +0 -6
- package/dist/lib/server/tasks.mjs.map +1 -1
- package/dist/lib/server/test-controller-bridge.mjs +0 -6
- package/dist/lib/server/test-controller-bridge.mjs.map +1 -1
- package/dist/lib/server/test-controller.mjs +0 -6
- package/dist/lib/server/test-controller.mjs.map +1 -1
- package/dist/lib/server/upstream-helpers.mjs +0 -6
- package/dist/lib/server/upstream-helpers.mjs.map +1 -1
- package/dist/lib/server/webhook-emitter.mjs +0 -6
- package/dist/lib/server/webhook-emitter.mjs.map +1 -1
- package/dist/lib/server/wire-safe.mjs +0 -6
- package/dist/lib/server/wire-safe.mjs.map +1 -1
- package/dist/lib/server/wire-spec-fields.generated.mjs +0 -6
- package/dist/lib/server/wire-spec-fields.generated.mjs.map +1 -1
- package/dist/lib/server/wrap-envelope.mjs +0 -6
- package/dist/lib/server/wrap-envelope.mjs.map +1 -1
- package/dist/lib/signing/agent-context.mjs +0 -6
- package/dist/lib/signing/agent-context.mjs.map +1 -1
- package/dist/lib/signing/agent-fetch.mjs +0 -6
- package/dist/lib/signing/agent-fetch.mjs.map +1 -1
- package/dist/lib/signing/agent-resolver/canonicalize.mjs +0 -6
- package/dist/lib/signing/agent-resolver/canonicalize.mjs.map +1 -1
- package/dist/lib/signing/agent-resolver/capabilities-types.mjs +0 -6
- package/dist/lib/signing/agent-resolver/capabilities-types.mjs.map +1 -1
- package/dist/lib/signing/agent-resolver/consistency.mjs +0 -6
- package/dist/lib/signing/agent-resolver/consistency.mjs.map +1 -1
- package/dist/lib/signing/agent-resolver/errors.mjs +0 -6
- package/dist/lib/signing/agent-resolver/errors.mjs.map +1 -1
- package/dist/lib/signing/agent-resolver/etld.mjs +0 -6
- package/dist/lib/signing/agent-resolver/etld.mjs.map +1 -1
- package/dist/lib/signing/agent-resolver/fetch-helpers.mjs +0 -6
- package/dist/lib/signing/agent-resolver/fetch-helpers.mjs.map +1 -1
- package/dist/lib/signing/agent-resolver/index.mjs +0 -6
- package/dist/lib/signing/agent-resolver/index.mjs.map +1 -1
- package/dist/lib/signing/agent-resolver/jwks-set.mjs +0 -6
- package/dist/lib/signing/agent-resolver/jwks-set.mjs.map +1 -1
- package/dist/lib/signing/agent-resolver/resolve-agent.mjs +0 -6
- package/dist/lib/signing/agent-resolver/resolve-agent.mjs.map +1 -1
- package/dist/lib/signing/agent-resolver/select-agent.mjs +0 -6
- package/dist/lib/signing/agent-resolver/select-agent.mjs.map +1 -1
- package/dist/lib/signing/agent-resolver/strict-json.mjs +0 -6
- package/dist/lib/signing/agent-resolver/strict-json.mjs.map +1 -1
- package/dist/lib/signing/brand-jwks.mjs +0 -6
- package/dist/lib/signing/brand-jwks.mjs.map +1 -1
- package/dist/lib/signing/canonicalize.mjs +0 -6
- package/dist/lib/signing/canonicalize.mjs.map +1 -1
- package/dist/lib/signing/capability-cache.mjs +0 -6
- package/dist/lib/signing/capability-cache.mjs.map +1 -1
- package/dist/lib/signing/capability-priming.mjs +0 -6
- package/dist/lib/signing/capability-priming.mjs.map +1 -1
- package/dist/lib/signing/client.mjs +0 -6
- package/dist/lib/signing/client.mjs.map +1 -1
- package/dist/lib/signing/content-digest.mjs +0 -6
- package/dist/lib/signing/content-digest.mjs.map +1 -1
- package/dist/lib/signing/crypto.mjs +0 -6
- package/dist/lib/signing/crypto.mjs.map +1 -1
- package/dist/lib/signing/ecdsa-encoding.mjs +0 -6
- package/dist/lib/signing/ecdsa-encoding.mjs.map +1 -1
- package/dist/lib/signing/errors.mjs +0 -6
- package/dist/lib/signing/errors.mjs.map +1 -1
- package/dist/lib/signing/fetch-async.mjs +0 -6
- package/dist/lib/signing/fetch-async.mjs.map +1 -1
- package/dist/lib/signing/fetch.mjs +0 -6
- package/dist/lib/signing/fetch.mjs.map +1 -1
- package/dist/lib/signing/index.mjs +0 -6
- package/dist/lib/signing/index.mjs.map +1 -1
- package/dist/lib/signing/jwks-helpers.mjs +0 -6
- package/dist/lib/signing/jwks-helpers.mjs.map +1 -1
- package/dist/lib/signing/jwks-https.mjs +0 -6
- package/dist/lib/signing/jwks-https.mjs.map +1 -1
- package/dist/lib/signing/jwks.mjs +0 -6
- package/dist/lib/signing/jwks.mjs.map +1 -1
- package/dist/lib/signing/middleware.mjs +0 -6
- package/dist/lib/signing/middleware.mjs.map +1 -1
- package/dist/lib/signing/parser.mjs +0 -6
- package/dist/lib/signing/parser.mjs.map +1 -1
- package/dist/lib/signing/postgres-replay-store.mjs +0 -6
- package/dist/lib/signing/postgres-replay-store.mjs.map +1 -1
- package/dist/lib/signing/protocol-response.mjs +0 -6
- package/dist/lib/signing/protocol-response.mjs.map +1 -1
- package/dist/lib/signing/provider.mjs +0 -6
- package/dist/lib/signing/redis-replay-store.mjs +0 -6
- package/dist/lib/signing/redis-replay-store.mjs.map +1 -1
- package/dist/lib/signing/replay.mjs +0 -6
- package/dist/lib/signing/replay.mjs.map +1 -1
- package/dist/lib/signing/request-context.mjs +0 -6
- package/dist/lib/signing/request-context.mjs.map +1 -1
- package/dist/lib/signing/revocation-https.mjs +0 -6
- package/dist/lib/signing/revocation-https.mjs.map +1 -1
- package/dist/lib/signing/revocation.mjs +0 -6
- package/dist/lib/signing/revocation.mjs.map +1 -1
- package/dist/lib/signing/server.mjs +0 -6
- package/dist/lib/signing/server.mjs.map +1 -1
- package/dist/lib/signing/signer-async.mjs +0 -6
- package/dist/lib/signing/signer-async.mjs.map +1 -1
- package/dist/lib/signing/signer.mjs +0 -6
- package/dist/lib/signing/signer.mjs.map +1 -1
- package/dist/lib/signing/testing.mjs +0 -6
- package/dist/lib/signing/testing.mjs.map +1 -1
- package/dist/lib/signing/types.mjs +0 -6
- package/dist/lib/signing/types.mjs.map +1 -1
- package/dist/lib/signing/verifier.mjs +0 -6
- package/dist/lib/signing/verifier.mjs.map +1 -1
- package/dist/lib/signing/webhook-auth-detection.mjs +0 -6
- package/dist/lib/signing/webhook-auth-detection.mjs.map +1 -1
- package/dist/lib/signing/webhook-verifier.mjs +0 -6
- package/dist/lib/signing/webhook-verifier.mjs.map +1 -1
- package/dist/lib/storage/MemoryStorage.mjs +0 -6
- package/dist/lib/storage/MemoryStorage.mjs.map +1 -1
- package/dist/lib/storage/interfaces.mjs +0 -6
- package/dist/lib/substitution/encoder/SubstitutionEncoder.mjs +0 -6
- package/dist/lib/substitution/encoder/SubstitutionEncoder.mjs.map +1 -1
- package/dist/lib/substitution/encoder/index.mjs +0 -6
- package/dist/lib/substitution/encoder/index.mjs.map +1 -1
- package/dist/lib/substitution/index.mjs +0 -6
- package/dist/lib/substitution/index.mjs.map +1 -1
- package/dist/lib/substitution/observer/SubstitutionObserver.mjs +0 -6
- package/dist/lib/substitution/observer/SubstitutionObserver.mjs.map +1 -1
- package/dist/lib/substitution/observer/alignment.mjs +0 -6
- package/dist/lib/substitution/observer/alignment.mjs.map +1 -1
- package/dist/lib/substitution/observer/assertions.mjs +0 -6
- package/dist/lib/substitution/observer/assertions.mjs.map +1 -1
- package/dist/lib/substitution/observer/html-parser.mjs +0 -6
- package/dist/lib/substitution/observer/html-parser.mjs.map +1 -1
- package/dist/lib/substitution/observer/index.mjs +0 -6
- package/dist/lib/substitution/observer/index.mjs.map +1 -1
- package/dist/lib/substitution/observer/ssrf.mjs +0 -6
- package/dist/lib/substitution/observer/ssrf.mjs.map +1 -1
- package/dist/lib/substitution/rfc3986.mjs +0 -6
- package/dist/lib/substitution/rfc3986.mjs.map +1 -1
- package/dist/lib/substitution/translate.mjs +0 -6
- package/dist/lib/substitution/translate.mjs.map +1 -1
- package/dist/lib/substitution/types.mjs +0 -6
- package/dist/lib/substitution/vectors.mjs +0 -6
- package/dist/lib/substitution/vectors.mjs.map +1 -1
- package/dist/lib/testing/agent-tester.mjs +0 -6
- package/dist/lib/testing/agent-tester.mjs.map +1 -1
- package/dist/lib/testing/client.mjs +0 -6
- package/dist/lib/testing/client.mjs.map +1 -1
- package/dist/lib/testing/compliance/briefs.mjs +0 -6
- package/dist/lib/testing/compliance/briefs.mjs.map +1 -1
- package/dist/lib/testing/compliance/comply.mjs +0 -6
- package/dist/lib/testing/compliance/comply.mjs.map +1 -1
- package/dist/lib/testing/compliance/index.mjs +0 -6
- package/dist/lib/testing/compliance/index.mjs.map +1 -1
- package/dist/lib/testing/compliance/spec-conformance.mjs +0 -6
- package/dist/lib/testing/compliance/spec-conformance.mjs.map +1 -1
- package/dist/lib/testing/compliance/storyboard-tracks.mjs +0 -6
- package/dist/lib/testing/compliance/storyboard-tracks.mjs.map +1 -1
- package/dist/lib/testing/compliance/summary.mjs +0 -6
- package/dist/lib/testing/compliance/summary.mjs.map +1 -1
- package/dist/lib/testing/compliance/types.mjs +0 -6
- package/dist/lib/testing/comply-controller.mjs +0 -6
- package/dist/lib/testing/comply-controller.mjs.map +1 -1
- package/dist/lib/testing/controller-assertions.mjs +0 -6
- package/dist/lib/testing/controller-assertions.mjs.map +1 -1
- package/dist/lib/testing/formatter.mjs +0 -6
- package/dist/lib/testing/formatter.mjs.map +1 -1
- package/dist/lib/testing/index.mjs +0 -6
- package/dist/lib/testing/index.mjs.map +1 -1
- package/dist/lib/testing/local-agent-runner.mjs +0 -6
- package/dist/lib/testing/local-agent-runner.mjs.map +1 -1
- package/dist/lib/testing/orchestrator.mjs +0 -6
- package/dist/lib/testing/orchestrator.mjs.map +1 -1
- package/dist/lib/testing/personas/index.mjs +0 -6
- package/dist/lib/testing/personas/index.mjs.map +1 -1
- package/dist/lib/testing/scenarios/brand-rights.mjs +0 -6
- package/dist/lib/testing/scenarios/brand-rights.mjs.map +1 -1
- package/dist/lib/testing/scenarios/capabilities.mjs +0 -6
- package/dist/lib/testing/scenarios/capabilities.mjs.map +1 -1
- package/dist/lib/testing/scenarios/creative.mjs +0 -6
- package/dist/lib/testing/scenarios/creative.mjs.map +1 -1
- package/dist/lib/testing/scenarios/deterministic.mjs +0 -6
- package/dist/lib/testing/scenarios/deterministic.mjs.map +1 -1
- package/dist/lib/testing/scenarios/discovery.mjs +0 -6
- package/dist/lib/testing/scenarios/discovery.mjs.map +1 -1
- package/dist/lib/testing/scenarios/edge-cases.mjs +0 -6
- package/dist/lib/testing/scenarios/edge-cases.mjs.map +1 -1
- package/dist/lib/testing/scenarios/error-compliance.mjs +0 -6
- package/dist/lib/testing/scenarios/error-compliance.mjs.map +1 -1
- package/dist/lib/testing/scenarios/governance.mjs +0 -6
- package/dist/lib/testing/scenarios/governance.mjs.map +1 -1
- package/dist/lib/testing/scenarios/health.mjs +0 -6
- package/dist/lib/testing/scenarios/health.mjs.map +1 -1
- package/dist/lib/testing/scenarios/index.mjs +0 -6
- package/dist/lib/testing/scenarios/index.mjs.map +1 -1
- package/dist/lib/testing/scenarios/media-buy.mjs +0 -6
- package/dist/lib/testing/scenarios/media-buy.mjs.map +1 -1
- package/dist/lib/testing/scenarios/schema-compliance.mjs +0 -6
- package/dist/lib/testing/scenarios/schema-compliance.mjs.map +1 -1
- package/dist/lib/testing/scenarios/signals.mjs +0 -6
- package/dist/lib/testing/scenarios/signals.mjs.map +1 -1
- package/dist/lib/testing/scenarios/sponsored-intelligence.mjs +0 -6
- package/dist/lib/testing/scenarios/sponsored-intelligence.mjs.map +1 -1
- package/dist/lib/testing/scenarios/trusted-match.mjs +0 -6
- package/dist/lib/testing/scenarios/trusted-match.mjs.map +1 -1
- package/dist/lib/testing/seed-merge.mjs +0 -6
- package/dist/lib/testing/seed-merge.mjs.map +1 -1
- package/dist/lib/testing/storyboard/agent-routing.mjs +0 -6
- package/dist/lib/testing/storyboard/agent-routing.mjs.map +1 -1
- package/dist/lib/testing/storyboard/assertions.mjs +0 -6
- package/dist/lib/testing/storyboard/assertions.mjs.map +1 -1
- package/dist/lib/testing/storyboard/canonical-format-satisfaction.mjs +0 -6
- package/dist/lib/testing/storyboard/canonical-format-satisfaction.mjs.map +1 -1
- package/dist/lib/testing/storyboard/compliance.mjs +5 -6
- package/dist/lib/testing/storyboard/compliance.mjs.map +1 -1
- package/dist/lib/testing/storyboard/context.mjs +0 -6
- package/dist/lib/testing/storyboard/context.mjs.map +1 -1
- package/dist/lib/testing/storyboard/default-invariants.mjs +0 -6
- package/dist/lib/testing/storyboard/default-invariants.mjs.map +1 -1
- package/dist/lib/testing/storyboard/index.mjs +0 -6
- package/dist/lib/testing/storyboard/index.mjs.map +1 -1
- package/dist/lib/testing/storyboard/junit.mjs +0 -6
- package/dist/lib/testing/storyboard/junit.mjs.map +1 -1
- package/dist/lib/testing/storyboard/loader.mjs +0 -6
- package/dist/lib/testing/storyboard/loader.mjs.map +1 -1
- package/dist/lib/testing/storyboard/parallel-dispatch.mjs +0 -6
- package/dist/lib/testing/storyboard/parallel-dispatch.mjs.map +1 -1
- package/dist/lib/testing/storyboard/path.mjs +0 -6
- package/dist/lib/testing/storyboard/path.mjs.map +1 -1
- package/dist/lib/testing/storyboard/probes.mjs +0 -6
- package/dist/lib/testing/storyboard/probes.mjs.map +1 -1
- package/dist/lib/testing/storyboard/rate-limit-trip.mjs +0 -6
- package/dist/lib/testing/storyboard/rate-limit-trip.mjs.map +1 -1
- package/dist/lib/testing/storyboard/rejection-hints.mjs +0 -6
- package/dist/lib/testing/storyboard/rejection-hints.mjs.map +1 -1
- package/dist/lib/testing/storyboard/request-builder.mjs +0 -6
- package/dist/lib/testing/storyboard/request-builder.mjs.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/builder.mjs +0 -6
- package/dist/lib/testing/storyboard/request-signing/builder.mjs.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/grader.mjs +0 -6
- package/dist/lib/testing/storyboard/request-signing/grader.mjs.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/index.mjs +0 -6
- package/dist/lib/testing/storyboard/request-signing/index.mjs.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/probe-dispatch.mjs +0 -6
- package/dist/lib/testing/storyboard/request-signing/probe-dispatch.mjs.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/probe.mjs +0 -6
- package/dist/lib/testing/storyboard/request-signing/probe.mjs.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/synthesize.mjs +0 -6
- package/dist/lib/testing/storyboard/request-signing/synthesize.mjs.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/test-kit.mjs +0 -6
- package/dist/lib/testing/storyboard/request-signing/test-kit.mjs.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/types.mjs +0 -6
- package/dist/lib/testing/storyboard/request-signing/types.mjs.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/vector-loader.mjs +0 -6
- package/dist/lib/testing/storyboard/request-signing/vector-loader.mjs.map +1 -1
- package/dist/lib/testing/storyboard/runner.mjs +0 -6
- package/dist/lib/testing/storyboard/runner.mjs.map +1 -1
- package/dist/lib/testing/storyboard/sandbox-entities.mjs +0 -6
- package/dist/lib/testing/storyboard/sandbox-entities.mjs.map +1 -1
- package/dist/lib/testing/storyboard/seeding.mjs +0 -6
- package/dist/lib/testing/storyboard/seeding.mjs.map +1 -1
- package/dist/lib/testing/storyboard/shape-drift-hints.mjs +0 -6
- package/dist/lib/testing/storyboard/shape-drift-hints.mjs.map +1 -1
- package/dist/lib/testing/storyboard/signer-grader/grader.mjs +0 -6
- package/dist/lib/testing/storyboard/signer-grader/grader.mjs.map +1 -1
- package/dist/lib/testing/storyboard/signer-grader/index.mjs +0 -6
- package/dist/lib/testing/storyboard/signer-grader/index.mjs.map +1 -1
- package/dist/lib/testing/storyboard/signer-grader/types.mjs +0 -6
- package/dist/lib/testing/storyboard/strict-validation-hints.mjs +0 -6
- package/dist/lib/testing/storyboard/strict-validation-hints.mjs.map +1 -1
- package/dist/lib/testing/storyboard/task-map.mjs +0 -6
- package/dist/lib/testing/storyboard/task-map.mjs.map +1 -1
- package/dist/lib/testing/storyboard/test-kit.mjs +0 -6
- package/dist/lib/testing/storyboard/test-kit.mjs.map +1 -1
- package/dist/lib/testing/storyboard/types.mjs +0 -6
- package/dist/lib/testing/storyboard/types.mjs.map +1 -1
- package/dist/lib/testing/storyboard/validations.mjs +0 -6
- package/dist/lib/testing/storyboard/validations.mjs.map +1 -1
- package/dist/lib/testing/storyboard/webhook-assertions.mjs +0 -6
- package/dist/lib/testing/storyboard/webhook-assertions.mjs.map +1 -1
- package/dist/lib/testing/storyboard/webhook-receiver.mjs +0 -6
- package/dist/lib/testing/storyboard/webhook-receiver.mjs.map +1 -1
- package/dist/lib/testing/stubs/governance-agent-stub.mjs +0 -6
- package/dist/lib/testing/stubs/governance-agent-stub.mjs.map +1 -1
- package/dist/lib/testing/stubs/index.mjs +0 -6
- package/dist/lib/testing/stubs/index.mjs.map +1 -1
- package/dist/lib/testing/test-controller.mjs +0 -6
- package/dist/lib/testing/test-controller.mjs.map +1 -1
- package/dist/lib/testing/test-helpers.mjs +0 -6
- package/dist/lib/testing/test-helpers.mjs.map +1 -1
- package/dist/lib/testing/types.mjs +0 -6
- package/dist/lib/types/adcp.mjs +0 -6
- package/dist/lib/types/adcp.mjs.map +1 -1
- package/dist/lib/types/asset-instances.mjs +0 -6
- package/dist/lib/types/compat.mjs +0 -6
- package/dist/lib/types/compat.mjs.map +1 -1
- package/dist/lib/types/core.generated.mjs +0 -6
- package/dist/lib/types/enums.generated.mjs +0 -6
- package/dist/lib/types/enums.generated.mjs.map +1 -1
- package/dist/lib/types/error-codes.mjs +0 -6
- package/dist/lib/types/error-codes.mjs.map +1 -1
- package/dist/lib/types/error-details.aliases.mjs +0 -6
- package/dist/lib/types/error-details.aliases.mjs.map +1 -1
- package/dist/lib/types/format-asset-slots.mjs +0 -6
- package/dist/lib/types/forward-compat-error-codes.mjs +0 -6
- package/dist/lib/types/forward-compat-error-codes.mjs.map +1 -1
- package/dist/lib/types/index.mjs +0 -6
- package/dist/lib/types/index.mjs.map +1 -1
- package/dist/lib/types/inline-enums.aliases.mjs +0 -6
- package/dist/lib/types/inline-enums.aliases.mjs.map +1 -1
- package/dist/lib/types/inline-enums.generated.mjs +0 -6
- package/dist/lib/types/inline-enums.generated.mjs.map +1 -1
- package/dist/lib/types/manifest.generated.mjs +0 -6
- package/dist/lib/types/manifest.generated.mjs.map +1 -1
- package/dist/lib/types/schemas.generated.mjs +0 -6
- package/dist/lib/types/schemas.generated.mjs.map +1 -1
- package/dist/lib/types/server-payload-aliases.mjs +0 -6
- package/dist/lib/types/server-payload.mjs +0 -6
- package/dist/lib/types/sync-rows.mjs +0 -6
- package/dist/lib/types/tools.generated.mjs +0 -6
- package/dist/lib/types/v2-5/index.mjs +0 -6
- package/dist/lib/types/v2-5/index.mjs.map +1 -1
- package/dist/lib/types/v2-5/tools.generated.mjs +0 -6
- package/dist/lib/types/v3-1-beta/index.mjs +0 -6
- package/dist/lib/types/v3-1-beta/index.mjs.map +1 -1
- package/dist/lib/types/v3-1-beta/tools.generated.mjs +0 -6
- package/dist/lib/types/wellknown-schemas.generated.mjs +0 -6
- package/dist/lib/types/wellknown-schemas.generated.mjs.map +1 -1
- package/dist/lib/upstream-recorder/constants.mjs +0 -6
- package/dist/lib/upstream-recorder/constants.mjs.map +1 -1
- package/dist/lib/upstream-recorder/index.mjs +0 -6
- package/dist/lib/upstream-recorder/index.mjs.map +1 -1
- package/dist/lib/upstream-recorder/recorder.mjs +0 -6
- package/dist/lib/upstream-recorder/recorder.mjs.map +1 -1
- package/dist/lib/upstream-recorder/types.mjs +0 -6
- package/dist/lib/upstream-recorder/types.mjs.map +1 -1
- package/dist/lib/utils/a2a-artifacts.mjs +0 -6
- package/dist/lib/utils/a2a-artifacts.mjs.map +1 -1
- package/dist/lib/utils/a2a-discovery.mjs +0 -6
- package/dist/lib/utils/a2a-discovery.mjs.map +1 -1
- package/dist/lib/utils/activation-key-builders.mjs +0 -6
- package/dist/lib/utils/activation-key-builders.mjs.map +1 -1
- package/dist/lib/utils/adcp-version-config.mjs +0 -6
- package/dist/lib/utils/adcp-version-config.mjs.map +1 -1
- package/dist/lib/utils/asset-builders.mjs +0 -6
- package/dist/lib/utils/asset-builders.mjs.map +1 -1
- package/dist/lib/utils/build-creative-return-builders.mjs +0 -6
- package/dist/lib/utils/build-creative-return-builders.mjs.map +1 -1
- package/dist/lib/utils/buyer-retry-policy.mjs +0 -6
- package/dist/lib/utils/buyer-retry-policy.mjs.map +1 -1
- package/dist/lib/utils/capabilities.mjs +0 -6
- package/dist/lib/utils/capabilities.mjs.map +1 -1
- package/dist/lib/utils/capability-rollups.mjs +0 -6
- package/dist/lib/utils/capability-rollups.mjs.map +1 -1
- package/dist/lib/utils/creative-adapter.mjs +0 -6
- package/dist/lib/utils/creative-adapter.mjs.map +1 -1
- package/dist/lib/utils/creative-delivery.mjs +0 -6
- package/dist/lib/utils/creative-delivery.mjs.map +1 -1
- package/dist/lib/utils/deprecation.mjs +0 -6
- package/dist/lib/utils/deprecation.mjs.map +1 -1
- package/dist/lib/utils/envelope-status-compat.mjs +0 -6
- package/dist/lib/utils/envelope-status-compat.mjs.map +1 -1
- package/dist/lib/utils/error-extraction.mjs +0 -6
- package/dist/lib/utils/error-extraction.mjs.map +1 -1
- package/dist/lib/utils/extract-result.mjs +0 -6
- package/dist/lib/utils/extract-result.mjs.map +1 -1
- package/dist/lib/utils/format-asset-slot-builders.mjs +0 -6
- package/dist/lib/utils/format-asset-slot-builders.mjs.map +1 -1
- package/dist/lib/utils/format-assets.mjs +0 -6
- package/dist/lib/utils/format-assets.mjs.map +1 -1
- package/dist/lib/utils/format-render-builders.mjs +0 -6
- package/dist/lib/utils/format-render-builders.mjs.map +1 -1
- package/dist/lib/utils/format-renders.mjs +0 -6
- package/dist/lib/utils/format-renders.mjs.map +1 -1
- package/dist/lib/utils/get-products-cache-scope.mjs +0 -6
- package/dist/lib/utils/get-products-cache-scope.mjs.map +1 -1
- package/dist/lib/utils/glob.mjs +0 -6
- package/dist/lib/utils/glob.mjs.map +1 -1
- package/dist/lib/utils/global-async-local-storage.mjs +0 -6
- package/dist/lib/utils/global-async-local-storage.mjs.map +1 -1
- package/dist/lib/utils/idempotency.mjs +0 -6
- package/dist/lib/utils/idempotency.mjs.map +1 -1
- package/dist/lib/utils/index.mjs +0 -6
- package/dist/lib/utils/index.mjs.map +1 -1
- package/dist/lib/utils/jcs.mjs +0 -6
- package/dist/lib/utils/jcs.mjs.map +1 -1
- package/dist/lib/utils/json-depth.mjs +0 -6
- package/dist/lib/utils/json-depth.mjs.map +1 -1
- package/dist/lib/utils/logger.mjs +0 -6
- package/dist/lib/utils/logger.mjs.map +1 -1
- package/dist/lib/utils/media-buy-delivery-notification-builders.mjs +0 -6
- package/dist/lib/utils/media-buy-delivery-notification-builders.mjs.map +1 -1
- package/dist/lib/utils/media-buy-status.mjs +0 -6
- package/dist/lib/utils/media-buy-status.mjs.map +1 -1
- package/dist/lib/utils/pagination.mjs +0 -6
- package/dist/lib/utils/pagination.mjs.map +1 -1
- package/dist/lib/utils/preview-creative-builders.mjs +0 -6
- package/dist/lib/utils/preview-creative-builders.mjs.map +1 -1
- package/dist/lib/utils/preview-normalizer.mjs +0 -6
- package/dist/lib/utils/preview-normalizer.mjs.map +1 -1
- package/dist/lib/utils/preview-utils.mjs +0 -6
- package/dist/lib/utils/preview-utils.mjs.map +1 -1
- package/dist/lib/utils/pricing-adapter.mjs +0 -6
- package/dist/lib/utils/pricing-adapter.mjs.map +1 -1
- package/dist/lib/utils/probe-policy.mjs +0 -6
- package/dist/lib/utils/probe-policy.mjs.map +1 -1
- package/dist/lib/utils/protocol-detection.mjs +0 -6
- package/dist/lib/utils/protocol-detection.mjs.map +1 -1
- package/dist/lib/utils/redact-secrets.mjs +0 -6
- package/dist/lib/utils/redact-secrets.mjs.map +1 -1
- package/dist/lib/utils/redis-default-prefix-warn.mjs +0 -6
- package/dist/lib/utils/redis-default-prefix-warn.mjs.map +1 -1
- package/dist/lib/utils/render-builders.mjs +0 -6
- package/dist/lib/utils/render-builders.mjs.map +1 -1
- package/dist/lib/utils/request-normalizer.mjs +0 -6
- package/dist/lib/utils/request-normalizer.mjs.map +1 -1
- package/dist/lib/utils/response-schemas.mjs +0 -6
- package/dist/lib/utils/response-schemas.mjs.map +1 -1
- package/dist/lib/utils/response-unwrapper.mjs +0 -6
- package/dist/lib/utils/response-unwrapper.mjs.map +1 -1
- package/dist/lib/utils/retry.mjs +0 -6
- package/dist/lib/utils/retry.mjs.map +1 -1
- package/dist/lib/utils/signal-discovery-helpers.mjs +0 -6
- package/dist/lib/utils/signal-discovery-helpers.mjs.map +1 -1
- package/dist/lib/utils/signal-id-builders.mjs +0 -6
- package/dist/lib/utils/signal-id-builders.mjs.map +1 -1
- package/dist/lib/utils/sync-creatives-adapter.mjs +0 -6
- package/dist/lib/utils/sync-creatives-adapter.mjs.map +1 -1
- package/dist/lib/utils/task-state.mjs +0 -6
- package/dist/lib/utils/task-state.mjs.map +1 -1
- package/dist/lib/utils/tool-request-schemas.mjs +0 -6
- package/dist/lib/utils/tool-request-schemas.mjs.map +1 -1
- package/dist/lib/utils/typeGuards.mjs +0 -6
- package/dist/lib/utils/typeGuards.mjs.map +1 -1
- package/dist/lib/utils/union-errors.mjs +0 -6
- package/dist/lib/utils/union-errors.mjs.map +1 -1
- package/dist/lib/utils/validate-user-agent.mjs +0 -6
- package/dist/lib/utils/validate-user-agent.mjs.map +1 -1
- package/dist/lib/v2/format-schema/fetch.mjs +0 -6
- package/dist/lib/v2/format-schema/fetch.mjs.map +1 -1
- package/dist/lib/v2/format-schema/index.mjs +0 -6
- package/dist/lib/v2/format-schema/index.mjs.map +1 -1
- package/dist/lib/v2/format-schema/regex-safety.mjs +0 -6
- package/dist/lib/v2/format-schema/regex-safety.mjs.map +1 -1
- package/dist/lib/v2/format-schema/resolver.mjs +7 -8
- package/dist/lib/v2/format-schema/resolver.mjs.map +1 -1
- package/dist/lib/v2/format-schema/sandbox-refs.mjs +0 -6
- package/dist/lib/v2/format-schema/sandbox-refs.mjs.map +1 -1
- package/dist/lib/v2/projection/augment-response.mjs +0 -6
- package/dist/lib/v2/projection/augment-response.mjs.map +1 -1
- package/dist/lib/v2/projection/builders.mjs +0 -6
- package/dist/lib/v2/projection/builders.mjs.map +1 -1
- package/dist/lib/v2/projection/cache-versions.mjs +0 -6
- package/dist/lib/v2/projection/cache-versions.mjs.map +1 -1
- package/dist/lib/v2/projection/canonical-properties.mjs +5 -6
- package/dist/lib/v2/projection/canonical-properties.mjs.map +1 -1
- package/dist/lib/v2/projection/catalog.mjs +5 -6
- package/dist/lib/v2/projection/catalog.mjs.map +1 -1
- package/dist/lib/v2/projection/constants.mjs +0 -6
- package/dist/lib/v2/projection/constants.mjs.map +1 -1
- package/dist/lib/v2/projection/index.mjs +0 -6
- package/dist/lib/v2/projection/index.mjs.map +1 -1
- package/dist/lib/v2/projection/registry.mjs +5 -6
- package/dist/lib/v2/projection/registry.mjs.map +1 -1
- package/dist/lib/v2/projection/types.mjs +0 -6
- package/dist/lib/v2/projection/v1-to-v2.mjs +0 -6
- package/dist/lib/v2/projection/v1-to-v2.mjs.map +1 -1
- package/dist/lib/v2/projection/v2-to-v1.mjs +0 -6
- package/dist/lib/v2/projection/v2-to-v1.mjs.map +1 -1
- package/dist/lib/v2/projection/write-side.mjs +0 -6
- package/dist/lib/v2/projection/write-side.mjs.map +1 -1
- package/dist/lib/v2/publisher-catalog/formats.mjs +0 -6
- package/dist/lib/v2/publisher-catalog/formats.mjs.map +1 -1
- package/dist/lib/v2/publisher-catalog/index.mjs +0 -6
- package/dist/lib/v2/publisher-catalog/index.mjs.map +1 -1
- package/dist/lib/validation/client-hooks.mjs +0 -6
- package/dist/lib/validation/client-hooks.mjs.map +1 -1
- package/dist/lib/validation/hints.mjs +0 -6
- package/dist/lib/validation/hints.mjs.map +1 -1
- package/dist/lib/validation/index.mjs +0 -6
- package/dist/lib/validation/index.mjs.map +1 -1
- package/dist/lib/validation/schema-errors.mjs +0 -6
- package/dist/lib/validation/schema-errors.mjs.map +1 -1
- package/dist/lib/validation/schema-loader.mjs +5 -6
- package/dist/lib/validation/schema-loader.mjs.map +1 -1
- package/dist/lib/validation/schema-validator.mjs +0 -6
- package/dist/lib/validation/schema-validator.mjs.map +1 -1
- package/dist/lib/validation/sync-creatives.mjs +0 -6
- package/dist/lib/validation/sync-creatives.mjs.map +1 -1
- package/dist/lib/version.d.mts +3 -3
- package/dist/lib/version.d.ts +3 -3
- package/dist/lib/version.js +3 -3
- package/dist/lib/version.js.map +1 -1
- package/dist/lib/version.mjs +3 -9
- package/dist/lib/version.mjs.map +1 -1
- package/dist/lib/webhooks/index.mjs +0 -6
- package/dist/lib/webhooks/index.mjs.map +1 -1
- package/dist/lib/wholesale-feed-sync/index.mjs +0 -6
- package/dist/lib/wholesale-feed-sync/index.mjs.map +1 -1
- package/dist/lib/wholesale-feed-sync/sync.mjs +0 -6
- package/dist/lib/wholesale-feed-sync/sync.mjs.map +1 -1
- package/dist/lib/wholesale-feed-sync/types.mjs +0 -6
- package/dist/lib/wholesale-feed-sync/webhook-notification.mjs +0 -6
- package/dist/lib/wholesale-feed-sync/webhook-notification.mjs.map +1 -1
- package/package.json +2 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/lib/testing/storyboard/rate-limit-trip.ts"],"sourcesContent":["/**\n * Sequential rate-limit trip/replay support for the\n * `rate_limit_trip_runner` test-kit contract.\n *\n * The observer sends fresh-idempotency-key requests until a seller returns\n * `RATE_LIMITED`, waits the advertised retry_after, then replays the same\n * key/payload to verify the transient rate-limit response was not cached as\n * the idempotency replay result.\n */\n\nimport { randomUUID } from 'node:crypto';\nimport type { AdcpErrorInfo } from '../../core/ConversationTypes';\nimport { ADCPError, ProtocolError, ResponseTooLargeError, TaskTimeoutError } from '../../errors';\nimport { extractAdcpErrorInfo } from '../../utils/error-extraction';\nimport { isTerminalAdcpError } from '../../utils/response-unwrapper';\nimport type { TaskResult } from '../types';\nimport { executeStoryboardTask } from './task-map';\nimport type { RateLimitTripSpec } from './types';\n\nexport const RATE_LIMIT_TRIP_CONTRACT = 'rate_limit_trip_runner';\nexport const RATE_LIMIT_TRIP_MAX_ATTEMPTS_MIN = 50;\nexport const RATE_LIMIT_TRIP_MAX_ATTEMPTS_MAX = 500;\nexport const RATE_LIMIT_TRIP_DEFAULT_REPLAY_MAX_WAIT_SECONDS = 30;\n\nexport type RateLimitTripFailureCode =\n | 'rate_limit_trip_misconfigured'\n | 'rate_limit_trip_request_error'\n | 'rate_limit_trip_transport_error'\n | 'missing_retry_after'\n | 'replay_wait_exceeded';\n\nexport interface RateLimitTripResponseSnapshot {\n success: boolean;\n data?: unknown;\n /** Structured AdCP error when one was present; otherwise omitted. */\n error?: Pick<AdcpErrorInfo, 'code' | 'message' | 'recovery' | 'field' | 'suggestion' | 'retry_after' | 'details'>;\n /** Original TaskResult.error string, retained for diagnostics. */\n error_message?: string;\n adcp_error?: AdcpErrorInfo;\n}\n\nexport interface RateLimitTripStructuredResult {\n attempts: number;\n target_task?: string;\n target_transport?: 'mcp' | 'a2a';\n trip_request?: Record<string, unknown>;\n replay_request?: Record<string, unknown>;\n rate_limited_request?: {\n task: string;\n attempt: number;\n idempotency_key: string;\n };\n trip_response?: RateLimitTripResponseSnapshot;\n replay_response?: RateLimitTripResponseSnapshot;\n}\n\nexport type RateLimitTripObservation =\n | {\n status: 'completed';\n body: RateLimitTripStructuredResult;\n }\n | {\n status: 'not_applicable';\n skip_reason: 'rate_limit_not_triggered';\n message: string;\n body: RateLimitTripStructuredResult;\n }\n | {\n status: 'failed';\n error: RateLimitTripFailureCode;\n message: string;\n body: RateLimitTripStructuredResult;\n };\n\nexport interface RateLimitTripObserverOptions {\n /** Mint fresh idempotency keys for each burst attempt. */\n keyMinter?: () => string;\n /** Injectable sleep for fast deterministic tests. */\n sleep?: (ms: number) => Promise<void>;\n /** Prefix for context.correlation_id tags. */\n correlationPrefix?: string;\n /** Transport label for runner output diagnostics. */\n transport?: 'mcp' | 'a2a';\n}\n\nexport interface RateLimitTripTaskOptions {\n skipIdempotencyAutoInject?: boolean;\n skipAccountValidation?: boolean;\n}\n\n/**\n * Minimal client surface required by {@link RateLimitTripObserver}. The\n * easiest way to obtain one is `createTestClient(...)` from\n * `@adcp/sdk/testing`, but `SingleAgentClient` and compatible test doubles\n * also satisfy this interface.\n */\nexport interface RateLimitTripClient {\n executeTask(\n taskName: string,\n params: Record<string, unknown>,\n inputHandler?: unknown,\n options?: RateLimitTripTaskOptions\n ): Promise<TaskResult>;\n [methodName: string]: unknown;\n}\n\nexport function validateRateLimitTripSpec(spec: RateLimitTripSpec | undefined): string | null {\n if (!spec || typeof spec !== 'object') {\n return 'rate_limit_trip is required for expect_rate_limit_not_replayed';\n }\n if (typeof spec.trip_target_task !== 'string' || spec.trip_target_task.length === 0) {\n return 'rate_limit_trip.trip_target_task must be a non-empty string';\n }\n if (\n !spec.trip_target_sample_request ||\n typeof spec.trip_target_sample_request !== 'object' ||\n Array.isArray(spec.trip_target_sample_request)\n ) {\n return 'rate_limit_trip.trip_target_sample_request must be an object';\n }\n if (!Number.isInteger(spec.max_attempts)) {\n return `rate_limit_trip.max_attempts must be an integer; received ${spec.max_attempts}`;\n }\n if (spec.max_attempts < RATE_LIMIT_TRIP_MAX_ATTEMPTS_MIN || spec.max_attempts > RATE_LIMIT_TRIP_MAX_ATTEMPTS_MAX) {\n return (\n `rate_limit_trip.max_attempts must be in [${RATE_LIMIT_TRIP_MAX_ATTEMPTS_MIN}, ` +\n `${RATE_LIMIT_TRIP_MAX_ATTEMPTS_MAX}] per test-kits/rate-limit-trip-runner.yaml; ` +\n `received ${spec.max_attempts}`\n );\n }\n if (spec.replay_max_wait_seconds !== undefined) {\n if (!Number.isFinite(spec.replay_max_wait_seconds) || spec.replay_max_wait_seconds <= 0) {\n return `rate_limit_trip.replay_max_wait_seconds must be a positive finite number; received ${spec.replay_max_wait_seconds}`;\n }\n }\n return null;\n}\n\nconst defaultSleep = (ms: number): Promise<void> => new Promise(resolve => setTimeout(resolve, Math.max(0, ms)));\n\n/**\n * Public primitive for the rate-limit trip/replay contract. Callers supply a\n * {@link RateLimitTripClient}; use `createTestClient(...)` for the standard\n * compliance-runner client.\n */\nexport class RateLimitTripObserver {\n constructor(\n private readonly client: RateLimitTripClient,\n private readonly defaults: RateLimitTripObserverOptions = {}\n ) {}\n\n async run(spec: RateLimitTripSpec, opts: RateLimitTripObserverOptions = {}): Promise<RateLimitTripObservation> {\n const validationError = validateRateLimitTripSpec(spec);\n if (validationError) {\n return {\n status: 'failed',\n error: 'rate_limit_trip_misconfigured',\n message: validationError,\n body: { attempts: 0 },\n };\n }\n\n const keyMinter = opts.keyMinter ?? this.defaults.keyMinter ?? defaultKeyMinter;\n const sleep = opts.sleep ?? this.defaults.sleep ?? defaultSleep;\n const correlationPrefix = opts.correlationPrefix ?? this.defaults.correlationPrefix ?? 'rate_limit_trip';\n const replayMaxWaitSeconds = spec.replay_max_wait_seconds ?? RATE_LIMIT_TRIP_DEFAULT_REPLAY_MAX_WAIT_SECONDS;\n const baseRequest = cloneRequest(spec.trip_target_sample_request);\n const baseBody: RateLimitTripStructuredResult = {\n attempts: 0,\n target_task: spec.trip_target_task,\n target_transport: opts.transport ?? this.defaults.transport,\n };\n\n let trip:\n | {\n attempt: number;\n idempotencyKey: string;\n response: TaskResult;\n error: AdcpErrorInfo;\n }\n | undefined;\n let lastTripRequest: Record<string, unknown> | undefined;\n\n for (let attempt = 1; attempt <= spec.max_attempts; attempt++) {\n const idempotencyKey = keyMinter();\n const request = withTripEnvelope(baseRequest, idempotencyKey, `${correlationPrefix}#trip-${attempt}`);\n lastTripRequest = request;\n let response: TaskResult;\n try {\n response = await executeStoryboardTask(this.client, spec.trip_target_task, request);\n } catch (err) {\n return transportFailureObservation(err, { ...baseBody, attempts: attempt, trip_request: request });\n }\n const error = extractTaskAdcpError(response, spec.trip_target_task);\n if (error?.code === 'RATE_LIMITED') {\n trip = { attempt, idempotencyKey, response, error };\n break;\n }\n if (error) {\n return {\n status: 'failed',\n error: 'rate_limit_trip_request_error',\n message: `Target task returned ${error.code}: ${error.message}`,\n body: {\n ...baseBody,\n attempts: attempt,\n trip_request: request,\n trip_response: snapshotTaskResult(response, spec.trip_target_task),\n },\n };\n }\n }\n\n if (!trip) {\n return {\n status: 'not_applicable',\n skip_reason: 'rate_limit_not_triggered',\n message: `No RATE_LIMITED response observed within ${spec.max_attempts} attempt(s).`,\n body: { ...baseBody, attempts: spec.max_attempts, ...(lastTripRequest && { trip_request: lastTripRequest }) },\n };\n }\n\n const commonBody: RateLimitTripStructuredResult = {\n ...baseBody,\n attempts: trip.attempt,\n trip_request: withTripEnvelope(baseRequest, trip.idempotencyKey, `${correlationPrefix}#trip-${trip.attempt}`),\n rate_limited_request: {\n task: spec.trip_target_task,\n attempt: trip.attempt,\n idempotency_key: trip.idempotencyKey,\n },\n trip_response: snapshotTaskResult(trip.response, spec.trip_target_task),\n };\n\n const retryAfterSeconds = readRetryAfterSeconds(trip.error);\n if (retryAfterSeconds === undefined || retryAfterSeconds <= 0) {\n return {\n status: 'failed',\n error: 'missing_retry_after',\n message: 'RATE_LIMITED response did not include a positive retry_after value.',\n body: commonBody,\n };\n }\n if (retryAfterSeconds > replayMaxWaitSeconds) {\n return {\n status: 'failed',\n error: 'replay_wait_exceeded',\n message:\n `RATE_LIMITED retry_after (${retryAfterSeconds}s) exceeds ` +\n `rate_limit_trip.replay_max_wait_seconds (${replayMaxWaitSeconds}s).`,\n body: commonBody,\n };\n }\n\n await sleep(Math.max(1, Math.ceil(retryAfterSeconds * 1000)));\n const replayRequest = withTripEnvelope(baseRequest, trip.idempotencyKey, `${correlationPrefix}#replay`);\n let replayResponse: TaskResult;\n try {\n replayResponse = await executeStoryboardTask(this.client, spec.trip_target_task, replayRequest);\n } catch (err) {\n return transportFailureObservation(err, { ...commonBody, replay_request: replayRequest });\n }\n\n return {\n status: 'completed',\n body: {\n ...commonBody,\n replay_request: replayRequest,\n replay_response: snapshotTaskResult(replayResponse, spec.trip_target_task),\n },\n };\n }\n}\n\nfunction transportFailureObservation(\n err: unknown,\n body: RateLimitTripStructuredResult\n): Extract<RateLimitTripObservation, { status: 'failed' }> {\n const message = err instanceof Error ? err.message : String(err);\n const error =\n err instanceof ADCPError && !isTransportAdcpError(err)\n ? 'rate_limit_trip_request_error'\n : 'rate_limit_trip_transport_error';\n return {\n status: 'failed',\n error,\n message,\n body,\n };\n}\n\nfunction isTransportAdcpError(err: ADCPError): boolean {\n return err instanceof ProtocolError || err instanceof ResponseTooLargeError || err instanceof TaskTimeoutError;\n}\n\nfunction defaultKeyMinter(): string {\n return randomUUID();\n}\n\nfunction cloneRequest(request: Record<string, unknown>): Record<string, unknown> {\n if (typeof globalThis.structuredClone === 'function') {\n return globalThis.structuredClone(request);\n }\n return JSON.parse(JSON.stringify(request)) as Record<string, unknown>;\n}\n\nfunction withTripEnvelope(\n baseRequest: Record<string, unknown>,\n idempotencyKey: string,\n correlationId: string\n): Record<string, unknown> {\n const request = cloneRequest(baseRequest);\n request.idempotency_key = idempotencyKey;\n const existingContext = request.context;\n if (existingContext && typeof existingContext === 'object' && !Array.isArray(existingContext)) {\n request.context = { ...(existingContext as Record<string, unknown>), correlation_id: correlationId };\n } else if (existingContext === undefined) {\n request.context = { correlation_id: correlationId };\n }\n return request;\n}\n\nexport function extractTaskAdcpError(taskResult: TaskResult, taskName?: string): AdcpErrorInfo | undefined {\n if (taskResult.adcp_error) return taskResult.adcp_error;\n const data = taskResult.data as Record<string, unknown> | undefined;\n if (Array.isArray(data?.errors)) {\n if (taskResult.success !== false && taskName && !isTerminalAdcpError(data, taskName)) {\n return undefined;\n }\n const first = data.errors[0];\n if (first && typeof first === 'object' && typeof (first as Record<string, unknown>).code === 'string') {\n return buildAdcpErrorInfo(first as Record<string, unknown>);\n }\n }\n const extracted = extractAdcpErrorInfo(data);\n if (extracted) return extracted;\n const dataError = data?.error;\n if (dataError && typeof dataError === 'object' && typeof (dataError as Record<string, unknown>).code === 'string') {\n return buildAdcpErrorInfo(dataError as Record<string, unknown>);\n }\n return undefined;\n}\n\nfunction buildAdcpErrorInfo(obj: Record<string, unknown>): AdcpErrorInfo {\n const code = String(obj.code);\n const message = typeof obj.message === 'string' ? obj.message : code;\n const info: AdcpErrorInfo = { code, message };\n if (obj.recovery === 'transient' || obj.recovery === 'correctable' || obj.recovery === 'terminal') {\n info.recovery = obj.recovery;\n }\n if (typeof obj.field === 'string') info.field = obj.field;\n if (typeof obj.suggestion === 'string') info.suggestion = obj.suggestion;\n if (typeof obj.retry_after === 'number' && Number.isFinite(obj.retry_after)) info.retry_after = obj.retry_after;\n if (obj.details && typeof obj.details === 'object') info.details = obj.details as Record<string, unknown>;\n return info;\n}\n\nfunction readRetryAfterSeconds(error: AdcpErrorInfo): number | undefined {\n if (typeof error.retry_after === 'number' && Number.isFinite(error.retry_after)) return error.retry_after;\n const detailsRetry = error.details?.retry_after;\n if (typeof detailsRetry === 'number' && Number.isFinite(detailsRetry)) return detailsRetry;\n return undefined;\n}\n\nfunction snapshotTaskResult(taskResult: TaskResult, taskName?: string): RateLimitTripResponseSnapshot {\n const adcpError = extractTaskAdcpError(taskResult, taskName);\n return {\n success: taskResult.success,\n ...(taskResult.data !== undefined && { data: taskResult.data }),\n ...(adcpError && {\n error: {\n code: adcpError.code,\n message: adcpError.message,\n ...(adcpError.recovery && { recovery: adcpError.recovery }),\n ...(adcpError.field && { field: adcpError.field }),\n ...(adcpError.suggestion && { suggestion: adcpError.suggestion }),\n ...(adcpError.retry_after !== undefined && { retry_after: adcpError.retry_after }),\n ...(adcpError.details && { details: adcpError.details }),\n },\n adcp_error: adcpError,\n }),\n ...(taskResult.error && { error_message: taskResult.error }),\n };\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../../src/lib/testing/storyboard/rate-limit-trip.ts"],"sourcesContent":["/**\n * Sequential rate-limit trip/replay support for the\n * `rate_limit_trip_runner` test-kit contract.\n *\n * The observer sends fresh-idempotency-key requests until a seller returns\n * `RATE_LIMITED`, waits the advertised retry_after, then replays the same\n * key/payload to verify the transient rate-limit response was not cached as\n * the idempotency replay result.\n */\n\nimport { randomUUID } from 'node:crypto';\nimport type { AdcpErrorInfo } from '../../core/ConversationTypes';\nimport { ADCPError, ProtocolError, ResponseTooLargeError, TaskTimeoutError } from '../../errors';\nimport { extractAdcpErrorInfo } from '../../utils/error-extraction';\nimport { isTerminalAdcpError } from '../../utils/response-unwrapper';\nimport type { TaskResult } from '../types';\nimport { executeStoryboardTask } from './task-map';\nimport type { RateLimitTripSpec } from './types';\n\nexport const RATE_LIMIT_TRIP_CONTRACT = 'rate_limit_trip_runner';\nexport const RATE_LIMIT_TRIP_MAX_ATTEMPTS_MIN = 50;\nexport const RATE_LIMIT_TRIP_MAX_ATTEMPTS_MAX = 500;\nexport const RATE_LIMIT_TRIP_DEFAULT_REPLAY_MAX_WAIT_SECONDS = 30;\n\nexport type RateLimitTripFailureCode =\n | 'rate_limit_trip_misconfigured'\n | 'rate_limit_trip_request_error'\n | 'rate_limit_trip_transport_error'\n | 'missing_retry_after'\n | 'replay_wait_exceeded';\n\nexport interface RateLimitTripResponseSnapshot {\n success: boolean;\n data?: unknown;\n /** Structured AdCP error when one was present; otherwise omitted. */\n error?: Pick<AdcpErrorInfo, 'code' | 'message' | 'recovery' | 'field' | 'suggestion' | 'retry_after' | 'details'>;\n /** Original TaskResult.error string, retained for diagnostics. */\n error_message?: string;\n adcp_error?: AdcpErrorInfo;\n}\n\nexport interface RateLimitTripStructuredResult {\n attempts: number;\n target_task?: string;\n target_transport?: 'mcp' | 'a2a';\n trip_request?: Record<string, unknown>;\n replay_request?: Record<string, unknown>;\n rate_limited_request?: {\n task: string;\n attempt: number;\n idempotency_key: string;\n };\n trip_response?: RateLimitTripResponseSnapshot;\n replay_response?: RateLimitTripResponseSnapshot;\n}\n\nexport type RateLimitTripObservation =\n | {\n status: 'completed';\n body: RateLimitTripStructuredResult;\n }\n | {\n status: 'not_applicable';\n skip_reason: 'rate_limit_not_triggered';\n message: string;\n body: RateLimitTripStructuredResult;\n }\n | {\n status: 'failed';\n error: RateLimitTripFailureCode;\n message: string;\n body: RateLimitTripStructuredResult;\n };\n\nexport interface RateLimitTripObserverOptions {\n /** Mint fresh idempotency keys for each burst attempt. */\n keyMinter?: () => string;\n /** Injectable sleep for fast deterministic tests. */\n sleep?: (ms: number) => Promise<void>;\n /** Prefix for context.correlation_id tags. */\n correlationPrefix?: string;\n /** Transport label for runner output diagnostics. */\n transport?: 'mcp' | 'a2a';\n}\n\nexport interface RateLimitTripTaskOptions {\n skipIdempotencyAutoInject?: boolean;\n skipAccountValidation?: boolean;\n}\n\n/**\n * Minimal client surface required by {@link RateLimitTripObserver}. The\n * easiest way to obtain one is `createTestClient(...)` from\n * `@adcp/sdk/testing`, but `SingleAgentClient` and compatible test doubles\n * also satisfy this interface.\n */\nexport interface RateLimitTripClient {\n executeTask(\n taskName: string,\n params: Record<string, unknown>,\n inputHandler?: unknown,\n options?: RateLimitTripTaskOptions\n ): Promise<TaskResult>;\n [methodName: string]: unknown;\n}\n\nexport function validateRateLimitTripSpec(spec: RateLimitTripSpec | undefined): string | null {\n if (!spec || typeof spec !== 'object') {\n return 'rate_limit_trip is required for expect_rate_limit_not_replayed';\n }\n if (typeof spec.trip_target_task !== 'string' || spec.trip_target_task.length === 0) {\n return 'rate_limit_trip.trip_target_task must be a non-empty string';\n }\n if (\n !spec.trip_target_sample_request ||\n typeof spec.trip_target_sample_request !== 'object' ||\n Array.isArray(spec.trip_target_sample_request)\n ) {\n return 'rate_limit_trip.trip_target_sample_request must be an object';\n }\n if (!Number.isInteger(spec.max_attempts)) {\n return `rate_limit_trip.max_attempts must be an integer; received ${spec.max_attempts}`;\n }\n if (spec.max_attempts < RATE_LIMIT_TRIP_MAX_ATTEMPTS_MIN || spec.max_attempts > RATE_LIMIT_TRIP_MAX_ATTEMPTS_MAX) {\n return (\n `rate_limit_trip.max_attempts must be in [${RATE_LIMIT_TRIP_MAX_ATTEMPTS_MIN}, ` +\n `${RATE_LIMIT_TRIP_MAX_ATTEMPTS_MAX}] per test-kits/rate-limit-trip-runner.yaml; ` +\n `received ${spec.max_attempts}`\n );\n }\n if (spec.replay_max_wait_seconds !== undefined) {\n if (!Number.isFinite(spec.replay_max_wait_seconds) || spec.replay_max_wait_seconds <= 0) {\n return `rate_limit_trip.replay_max_wait_seconds must be a positive finite number; received ${spec.replay_max_wait_seconds}`;\n }\n }\n return null;\n}\n\nconst defaultSleep = (ms: number): Promise<void> => new Promise(resolve => setTimeout(resolve, Math.max(0, ms)));\n\n/**\n * Public primitive for the rate-limit trip/replay contract. Callers supply a\n * {@link RateLimitTripClient}; use `createTestClient(...)` for the standard\n * compliance-runner client.\n */\nexport class RateLimitTripObserver {\n constructor(\n private readonly client: RateLimitTripClient,\n private readonly defaults: RateLimitTripObserverOptions = {}\n ) {}\n\n async run(spec: RateLimitTripSpec, opts: RateLimitTripObserverOptions = {}): Promise<RateLimitTripObservation> {\n const validationError = validateRateLimitTripSpec(spec);\n if (validationError) {\n return {\n status: 'failed',\n error: 'rate_limit_trip_misconfigured',\n message: validationError,\n body: { attempts: 0 },\n };\n }\n\n const keyMinter = opts.keyMinter ?? this.defaults.keyMinter ?? defaultKeyMinter;\n const sleep = opts.sleep ?? this.defaults.sleep ?? defaultSleep;\n const correlationPrefix = opts.correlationPrefix ?? this.defaults.correlationPrefix ?? 'rate_limit_trip';\n const replayMaxWaitSeconds = spec.replay_max_wait_seconds ?? RATE_LIMIT_TRIP_DEFAULT_REPLAY_MAX_WAIT_SECONDS;\n const baseRequest = cloneRequest(spec.trip_target_sample_request);\n const baseBody: RateLimitTripStructuredResult = {\n attempts: 0,\n target_task: spec.trip_target_task,\n target_transport: opts.transport ?? this.defaults.transport,\n };\n\n let trip:\n | {\n attempt: number;\n idempotencyKey: string;\n response: TaskResult;\n error: AdcpErrorInfo;\n }\n | undefined;\n let lastTripRequest: Record<string, unknown> | undefined;\n\n for (let attempt = 1; attempt <= spec.max_attempts; attempt++) {\n const idempotencyKey = keyMinter();\n const request = withTripEnvelope(baseRequest, idempotencyKey, `${correlationPrefix}#trip-${attempt}`);\n lastTripRequest = request;\n let response: TaskResult;\n try {\n response = await executeStoryboardTask(this.client, spec.trip_target_task, request);\n } catch (err) {\n return transportFailureObservation(err, { ...baseBody, attempts: attempt, trip_request: request });\n }\n const error = extractTaskAdcpError(response, spec.trip_target_task);\n if (error?.code === 'RATE_LIMITED') {\n trip = { attempt, idempotencyKey, response, error };\n break;\n }\n if (error) {\n return {\n status: 'failed',\n error: 'rate_limit_trip_request_error',\n message: `Target task returned ${error.code}: ${error.message}`,\n body: {\n ...baseBody,\n attempts: attempt,\n trip_request: request,\n trip_response: snapshotTaskResult(response, spec.trip_target_task),\n },\n };\n }\n }\n\n if (!trip) {\n return {\n status: 'not_applicable',\n skip_reason: 'rate_limit_not_triggered',\n message: `No RATE_LIMITED response observed within ${spec.max_attempts} attempt(s).`,\n body: { ...baseBody, attempts: spec.max_attempts, ...(lastTripRequest && { trip_request: lastTripRequest }) },\n };\n }\n\n const commonBody: RateLimitTripStructuredResult = {\n ...baseBody,\n attempts: trip.attempt,\n trip_request: withTripEnvelope(baseRequest, trip.idempotencyKey, `${correlationPrefix}#trip-${trip.attempt}`),\n rate_limited_request: {\n task: spec.trip_target_task,\n attempt: trip.attempt,\n idempotency_key: trip.idempotencyKey,\n },\n trip_response: snapshotTaskResult(trip.response, spec.trip_target_task),\n };\n\n const retryAfterSeconds = readRetryAfterSeconds(trip.error);\n if (retryAfterSeconds === undefined || retryAfterSeconds <= 0) {\n return {\n status: 'failed',\n error: 'missing_retry_after',\n message: 'RATE_LIMITED response did not include a positive retry_after value.',\n body: commonBody,\n };\n }\n if (retryAfterSeconds > replayMaxWaitSeconds) {\n return {\n status: 'failed',\n error: 'replay_wait_exceeded',\n message:\n `RATE_LIMITED retry_after (${retryAfterSeconds}s) exceeds ` +\n `rate_limit_trip.replay_max_wait_seconds (${replayMaxWaitSeconds}s).`,\n body: commonBody,\n };\n }\n\n await sleep(Math.max(1, Math.ceil(retryAfterSeconds * 1000)));\n const replayRequest = withTripEnvelope(baseRequest, trip.idempotencyKey, `${correlationPrefix}#replay`);\n let replayResponse: TaskResult;\n try {\n replayResponse = await executeStoryboardTask(this.client, spec.trip_target_task, replayRequest);\n } catch (err) {\n return transportFailureObservation(err, { ...commonBody, replay_request: replayRequest });\n }\n\n return {\n status: 'completed',\n body: {\n ...commonBody,\n replay_request: replayRequest,\n replay_response: snapshotTaskResult(replayResponse, spec.trip_target_task),\n },\n };\n }\n}\n\nfunction transportFailureObservation(\n err: unknown,\n body: RateLimitTripStructuredResult\n): Extract<RateLimitTripObservation, { status: 'failed' }> {\n const message = err instanceof Error ? err.message : String(err);\n const error =\n err instanceof ADCPError && !isTransportAdcpError(err)\n ? 'rate_limit_trip_request_error'\n : 'rate_limit_trip_transport_error';\n return {\n status: 'failed',\n error,\n message,\n body,\n };\n}\n\nfunction isTransportAdcpError(err: ADCPError): boolean {\n return err instanceof ProtocolError || err instanceof ResponseTooLargeError || err instanceof TaskTimeoutError;\n}\n\nfunction defaultKeyMinter(): string {\n return randomUUID();\n}\n\nfunction cloneRequest(request: Record<string, unknown>): Record<string, unknown> {\n if (typeof globalThis.structuredClone === 'function') {\n return globalThis.structuredClone(request);\n }\n return JSON.parse(JSON.stringify(request)) as Record<string, unknown>;\n}\n\nfunction withTripEnvelope(\n baseRequest: Record<string, unknown>,\n idempotencyKey: string,\n correlationId: string\n): Record<string, unknown> {\n const request = cloneRequest(baseRequest);\n request.idempotency_key = idempotencyKey;\n const existingContext = request.context;\n if (existingContext && typeof existingContext === 'object' && !Array.isArray(existingContext)) {\n request.context = { ...(existingContext as Record<string, unknown>), correlation_id: correlationId };\n } else if (existingContext === undefined) {\n request.context = { correlation_id: correlationId };\n }\n return request;\n}\n\nexport function extractTaskAdcpError(taskResult: TaskResult, taskName?: string): AdcpErrorInfo | undefined {\n if (taskResult.adcp_error) return taskResult.adcp_error;\n const data = taskResult.data as Record<string, unknown> | undefined;\n if (Array.isArray(data?.errors)) {\n if (taskResult.success !== false && taskName && !isTerminalAdcpError(data, taskName)) {\n return undefined;\n }\n const first = data.errors[0];\n if (first && typeof first === 'object' && typeof (first as Record<string, unknown>).code === 'string') {\n return buildAdcpErrorInfo(first as Record<string, unknown>);\n }\n }\n const extracted = extractAdcpErrorInfo(data);\n if (extracted) return extracted;\n const dataError = data?.error;\n if (dataError && typeof dataError === 'object' && typeof (dataError as Record<string, unknown>).code === 'string') {\n return buildAdcpErrorInfo(dataError as Record<string, unknown>);\n }\n return undefined;\n}\n\nfunction buildAdcpErrorInfo(obj: Record<string, unknown>): AdcpErrorInfo {\n const code = String(obj.code);\n const message = typeof obj.message === 'string' ? obj.message : code;\n const info: AdcpErrorInfo = { code, message };\n if (obj.recovery === 'transient' || obj.recovery === 'correctable' || obj.recovery === 'terminal') {\n info.recovery = obj.recovery;\n }\n if (typeof obj.field === 'string') info.field = obj.field;\n if (typeof obj.suggestion === 'string') info.suggestion = obj.suggestion;\n if (typeof obj.retry_after === 'number' && Number.isFinite(obj.retry_after)) info.retry_after = obj.retry_after;\n if (obj.details && typeof obj.details === 'object') info.details = obj.details as Record<string, unknown>;\n return info;\n}\n\nfunction readRetryAfterSeconds(error: AdcpErrorInfo): number | undefined {\n if (typeof error.retry_after === 'number' && Number.isFinite(error.retry_after)) return error.retry_after;\n const detailsRetry = error.details?.retry_after;\n if (typeof detailsRetry === 'number' && Number.isFinite(detailsRetry)) return detailsRetry;\n return undefined;\n}\n\nfunction snapshotTaskResult(taskResult: TaskResult, taskName?: string): RateLimitTripResponseSnapshot {\n const adcpError = extractTaskAdcpError(taskResult, taskName);\n return {\n success: taskResult.success,\n ...(taskResult.data !== undefined && { data: taskResult.data }),\n ...(adcpError && {\n error: {\n code: adcpError.code,\n message: adcpError.message,\n ...(adcpError.recovery && { recovery: adcpError.recovery }),\n ...(adcpError.field && { field: adcpError.field }),\n ...(adcpError.suggestion && { suggestion: adcpError.suggestion }),\n ...(adcpError.retry_after !== undefined && { retry_after: adcpError.retry_after }),\n ...(adcpError.details && { details: adcpError.details }),\n },\n adcp_error: adcpError,\n }),\n ...(taskResult.error && { error_message: taskResult.error }),\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 { resolvePath } from "./path.mjs";
|
|
8
2
|
const ACCEPTED_VALUE_KEYS = ["available", "allowed", "accepted_values"];
|
|
9
3
|
const FIELD_POINTER_KEYS = ["field", "path", "pointer"];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/lib/testing/storyboard/rejection-hints.ts"],"sourcesContent":["/**\n * Context-value-rejected hint detection.\n *\n * When a seller rejects a request field with a response that carries an\n * `available:` / `allowed:` / `accepted_values:` list (per AdCP core/error\n * with `details` being `additionalProperties: true`, sellers commonly put\n * the set of values they would have accepted there), and the rejected\n * value traces back to a prior-step `$context.*` write (either via\n * substitution or via a request-builder field populated from the same\n * context key), emit a non-fatal `context_value_rejected` hint.\n *\n * Diagnostic only — does not flip the step's pass/fail. See issue #870 for\n * the rationale: without this, the rejection in logs is indistinguishable\n * from an SDK bug.\n */\nimport type { TaskResult } from '../types';\nimport type { ContextProvenanceEntry, ContextValueRejectedHint, StoryboardContext, StoryboardStepHint } from './types';\nimport { resolvePath } from './path';\n\n/**\n * Response-body error keys the runner recognizes as carrying the seller's\n * \"here's what I would have accepted\" list. Matched in order; the first\n * array found is used.\n */\nconst ACCEPTED_VALUE_KEYS = ['available', 'allowed', 'accepted_values'] as const;\n\n/**\n * Fields on an error object (or `error.details`) the runner recognizes as\n * pointing at the rejected request field. Matches AdCP core/error's `field`\n * (spec-canonical) plus `details.field` (common vendor extension).\n */\nconst FIELD_POINTER_KEYS = ['field', 'path', 'pointer'] as const;\n\n/**\n * Extract `context_value_rejected` hints from a task result.\n *\n * The function is pure — safe to call regardless of pass/fail. Returns an\n * empty array when the response doesn't carry a recognizable rejection\n * shape or no context value matches.\n *\n * @param taskResult Raw task result from the runner.\n * @param request The exact request the runner sent (post-injection).\n * @param context The storyboard context as of the step being evaluated\n * (pre-write — the hints trace which PRIOR step's context\n * write the rejected value came from).\n * @param provenance Context-key → write-provenance map.\n */\nexport function detectContextRejectionHints(\n taskResult: TaskResult | undefined,\n request: Record<string, unknown>,\n context: StoryboardContext,\n provenance: ReadonlyMap<string, ContextProvenanceEntry>,\n /**\n * Current step's AdCP task name (e.g. `activate_signal`). Used only to\n * produce a human-readable closing sentence that cites both tools\n * involved in the catalog drift — \"Check that `get_signals` and\n * `activate_signal` catalogs agree.\" Optional for backwards\n * compatibility; callers that can't supply it get a generic closing.\n */\n currentTask?: string\n): StoryboardStepHint[] {\n if (!taskResult) return [];\n const data = taskResult.data as Record<string, unknown> | undefined;\n if (!data) return [];\n // Normalize both envelope shapes the AdCP error surface produces:\n // - `errors: [{...}]` — plural array (AdCP core/error.json)\n // - `adcp_error: {...}` — singular object, what the canonical\n // `adcpError()` SDK helper emits. Most sellers use the helper, so\n // without this normalization the hint would silently never fire\n // against them (dogfood surfaced this; see adcp-client#907).\n // Also accepts `adcp_error: [{...}]` defensively in case a seller\n // ever emits the key with an array payload.\n const errors = resolveErrorsList(data);\n if (!errors) return [];\n\n const hints: StoryboardStepHint[] = [];\n // De-dupe: the same (context_key, rejected_value) shouldn't produce\n // two hints from two errors in the same response.\n const emitted = new Set<string>();\n\n for (const err of errors) {\n if (!err || typeof err !== 'object') continue;\n const errObj = err as Record<string, unknown>;\n const details =\n errObj.details && typeof errObj.details === 'object' && !Array.isArray(errObj.details)\n ? (errObj.details as Record<string, unknown>)\n : undefined;\n\n const acceptedValues = findAcceptedValues(errObj, details);\n if (!acceptedValues) continue;\n\n const fieldPath = findFieldPointer(errObj, details);\n const errorCode = typeof errObj.code === 'string' ? errObj.code : undefined;\n\n // Case A: seller told us the field — look at exactly that field.\n if (fieldPath !== undefined) {\n const rejectedValue = resolvePath(request, fieldPath);\n const hint = buildHintForValue({\n rejectedValue,\n fieldPath,\n acceptedValues,\n errorCode,\n context,\n provenance,\n emitted,\n ...(currentTask !== undefined && { currentTask }),\n });\n if (hint) hints.push(hint);\n continue;\n }\n\n // Case B: no field pointer — scan the request for values that (a) came\n // from context and (b) are absent from the accepted list. Narrow to\n // context-sourced values so we don't chatter on every string leaf.\n for (const [key, entry] of provenance) {\n const contextValue = context[key];\n if (contextValue === undefined || contextValue === null) continue;\n if (!requestContainsValue(request, contextValue)) continue;\n if (acceptedListIncludes(acceptedValues, contextValue)) continue;\n const dedupeKey = `${key}::${stringify(contextValue)}`;\n if (emitted.has(dedupeKey)) continue;\n emitted.add(dedupeKey);\n hints.push(\n buildHint({\n contextKey: key,\n entry,\n rejectedValue: contextValue,\n acceptedValues,\n ...(errorCode !== undefined && { errorCode }),\n ...(currentTask !== undefined && { currentTask }),\n })\n );\n }\n }\n\n return hints;\n}\n\ninterface BuildForValueInput {\n rejectedValue: unknown;\n fieldPath: string;\n acceptedValues: unknown[];\n errorCode?: string;\n context: StoryboardContext;\n provenance: ReadonlyMap<string, ContextProvenanceEntry>;\n emitted: Set<string>;\n currentTask?: string;\n}\n\n/**\n * Returns the first provenance match for `rejectedValue`. When two context\n * keys happen to hold the same value, iteration order on the provenance\n * `Map` (Map iteration is insertion-ordered per ES spec, and the runner\n * inserts writes in step order) makes the earliest-written key win. This\n * is deterministic across runs but is a heuristic — the caller may need\n * to author distinct context keys to disambiguate.\n */\nfunction buildHintForValue(input: BuildForValueInput): StoryboardStepHint | undefined {\n const { rejectedValue, fieldPath, acceptedValues, errorCode, context, provenance, emitted, currentTask } = input;\n if (rejectedValue === undefined || rejectedValue === null) return undefined;\n // If the seller claims to accept the value they rejected, the error\n // shape is inconsistent — skip rather than emit a confusing hint.\n if (acceptedListIncludes(acceptedValues, rejectedValue)) return undefined;\n for (const [key, entry] of provenance) {\n const contextValue = context[key];\n if (contextValue === undefined || contextValue === null) continue;\n if (!valueEquals(contextValue, rejectedValue)) continue;\n const dedupeKey = `${key}::${stringify(rejectedValue)}`;\n if (emitted.has(dedupeKey)) return undefined;\n emitted.add(dedupeKey);\n return buildHint({\n contextKey: key,\n entry,\n rejectedValue,\n acceptedValues,\n requestField: fieldPath,\n ...(errorCode !== undefined && { errorCode }),\n ...(currentTask !== undefined && { currentTask }),\n });\n }\n return undefined;\n}\n\ninterface BuildHintInput {\n contextKey: string;\n entry: ContextProvenanceEntry;\n rejectedValue: unknown;\n acceptedValues: unknown[];\n requestField?: string;\n errorCode?: string;\n currentTask?: string;\n}\n\nfunction buildHint(input: BuildHintInput): ContextValueRejectedHint {\n const { contextKey, entry, rejectedValue, acceptedValues, requestField, errorCode, currentTask } = input;\n const message = formatMessage({\n contextKey,\n entry,\n rejectedValue,\n acceptedValues,\n ...(requestField !== undefined && { requestField }),\n ...(currentTask !== undefined && { currentTask }),\n });\n return {\n kind: 'context_value_rejected',\n message,\n context_key: contextKey,\n source_step_id: entry.source_step_id,\n source_kind: entry.source_kind,\n ...(entry.response_path !== undefined && { response_path: entry.response_path }),\n ...(entry.source_task !== undefined && { source_task: entry.source_task }),\n rejected_value: rejectedValue,\n ...(requestField !== undefined && { request_field: requestField }),\n accepted_values: acceptedValues,\n ...(errorCode !== undefined && { error_code: errorCode }),\n };\n}\n\ninterface FormatMessageInput {\n contextKey: string;\n entry: ContextProvenanceEntry;\n rejectedValue: unknown;\n acceptedValues: unknown[];\n requestField?: string;\n currentTask?: string;\n}\n\nfunction formatMessage(input: FormatMessageInput): string {\n const { contextKey, entry, rejectedValue, acceptedValues, requestField, currentTask } = input;\n const valueRepr = formatScalar(rejectedValue);\n const fieldRepr = requestField ? `\\`${requestField}: ${valueRepr}\\`` : `\\`${valueRepr}\\``;\n const sourceDetail =\n entry.source_kind === 'context_outputs' && entry.response_path\n ? `set by step \\`${entry.source_step_id}\\` from response path \\`${entry.response_path}\\``\n : entry.source_task\n ? `set by step \\`${entry.source_step_id}\\` (convention extractor for \\`${entry.source_task}\\`)`\n : `set by step \\`${entry.source_step_id}\\``;\n const acceptedRepr = `[${acceptedValues.map(formatScalar).join(', ')}]`;\n // Closing sentence prefers the two-task form (\"`get_signals` and\n // `activate_signal` catalogs agree\") when both source and current task\n // are known — reads naturally regardless of how the context key was\n // named. Falls back to the single-task form (\"the `get_signals`\n // catalog\") when only the source is known, and to a generic nudge when\n // neither is known. The earlier context-key-derived phrasing read\n // awkwardly for multi-word keys (`first_signal_pricing_option_id` →\n // \"for this first_signal_pricing_option\"); cited by dogfood findings.\n const closing = buildClosing(entry.source_task, currentTask);\n return (\n `Rejected ${fieldRepr} was extracted from \\`$context.${contextKey}\\` ` +\n `(${sourceDetail}). ` +\n `Seller's accepted values: ${acceptedRepr}. ` +\n closing\n );\n}\n\nfunction buildClosing(sourceTask: string | undefined, currentTask: string | undefined): string {\n if (sourceTask && currentTask && sourceTask !== currentTask) {\n return `Check that the seller's \\`${sourceTask}\\` and \\`${currentTask}\\` catalogs agree.`;\n }\n if (sourceTask) {\n return `Check that the seller's \\`${sourceTask}\\` catalog agrees with other tools.`;\n }\n return `Check that the seller's catalogs agree across tools.`;\n}\n\nfunction formatScalar(value: unknown): string {\n if (typeof value === 'string') return value;\n if (value === null) return 'null';\n if (value === undefined) return 'undefined';\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n}\n\n/**\n * Normalize the response body's error envelope into a flat list for the\n * hint detector. Recognizes:\n * - `errors: [{...}]` — AdCP core/error.json shape\n * - `adcp_error: {...}` — what `adcpError()` helper produces\n * - `adcp_error: [{...}]` — defensive; some sellers emit this\n * Returns undefined when neither shape is present or the values aren't\n * objects we can inspect.\n */\nfunction resolveErrorsList(data: Record<string, unknown>): unknown[] | undefined {\n const errors = data.errors;\n if (Array.isArray(errors)) return errors;\n const adcpError = data.adcp_error;\n if (Array.isArray(adcpError)) return adcpError;\n if (adcpError && typeof adcpError === 'object') return [adcpError];\n return undefined;\n}\n\nfunction findAcceptedValues(\n err: Record<string, unknown>,\n details: Record<string, unknown> | undefined\n): unknown[] | undefined {\n for (const key of ACCEPTED_VALUE_KEYS) {\n if (details && Array.isArray(details[key])) return details[key] as unknown[];\n }\n for (const key of ACCEPTED_VALUE_KEYS) {\n if (Array.isArray(err[key])) return err[key] as unknown[];\n }\n return undefined;\n}\n\nfunction findFieldPointer(\n err: Record<string, unknown>,\n details: Record<string, unknown> | undefined\n): string | undefined {\n for (const key of FIELD_POINTER_KEYS) {\n const v = err[key];\n if (typeof v === 'string' && v.length > 0) return normalizeFieldPath(v);\n }\n if (details) {\n for (const key of FIELD_POINTER_KEYS) {\n const v = details[key];\n if (typeof v === 'string' && v.length > 0) return normalizeFieldPath(v);\n }\n }\n return undefined;\n}\n\n/**\n * Accept RFC 6901 JSON pointers (`/packages/0/pricing_option_id`) and the\n * AdCP spec's dotted form (`packages[0].pricing_option_id`). Normalize to\n * the dotted form because that's what `resolvePath` speaks.\n */\nfunction normalizeFieldPath(path: string): string {\n if (!path.startsWith('/')) return path;\n return path\n .slice(1)\n .split('/')\n .map(seg => seg.replace(/~1/g, '/').replace(/~0/g, '~'))\n .map(seg => (/^\\d+$/.test(seg) ? `[${seg}]` : seg))\n .join('.')\n .replace(/\\.\\[/g, '[');\n}\n\nfunction acceptedListIncludes(accepted: unknown[], value: unknown): boolean {\n return accepted.some(a => valueEquals(a, value));\n}\n\n/**\n * Equality for rejection matching. IDs across the ecosystem get serialized\n * as either numbers or strings (\"42\" vs 42), so the scalar case does\n * number↔string coercion via `String(n)` — that's safe for integer ids\n * (the AdCP ID corpus) but not guaranteed for float round-trips, which\n * aren't a concern here. Object values fall through to a canonical\n * `JSON.stringify` compare — order-sensitive by design (`{a:1,b:2}` !=\n * `{b:2,a:1}`); request/response builders produce stable key order, so\n * this is fine in practice.\n */\nfunction valueEquals(a: unknown, b: unknown): boolean {\n if (a === b) return true;\n if (typeof a === 'number' && typeof b === 'string') return String(a) === b;\n if (typeof a === 'string' && typeof b === 'number') return a === String(b);\n if (a !== null && b !== null && typeof a === 'object' && typeof b === 'object') {\n return stringify(a) === stringify(b);\n }\n return false;\n}\n\nfunction stringify(value: unknown): string {\n try {\n return JSON.stringify(value) ?? String(value);\n } catch {\n return String(value);\n }\n}\n\n/**\n * Walk the request object looking for a leaf that equals `value`. Used in\n * the no-field-pointer fallback to confirm the context-sourced value is\n * actually present in the request we sent (avoids false hints when the\n * runner has a stale context key that happens to match some unrelated\n * error's rejection list).\n *\n * Carries a `seen` WeakSet to guard against self-referential request\n * objects. The runner's own injection pipeline produces fresh trees so\n * cycles shouldn't occur in practice; the guard is defensive for\n * user-supplied request overrides.\n */\nfunction requestContainsValue(request: unknown, value: unknown, seen: WeakSet<object> = new WeakSet()): boolean {\n if (valueEquals(request, value)) return true;\n if (request === null || typeof request !== 'object') return false;\n if (seen.has(request as object)) return false;\n seen.add(request as object);\n if (Array.isArray(request)) return request.some(item => requestContainsValue(item, value, seen));\n return Object.values(request as Record<string, unknown>).some(v => requestContainsValue(v, value, seen));\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../../src/lib/testing/storyboard/rejection-hints.ts"],"sourcesContent":["/**\n * Context-value-rejected hint detection.\n *\n * When a seller rejects a request field with a response that carries an\n * `available:` / `allowed:` / `accepted_values:` list (per AdCP core/error\n * with `details` being `additionalProperties: true`, sellers commonly put\n * the set of values they would have accepted there), and the rejected\n * value traces back to a prior-step `$context.*` write (either via\n * substitution or via a request-builder field populated from the same\n * context key), emit a non-fatal `context_value_rejected` hint.\n *\n * Diagnostic only — does not flip the step's pass/fail. See issue #870 for\n * the rationale: without this, the rejection in logs is indistinguishable\n * from an SDK bug.\n */\nimport type { TaskResult } from '../types';\nimport type { ContextProvenanceEntry, ContextValueRejectedHint, StoryboardContext, StoryboardStepHint } from './types';\nimport { resolvePath } from './path';\n\n/**\n * Response-body error keys the runner recognizes as carrying the seller's\n * \"here's what I would have accepted\" list. Matched in order; the first\n * array found is used.\n */\nconst ACCEPTED_VALUE_KEYS = ['available', 'allowed', 'accepted_values'] as const;\n\n/**\n * Fields on an error object (or `error.details`) the runner recognizes as\n * pointing at the rejected request field. Matches AdCP core/error's `field`\n * (spec-canonical) plus `details.field` (common vendor extension).\n */\nconst FIELD_POINTER_KEYS = ['field', 'path', 'pointer'] as const;\n\n/**\n * Extract `context_value_rejected` hints from a task result.\n *\n * The function is pure — safe to call regardless of pass/fail. Returns an\n * empty array when the response doesn't carry a recognizable rejection\n * shape or no context value matches.\n *\n * @param taskResult Raw task result from the runner.\n * @param request The exact request the runner sent (post-injection).\n * @param context The storyboard context as of the step being evaluated\n * (pre-write — the hints trace which PRIOR step's context\n * write the rejected value came from).\n * @param provenance Context-key → write-provenance map.\n */\nexport function detectContextRejectionHints(\n taskResult: TaskResult | undefined,\n request: Record<string, unknown>,\n context: StoryboardContext,\n provenance: ReadonlyMap<string, ContextProvenanceEntry>,\n /**\n * Current step's AdCP task name (e.g. `activate_signal`). Used only to\n * produce a human-readable closing sentence that cites both tools\n * involved in the catalog drift — \"Check that `get_signals` and\n * `activate_signal` catalogs agree.\" Optional for backwards\n * compatibility; callers that can't supply it get a generic closing.\n */\n currentTask?: string\n): StoryboardStepHint[] {\n if (!taskResult) return [];\n const data = taskResult.data as Record<string, unknown> | undefined;\n if (!data) return [];\n // Normalize both envelope shapes the AdCP error surface produces:\n // - `errors: [{...}]` — plural array (AdCP core/error.json)\n // - `adcp_error: {...}` — singular object, what the canonical\n // `adcpError()` SDK helper emits. Most sellers use the helper, so\n // without this normalization the hint would silently never fire\n // against them (dogfood surfaced this; see adcp-client#907).\n // Also accepts `adcp_error: [{...}]` defensively in case a seller\n // ever emits the key with an array payload.\n const errors = resolveErrorsList(data);\n if (!errors) return [];\n\n const hints: StoryboardStepHint[] = [];\n // De-dupe: the same (context_key, rejected_value) shouldn't produce\n // two hints from two errors in the same response.\n const emitted = new Set<string>();\n\n for (const err of errors) {\n if (!err || typeof err !== 'object') continue;\n const errObj = err as Record<string, unknown>;\n const details =\n errObj.details && typeof errObj.details === 'object' && !Array.isArray(errObj.details)\n ? (errObj.details as Record<string, unknown>)\n : undefined;\n\n const acceptedValues = findAcceptedValues(errObj, details);\n if (!acceptedValues) continue;\n\n const fieldPath = findFieldPointer(errObj, details);\n const errorCode = typeof errObj.code === 'string' ? errObj.code : undefined;\n\n // Case A: seller told us the field — look at exactly that field.\n if (fieldPath !== undefined) {\n const rejectedValue = resolvePath(request, fieldPath);\n const hint = buildHintForValue({\n rejectedValue,\n fieldPath,\n acceptedValues,\n errorCode,\n context,\n provenance,\n emitted,\n ...(currentTask !== undefined && { currentTask }),\n });\n if (hint) hints.push(hint);\n continue;\n }\n\n // Case B: no field pointer — scan the request for values that (a) came\n // from context and (b) are absent from the accepted list. Narrow to\n // context-sourced values so we don't chatter on every string leaf.\n for (const [key, entry] of provenance) {\n const contextValue = context[key];\n if (contextValue === undefined || contextValue === null) continue;\n if (!requestContainsValue(request, contextValue)) continue;\n if (acceptedListIncludes(acceptedValues, contextValue)) continue;\n const dedupeKey = `${key}::${stringify(contextValue)}`;\n if (emitted.has(dedupeKey)) continue;\n emitted.add(dedupeKey);\n hints.push(\n buildHint({\n contextKey: key,\n entry,\n rejectedValue: contextValue,\n acceptedValues,\n ...(errorCode !== undefined && { errorCode }),\n ...(currentTask !== undefined && { currentTask }),\n })\n );\n }\n }\n\n return hints;\n}\n\ninterface BuildForValueInput {\n rejectedValue: unknown;\n fieldPath: string;\n acceptedValues: unknown[];\n errorCode?: string;\n context: StoryboardContext;\n provenance: ReadonlyMap<string, ContextProvenanceEntry>;\n emitted: Set<string>;\n currentTask?: string;\n}\n\n/**\n * Returns the first provenance match for `rejectedValue`. When two context\n * keys happen to hold the same value, iteration order on the provenance\n * `Map` (Map iteration is insertion-ordered per ES spec, and the runner\n * inserts writes in step order) makes the earliest-written key win. This\n * is deterministic across runs but is a heuristic — the caller may need\n * to author distinct context keys to disambiguate.\n */\nfunction buildHintForValue(input: BuildForValueInput): StoryboardStepHint | undefined {\n const { rejectedValue, fieldPath, acceptedValues, errorCode, context, provenance, emitted, currentTask } = input;\n if (rejectedValue === undefined || rejectedValue === null) return undefined;\n // If the seller claims to accept the value they rejected, the error\n // shape is inconsistent — skip rather than emit a confusing hint.\n if (acceptedListIncludes(acceptedValues, rejectedValue)) return undefined;\n for (const [key, entry] of provenance) {\n const contextValue = context[key];\n if (contextValue === undefined || contextValue === null) continue;\n if (!valueEquals(contextValue, rejectedValue)) continue;\n const dedupeKey = `${key}::${stringify(rejectedValue)}`;\n if (emitted.has(dedupeKey)) return undefined;\n emitted.add(dedupeKey);\n return buildHint({\n contextKey: key,\n entry,\n rejectedValue,\n acceptedValues,\n requestField: fieldPath,\n ...(errorCode !== undefined && { errorCode }),\n ...(currentTask !== undefined && { currentTask }),\n });\n }\n return undefined;\n}\n\ninterface BuildHintInput {\n contextKey: string;\n entry: ContextProvenanceEntry;\n rejectedValue: unknown;\n acceptedValues: unknown[];\n requestField?: string;\n errorCode?: string;\n currentTask?: string;\n}\n\nfunction buildHint(input: BuildHintInput): ContextValueRejectedHint {\n const { contextKey, entry, rejectedValue, acceptedValues, requestField, errorCode, currentTask } = input;\n const message = formatMessage({\n contextKey,\n entry,\n rejectedValue,\n acceptedValues,\n ...(requestField !== undefined && { requestField }),\n ...(currentTask !== undefined && { currentTask }),\n });\n return {\n kind: 'context_value_rejected',\n message,\n context_key: contextKey,\n source_step_id: entry.source_step_id,\n source_kind: entry.source_kind,\n ...(entry.response_path !== undefined && { response_path: entry.response_path }),\n ...(entry.source_task !== undefined && { source_task: entry.source_task }),\n rejected_value: rejectedValue,\n ...(requestField !== undefined && { request_field: requestField }),\n accepted_values: acceptedValues,\n ...(errorCode !== undefined && { error_code: errorCode }),\n };\n}\n\ninterface FormatMessageInput {\n contextKey: string;\n entry: ContextProvenanceEntry;\n rejectedValue: unknown;\n acceptedValues: unknown[];\n requestField?: string;\n currentTask?: string;\n}\n\nfunction formatMessage(input: FormatMessageInput): string {\n const { contextKey, entry, rejectedValue, acceptedValues, requestField, currentTask } = input;\n const valueRepr = formatScalar(rejectedValue);\n const fieldRepr = requestField ? `\\`${requestField}: ${valueRepr}\\`` : `\\`${valueRepr}\\``;\n const sourceDetail =\n entry.source_kind === 'context_outputs' && entry.response_path\n ? `set by step \\`${entry.source_step_id}\\` from response path \\`${entry.response_path}\\``\n : entry.source_task\n ? `set by step \\`${entry.source_step_id}\\` (convention extractor for \\`${entry.source_task}\\`)`\n : `set by step \\`${entry.source_step_id}\\``;\n const acceptedRepr = `[${acceptedValues.map(formatScalar).join(', ')}]`;\n // Closing sentence prefers the two-task form (\"`get_signals` and\n // `activate_signal` catalogs agree\") when both source and current task\n // are known — reads naturally regardless of how the context key was\n // named. Falls back to the single-task form (\"the `get_signals`\n // catalog\") when only the source is known, and to a generic nudge when\n // neither is known. The earlier context-key-derived phrasing read\n // awkwardly for multi-word keys (`first_signal_pricing_option_id` →\n // \"for this first_signal_pricing_option\"); cited by dogfood findings.\n const closing = buildClosing(entry.source_task, currentTask);\n return (\n `Rejected ${fieldRepr} was extracted from \\`$context.${contextKey}\\` ` +\n `(${sourceDetail}). ` +\n `Seller's accepted values: ${acceptedRepr}. ` +\n closing\n );\n}\n\nfunction buildClosing(sourceTask: string | undefined, currentTask: string | undefined): string {\n if (sourceTask && currentTask && sourceTask !== currentTask) {\n return `Check that the seller's \\`${sourceTask}\\` and \\`${currentTask}\\` catalogs agree.`;\n }\n if (sourceTask) {\n return `Check that the seller's \\`${sourceTask}\\` catalog agrees with other tools.`;\n }\n return `Check that the seller's catalogs agree across tools.`;\n}\n\nfunction formatScalar(value: unknown): string {\n if (typeof value === 'string') return value;\n if (value === null) return 'null';\n if (value === undefined) return 'undefined';\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n}\n\n/**\n * Normalize the response body's error envelope into a flat list for the\n * hint detector. Recognizes:\n * - `errors: [{...}]` — AdCP core/error.json shape\n * - `adcp_error: {...}` — what `adcpError()` helper produces\n * - `adcp_error: [{...}]` — defensive; some sellers emit this\n * Returns undefined when neither shape is present or the values aren't\n * objects we can inspect.\n */\nfunction resolveErrorsList(data: Record<string, unknown>): unknown[] | undefined {\n const errors = data.errors;\n if (Array.isArray(errors)) return errors;\n const adcpError = data.adcp_error;\n if (Array.isArray(adcpError)) return adcpError;\n if (adcpError && typeof adcpError === 'object') return [adcpError];\n return undefined;\n}\n\nfunction findAcceptedValues(\n err: Record<string, unknown>,\n details: Record<string, unknown> | undefined\n): unknown[] | undefined {\n for (const key of ACCEPTED_VALUE_KEYS) {\n if (details && Array.isArray(details[key])) return details[key] as unknown[];\n }\n for (const key of ACCEPTED_VALUE_KEYS) {\n if (Array.isArray(err[key])) return err[key] as unknown[];\n }\n return undefined;\n}\n\nfunction findFieldPointer(\n err: Record<string, unknown>,\n details: Record<string, unknown> | undefined\n): string | undefined {\n for (const key of FIELD_POINTER_KEYS) {\n const v = err[key];\n if (typeof v === 'string' && v.length > 0) return normalizeFieldPath(v);\n }\n if (details) {\n for (const key of FIELD_POINTER_KEYS) {\n const v = details[key];\n if (typeof v === 'string' && v.length > 0) return normalizeFieldPath(v);\n }\n }\n return undefined;\n}\n\n/**\n * Accept RFC 6901 JSON pointers (`/packages/0/pricing_option_id`) and the\n * AdCP spec's dotted form (`packages[0].pricing_option_id`). Normalize to\n * the dotted form because that's what `resolvePath` speaks.\n */\nfunction normalizeFieldPath(path: string): string {\n if (!path.startsWith('/')) return path;\n return path\n .slice(1)\n .split('/')\n .map(seg => seg.replace(/~1/g, '/').replace(/~0/g, '~'))\n .map(seg => (/^\\d+$/.test(seg) ? `[${seg}]` : seg))\n .join('.')\n .replace(/\\.\\[/g, '[');\n}\n\nfunction acceptedListIncludes(accepted: unknown[], value: unknown): boolean {\n return accepted.some(a => valueEquals(a, value));\n}\n\n/**\n * Equality for rejection matching. IDs across the ecosystem get serialized\n * as either numbers or strings (\"42\" vs 42), so the scalar case does\n * number↔string coercion via `String(n)` — that's safe for integer ids\n * (the AdCP ID corpus) but not guaranteed for float round-trips, which\n * aren't a concern here. Object values fall through to a canonical\n * `JSON.stringify` compare — order-sensitive by design (`{a:1,b:2}` !=\n * `{b:2,a:1}`); request/response builders produce stable key order, so\n * this is fine in practice.\n */\nfunction valueEquals(a: unknown, b: unknown): boolean {\n if (a === b) return true;\n if (typeof a === 'number' && typeof b === 'string') return String(a) === b;\n if (typeof a === 'string' && typeof b === 'number') return a === String(b);\n if (a !== null && b !== null && typeof a === 'object' && typeof b === 'object') {\n return stringify(a) === stringify(b);\n }\n return false;\n}\n\nfunction stringify(value: unknown): string {\n try {\n return JSON.stringify(value) ?? String(value);\n } catch {\n return String(value);\n }\n}\n\n/**\n * Walk the request object looking for a leaf that equals `value`. Used in\n * the no-field-pointer fallback to confirm the context-sourced value is\n * actually present in the request we sent (avoids false hints when the\n * runner has a stale context key that happens to match some unrelated\n * error's rejection list).\n *\n * Carries a `seen` WeakSet to guard against self-referential request\n * objects. The runner's own injection pipeline produces fresh trees so\n * cycles shouldn't occur in practice; the guard is defensive for\n * user-supplied request overrides.\n */\nfunction requestContainsValue(request: unknown, value: unknown, seen: WeakSet<object> = new WeakSet()): boolean {\n if (valueEquals(request, value)) return true;\n if (request === null || typeof request !== 'object') return false;\n if (seen.has(request as object)) return false;\n seen.add(request as object);\n if (Array.isArray(request)) return request.some(item => requestContainsValue(item, value, seen));\n return Object.values(request as Record<string, unknown>).some(v => requestContainsValue(v, value, seen));\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
import { fileURLToPath as __adcpFileURLToPath } from 'node:url';
|
|
2
|
-
import { dirname as __adcpDirname } from 'node:path';
|
|
3
|
-
import { createRequire as __adcpCreateRequire } from 'node:module';
|
|
4
|
-
const __filename = __adcpFileURLToPath(import.meta.url);
|
|
5
|
-
const __dirname = __adcpDirname(__filename);
|
|
6
|
-
const require = __adcpCreateRequire(import.meta.url);
|
|
7
1
|
import { createHash } from "node:crypto";
|
|
8
2
|
import { resolveBrand, resolveAccount } from "../client.mjs";
|
|
9
3
|
import { injectContext } from "./context.mjs";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/lib/testing/storyboard/request-builder.ts"],"sourcesContent":["/**\n * Request enrichers for storyboard steps.\n *\n * Contract (see issue #820):\n * - `sample_request` (when authored) is the authoritative base payload.\n * The runner injects context placeholders into it and passes it through\n * to the agent under test.\n * - An enricher fills top-level fields the fixture didn't specify —\n * typically discovery-derived identifiers (`product_id`, `format_id`,\n * `account`, `media_buy_id`) or envelope fields that only the harness\n * knows.\n * - For conflicts at the top level, the fixture wins — storyboard authors'\n * intent is not silently overridden.\n *\n * A short list of tasks need to splice discovery-derived fields INTO\n * nested structures in the fixture (e.g. `create_media_buy` injects\n * `product_id` into `packages[0]`) and can't be expressed by a top-level\n * overlay. Those enrichers declare themselves fixture-aware via\n * `FIXTURE_AWARE_ENRICHERS` below and the runner uses their output as-is.\n *\n * `sample_request` from YAML, when a task has no enricher, is used directly\n * after context injection — preserves the \"no handler, fixture is the wire\n * payload\" pattern.\n */\n\nimport { createHash } from 'node:crypto';\nimport { resolveBrand, resolveAccount } from '../client';\nimport type { TestOptions } from '../types';\nimport type { StoryboardContext, StoryboardStep } from './types';\nimport { injectContext, type RunnerVariables } from './context';\n\ntype RequestEnricher = (\n step: StoryboardStep,\n context: StoryboardContext,\n options: TestOptions,\n runnerVars?: RunnerVariables\n) => Record<string, unknown>;\n\n/** Legacy alias kept for external consumers pinned to the old terminology. */\ntype RequestBuilder = RequestEnricher;\n\n/**\n * Placeholder `format_id` used when neither `list_creative_formats` discovery\n * nor accumulated `context.format_id` supplied one. Schema\n * (core/format-id.json) requires `agent_url` in URI form, so a bare\n * `\"unknown\"` string fails validation. `example.com` is reserved for\n * documentation per RFC 2606 — an obvious fixture that strict JSON-schema\n * validators accept and that downstream handlers resolve to a clean\n * format-not-found error rather than an unrelated crash. Frozen so a\n * builder that accidentally spread-mutates the shared constant hits a\n * TypeError instead of silently corrupting sibling calls.\n */\nconst UNKNOWN_FORMAT_ID = Object.freeze({ agent_url: 'https://unknown.example.com/', id: 'unknown' });\n\n/**\n * Placeholder `caller` URL for tasks whose schema names the CALLER-AGENT's\n * URL (not the brand or the seller). `check_governance.caller` is the canonical\n * case: governance agents bind this field to agent identity for rate limiting,\n * audit trails, and (with `signed-requests`) JWS issuer correlation — emitting\n * the brand domain here names the wrong entity and will confuse strict\n * governance agents. Storyboards that care about a specific caller identity\n * author sample_request; this is the fallback when neither fixture nor\n * harness-supplied agent URL is present.\n */\nconst FALLBACK_CALLER_AGENT_URL = 'https://e2e-orchestrator.adcontextprotocol.org/';\n\n/**\n * Tasks whose enricher must see `sample_request` to produce the final\n * payload — typically because it needs to splice discovery-derived fields\n * INTO nested structures the fixture owns (arrays, object trees). For\n * these, the runner uses the enricher's output verbatim and does not\n * layer the fixture on top; the enricher is responsible for fixture\n * precedence internally.\n */\nconst FIXTURE_AWARE_ENRICHERS = new Set<string>([\n 'create_media_buy', // merges discovery-derived product_id / pricing_option_id INTO fixture packages[0]\n 'comply_test_controller', // forces account.sandbox: true regardless of fixture\n 'update_media_buy', // resolves account via resolveAccount(options) so sandbox routing matches create_media_buy\n 'get_media_buys', // resolves account via resolveAccount(options) so sandbox routing matches create_media_buy\n 'get_media_buy_delivery', // resolves account via resolveAccount(options) so sandbox routing matches create_media_buy\n]);\n\n/**\n * Placeholder identifiers that the upstream universal compliance\n * storyboards ship inside `create_media_buy.packages[0]` expecting the\n * runner to substitute the seller's discovered identifiers. The fixtures\n * live in `adcontextprotocol/adcp` and can't be rewritten from the SDK,\n * so the enricher treats these literals as \"defer to discovery\" while\n * real seller ids (e.g. `cpm_guaranteed`) win over discovery per the\n * fixture-authoritative contract. If a future storyboard wants\n * placeholder-then-discovery semantics for another value, author it as\n * a `$context.<key>` substitution rather than a magic literal so the\n * intent is explicit at the fixture level.\n */\nconst PRODUCT_ID_SENTINELS = new Set(['test-product']);\nconst PRICING_OPTION_ID_SENTINELS = new Set(['test-pricing']);\nconst DAY_MS = 24 * 60 * 60 * 1000;\nconst DEFAULT_MEDIA_BUY_WINDOW_MS = 7 * DAY_MS;\n\nfunction asNonSentinel(value: unknown, sentinels: Set<string>): string | undefined {\n if (typeof value !== 'string') return undefined;\n return sentinels.has(value) ? undefined : value;\n}\n\nfunction parseTime(value: string | undefined): number | undefined {\n if (value === undefined) return undefined;\n const parsed = Date.parse(value);\n return Number.isFinite(parsed) ? parsed : undefined;\n}\n\nfunction resolveMediaBuyWindow(\n sampleStart: string | undefined,\n sampleEnd: string | undefined,\n nowMs?: number\n): {\n startTime: string;\n endTime: string;\n} {\n const now = nowMs ?? Date.now();\n const defaultStart = new Date(now + DAY_MS).toISOString();\n const defaultEnd = new Date(now + 8 * DAY_MS).toISOString();\n const sampleStartMs = parseTime(sampleStart);\n const sampleEndMs = parseTime(sampleEnd);\n const startIsAsap = sampleStart === 'asap';\n\n const startTime = startIsAsap\n ? 'asap'\n : sampleStart && sampleStartMs !== undefined && sampleStartMs >= now\n ? sampleStart\n : defaultStart;\n let endTime = sampleEnd && sampleEndMs !== undefined && sampleEndMs >= now ? sampleEnd : defaultEnd;\n\n const startTimeMs = startIsAsap ? now : Date.parse(startTime);\n if (Date.parse(endTime) <= startTimeMs) {\n const sampleDurationMs =\n sampleStartMs !== undefined && sampleEndMs !== undefined && sampleEndMs > sampleStartMs\n ? sampleEndMs - sampleStartMs\n : DEFAULT_MEDIA_BUY_WINDOW_MS;\n endTime = new Date(startTimeMs + sampleDurationMs).toISOString();\n }\n\n return { startTime, endTime };\n}\n\nfunction runClockMs(runnerVars: RunnerVariables | undefined): number {\n return runnerVars?.runStartMs ?? Date.now();\n}\n\nfunction generatedIdSuffix(step: StoryboardStep, runnerVars: RunnerVariables | undefined, nowMs?: number): string {\n const timestamp = nowMs ?? runClockMs(runnerVars);\n if (runnerVars?.runStartMs === undefined) return String(timestamp);\n const stepHash = createHash('sha256').update(step.id).digest('hex').slice(0, 12);\n return `${timestamp}-${stepHash}`;\n}\n\n/**\n * Envelope fields that live on every AdCP request and are owned by the\n * storyboard author — `context.correlation_id`, runner-supplied\n * `idempotency_key` aliases, webhook pointers, per-request extensions.\n * Fixture-aware enrichers must NOT spread these from their internal\n * `injectContext(sample_request, context)` call: that internal call\n * doesn't receive `runnerVars`, so mustache tokens (`{{runner.webhook_url:<step_id>}}`)\n * would ship to the wire literally. The outer `enrichRequest` overlays\n * envelope fields from a `runnerVars`-aware fixture re-injection.\n */\nconst ENVELOPE_FIELDS = ['context', 'ext', 'push_notification_config', 'idempotency_key'] as const;\n\nfunction omitEnvelopeFields(obj: Record<string, unknown>): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(obj)) {\n if (!(ENVELOPE_FIELDS as readonly string[]).includes(k)) out[k] = v;\n }\n return out;\n}\n\nconst REQUEST_ENRICHERS: Record<string, RequestEnricher> = {\n // ── Account & Audience ─────────────────────────────────\n\n sync_accounts(_step, _context, options) {\n return {\n accounts: [\n {\n brand: resolveBrand(options),\n operator: resolveBrand(options).domain,\n billing: 'operator',\n payment_terms: 'net_30',\n },\n ],\n };\n },\n\n list_accounts(_step, _context, options) {\n return {\n brand: resolveBrand(options),\n };\n },\n\n sync_audiences(step, context, options, runnerVars) {\n // Honor hand-authored sample_request so storyboards can register a\n // specific audience_id that downstream steps reference. Without this,\n // add-shaped sample_request blocks (authored with audience_id + add[])\n // fell through to the generated fallback id, and a later delete_audience\n // or context-substitution step would hit AUDIENCE_NOT_FOUND because the\n // sync had registered a different id. Matches the pattern used by\n // sync_event_sources, sync_catalogs, and sync_creatives.\n if (step.sample_request) {\n return injectContext({ ...step.sample_request, account: context.account ?? resolveAccount(options) }, context);\n }\n return {\n account: context.account ?? resolveAccount(options),\n audiences: [\n {\n audience_id: context.audience_id ?? `test-audience-${generatedIdSuffix(step, runnerVars)}`,\n name: 'E2E Test Audience',\n add: [\n {\n external_id: 'user-001',\n hashed_email: 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855',\n },\n ],\n },\n ],\n };\n },\n\n // ── Brand & Rights ───────────────────────────────────────\n\n get_brand_identity(step, context, options) {\n const brand = resolveBrand(options);\n return {\n brand_id: context.brand_id ?? (step.sample_request?.brand_id as string) ?? brand.brand_id ?? brand.domain,\n };\n },\n\n get_rights(step, context, options) {\n // Honor hand-authored sample_request so storyboards can specify\n // scenario-specific query text, uses, countries, or buyer_brand.\n // Peer builders (sync_plans, check_governance, list_creative_formats,\n // create_content_standards, etc.) follow the same pattern.\n //\n // Without this, any get_rights step hits the wire with the generic\n // fallback and a brand_id derived from the caller's domain — which\n // rights-holder rosters reject as unknown, so rights[0] is undefined,\n // $context.rights_id doesn't resolve, and downstream acquire_rights\n // steps fail with rights_not_found instead of the error the\n // storyboard is actually asserting (e.g., GOVERNANCE_DENIED).\n const brand = resolveBrand(options);\n return {\n query: 'available rights for advertising',\n uses: ['ai_generated_image'],\n brand_id: context.brand_id ?? brand.brand_id ?? brand.domain,\n };\n },\n\n // ── Product Discovery ──────────────────────────────────\n\n get_products(step, context, options) {\n // If the step is a \"refine\" step, build a refine request\n if (step.sample_request?.buying_mode === 'refine' && context.products) {\n return {\n buying_mode: 'refine',\n refine: [\n {\n scope: 'request',\n ask: 'Only guaranteed packages with premium placement.',\n },\n ],\n brand: resolveBrand(options),\n account: context.account ?? resolveAccount(options),\n };\n }\n\n return {\n buying_mode: 'brief',\n brief: options.brief || 'Show me all available advertising products across all channels',\n brand: resolveBrand(options),\n };\n },\n\n // ── Media Buy ──────────────────────────────────────────\n\n create_media_buy(step, context, options, runnerVars) {\n const product = selectProduct(context);\n const pricingOption = selectPricingOption(product);\n\n // Respect sample_request dates when they're future-dated — needed for\n // storyboards that test replay semantics where initial + replay must\n // produce byte-for-byte identical canonical payloads. Two calls\n // generated 5ms apart with `Date.now()` would hash differently,\n // triggering IDEMPOTENCY_CONFLICT on replay. Stale sample dates\n // (authored before the run date) fall back to the run-start-relative\n // default. Resolve the pair together so a stale start and same-day\n // future end cannot produce start_time > end_time.\n const sampleStart =\n typeof step.sample_request?.start_time === 'string' ? step.sample_request.start_time : undefined;\n const sampleEnd = typeof step.sample_request?.end_time === 'string' ? step.sample_request.end_time : undefined;\n const { startTime, endTime } = resolveMediaBuyWindow(sampleStart, sampleEnd, runnerVars?.runStartMs);\n\n // Merge hand-authored package fields from sample_request (targeting_overlay,\n // measurement_terms, creative_assignments, performance_standards, etc.) so\n // scenario-specific behaviors are exercised. The first package's\n // identifiers (product_id, pricing_option_id) follow a two-tier rule:\n //\n // - Real seller ids in the fixture win over discovery — storyboards\n // that name `cpm_guaranteed` (or any seller-specific identifier)\n // are asserting that specific id reaches the wire. Discovery must\n // not override.\n // - Well-known sentinel placeholders (`test-product`, `test-pricing`)\n // from the upstream universal compliance storyboards defer to\n // discovery. Those storyboards ship package fixtures with\n // `product_id: \"test-product\"` expecting the runner to substitute\n // the seller's discovered product — they'd fail against every\n // seller that doesn't literally ship a \"test-product\" catalog\n // entry if the fixture value survived to the wire.\n //\n // The sentinel list is conservative. If a future storyboard wants\n // placeholder-then-discovery semantics for another field, author it\n // as `$context.<key>` substitution instead of a magic literal.\n const samplePackages = (step.sample_request?.packages as Array<Record<string, unknown>> | undefined) ?? [];\n const baseSample = samplePackages[0]\n ? (injectContext({ ...samplePackages[0] }, context) as Record<string, unknown>)\n : {};\n\n const fixtureProductId = asNonSentinel(baseSample.product_id, PRODUCT_ID_SENTINELS);\n const fixturePricingOptionId = asNonSentinel(baseSample.pricing_option_id, PRICING_OPTION_ID_SENTINELS);\n\n const firstPkg: Record<string, unknown> = {\n ...baseSample,\n product_id: fixtureProductId ?? product?.product_id ?? context.product_id ?? 'test-product',\n budget:\n (typeof baseSample.budget === 'number' ? baseSample.budget : undefined) ??\n options.budget ??\n Math.max(1000, (pricingOption?.min_spend_per_package as number) ?? 1000),\n pricing_option_id:\n fixturePricingOptionId ?? pricingOption?.pricing_option_id ?? context.pricing_option_id ?? 'default',\n };\n\n // Synthesize a bid_price for auction/cpm pricing only when the fixture\n // didn't author one. Storyboards that test auction flows (e.g.\n // sales-non-guaranteed) author explicit bid_prices the seller validates\n // against floor_price; discovery-synthesized values would silently\n // override intentional bid-floor-boundary tests.\n if (\n baseSample.bid_price === undefined &&\n (pricingOption?.pricing_model === 'auction' || pricingOption?.pricing_model === 'cpm')\n ) {\n const floor = Number(pricingOption?.floor_price) || 5;\n firstPkg.bid_price = Math.round(floor * 1.5 * 100) / 100;\n }\n\n const additionalPkgs = samplePackages\n .slice(1)\n .map(p => injectContext({ ...p }, context) as Record<string, unknown>);\n\n // Proposal-mode: when context carries a proposal_id (captured from\n // `proposals[0]` in a prior brief/refine/finalize step), forward it\n // and omit `packages`. The schema disallows synthesising packages\n // alongside `proposal_id` — `dependencies.proposal_id` requires\n // `total_budget` and the seller derives packages from the committed\n // allocation.\n //\n // Spread the fixture (after $context injection) so proposal-mode-\n // required fields like `total_budget` flow through. Prefer the\n // fixture's account/brand when supplied — proposal-mode storyboards\n // author a non-default brand (e.g. `acmeoutdoor.example`) that the\n // adapter resolves end-to-end through brief/refine/finalize, and\n // the harness-default `test.example` would fail account resolution\n // at the accept step (adcp-client#1600). Dates and proposal_id\n // are still normalised by the enricher.\n const fixture =\n step.sample_request !== undefined\n ? (injectContext({ ...(step.sample_request as Record<string, unknown>) }, context) as Record<string, unknown>)\n : {};\n // Drop envelope fields from the local spread — the outer enrichRequest\n // re-injects them with `runnerVars` so `{{runner.webhook_url:<step_id>}}`\n // tokens inside push_notification_config expand. Spreading them here\n // would carry through unexpanded mustache strings.\n const fixtureBody = omitEnvelopeFields(fixture);\n\n // Proposal-mode applies only when the storyboard's fixture explicitly\n // names a `proposal_id`. `context.proposal_id` is auto-captured from\n // any prior `get_products` response that returned `proposals[0]` (see\n // context.ts), which is essentially every brief flow — using it as a\n // fallback would override a fixture's explicit `packages` direction\n // and force every sales storyboard through proposal-mode validation,\n // including ones that intentionally exercise package-mode (sales-\n // guaranteed, schema_validation, media_buy_seller/*). Fixture is the\n // storyboard author's intent; respect it.\n const fixtureHasPackages = Array.isArray(fixture.packages) && (fixture.packages as unknown[]).length > 0;\n const proposalId: unknown =\n fixture.proposal_id !== undefined ? fixture.proposal_id : fixtureHasPackages ? undefined : context.proposal_id;\n if (typeof proposalId === 'string') {\n const { packages: _droppedPackages, ...fixtureWithoutPackages } = fixtureBody;\n return {\n ...fixtureWithoutPackages,\n account: fixtureWithoutPackages.account ?? context.account ?? resolveAccount(options),\n brand: fixtureWithoutPackages.brand ?? resolveBrand(options),\n start_time: startTime,\n end_time: endTime,\n proposal_id: proposalId,\n };\n }\n\n // Spread the fixture (after $context injection) so any sample_request\n // fields the enricher doesn't explicitly normalise (total_budget,\n // buyer_ref, currency, scenario-specific extensions) reach the wire.\n // The enricher then layers its own substitutions on top: packages is\n // replaced (the enricher merged samplePackages internally with\n // discovery-derived identifiers above), account/brand/start_time/\n // end_time are normalised. Per issue #1604, build-from-scratch dropped\n // every fixture field outside this enumerated set — fixture-aware\n // enrichers must spread sample_request first, then override. Envelope\n // fields are omitted here and re-applied by the outer enrichRequest\n // with `runnerVars` so mustache tokens expand correctly.\n return {\n ...fixtureBody,\n account: context.account ?? resolveAccount(options),\n brand: resolveBrand(options),\n start_time: startTime,\n end_time: endTime,\n packages: [firstPkg, ...additionalPkgs],\n };\n },\n\n update_media_buy(step, context, options) {\n // Fixture-aware: spread the storyboard's sample_request first so hand-authored\n // intent (targeting_overlay swaps, creative assignments, pause/resume/cancel)\n // is preserved verbatim, then force `account` to the harness-resolved value so\n // sandbox routing matches create_media_buy's namespace on every round-trip\n // (otherwise the fixture's account silently routes update writes to a different\n // partition than the create wrote to, and a subsequent get_media_buys reading\n // from the create-time partition surfaces stale data — see adcp-client#1505).\n // Envelope fields are dropped from the local spread; the outer enrichRequest\n // re-injects them with `runnerVars` so mustache substitutions expand.\n const fixtureFields = step.sample_request\n ? omitEnvelopeFields(\n injectContext({ ...(step.sample_request as Record<string, unknown>) }, context) as Record<string, unknown>\n )\n : {};\n const request: Record<string, unknown> = { ...fixtureFields };\n request.account = context.account ?? resolveAccount(options);\n if (request.media_buy_id === undefined) {\n request.media_buy_id = context.media_buy_id ?? 'unknown';\n }\n\n // No sample_request: fall back to step-id keyword inference so legacy\n // storyboards without an authored fixture still exercise sensible flows.\n if (!step.sample_request) {\n if (step.id.includes('pause')) {\n request.paused = true;\n } else if (step.id.includes('resume')) {\n request.paused = false;\n } else if (step.id.includes('cancel')) {\n request.canceled = true;\n } else {\n request.packages = [\n {\n package_id: (context.package_id as string | undefined) ?? 'unknown',\n budget: 2000,\n },\n ];\n }\n }\n\n return request;\n },\n\n get_media_buys(step, context, options) {\n // Spread fixture fields so storyboards can author filters, status, pagination, etc.\n // account is always overridden by the harness-resolved value so sandbox routing\n // matches create_media_buy's namespace on every round-trip. Envelope fields are\n // dropped from the local spread so the outer enrichRequest re-injects them with\n // `runnerVars` (mustache substitutions expand against the runner's webhook base).\n const fixtureFields = step.sample_request\n ? omitEnvelopeFields(\n injectContext({ ...(step.sample_request as Record<string, unknown>) }, context) as Record<string, unknown>\n )\n : {};\n const result: Record<string, unknown> = { ...fixtureFields };\n result.account = context.account ?? resolveAccount(options);\n if (context.media_buy_id != null && result.media_buy_ids === undefined) {\n result.media_buy_ids = [context.media_buy_id];\n }\n return result;\n },\n\n get_media_buy_delivery(step, context, options) {\n // Same account-resolution rule as get_media_buys — fixture fields preserved,\n // account overridden by harness so sandbox routing matches create_media_buy.\n // Envelope fields dropped from the local spread; outer enrichRequest re-applies\n // them with `runnerVars` so mustache substitutions expand.\n const fixtureFields = step.sample_request\n ? omitEnvelopeFields(\n injectContext({ ...(step.sample_request as Record<string, unknown>) }, context) as Record<string, unknown>\n )\n : {};\n const result: Record<string, unknown> = { ...fixtureFields };\n result.account = context.account ?? resolveAccount(options);\n if (context.media_buy_id != null && result.media_buy_ids === undefined) {\n result.media_buy_ids = [context.media_buy_id];\n }\n return result;\n },\n\n // provide_performance_feedback intentionally has no builder — storyboard\n // sample_request is authoritative because the spec's oneOf requires a\n // performance_index variant that only the storyboard author knows the\n // shape of. A synthesized payload here would emit non-spec fields\n // (feedback/satisfaction/notes) and get rejected with INVALID_REQUEST.\n\n // ── Catalogs & Events ─────────────────────────────────\n\n sync_catalogs(step, context, options, runnerVars) {\n // Prefer the fixture's sample_request — it's the authoritative request\n // shape for the storyboard step. The fallback's hardcoded feed_format\n // ('json') is NOT in the spec's 5-literal union and its `type` is\n // missing entirely, so any agent running the generated Zod schema\n // rejects the fallback with -32602 on both fields.\n return {\n account: context.account ?? resolveAccount(options),\n catalogs: [\n {\n catalog_id: `test-catalog-${generatedIdSuffix(step, runnerVars)}`,\n name: 'E2E Test Catalog',\n type: 'product',\n feed_url: 'https://test-assets.adcontextprotocol.org/feeds/test-catalog.json',\n feed_format: 'custom',\n },\n ],\n };\n },\n\n sync_event_sources(step, context, options, runnerVars) {\n return {\n account: context.account ?? resolveAccount(options),\n event_sources: [\n {\n event_source_id: `test-source-${generatedIdSuffix(step, runnerVars)}`,\n name: 'E2E Test Event Source',\n event_types: ['purchase', 'add_to_cart'],\n },\n ],\n };\n },\n\n log_event(step, context, _options, runnerVars) {\n // Storyboards routinely ship spec-conformant event payloads with\n // event_time, content_ids, and custom_data siblings that only the\n // author knows. Honor sample_request when present.\n return {\n event_source_id: context.event_source_id ?? 'test-source',\n events: [\n {\n event_id: `evt-${generatedIdSuffix(step, runnerVars)}`,\n event_type: 'purchase',\n event_time: new Date(runClockMs(runnerVars)).toISOString(),\n custom_data: { value: 49.99, currency: 'USD' },\n },\n ],\n };\n },\n\n report_usage(step, context, options, runnerVars) {\n // Prefer the fixture's sample_request — creative-ad-server and other\n // specialisms carry per-usage-entry fields (vendor_cost, currency,\n // pricing_option_id) that the hardcoded fallback here omits, causing\n // agents running the generated Zod schema to reject every step.\n const now = new Date(runClockMs(runnerVars));\n const monthAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);\n return {\n account: context.account ?? resolveAccount(options),\n reporting_period: {\n start: monthAgo.toISOString(),\n end: now.toISOString(),\n },\n usage: [\n {\n account: context.account ?? resolveAccount(options),\n creative_id: context.creative_id ?? 'unknown',\n impressions: 10000,\n vendor_cost: 500,\n currency: 'USD',\n },\n ],\n };\n },\n\n // ── Creative ───────────────────────────────────────────\n\n list_creative_formats(step, context) {\n // Mirror the pattern used by peer builders (build_creative, sync_creatives,\n // etc.): honor hand-authored sample_request so storyboards can exercise\n // format_ids filters and other query params. Without this, any step that\n // declares `format_ids: [\"...\"]` in sample_request hits the wire as an\n // empty request and the agent returns unfiltered results — failing\n // round-trip / substitution-observer assertions silently.\n return {};\n },\n\n build_creative(step, context, options) {\n // Hand-authored sample_request can exercise slot-specific briefs, target\n // format overrides, or multi-format requests — honor it when present.\n const format = selectFormat(context);\n const sample = step.sample_request;\n const hasPluralTargets =\n sample !== undefined &&\n typeof sample === 'object' &&\n !Array.isArray(sample) &&\n Array.isArray((sample as Record<string, unknown>).target_format_ids);\n return {\n ...(hasPluralTargets ? {} : { target_format_id: format?.format_id ?? context.format_id ?? UNKNOWN_FORMAT_ID }),\n brand: resolveBrand(options),\n message: 'Create a test advertisement for an e-commerce brand promoting a summer sale.',\n quality: 'draft',\n include_preview: true,\n };\n },\n\n preview_creative(_step, context, _options) {\n const format = selectFormat(context);\n return {\n request_type: 'single',\n creative_manifest: {\n format_id: format?.format_id ?? context.format_id ?? UNKNOWN_FORMAT_ID,\n name: 'E2E Test Creative',\n assets: {},\n },\n };\n },\n\n sync_creatives(step, context, options, runnerVars) {\n // Honor hand-authored sample_request for scenarios that require specific\n // creative shapes (delete/patch flows, format-scoped uploads, etc).\n const formats = (context.formats as Array<Record<string, unknown>> | undefined) ?? [];\n const idSuffix = generatedIdSuffix(step, runnerVars);\n\n // Send one creative per discovered format so downstream steps\n // (e.g., build_video_tag) can find creatives in every format.\n const creatives =\n formats.length > 0\n ? formats.map((fmt, i) => ({\n creative_id: `test-creative-${idSuffix}-${i}`,\n name: `E2E Test Creative ${i + 1}`,\n format_id: fmt.format_id ?? context.format_id ?? UNKNOWN_FORMAT_ID,\n assets: buildAssetsForFormat(fmt),\n }))\n : [\n {\n creative_id: `test-creative-${idSuffix}`,\n name: 'E2E Test Creative',\n format_id: context.format_id ?? UNKNOWN_FORMAT_ID,\n assets: {\n primary: {\n asset_type: 'image',\n url: 'https://test-assets.adcontextprotocol.org/acme-outdoor/hero-master.jpg',\n width: 1200,\n height: 628,\n format: 'image/jpeg',\n },\n },\n },\n ];\n\n return {\n account: context.account ?? resolveAccount(options),\n creatives,\n };\n },\n\n list_creatives(_step, context, options) {\n return {\n account: context.account ?? resolveAccount(options),\n };\n },\n\n // ── Signals ────────────────────────────────────────────\n\n get_signals(step, context, options) {\n if (options.brief) return { signal_spec: options.brief };\n if (step.sample_request?.signal_ids) {\n return injectContext({ signal_ids: step.sample_request.signal_ids }, context);\n }\n // `anyOf: [{required: [signal_spec]}, {required: [signal_ids]}]` — the\n // schema rejects an empty object. Default to a discovery-style\n // `signal_spec` so storyboards that omit `options.brief` still send a\n // conforming request. A real test should author sample_request or pass\n // options.brief; this is the minimally valid fallback.\n return { signal_spec: 'E2E fallback signal discovery' };\n },\n\n activate_signal(step, context, _options) {\n const signal = selectSignal(context);\n const destinations = step.sample_request?.destinations as Array<Record<string, unknown>> | undefined;\n const request: Record<string, unknown> = {\n signal_agent_segment_id: signal?.signal_agent_segment_id ?? context.signal_id ?? 'test-signal',\n pricing_option_id:\n (signal?.pricing_options as Array<Record<string, unknown>> | undefined)?.[0]?.pricing_option_id ??\n context.pricing_option_id,\n };\n request.destinations = destinations\n ? (injectContext({ destinations }, context) as Record<string, unknown>).destinations\n : [{ type: 'agent', agent_url: 'https://test.example/signals' }];\n return request;\n },\n\n // ── Capabilities ───────────────────────────────────────\n\n get_adcp_capabilities() {\n return {};\n },\n\n // ── Governance ─────────────────────────────────────────\n\n sync_governance(step, context, options) {\n // `governance_agents[]` items are `additionalProperties: false` per\n // AdCP 3.1.0-beta.3 (the single-agent-owns-full-lifecycle clarification\n // dropped per-agent `categories`). Only `url` + `authentication` are\n // permitted on the wire.\n return {\n accounts: [\n {\n account: context.account ?? resolveAccount(options),\n governance_agents: [\n {\n url: 'https://governance.test.example',\n authentication: {\n schemes: ['Bearer'],\n credentials: 'test-governance-token-padded-to-meet-min-length-32',\n },\n },\n ],\n },\n ],\n };\n },\n\n list_content_standards() {\n return {};\n },\n\n get_content_standards(_step, context, _options) {\n // `standards_id` is required by GetContentStandardsRequestSchema (no optional()).\n // Unlike `get_media_buys` / `get_media_buy_delivery` (where the id field is\n // optional), we cannot return {} here — that would fail the schema round-trip\n // test. `'unknown'` triggers a clean NOT_FOUND when context lacks a real id,\n // surfacing the authoring gap (\"wire context_outputs from create_content_standards\").\n return {\n standards_id: context.content_standards_id ?? 'unknown',\n };\n },\n\n calibrate_content(step, context, _options) {\n return {\n standards_id: context.content_standards_id ?? 'unknown',\n artifact: {\n property_rid: 'test-publisher.example',\n artifact_id: context.creative_id ?? 'unknown',\n assets: [],\n },\n };\n },\n\n sync_plans(step, context, options, runnerVars) {\n // Governance storyboards define scenario-specific plans in sample_request\n // (e.g., custom_policies for conditions, reallocation_threshold for denied).\n // Delegate to sample_request when present.\n const now = runClockMs(runnerVars);\n const startDate = new Date(now + 24 * 60 * 60 * 1000).toISOString();\n const endDate = new Date(now + 90 * 24 * 60 * 60 * 1000).toISOString();\n const idSuffix = generatedIdSuffix(step, runnerVars, now);\n return {\n plans: [\n {\n plan_id: `test-plan-${idSuffix}`,\n brand: resolveBrand(options),\n objectives: 'E2E test campaign — maximize reach across digital channels',\n budget: { total: options.budget ?? 10000, currency: 'USD', reallocation_unlimited: true },\n flight: { start: startDate, end: endDate },\n approved_sellers: null,\n },\n ],\n };\n },\n\n check_governance(step, context, options) {\n // `caller` names the CALLER-AGENT's URL, not the brand — governance agents\n // use it for agent identity (rate limits, audit, JWS issuer correlation).\n // The brand belongs inside `payload`, where governance rules about the\n // advertised entity are evaluated. Using the fallback harness-orchestrator\n // URL keeps the semantics honest when no sample_request is authored.\n return {\n plan_id: context.plan_id ?? 'unknown',\n caller: FALLBACK_CALLER_AGENT_URL,\n payload: {\n type: 'media_buy',\n account: context.account ?? resolveAccount(options),\n brand: resolveBrand(options),\n total_budget: options.budget ?? 10000,\n },\n };\n },\n\n get_account_financials(_step, context, options) {\n return {\n account: context.account ?? resolveAccount(options),\n };\n },\n\n create_content_standards(step, context, _options, runnerVars) {\n // `anyOf: [{required: [policies]}, {required: [registry_policy_ids]}]` —\n // one must be present. Emit a minimal inline bespoke policy rather than\n // pinning a registry id the agent may not carry; storyboards that want\n // real governance coverage author sample_request.\n //\n // Contamination safeguards for the rare case a fallback hits a shared\n // sandbox: `enforcement: \"should\"` keeps this from hardening into a deny\n // rule against real content, and the ephemeral `policy_id` (timestamped\n // + \"e2e-fallback-\" prefix) guarantees uniqueness per run so a stale\n // policy can't be matched by accident.\n return {\n scope: {\n languages_any: ['en'],\n description: 'E2E fallback content standards — replace via sample_request for real governance coverage',\n },\n policies: [\n {\n policy_id: `e2e-fallback-${generatedIdSuffix(step, runnerVars)}`,\n enforcement: 'should',\n policy: 'E2E fallback policy — storyboard author did not supply sample_request.',\n },\n ],\n };\n },\n\n update_content_standards(step, context, _options) {\n return {\n standards_id: context.content_standards_id ?? 'unknown',\n };\n },\n\n validate_content_delivery(step, context, _options) {\n return {\n standards_id: context.content_standards_id ?? 'unknown',\n records: [\n {\n record_id: 'delivery_001',\n artifact: {\n property_rid: 'test-publisher.example',\n artifact_id: context.creative_id ?? 'unknown',\n assets: [],\n },\n },\n ],\n };\n },\n\n acquire_rights(step, context, options) {\n return {\n rights_id: context.rights_id ?? 'unknown',\n pricing_option_id: 'standard',\n buyer: { domain: resolveBrand(options).domain },\n campaign: {\n description: 'E2E storyboard test campaign',\n uses: ['commercial'],\n },\n revocation_webhook: {\n url: 'https://test.example/webhooks/revocation',\n authentication: {\n schemes: ['Bearer'],\n credentials: 'test-revocation-webhook-secret-token',\n },\n },\n };\n },\n\n update_rights(step, context, _options) {\n return {\n rights_id: context.rights_id ?? 'unknown',\n };\n },\n\n creative_approval(step, context, _options) {\n return {\n rights_id: context.rights_id ?? 'unknown',\n creative_id: context.creative_id ?? 'unknown',\n creative_url:\n (context.creative_url as string | undefined) ??\n 'https://test-assets.adcontextprotocol.org/acme-outdoor/hero-master.jpg',\n };\n },\n\n // ── Sponsored Intelligence ─────────────────────────────\n\n si_get_offering(step, context, options) {\n return {\n offering_id: options.si_offering_id ?? 'e2e-test-offering',\n intent: options.si_context ?? 'E2E testing - checking SI offering availability',\n };\n },\n\n si_initiate_session(step, context, options, runnerVars) {\n // `intent` is required and represents the user's ask. Default to a\n // semantically plausible one so agents that dispatch on intent still\n // behave sensibly; storyboards override via sample_request when\n // testing intent-specific paths.\n return {\n offering_id: context.offering_id ?? options.si_offering_id ?? 'e2e-test-offering',\n offering_token: context.offering_token,\n identity: {\n consent_granted: false,\n anonymous_session_id: `e2e-anon-${generatedIdSuffix(step, runnerVars)}`,\n },\n intent: options.si_context ?? 'Browse available offerings',\n placement: 'e2e-test',\n supported_capabilities: {\n modalities: { conversational: true, rich_media: true },\n },\n };\n },\n\n si_send_message(step, context, _options) {\n return {\n session_id: context.session_id ?? 'unknown',\n message: 'Tell me more about this product.',\n };\n },\n\n si_terminate_session(step, context, _options) {\n return {\n session_id: context.session_id ?? 'unknown',\n reason: 'user_exit',\n };\n },\n\n // ── Test Controller ────────────────────────────────────\n\n comply_test_controller(step, context, options) {\n // The test controller requires account.sandbox: true to be set.\n const account: Record<string, unknown> = {\n ...(context.account ?? resolveAccount(options)),\n sandbox: true,\n };\n // Natural-key arm of AccountReference requires `operator` per the spec\n // schema. If `context.account` came from a sync_accounts response that\n // omitted `operator` (#1419), fall back to brand.domain so the synthetic\n // ref the controller receives is spec-valid.\n if (typeof account.account_id !== 'string' && typeof account.operator !== 'string') {\n const brand = account.brand as { domain?: unknown } | undefined;\n if (brand && typeof brand.domain === 'string') {\n account.operator = brand.domain;\n }\n }\n if (step.sample_request) {\n // Drop envelope fields; outer enrichRequest re-applies them with\n // `runnerVars` so mustache substitutions expand correctly.\n return {\n ...omitEnvelopeFields(injectContext({ ...step.sample_request }, context) as Record<string, unknown>),\n account,\n };\n }\n return {\n account,\n scenario: context.controller_scenario ?? 'list_scenarios',\n };\n },\n};\n\n/**\n * Enrich a storyboard step's request payload.\n *\n * Contract (issue #820):\n * - `sample_request`, when authored, is the authoritative base. The runner\n * injects context placeholders into it before calling here.\n * - An enricher (if registered for the task) produces fields that should\n * fill gaps the fixture left unset — discovery-derived identifiers,\n * envelope fields the author couldn't know at YAML-authoring time.\n * - Top-level merge: fixture wins on key conflicts. Fixture-aware\n * enrichers (see `FIXTURE_AWARE_ENRICHERS`) skip the generic merge and\n * return the final payload themselves.\n *\n * Returns `{}` when the task has no enricher and no `sample_request` — the\n * runner's load-time validator prevents this for mutating tasks, so the\n * empty return is reachable only for read tasks that have no fixture and\n * no registered enricher (rare).\n */\nexport function enrichRequest(\n step: StoryboardStep,\n context: StoryboardContext,\n options: TestOptions,\n runnerVars?: RunnerVariables\n): Record<string, unknown> {\n const enricher = REQUEST_ENRICHERS[step.task];\n const fixture =\n step.sample_request !== undefined\n ? (injectContext({ ...(step.sample_request as Record<string, unknown>) }, context, runnerVars) as Record<\n string,\n unknown\n >)\n : undefined;\n\n if (!enricher) return fixture ?? {};\n\n const enriched = enricher(step, context, options, runnerVars);\n\n // Fixture-aware enrichers already did the body merge internally and know\n // the array/nested shapes better than a generic top-level overlay can.\n // Envelope fields still flow through from sample_request.\n if (FIXTURE_AWARE_ENRICHERS.has(step.task)) {\n if (!fixture) return enriched;\n const out: Record<string, unknown> = { ...enriched };\n for (const field of ENVELOPE_FIELDS) {\n if (fixture[field] !== undefined && out[field] === undefined) out[field] = fixture[field];\n }\n return out;\n }\n\n // Generic fixture-authoritative merge: fixture keys overlay enricher keys.\n return fixture ? { ...enriched, ...fixture } : enriched;\n}\n\n/** True iff a request enricher is registered for this task. */\nexport function hasRequestEnricher(taskName: string): boolean {\n return taskName in REQUEST_ENRICHERS;\n}\n\n// ────────────────────────────────────────────────────────────\n// Legacy aliases — pre-#820 terminology. Kept for one release so\n// external consumers (repo greps found none, but public exports may\n// have downstream users) migrate at their own pace.\n// ────────────────────────────────────────────────────────────\n\n/** @deprecated Renamed to `enrichRequest`. Same behavior. */\nexport const buildRequest = enrichRequest;\n\n/** @deprecated Renamed to `hasRequestEnricher`. Same behavior. */\nexport const hasRequestBuilder = hasRequestEnricher;\n\n// ────────────────────────────────────────────────────────────\n// Selection helpers: pick the best item from discovered data\n// ────────────────────────────────────────────────────────────\n\nfunction selectProduct(context: StoryboardContext): Record<string, unknown> | undefined {\n const products = context.products as Array<Record<string, unknown>> | undefined;\n if (!products?.length) return undefined;\n\n // Prefer products with guaranteed delivery and pricing options\n const withPricing = products.filter(p => {\n const opts = p.pricing_options as unknown[] | undefined;\n return opts && opts.length > 0;\n });\n\n return withPricing[0] ?? products[0];\n}\n\nfunction selectPricingOption(product: Record<string, unknown> | undefined): Record<string, unknown> | undefined {\n if (!product) return undefined;\n const options = product.pricing_options as Array<Record<string, unknown>> | undefined;\n if (!options?.length) return undefined;\n\n // Prefer fixed pricing (cpm, flat) over auction\n const fixed = options.filter(o => o.pricing_model === 'cpm' || o.pricing_model === 'flat');\n return fixed[0] ?? options[0];\n}\n\nfunction selectFormat(context: StoryboardContext): Record<string, unknown> | undefined {\n const formats = context.formats as Array<Record<string, unknown>> | undefined;\n if (!formats?.length) return undefined;\n return formats[0];\n}\n\nfunction selectSignal(context: StoryboardContext): Record<string, unknown> | undefined {\n const signals = context.signals as Array<Record<string, unknown>> | undefined;\n if (!signals?.length) return undefined;\n return signals[0];\n}\n\n/**\n * Build placeholder assets appropriate for a format's type.\n * Uses the format name to guess whether it's video, native, or display.\n */\nfunction buildAssetsForFormat(format: Record<string, unknown>): Record<string, unknown> {\n const name = String(format.name ?? format.format_id ?? '').toLowerCase();\n const type = String(format.type ?? '').toLowerCase();\n\n if (type === 'video' || name.includes('video') || name.includes('vast')) {\n return {\n video: {\n asset_type: 'video',\n url: 'https://test-assets.adcontextprotocol.org/acme-outdoor/trail-pro-30s.mp4',\n width: 1920,\n height: 1080,\n duration_ms: 30000,\n container_format: 'video/mp4',\n },\n };\n }\n\n if (type === 'native' || name.includes('native')) {\n return {\n image: {\n asset_type: 'image',\n url: 'https://test-assets.adcontextprotocol.org/acme-outdoor/hero-master.jpg',\n width: 1200,\n height: 628,\n format: 'image/jpeg',\n },\n headline: {\n asset_type: 'text',\n content: 'Trail Pro 3000 — Built for the Summit',\n },\n };\n }\n\n // Default: display image\n return {\n primary: {\n asset_type: 'image',\n url: 'https://test-assets.adcontextprotocol.org/acme-outdoor/hero-master.jpg',\n width: 1200,\n height: 628,\n format: 'image/jpeg',\n },\n };\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../../src/lib/testing/storyboard/request-builder.ts"],"sourcesContent":["/**\n * Request enrichers for storyboard steps.\n *\n * Contract (see issue #820):\n * - `sample_request` (when authored) is the authoritative base payload.\n * The runner injects context placeholders into it and passes it through\n * to the agent under test.\n * - An enricher fills top-level fields the fixture didn't specify —\n * typically discovery-derived identifiers (`product_id`, `format_id`,\n * `account`, `media_buy_id`) or envelope fields that only the harness\n * knows.\n * - For conflicts at the top level, the fixture wins — storyboard authors'\n * intent is not silently overridden.\n *\n * A short list of tasks need to splice discovery-derived fields INTO\n * nested structures in the fixture (e.g. `create_media_buy` injects\n * `product_id` into `packages[0]`) and can't be expressed by a top-level\n * overlay. Those enrichers declare themselves fixture-aware via\n * `FIXTURE_AWARE_ENRICHERS` below and the runner uses their output as-is.\n *\n * `sample_request` from YAML, when a task has no enricher, is used directly\n * after context injection — preserves the \"no handler, fixture is the wire\n * payload\" pattern.\n */\n\nimport { createHash } from 'node:crypto';\nimport { resolveBrand, resolveAccount } from '../client';\nimport type { TestOptions } from '../types';\nimport type { StoryboardContext, StoryboardStep } from './types';\nimport { injectContext, type RunnerVariables } from './context';\n\ntype RequestEnricher = (\n step: StoryboardStep,\n context: StoryboardContext,\n options: TestOptions,\n runnerVars?: RunnerVariables\n) => Record<string, unknown>;\n\n/** Legacy alias kept for external consumers pinned to the old terminology. */\ntype RequestBuilder = RequestEnricher;\n\n/**\n * Placeholder `format_id` used when neither `list_creative_formats` discovery\n * nor accumulated `context.format_id` supplied one. Schema\n * (core/format-id.json) requires `agent_url` in URI form, so a bare\n * `\"unknown\"` string fails validation. `example.com` is reserved for\n * documentation per RFC 2606 — an obvious fixture that strict JSON-schema\n * validators accept and that downstream handlers resolve to a clean\n * format-not-found error rather than an unrelated crash. Frozen so a\n * builder that accidentally spread-mutates the shared constant hits a\n * TypeError instead of silently corrupting sibling calls.\n */\nconst UNKNOWN_FORMAT_ID = Object.freeze({ agent_url: 'https://unknown.example.com/', id: 'unknown' });\n\n/**\n * Placeholder `caller` URL for tasks whose schema names the CALLER-AGENT's\n * URL (not the brand or the seller). `check_governance.caller` is the canonical\n * case: governance agents bind this field to agent identity for rate limiting,\n * audit trails, and (with `signed-requests`) JWS issuer correlation — emitting\n * the brand domain here names the wrong entity and will confuse strict\n * governance agents. Storyboards that care about a specific caller identity\n * author sample_request; this is the fallback when neither fixture nor\n * harness-supplied agent URL is present.\n */\nconst FALLBACK_CALLER_AGENT_URL = 'https://e2e-orchestrator.adcontextprotocol.org/';\n\n/**\n * Tasks whose enricher must see `sample_request` to produce the final\n * payload — typically because it needs to splice discovery-derived fields\n * INTO nested structures the fixture owns (arrays, object trees). For\n * these, the runner uses the enricher's output verbatim and does not\n * layer the fixture on top; the enricher is responsible for fixture\n * precedence internally.\n */\nconst FIXTURE_AWARE_ENRICHERS = new Set<string>([\n 'create_media_buy', // merges discovery-derived product_id / pricing_option_id INTO fixture packages[0]\n 'comply_test_controller', // forces account.sandbox: true regardless of fixture\n 'update_media_buy', // resolves account via resolveAccount(options) so sandbox routing matches create_media_buy\n 'get_media_buys', // resolves account via resolveAccount(options) so sandbox routing matches create_media_buy\n 'get_media_buy_delivery', // resolves account via resolveAccount(options) so sandbox routing matches create_media_buy\n]);\n\n/**\n * Placeholder identifiers that the upstream universal compliance\n * storyboards ship inside `create_media_buy.packages[0]` expecting the\n * runner to substitute the seller's discovered identifiers. The fixtures\n * live in `adcontextprotocol/adcp` and can't be rewritten from the SDK,\n * so the enricher treats these literals as \"defer to discovery\" while\n * real seller ids (e.g. `cpm_guaranteed`) win over discovery per the\n * fixture-authoritative contract. If a future storyboard wants\n * placeholder-then-discovery semantics for another value, author it as\n * a `$context.<key>` substitution rather than a magic literal so the\n * intent is explicit at the fixture level.\n */\nconst PRODUCT_ID_SENTINELS = new Set(['test-product']);\nconst PRICING_OPTION_ID_SENTINELS = new Set(['test-pricing']);\nconst DAY_MS = 24 * 60 * 60 * 1000;\nconst DEFAULT_MEDIA_BUY_WINDOW_MS = 7 * DAY_MS;\n\nfunction asNonSentinel(value: unknown, sentinels: Set<string>): string | undefined {\n if (typeof value !== 'string') return undefined;\n return sentinels.has(value) ? undefined : value;\n}\n\nfunction parseTime(value: string | undefined): number | undefined {\n if (value === undefined) return undefined;\n const parsed = Date.parse(value);\n return Number.isFinite(parsed) ? parsed : undefined;\n}\n\nfunction resolveMediaBuyWindow(\n sampleStart: string | undefined,\n sampleEnd: string | undefined,\n nowMs?: number\n): {\n startTime: string;\n endTime: string;\n} {\n const now = nowMs ?? Date.now();\n const defaultStart = new Date(now + DAY_MS).toISOString();\n const defaultEnd = new Date(now + 8 * DAY_MS).toISOString();\n const sampleStartMs = parseTime(sampleStart);\n const sampleEndMs = parseTime(sampleEnd);\n const startIsAsap = sampleStart === 'asap';\n\n const startTime = startIsAsap\n ? 'asap'\n : sampleStart && sampleStartMs !== undefined && sampleStartMs >= now\n ? sampleStart\n : defaultStart;\n let endTime = sampleEnd && sampleEndMs !== undefined && sampleEndMs >= now ? sampleEnd : defaultEnd;\n\n const startTimeMs = startIsAsap ? now : Date.parse(startTime);\n if (Date.parse(endTime) <= startTimeMs) {\n const sampleDurationMs =\n sampleStartMs !== undefined && sampleEndMs !== undefined && sampleEndMs > sampleStartMs\n ? sampleEndMs - sampleStartMs\n : DEFAULT_MEDIA_BUY_WINDOW_MS;\n endTime = new Date(startTimeMs + sampleDurationMs).toISOString();\n }\n\n return { startTime, endTime };\n}\n\nfunction runClockMs(runnerVars: RunnerVariables | undefined): number {\n return runnerVars?.runStartMs ?? Date.now();\n}\n\nfunction generatedIdSuffix(step: StoryboardStep, runnerVars: RunnerVariables | undefined, nowMs?: number): string {\n const timestamp = nowMs ?? runClockMs(runnerVars);\n if (runnerVars?.runStartMs === undefined) return String(timestamp);\n const stepHash = createHash('sha256').update(step.id).digest('hex').slice(0, 12);\n return `${timestamp}-${stepHash}`;\n}\n\n/**\n * Envelope fields that live on every AdCP request and are owned by the\n * storyboard author — `context.correlation_id`, runner-supplied\n * `idempotency_key` aliases, webhook pointers, per-request extensions.\n * Fixture-aware enrichers must NOT spread these from their internal\n * `injectContext(sample_request, context)` call: that internal call\n * doesn't receive `runnerVars`, so mustache tokens (`{{runner.webhook_url:<step_id>}}`)\n * would ship to the wire literally. The outer `enrichRequest` overlays\n * envelope fields from a `runnerVars`-aware fixture re-injection.\n */\nconst ENVELOPE_FIELDS = ['context', 'ext', 'push_notification_config', 'idempotency_key'] as const;\n\nfunction omitEnvelopeFields(obj: Record<string, unknown>): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(obj)) {\n if (!(ENVELOPE_FIELDS as readonly string[]).includes(k)) out[k] = v;\n }\n return out;\n}\n\nconst REQUEST_ENRICHERS: Record<string, RequestEnricher> = {\n // ── Account & Audience ─────────────────────────────────\n\n sync_accounts(_step, _context, options) {\n return {\n accounts: [\n {\n brand: resolveBrand(options),\n operator: resolveBrand(options).domain,\n billing: 'operator',\n payment_terms: 'net_30',\n },\n ],\n };\n },\n\n list_accounts(_step, _context, options) {\n return {\n brand: resolveBrand(options),\n };\n },\n\n sync_audiences(step, context, options, runnerVars) {\n // Honor hand-authored sample_request so storyboards can register a\n // specific audience_id that downstream steps reference. Without this,\n // add-shaped sample_request blocks (authored with audience_id + add[])\n // fell through to the generated fallback id, and a later delete_audience\n // or context-substitution step would hit AUDIENCE_NOT_FOUND because the\n // sync had registered a different id. Matches the pattern used by\n // sync_event_sources, sync_catalogs, and sync_creatives.\n if (step.sample_request) {\n return injectContext({ ...step.sample_request, account: context.account ?? resolveAccount(options) }, context);\n }\n return {\n account: context.account ?? resolveAccount(options),\n audiences: [\n {\n audience_id: context.audience_id ?? `test-audience-${generatedIdSuffix(step, runnerVars)}`,\n name: 'E2E Test Audience',\n add: [\n {\n external_id: 'user-001',\n hashed_email: 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855',\n },\n ],\n },\n ],\n };\n },\n\n // ── Brand & Rights ───────────────────────────────────────\n\n get_brand_identity(step, context, options) {\n const brand = resolveBrand(options);\n return {\n brand_id: context.brand_id ?? (step.sample_request?.brand_id as string) ?? brand.brand_id ?? brand.domain,\n };\n },\n\n get_rights(step, context, options) {\n // Honor hand-authored sample_request so storyboards can specify\n // scenario-specific query text, uses, countries, or buyer_brand.\n // Peer builders (sync_plans, check_governance, list_creative_formats,\n // create_content_standards, etc.) follow the same pattern.\n //\n // Without this, any get_rights step hits the wire with the generic\n // fallback and a brand_id derived from the caller's domain — which\n // rights-holder rosters reject as unknown, so rights[0] is undefined,\n // $context.rights_id doesn't resolve, and downstream acquire_rights\n // steps fail with rights_not_found instead of the error the\n // storyboard is actually asserting (e.g., GOVERNANCE_DENIED).\n const brand = resolveBrand(options);\n return {\n query: 'available rights for advertising',\n uses: ['ai_generated_image'],\n brand_id: context.brand_id ?? brand.brand_id ?? brand.domain,\n };\n },\n\n // ── Product Discovery ──────────────────────────────────\n\n get_products(step, context, options) {\n // If the step is a \"refine\" step, build a refine request\n if (step.sample_request?.buying_mode === 'refine' && context.products) {\n return {\n buying_mode: 'refine',\n refine: [\n {\n scope: 'request',\n ask: 'Only guaranteed packages with premium placement.',\n },\n ],\n brand: resolveBrand(options),\n account: context.account ?? resolveAccount(options),\n };\n }\n\n return {\n buying_mode: 'brief',\n brief: options.brief || 'Show me all available advertising products across all channels',\n brand: resolveBrand(options),\n };\n },\n\n // ── Media Buy ──────────────────────────────────────────\n\n create_media_buy(step, context, options, runnerVars) {\n const product = selectProduct(context);\n const pricingOption = selectPricingOption(product);\n\n // Respect sample_request dates when they're future-dated — needed for\n // storyboards that test replay semantics where initial + replay must\n // produce byte-for-byte identical canonical payloads. Two calls\n // generated 5ms apart with `Date.now()` would hash differently,\n // triggering IDEMPOTENCY_CONFLICT on replay. Stale sample dates\n // (authored before the run date) fall back to the run-start-relative\n // default. Resolve the pair together so a stale start and same-day\n // future end cannot produce start_time > end_time.\n const sampleStart =\n typeof step.sample_request?.start_time === 'string' ? step.sample_request.start_time : undefined;\n const sampleEnd = typeof step.sample_request?.end_time === 'string' ? step.sample_request.end_time : undefined;\n const { startTime, endTime } = resolveMediaBuyWindow(sampleStart, sampleEnd, runnerVars?.runStartMs);\n\n // Merge hand-authored package fields from sample_request (targeting_overlay,\n // measurement_terms, creative_assignments, performance_standards, etc.) so\n // scenario-specific behaviors are exercised. The first package's\n // identifiers (product_id, pricing_option_id) follow a two-tier rule:\n //\n // - Real seller ids in the fixture win over discovery — storyboards\n // that name `cpm_guaranteed` (or any seller-specific identifier)\n // are asserting that specific id reaches the wire. Discovery must\n // not override.\n // - Well-known sentinel placeholders (`test-product`, `test-pricing`)\n // from the upstream universal compliance storyboards defer to\n // discovery. Those storyboards ship package fixtures with\n // `product_id: \"test-product\"` expecting the runner to substitute\n // the seller's discovered product — they'd fail against every\n // seller that doesn't literally ship a \"test-product\" catalog\n // entry if the fixture value survived to the wire.\n //\n // The sentinel list is conservative. If a future storyboard wants\n // placeholder-then-discovery semantics for another field, author it\n // as `$context.<key>` substitution instead of a magic literal.\n const samplePackages = (step.sample_request?.packages as Array<Record<string, unknown>> | undefined) ?? [];\n const baseSample = samplePackages[0]\n ? (injectContext({ ...samplePackages[0] }, context) as Record<string, unknown>)\n : {};\n\n const fixtureProductId = asNonSentinel(baseSample.product_id, PRODUCT_ID_SENTINELS);\n const fixturePricingOptionId = asNonSentinel(baseSample.pricing_option_id, PRICING_OPTION_ID_SENTINELS);\n\n const firstPkg: Record<string, unknown> = {\n ...baseSample,\n product_id: fixtureProductId ?? product?.product_id ?? context.product_id ?? 'test-product',\n budget:\n (typeof baseSample.budget === 'number' ? baseSample.budget : undefined) ??\n options.budget ??\n Math.max(1000, (pricingOption?.min_spend_per_package as number) ?? 1000),\n pricing_option_id:\n fixturePricingOptionId ?? pricingOption?.pricing_option_id ?? context.pricing_option_id ?? 'default',\n };\n\n // Synthesize a bid_price for auction/cpm pricing only when the fixture\n // didn't author one. Storyboards that test auction flows (e.g.\n // sales-non-guaranteed) author explicit bid_prices the seller validates\n // against floor_price; discovery-synthesized values would silently\n // override intentional bid-floor-boundary tests.\n if (\n baseSample.bid_price === undefined &&\n (pricingOption?.pricing_model === 'auction' || pricingOption?.pricing_model === 'cpm')\n ) {\n const floor = Number(pricingOption?.floor_price) || 5;\n firstPkg.bid_price = Math.round(floor * 1.5 * 100) / 100;\n }\n\n const additionalPkgs = samplePackages\n .slice(1)\n .map(p => injectContext({ ...p }, context) as Record<string, unknown>);\n\n // Proposal-mode: when context carries a proposal_id (captured from\n // `proposals[0]` in a prior brief/refine/finalize step), forward it\n // and omit `packages`. The schema disallows synthesising packages\n // alongside `proposal_id` — `dependencies.proposal_id` requires\n // `total_budget` and the seller derives packages from the committed\n // allocation.\n //\n // Spread the fixture (after $context injection) so proposal-mode-\n // required fields like `total_budget` flow through. Prefer the\n // fixture's account/brand when supplied — proposal-mode storyboards\n // author a non-default brand (e.g. `acmeoutdoor.example`) that the\n // adapter resolves end-to-end through brief/refine/finalize, and\n // the harness-default `test.example` would fail account resolution\n // at the accept step (adcp-client#1600). Dates and proposal_id\n // are still normalised by the enricher.\n const fixture =\n step.sample_request !== undefined\n ? (injectContext({ ...(step.sample_request as Record<string, unknown>) }, context) as Record<string, unknown>)\n : {};\n // Drop envelope fields from the local spread — the outer enrichRequest\n // re-injects them with `runnerVars` so `{{runner.webhook_url:<step_id>}}`\n // tokens inside push_notification_config expand. Spreading them here\n // would carry through unexpanded mustache strings.\n const fixtureBody = omitEnvelopeFields(fixture);\n\n // Proposal-mode applies only when the storyboard's fixture explicitly\n // names a `proposal_id`. `context.proposal_id` is auto-captured from\n // any prior `get_products` response that returned `proposals[0]` (see\n // context.ts), which is essentially every brief flow — using it as a\n // fallback would override a fixture's explicit `packages` direction\n // and force every sales storyboard through proposal-mode validation,\n // including ones that intentionally exercise package-mode (sales-\n // guaranteed, schema_validation, media_buy_seller/*). Fixture is the\n // storyboard author's intent; respect it.\n const fixtureHasPackages = Array.isArray(fixture.packages) && (fixture.packages as unknown[]).length > 0;\n const proposalId: unknown =\n fixture.proposal_id !== undefined ? fixture.proposal_id : fixtureHasPackages ? undefined : context.proposal_id;\n if (typeof proposalId === 'string') {\n const { packages: _droppedPackages, ...fixtureWithoutPackages } = fixtureBody;\n return {\n ...fixtureWithoutPackages,\n account: fixtureWithoutPackages.account ?? context.account ?? resolveAccount(options),\n brand: fixtureWithoutPackages.brand ?? resolveBrand(options),\n start_time: startTime,\n end_time: endTime,\n proposal_id: proposalId,\n };\n }\n\n // Spread the fixture (after $context injection) so any sample_request\n // fields the enricher doesn't explicitly normalise (total_budget,\n // buyer_ref, currency, scenario-specific extensions) reach the wire.\n // The enricher then layers its own substitutions on top: packages is\n // replaced (the enricher merged samplePackages internally with\n // discovery-derived identifiers above), account/brand/start_time/\n // end_time are normalised. Per issue #1604, build-from-scratch dropped\n // every fixture field outside this enumerated set — fixture-aware\n // enrichers must spread sample_request first, then override. Envelope\n // fields are omitted here and re-applied by the outer enrichRequest\n // with `runnerVars` so mustache tokens expand correctly.\n return {\n ...fixtureBody,\n account: context.account ?? resolveAccount(options),\n brand: resolveBrand(options),\n start_time: startTime,\n end_time: endTime,\n packages: [firstPkg, ...additionalPkgs],\n };\n },\n\n update_media_buy(step, context, options) {\n // Fixture-aware: spread the storyboard's sample_request first so hand-authored\n // intent (targeting_overlay swaps, creative assignments, pause/resume/cancel)\n // is preserved verbatim, then force `account` to the harness-resolved value so\n // sandbox routing matches create_media_buy's namespace on every round-trip\n // (otherwise the fixture's account silently routes update writes to a different\n // partition than the create wrote to, and a subsequent get_media_buys reading\n // from the create-time partition surfaces stale data — see adcp-client#1505).\n // Envelope fields are dropped from the local spread; the outer enrichRequest\n // re-injects them with `runnerVars` so mustache substitutions expand.\n const fixtureFields = step.sample_request\n ? omitEnvelopeFields(\n injectContext({ ...(step.sample_request as Record<string, unknown>) }, context) as Record<string, unknown>\n )\n : {};\n const request: Record<string, unknown> = { ...fixtureFields };\n request.account = context.account ?? resolveAccount(options);\n if (request.media_buy_id === undefined) {\n request.media_buy_id = context.media_buy_id ?? 'unknown';\n }\n\n // No sample_request: fall back to step-id keyword inference so legacy\n // storyboards without an authored fixture still exercise sensible flows.\n if (!step.sample_request) {\n if (step.id.includes('pause')) {\n request.paused = true;\n } else if (step.id.includes('resume')) {\n request.paused = false;\n } else if (step.id.includes('cancel')) {\n request.canceled = true;\n } else {\n request.packages = [\n {\n package_id: (context.package_id as string | undefined) ?? 'unknown',\n budget: 2000,\n },\n ];\n }\n }\n\n return request;\n },\n\n get_media_buys(step, context, options) {\n // Spread fixture fields so storyboards can author filters, status, pagination, etc.\n // account is always overridden by the harness-resolved value so sandbox routing\n // matches create_media_buy's namespace on every round-trip. Envelope fields are\n // dropped from the local spread so the outer enrichRequest re-injects them with\n // `runnerVars` (mustache substitutions expand against the runner's webhook base).\n const fixtureFields = step.sample_request\n ? omitEnvelopeFields(\n injectContext({ ...(step.sample_request as Record<string, unknown>) }, context) as Record<string, unknown>\n )\n : {};\n const result: Record<string, unknown> = { ...fixtureFields };\n result.account = context.account ?? resolveAccount(options);\n if (context.media_buy_id != null && result.media_buy_ids === undefined) {\n result.media_buy_ids = [context.media_buy_id];\n }\n return result;\n },\n\n get_media_buy_delivery(step, context, options) {\n // Same account-resolution rule as get_media_buys — fixture fields preserved,\n // account overridden by harness so sandbox routing matches create_media_buy.\n // Envelope fields dropped from the local spread; outer enrichRequest re-applies\n // them with `runnerVars` so mustache substitutions expand.\n const fixtureFields = step.sample_request\n ? omitEnvelopeFields(\n injectContext({ ...(step.sample_request as Record<string, unknown>) }, context) as Record<string, unknown>\n )\n : {};\n const result: Record<string, unknown> = { ...fixtureFields };\n result.account = context.account ?? resolveAccount(options);\n if (context.media_buy_id != null && result.media_buy_ids === undefined) {\n result.media_buy_ids = [context.media_buy_id];\n }\n return result;\n },\n\n // provide_performance_feedback intentionally has no builder — storyboard\n // sample_request is authoritative because the spec's oneOf requires a\n // performance_index variant that only the storyboard author knows the\n // shape of. A synthesized payload here would emit non-spec fields\n // (feedback/satisfaction/notes) and get rejected with INVALID_REQUEST.\n\n // ── Catalogs & Events ─────────────────────────────────\n\n sync_catalogs(step, context, options, runnerVars) {\n // Prefer the fixture's sample_request — it's the authoritative request\n // shape for the storyboard step. The fallback's hardcoded feed_format\n // ('json') is NOT in the spec's 5-literal union and its `type` is\n // missing entirely, so any agent running the generated Zod schema\n // rejects the fallback with -32602 on both fields.\n return {\n account: context.account ?? resolveAccount(options),\n catalogs: [\n {\n catalog_id: `test-catalog-${generatedIdSuffix(step, runnerVars)}`,\n name: 'E2E Test Catalog',\n type: 'product',\n feed_url: 'https://test-assets.adcontextprotocol.org/feeds/test-catalog.json',\n feed_format: 'custom',\n },\n ],\n };\n },\n\n sync_event_sources(step, context, options, runnerVars) {\n return {\n account: context.account ?? resolveAccount(options),\n event_sources: [\n {\n event_source_id: `test-source-${generatedIdSuffix(step, runnerVars)}`,\n name: 'E2E Test Event Source',\n event_types: ['purchase', 'add_to_cart'],\n },\n ],\n };\n },\n\n log_event(step, context, _options, runnerVars) {\n // Storyboards routinely ship spec-conformant event payloads with\n // event_time, content_ids, and custom_data siblings that only the\n // author knows. Honor sample_request when present.\n return {\n event_source_id: context.event_source_id ?? 'test-source',\n events: [\n {\n event_id: `evt-${generatedIdSuffix(step, runnerVars)}`,\n event_type: 'purchase',\n event_time: new Date(runClockMs(runnerVars)).toISOString(),\n custom_data: { value: 49.99, currency: 'USD' },\n },\n ],\n };\n },\n\n report_usage(step, context, options, runnerVars) {\n // Prefer the fixture's sample_request — creative-ad-server and other\n // specialisms carry per-usage-entry fields (vendor_cost, currency,\n // pricing_option_id) that the hardcoded fallback here omits, causing\n // agents running the generated Zod schema to reject every step.\n const now = new Date(runClockMs(runnerVars));\n const monthAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);\n return {\n account: context.account ?? resolveAccount(options),\n reporting_period: {\n start: monthAgo.toISOString(),\n end: now.toISOString(),\n },\n usage: [\n {\n account: context.account ?? resolveAccount(options),\n creative_id: context.creative_id ?? 'unknown',\n impressions: 10000,\n vendor_cost: 500,\n currency: 'USD',\n },\n ],\n };\n },\n\n // ── Creative ───────────────────────────────────────────\n\n list_creative_formats(step, context) {\n // Mirror the pattern used by peer builders (build_creative, sync_creatives,\n // etc.): honor hand-authored sample_request so storyboards can exercise\n // format_ids filters and other query params. Without this, any step that\n // declares `format_ids: [\"...\"]` in sample_request hits the wire as an\n // empty request and the agent returns unfiltered results — failing\n // round-trip / substitution-observer assertions silently.\n return {};\n },\n\n build_creative(step, context, options) {\n // Hand-authored sample_request can exercise slot-specific briefs, target\n // format overrides, or multi-format requests — honor it when present.\n const format = selectFormat(context);\n const sample = step.sample_request;\n const hasPluralTargets =\n sample !== undefined &&\n typeof sample === 'object' &&\n !Array.isArray(sample) &&\n Array.isArray((sample as Record<string, unknown>).target_format_ids);\n return {\n ...(hasPluralTargets ? {} : { target_format_id: format?.format_id ?? context.format_id ?? UNKNOWN_FORMAT_ID }),\n brand: resolveBrand(options),\n message: 'Create a test advertisement for an e-commerce brand promoting a summer sale.',\n quality: 'draft',\n include_preview: true,\n };\n },\n\n preview_creative(_step, context, _options) {\n const format = selectFormat(context);\n return {\n request_type: 'single',\n creative_manifest: {\n format_id: format?.format_id ?? context.format_id ?? UNKNOWN_FORMAT_ID,\n name: 'E2E Test Creative',\n assets: {},\n },\n };\n },\n\n sync_creatives(step, context, options, runnerVars) {\n // Honor hand-authored sample_request for scenarios that require specific\n // creative shapes (delete/patch flows, format-scoped uploads, etc).\n const formats = (context.formats as Array<Record<string, unknown>> | undefined) ?? [];\n const idSuffix = generatedIdSuffix(step, runnerVars);\n\n // Send one creative per discovered format so downstream steps\n // (e.g., build_video_tag) can find creatives in every format.\n const creatives =\n formats.length > 0\n ? formats.map((fmt, i) => ({\n creative_id: `test-creative-${idSuffix}-${i}`,\n name: `E2E Test Creative ${i + 1}`,\n format_id: fmt.format_id ?? context.format_id ?? UNKNOWN_FORMAT_ID,\n assets: buildAssetsForFormat(fmt),\n }))\n : [\n {\n creative_id: `test-creative-${idSuffix}`,\n name: 'E2E Test Creative',\n format_id: context.format_id ?? UNKNOWN_FORMAT_ID,\n assets: {\n primary: {\n asset_type: 'image',\n url: 'https://test-assets.adcontextprotocol.org/acme-outdoor/hero-master.jpg',\n width: 1200,\n height: 628,\n format: 'image/jpeg',\n },\n },\n },\n ];\n\n return {\n account: context.account ?? resolveAccount(options),\n creatives,\n };\n },\n\n list_creatives(_step, context, options) {\n return {\n account: context.account ?? resolveAccount(options),\n };\n },\n\n // ── Signals ────────────────────────────────────────────\n\n get_signals(step, context, options) {\n if (options.brief) return { signal_spec: options.brief };\n if (step.sample_request?.signal_ids) {\n return injectContext({ signal_ids: step.sample_request.signal_ids }, context);\n }\n // `anyOf: [{required: [signal_spec]}, {required: [signal_ids]}]` — the\n // schema rejects an empty object. Default to a discovery-style\n // `signal_spec` so storyboards that omit `options.brief` still send a\n // conforming request. A real test should author sample_request or pass\n // options.brief; this is the minimally valid fallback.\n return { signal_spec: 'E2E fallback signal discovery' };\n },\n\n activate_signal(step, context, _options) {\n const signal = selectSignal(context);\n const destinations = step.sample_request?.destinations as Array<Record<string, unknown>> | undefined;\n const request: Record<string, unknown> = {\n signal_agent_segment_id: signal?.signal_agent_segment_id ?? context.signal_id ?? 'test-signal',\n pricing_option_id:\n (signal?.pricing_options as Array<Record<string, unknown>> | undefined)?.[0]?.pricing_option_id ??\n context.pricing_option_id,\n };\n request.destinations = destinations\n ? (injectContext({ destinations }, context) as Record<string, unknown>).destinations\n : [{ type: 'agent', agent_url: 'https://test.example/signals' }];\n return request;\n },\n\n // ── Capabilities ───────────────────────────────────────\n\n get_adcp_capabilities() {\n return {};\n },\n\n // ── Governance ─────────────────────────────────────────\n\n sync_governance(step, context, options) {\n // `governance_agents[]` items are `additionalProperties: false` per\n // AdCP 3.1.0-beta.3 (the single-agent-owns-full-lifecycle clarification\n // dropped per-agent `categories`). Only `url` + `authentication` are\n // permitted on the wire.\n return {\n accounts: [\n {\n account: context.account ?? resolveAccount(options),\n governance_agents: [\n {\n url: 'https://governance.test.example',\n authentication: {\n schemes: ['Bearer'],\n credentials: 'test-governance-token-padded-to-meet-min-length-32',\n },\n },\n ],\n },\n ],\n };\n },\n\n list_content_standards() {\n return {};\n },\n\n get_content_standards(_step, context, _options) {\n // `standards_id` is required by GetContentStandardsRequestSchema (no optional()).\n // Unlike `get_media_buys` / `get_media_buy_delivery` (where the id field is\n // optional), we cannot return {} here — that would fail the schema round-trip\n // test. `'unknown'` triggers a clean NOT_FOUND when context lacks a real id,\n // surfacing the authoring gap (\"wire context_outputs from create_content_standards\").\n return {\n standards_id: context.content_standards_id ?? 'unknown',\n };\n },\n\n calibrate_content(step, context, _options) {\n return {\n standards_id: context.content_standards_id ?? 'unknown',\n artifact: {\n property_rid: 'test-publisher.example',\n artifact_id: context.creative_id ?? 'unknown',\n assets: [],\n },\n };\n },\n\n sync_plans(step, context, options, runnerVars) {\n // Governance storyboards define scenario-specific plans in sample_request\n // (e.g., custom_policies for conditions, reallocation_threshold for denied).\n // Delegate to sample_request when present.\n const now = runClockMs(runnerVars);\n const startDate = new Date(now + 24 * 60 * 60 * 1000).toISOString();\n const endDate = new Date(now + 90 * 24 * 60 * 60 * 1000).toISOString();\n const idSuffix = generatedIdSuffix(step, runnerVars, now);\n return {\n plans: [\n {\n plan_id: `test-plan-${idSuffix}`,\n brand: resolveBrand(options),\n objectives: 'E2E test campaign — maximize reach across digital channels',\n budget: { total: options.budget ?? 10000, currency: 'USD', reallocation_unlimited: true },\n flight: { start: startDate, end: endDate },\n approved_sellers: null,\n },\n ],\n };\n },\n\n check_governance(step, context, options) {\n // `caller` names the CALLER-AGENT's URL, not the brand — governance agents\n // use it for agent identity (rate limits, audit, JWS issuer correlation).\n // The brand belongs inside `payload`, where governance rules about the\n // advertised entity are evaluated. Using the fallback harness-orchestrator\n // URL keeps the semantics honest when no sample_request is authored.\n return {\n plan_id: context.plan_id ?? 'unknown',\n caller: FALLBACK_CALLER_AGENT_URL,\n payload: {\n type: 'media_buy',\n account: context.account ?? resolveAccount(options),\n brand: resolveBrand(options),\n total_budget: options.budget ?? 10000,\n },\n };\n },\n\n get_account_financials(_step, context, options) {\n return {\n account: context.account ?? resolveAccount(options),\n };\n },\n\n create_content_standards(step, context, _options, runnerVars) {\n // `anyOf: [{required: [policies]}, {required: [registry_policy_ids]}]` —\n // one must be present. Emit a minimal inline bespoke policy rather than\n // pinning a registry id the agent may not carry; storyboards that want\n // real governance coverage author sample_request.\n //\n // Contamination safeguards for the rare case a fallback hits a shared\n // sandbox: `enforcement: \"should\"` keeps this from hardening into a deny\n // rule against real content, and the ephemeral `policy_id` (timestamped\n // + \"e2e-fallback-\" prefix) guarantees uniqueness per run so a stale\n // policy can't be matched by accident.\n return {\n scope: {\n languages_any: ['en'],\n description: 'E2E fallback content standards — replace via sample_request for real governance coverage',\n },\n policies: [\n {\n policy_id: `e2e-fallback-${generatedIdSuffix(step, runnerVars)}`,\n enforcement: 'should',\n policy: 'E2E fallback policy — storyboard author did not supply sample_request.',\n },\n ],\n };\n },\n\n update_content_standards(step, context, _options) {\n return {\n standards_id: context.content_standards_id ?? 'unknown',\n };\n },\n\n validate_content_delivery(step, context, _options) {\n return {\n standards_id: context.content_standards_id ?? 'unknown',\n records: [\n {\n record_id: 'delivery_001',\n artifact: {\n property_rid: 'test-publisher.example',\n artifact_id: context.creative_id ?? 'unknown',\n assets: [],\n },\n },\n ],\n };\n },\n\n acquire_rights(step, context, options) {\n return {\n rights_id: context.rights_id ?? 'unknown',\n pricing_option_id: 'standard',\n buyer: { domain: resolveBrand(options).domain },\n campaign: {\n description: 'E2E storyboard test campaign',\n uses: ['commercial'],\n },\n revocation_webhook: {\n url: 'https://test.example/webhooks/revocation',\n authentication: {\n schemes: ['Bearer'],\n credentials: 'test-revocation-webhook-secret-token',\n },\n },\n };\n },\n\n update_rights(step, context, _options) {\n return {\n rights_id: context.rights_id ?? 'unknown',\n };\n },\n\n creative_approval(step, context, _options) {\n return {\n rights_id: context.rights_id ?? 'unknown',\n creative_id: context.creative_id ?? 'unknown',\n creative_url:\n (context.creative_url as string | undefined) ??\n 'https://test-assets.adcontextprotocol.org/acme-outdoor/hero-master.jpg',\n };\n },\n\n // ── Sponsored Intelligence ─────────────────────────────\n\n si_get_offering(step, context, options) {\n return {\n offering_id: options.si_offering_id ?? 'e2e-test-offering',\n intent: options.si_context ?? 'E2E testing - checking SI offering availability',\n };\n },\n\n si_initiate_session(step, context, options, runnerVars) {\n // `intent` is required and represents the user's ask. Default to a\n // semantically plausible one so agents that dispatch on intent still\n // behave sensibly; storyboards override via sample_request when\n // testing intent-specific paths.\n return {\n offering_id: context.offering_id ?? options.si_offering_id ?? 'e2e-test-offering',\n offering_token: context.offering_token,\n identity: {\n consent_granted: false,\n anonymous_session_id: `e2e-anon-${generatedIdSuffix(step, runnerVars)}`,\n },\n intent: options.si_context ?? 'Browse available offerings',\n placement: 'e2e-test',\n supported_capabilities: {\n modalities: { conversational: true, rich_media: true },\n },\n };\n },\n\n si_send_message(step, context, _options) {\n return {\n session_id: context.session_id ?? 'unknown',\n message: 'Tell me more about this product.',\n };\n },\n\n si_terminate_session(step, context, _options) {\n return {\n session_id: context.session_id ?? 'unknown',\n reason: 'user_exit',\n };\n },\n\n // ── Test Controller ────────────────────────────────────\n\n comply_test_controller(step, context, options) {\n // The test controller requires account.sandbox: true to be set.\n const account: Record<string, unknown> = {\n ...(context.account ?? resolveAccount(options)),\n sandbox: true,\n };\n // Natural-key arm of AccountReference requires `operator` per the spec\n // schema. If `context.account` came from a sync_accounts response that\n // omitted `operator` (#1419), fall back to brand.domain so the synthetic\n // ref the controller receives is spec-valid.\n if (typeof account.account_id !== 'string' && typeof account.operator !== 'string') {\n const brand = account.brand as { domain?: unknown } | undefined;\n if (brand && typeof brand.domain === 'string') {\n account.operator = brand.domain;\n }\n }\n if (step.sample_request) {\n // Drop envelope fields; outer enrichRequest re-applies them with\n // `runnerVars` so mustache substitutions expand correctly.\n return {\n ...omitEnvelopeFields(injectContext({ ...step.sample_request }, context) as Record<string, unknown>),\n account,\n };\n }\n return {\n account,\n scenario: context.controller_scenario ?? 'list_scenarios',\n };\n },\n};\n\n/**\n * Enrich a storyboard step's request payload.\n *\n * Contract (issue #820):\n * - `sample_request`, when authored, is the authoritative base. The runner\n * injects context placeholders into it before calling here.\n * - An enricher (if registered for the task) produces fields that should\n * fill gaps the fixture left unset — discovery-derived identifiers,\n * envelope fields the author couldn't know at YAML-authoring time.\n * - Top-level merge: fixture wins on key conflicts. Fixture-aware\n * enrichers (see `FIXTURE_AWARE_ENRICHERS`) skip the generic merge and\n * return the final payload themselves.\n *\n * Returns `{}` when the task has no enricher and no `sample_request` — the\n * runner's load-time validator prevents this for mutating tasks, so the\n * empty return is reachable only for read tasks that have no fixture and\n * no registered enricher (rare).\n */\nexport function enrichRequest(\n step: StoryboardStep,\n context: StoryboardContext,\n options: TestOptions,\n runnerVars?: RunnerVariables\n): Record<string, unknown> {\n const enricher = REQUEST_ENRICHERS[step.task];\n const fixture =\n step.sample_request !== undefined\n ? (injectContext({ ...(step.sample_request as Record<string, unknown>) }, context, runnerVars) as Record<\n string,\n unknown\n >)\n : undefined;\n\n if (!enricher) return fixture ?? {};\n\n const enriched = enricher(step, context, options, runnerVars);\n\n // Fixture-aware enrichers already did the body merge internally and know\n // the array/nested shapes better than a generic top-level overlay can.\n // Envelope fields still flow through from sample_request.\n if (FIXTURE_AWARE_ENRICHERS.has(step.task)) {\n if (!fixture) return enriched;\n const out: Record<string, unknown> = { ...enriched };\n for (const field of ENVELOPE_FIELDS) {\n if (fixture[field] !== undefined && out[field] === undefined) out[field] = fixture[field];\n }\n return out;\n }\n\n // Generic fixture-authoritative merge: fixture keys overlay enricher keys.\n return fixture ? { ...enriched, ...fixture } : enriched;\n}\n\n/** True iff a request enricher is registered for this task. */\nexport function hasRequestEnricher(taskName: string): boolean {\n return taskName in REQUEST_ENRICHERS;\n}\n\n// ────────────────────────────────────────────────────────────\n// Legacy aliases — pre-#820 terminology. Kept for one release so\n// external consumers (repo greps found none, but public exports may\n// have downstream users) migrate at their own pace.\n// ────────────────────────────────────────────────────────────\n\n/** @deprecated Renamed to `enrichRequest`. Same behavior. */\nexport const buildRequest = enrichRequest;\n\n/** @deprecated Renamed to `hasRequestEnricher`. Same behavior. */\nexport const hasRequestBuilder = hasRequestEnricher;\n\n// ────────────────────────────────────────────────────────────\n// Selection helpers: pick the best item from discovered data\n// ────────────────────────────────────────────────────────────\n\nfunction selectProduct(context: StoryboardContext): Record<string, unknown> | undefined {\n const products = context.products as Array<Record<string, unknown>> | undefined;\n if (!products?.length) return undefined;\n\n // Prefer products with guaranteed delivery and pricing options\n const withPricing = products.filter(p => {\n const opts = p.pricing_options as unknown[] | undefined;\n return opts && opts.length > 0;\n });\n\n return withPricing[0] ?? products[0];\n}\n\nfunction selectPricingOption(product: Record<string, unknown> | undefined): Record<string, unknown> | undefined {\n if (!product) return undefined;\n const options = product.pricing_options as Array<Record<string, unknown>> | undefined;\n if (!options?.length) return undefined;\n\n // Prefer fixed pricing (cpm, flat) over auction\n const fixed = options.filter(o => o.pricing_model === 'cpm' || o.pricing_model === 'flat');\n return fixed[0] ?? options[0];\n}\n\nfunction selectFormat(context: StoryboardContext): Record<string, unknown> | undefined {\n const formats = context.formats as Array<Record<string, unknown>> | undefined;\n if (!formats?.length) return undefined;\n return formats[0];\n}\n\nfunction selectSignal(context: StoryboardContext): Record<string, unknown> | undefined {\n const signals = context.signals as Array<Record<string, unknown>> | undefined;\n if (!signals?.length) return undefined;\n return signals[0];\n}\n\n/**\n * Build placeholder assets appropriate for a format's type.\n * Uses the format name to guess whether it's video, native, or display.\n */\nfunction buildAssetsForFormat(format: Record<string, unknown>): Record<string, unknown> {\n const name = String(format.name ?? format.format_id ?? '').toLowerCase();\n const type = String(format.type ?? '').toLowerCase();\n\n if (type === 'video' || name.includes('video') || name.includes('vast')) {\n return {\n video: {\n asset_type: 'video',\n url: 'https://test-assets.adcontextprotocol.org/acme-outdoor/trail-pro-30s.mp4',\n width: 1920,\n height: 1080,\n duration_ms: 30000,\n container_format: 'video/mp4',\n },\n };\n }\n\n if (type === 'native' || name.includes('native')) {\n return {\n image: {\n asset_type: 'image',\n url: 'https://test-assets.adcontextprotocol.org/acme-outdoor/hero-master.jpg',\n width: 1200,\n height: 628,\n format: 'image/jpeg',\n },\n headline: {\n asset_type: 'text',\n content: 'Trail Pro 3000 — Built for the Summit',\n },\n };\n }\n\n // Default: display image\n return {\n primary: {\n asset_type: 'image',\n url: 'https://test-assets.adcontextprotocol.org/acme-outdoor/hero-master.jpg',\n width: 1200,\n height: 628,\n format: 'image/jpeg',\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 { createPrivateKey, randomBytes, randomUUID, sign as nodeSign } from "crypto";
|
|
8
2
|
import {
|
|
9
3
|
buildSignatureBase,
|