@adcp/sdk 12.1.0 → 13.0.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -3
- package/bin/adcp-fuzz.js +3 -1
- package/dist/lib/adapters/content-standards-adapter.d.mts +9 -9
- package/dist/lib/adapters/content-standards-adapter.d.ts +9 -9
- package/dist/lib/adapters/content-standards-adapter.d.ts.map +1 -1
- package/dist/lib/adapters/content-standards-adapter.js +22 -22
- package/dist/lib/adapters/content-standards-adapter.js.map +1 -1
- package/dist/lib/adapters/content-standards-adapter.mjs +18 -18
- package/dist/lib/adapters/content-standards-adapter.mjs.map +1 -1
- package/dist/lib/adapters/index.d.mts +2 -2
- package/dist/lib/adapters/index.d.ts +2 -2
- package/dist/lib/adapters/index.d.ts.map +1 -1
- package/dist/lib/adapters/index.js +10 -10
- package/dist/lib/adapters/index.js.map +1 -1
- package/dist/lib/adapters/index.mjs +9 -9
- package/dist/lib/adapters/index.mjs.map +1 -1
- package/dist/lib/auth/oauth/authorization-required.d.mts +3 -0
- package/dist/lib/auth/oauth/authorization-required.d.ts +3 -0
- package/dist/lib/auth/oauth/authorization-required.d.ts.map +1 -1
- package/dist/lib/auth/oauth/authorization-required.js +12 -9
- package/dist/lib/auth/oauth/authorization-required.js.map +1 -1
- package/dist/lib/auth/oauth/authorization-required.mjs +12 -9
- package/dist/lib/auth/oauth/authorization-required.mjs.map +1 -1
- package/dist/lib/auth/oauth/provider-cache.d.mts +7 -0
- package/dist/lib/auth/oauth/provider-cache.d.ts +8 -0
- package/dist/lib/auth/oauth/provider-cache.d.ts.map +1 -0
- package/dist/lib/auth/oauth/provider-cache.js +44 -0
- package/dist/lib/auth/oauth/provider-cache.js.map +1 -0
- package/dist/lib/auth/oauth/provider-cache.mjs +19 -0
- package/dist/lib/auth/oauth/provider-cache.mjs.map +1 -0
- package/dist/lib/conformance/invariants/uniformError.js +1 -1
- package/dist/lib/conformance/invariants/uniformError.js.map +1 -1
- package/dist/lib/conformance/invariants/uniformError.mjs +1 -1
- package/dist/lib/conformance/invariants/uniformError.mjs.map +1 -1
- package/dist/lib/conformance/runners.js +1 -1
- package/dist/lib/conformance/runners.js.map +1 -1
- package/dist/lib/conformance/runners.mjs +1 -1
- package/dist/lib/conformance/runners.mjs.map +1 -1
- package/dist/lib/conformance/seeder.js +5 -5
- package/dist/lib/conformance/seeder.js.map +1 -1
- package/dist/lib/conformance/seeder.mjs +5 -5
- package/dist/lib/conformance/seeder.mjs.map +1 -1
- package/dist/lib/core/ADCPMultiAgentClient.d.mts +26 -29
- package/dist/lib/core/ADCPMultiAgentClient.d.ts +26 -29
- package/dist/lib/core/ADCPMultiAgentClient.d.ts.map +1 -1
- package/dist/lib/core/ADCPMultiAgentClient.js +27 -19
- package/dist/lib/core/ADCPMultiAgentClient.js.map +1 -1
- package/dist/lib/core/ADCPMultiAgentClient.mjs +27 -19
- package/dist/lib/core/ADCPMultiAgentClient.mjs.map +1 -1
- package/dist/lib/core/AgentClient.d.mts +148 -138
- package/dist/lib/core/AgentClient.d.ts +148 -138
- package/dist/lib/core/AgentClient.d.ts.map +1 -1
- package/dist/lib/core/AgentClient.js +174 -85
- package/dist/lib/core/AgentClient.js.map +1 -1
- package/dist/lib/core/AgentClient.mjs +174 -85
- package/dist/lib/core/AgentClient.mjs.map +1 -1
- package/dist/lib/core/AsyncHandler.d.mts +18 -16
- package/dist/lib/core/AsyncHandler.d.ts +18 -16
- package/dist/lib/core/AsyncHandler.d.ts.map +1 -1
- package/dist/lib/core/AsyncHandler.js +22 -1
- package/dist/lib/core/AsyncHandler.js.map +1 -1
- package/dist/lib/core/AsyncHandler.mjs +22 -1
- package/dist/lib/core/AsyncHandler.mjs.map +1 -1
- package/dist/lib/core/ConversationTypes.d.mts +6 -2
- package/dist/lib/core/ConversationTypes.d.ts +6 -2
- package/dist/lib/core/ConversationTypes.d.ts.map +1 -1
- package/dist/lib/core/ConversationTypes.js.map +1 -1
- package/dist/lib/core/CreativeAgentClient.d.mts +41 -17
- package/dist/lib/core/CreativeAgentClient.d.ts +41 -17
- package/dist/lib/core/CreativeAgentClient.d.ts.map +1 -1
- package/dist/lib/core/CreativeAgentClient.js +39 -12
- package/dist/lib/core/CreativeAgentClient.js.map +1 -1
- package/dist/lib/core/CreativeAgentClient.mjs +39 -12
- package/dist/lib/core/CreativeAgentClient.mjs.map +1 -1
- package/dist/lib/core/SingleAgentClient.d.mts +129 -52
- package/dist/lib/core/SingleAgentClient.d.ts +129 -52
- package/dist/lib/core/SingleAgentClient.d.ts.map +1 -1
- package/dist/lib/core/SingleAgentClient.js +1748 -218
- package/dist/lib/core/SingleAgentClient.js.map +1 -1
- package/dist/lib/core/SingleAgentClient.mjs +1758 -218
- package/dist/lib/core/SingleAgentClient.mjs.map +1 -1
- package/dist/lib/core/TaskExecutor.d.mts +9 -0
- package/dist/lib/core/TaskExecutor.d.ts +9 -0
- package/dist/lib/core/TaskExecutor.d.ts.map +1 -1
- package/dist/lib/core/TaskExecutor.js +137 -44
- package/dist/lib/core/TaskExecutor.js.map +1 -1
- package/dist/lib/core/TaskExecutor.mjs +137 -44
- package/dist/lib/core/TaskExecutor.mjs.map +1 -1
- package/dist/lib/index.d.mts +111 -17
- package/dist/lib/index.d.ts +111 -17
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/index.js +94 -94
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/index.mjs +93 -94
- package/dist/lib/index.mjs.map +1 -1
- package/dist/lib/net/ssrf-fetch.d.mts +13 -1
- package/dist/lib/net/ssrf-fetch.d.ts +13 -1
- package/dist/lib/net/ssrf-fetch.d.ts.map +1 -1
- package/dist/lib/net/ssrf-fetch.js +14 -7
- package/dist/lib/net/ssrf-fetch.js.map +1 -1
- package/dist/lib/net/ssrf-fetch.mjs +14 -7
- package/dist/lib/net/ssrf-fetch.mjs.map +1 -1
- package/dist/lib/protocols/a2a.d.mts +2 -2
- package/dist/lib/protocols/a2a.d.ts +2 -2
- package/dist/lib/protocols/a2a.d.ts.map +1 -1
- package/dist/lib/protocols/a2a.js +14 -9
- package/dist/lib/protocols/a2a.js.map +1 -1
- package/dist/lib/protocols/a2a.mjs +14 -9
- package/dist/lib/protocols/a2a.mjs.map +1 -1
- package/dist/lib/protocols/index.d.mts +20 -3
- package/dist/lib/protocols/index.d.ts +20 -3
- package/dist/lib/protocols/index.d.ts.map +1 -1
- package/dist/lib/protocols/index.js +44 -31
- package/dist/lib/protocols/index.js.map +1 -1
- package/dist/lib/protocols/index.mjs +44 -32
- package/dist/lib/protocols/index.mjs.map +1 -1
- package/dist/lib/protocols/mcp-modern.d.ts.map +1 -1
- package/dist/lib/protocols/mcp-modern.js +87 -12
- package/dist/lib/protocols/mcp-modern.js.map +1 -1
- package/dist/lib/protocols/mcp-modern.mjs +89 -12
- package/dist/lib/protocols/mcp-modern.mjs.map +1 -1
- package/dist/lib/protocols/mcp-tasks.d.mts +3 -0
- package/dist/lib/protocols/mcp-tasks.d.ts +3 -0
- package/dist/lib/protocols/mcp-tasks.d.ts.map +1 -1
- package/dist/lib/protocols/mcp-tasks.js +21 -11
- package/dist/lib/protocols/mcp-tasks.js.map +1 -1
- package/dist/lib/protocols/mcp-tasks.mjs +21 -11
- package/dist/lib/protocols/mcp-tasks.mjs.map +1 -1
- package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
- package/dist/lib/server/create-adcp-server.d.mts +6 -0
- package/dist/lib/server/create-adcp-server.d.ts +6 -0
- package/dist/lib/server/create-adcp-server.d.ts.map +1 -1
- package/dist/lib/server/create-adcp-server.js +295 -48
- package/dist/lib/server/create-adcp-server.js.map +1 -1
- package/dist/lib/server/create-adcp-server.mjs +301 -50
- package/dist/lib/server/create-adcp-server.mjs.map +1 -1
- package/dist/lib/server/decisioning/assembly-helpers.d.mts +27 -13
- package/dist/lib/server/decisioning/assembly-helpers.d.ts +27 -13
- package/dist/lib/server/decisioning/assembly-helpers.d.ts.map +1 -1
- package/dist/lib/server/decisioning/assembly-helpers.js +136 -14
- package/dist/lib/server/decisioning/assembly-helpers.js.map +1 -1
- package/dist/lib/server/decisioning/assembly-helpers.mjs +134 -13
- package/dist/lib/server/decisioning/assembly-helpers.mjs.map +1 -1
- package/dist/lib/server/decisioning/index.d.mts +13 -12
- package/dist/lib/server/decisioning/index.d.ts +13 -12
- package/dist/lib/server/decisioning/index.d.ts.map +1 -1
- package/dist/lib/server/decisioning/index.js +6 -0
- package/dist/lib/server/decisioning/index.js.map +1 -1
- package/dist/lib/server/decisioning/index.mjs +9 -3
- package/dist/lib/server/decisioning/index.mjs.map +1 -1
- package/dist/lib/server/decisioning/list-helpers.d.mts +13 -3
- package/dist/lib/server/decisioning/list-helpers.d.ts +13 -3
- package/dist/lib/server/decisioning/list-helpers.d.ts.map +1 -1
- package/dist/lib/server/decisioning/list-helpers.js +13 -3
- package/dist/lib/server/decisioning/list-helpers.js.map +1 -1
- package/dist/lib/server/decisioning/list-helpers.mjs +11 -2
- package/dist/lib/server/decisioning/list-helpers.mjs.map +1 -1
- package/dist/lib/server/decisioning/manifest-helpers.d.mts +5 -3
- package/dist/lib/server/decisioning/manifest-helpers.d.ts +5 -3
- package/dist/lib/server/decisioning/manifest-helpers.d.ts.map +1 -1
- package/dist/lib/server/decisioning/manifest-helpers.js.map +1 -1
- package/dist/lib/server/decisioning/manifest-helpers.mjs.map +1 -1
- package/dist/lib/server/decisioning/proposal/dispatch.d.mts +2 -2
- package/dist/lib/server/decisioning/proposal/dispatch.d.ts +2 -2
- package/dist/lib/server/decisioning/proposal/dispatch.d.ts.map +1 -1
- package/dist/lib/server/decisioning/proposal/dispatch.js.map +1 -1
- package/dist/lib/server/decisioning/proposal/dispatch.mjs.map +1 -1
- package/dist/lib/server/decisioning/proposal/index.d.mts +1 -1
- package/dist/lib/server/decisioning/proposal/index.d.ts +1 -1
- package/dist/lib/server/decisioning/proposal/index.d.ts.map +1 -1
- package/dist/lib/server/decisioning/proposal/index.js.map +1 -1
- package/dist/lib/server/decisioning/proposal/index.mjs.map +1 -1
- package/dist/lib/server/decisioning/proposal/lifecycle.d.mts +3 -2
- package/dist/lib/server/decisioning/proposal/lifecycle.d.ts +3 -2
- package/dist/lib/server/decisioning/proposal/lifecycle.d.ts.map +1 -1
- package/dist/lib/server/decisioning/proposal/lifecycle.js.map +1 -1
- package/dist/lib/server/decisioning/proposal/lifecycle.mjs.map +1 -1
- package/dist/lib/server/decisioning/proposal/mock-manager.d.mts +3 -3
- package/dist/lib/server/decisioning/proposal/mock-manager.d.ts +3 -3
- package/dist/lib/server/decisioning/proposal/mock-manager.d.ts.map +1 -1
- package/dist/lib/server/decisioning/proposal/mock-manager.js.map +1 -1
- package/dist/lib/server/decisioning/proposal/mock-manager.mjs.map +1 -1
- package/dist/lib/server/decisioning/proposal/types.d.mts +10 -6
- package/dist/lib/server/decisioning/proposal/types.d.ts +10 -6
- package/dist/lib/server/decisioning/proposal/types.d.ts.map +1 -1
- package/dist/lib/server/decisioning/proposal/types.js.map +1 -1
- package/dist/lib/server/decisioning/proposal/types.mjs.map +1 -1
- package/dist/lib/server/decisioning/runtime/from-platform.d.mts +21 -2
- package/dist/lib/server/decisioning/runtime/from-platform.d.ts +21 -2
- package/dist/lib/server/decisioning/runtime/from-platform.d.ts.map +1 -1
- package/dist/lib/server/decisioning/runtime/from-platform.js +754 -81
- package/dist/lib/server/decisioning/runtime/from-platform.js.map +1 -1
- package/dist/lib/server/decisioning/runtime/from-platform.mjs +763 -81
- package/dist/lib/server/decisioning/runtime/from-platform.mjs.map +1 -1
- package/dist/lib/server/decisioning/specialisms/brand-rights.d.mts +9 -9
- package/dist/lib/server/decisioning/specialisms/brand-rights.d.ts +9 -9
- package/dist/lib/server/decisioning/specialisms/brand-rights.d.ts.map +1 -1
- package/dist/lib/server/decisioning/specialisms/brand-rights.js.map +1 -1
- package/dist/lib/server/decisioning/specialisms/content-standards.d.mts +16 -16
- package/dist/lib/server/decisioning/specialisms/content-standards.d.ts +16 -16
- package/dist/lib/server/decisioning/specialisms/content-standards.d.ts.map +1 -1
- package/dist/lib/server/decisioning/specialisms/content-standards.js.map +1 -1
- package/dist/lib/server/decisioning/specialisms/creative-ad-server.d.mts +15 -13
- package/dist/lib/server/decisioning/specialisms/creative-ad-server.d.ts +15 -13
- package/dist/lib/server/decisioning/specialisms/creative-ad-server.d.ts.map +1 -1
- package/dist/lib/server/decisioning/specialisms/creative-ad-server.js.map +1 -1
- package/dist/lib/server/decisioning/specialisms/creative.d.mts +12 -11
- package/dist/lib/server/decisioning/specialisms/creative.d.ts +12 -11
- package/dist/lib/server/decisioning/specialisms/creative.d.ts.map +1 -1
- package/dist/lib/server/decisioning/specialisms/creative.js.map +1 -1
- package/dist/lib/server/decisioning/specialisms/sales.d.mts +25 -15
- package/dist/lib/server/decisioning/specialisms/sales.d.ts +25 -15
- package/dist/lib/server/decisioning/specialisms/sales.d.ts.map +1 -1
- package/dist/lib/server/decisioning/specialisms/sales.js.map +1 -1
- package/dist/lib/server/index.d.mts +3 -3
- package/dist/lib/server/index.d.ts +3 -3
- package/dist/lib/server/index.d.ts.map +1 -1
- package/dist/lib/server/index.js +72 -72
- package/dist/lib/server/index.js.map +1 -1
- package/dist/lib/server/index.mjs +36 -36
- package/dist/lib/server/index.mjs.map +1 -1
- package/dist/lib/server/legacy/v5/index.d.mts +1 -0
- package/dist/lib/server/legacy/v5/index.d.ts +1 -0
- package/dist/lib/server/legacy/v5/index.d.ts.map +1 -1
- package/dist/lib/server/legacy/v5/index.js +74 -1
- package/dist/lib/server/legacy/v5/index.js.map +1 -1
- package/dist/lib/server/legacy/v5/index.mjs +75 -1
- package/dist/lib/server/legacy/v5/index.mjs.map +1 -1
- package/dist/lib/server/responses.d.mts +2 -2
- package/dist/lib/server/responses.d.ts +2 -2
- package/dist/lib/server/responses.js.map +1 -1
- package/dist/lib/server/responses.mjs.map +1 -1
- package/dist/lib/testing/client.d.ts.map +1 -1
- package/dist/lib/testing/client.js +13 -5
- package/dist/lib/testing/client.js.map +1 -1
- package/dist/lib/testing/client.mjs +13 -5
- package/dist/lib/testing/client.mjs.map +1 -1
- package/dist/lib/testing/compliance/comply.d.mts +1 -1
- package/dist/lib/testing/compliance/comply.d.ts +1 -1
- package/dist/lib/testing/compliance/comply.d.ts.map +1 -1
- package/dist/lib/testing/compliance/comply.js +16 -6
- package/dist/lib/testing/compliance/comply.js.map +1 -1
- package/dist/lib/testing/compliance/comply.mjs +16 -6
- package/dist/lib/testing/compliance/comply.mjs.map +1 -1
- package/dist/lib/testing/scenarios/brand-rights.js +6 -6
- package/dist/lib/testing/scenarios/brand-rights.js.map +1 -1
- package/dist/lib/testing/scenarios/brand-rights.mjs +6 -6
- package/dist/lib/testing/scenarios/brand-rights.mjs.map +1 -1
- package/dist/lib/testing/scenarios/creative.js +7 -7
- package/dist/lib/testing/scenarios/creative.js.map +1 -1
- package/dist/lib/testing/scenarios/creative.mjs +7 -7
- package/dist/lib/testing/scenarios/creative.mjs.map +1 -1
- package/dist/lib/testing/scenarios/discovery.js +2 -2
- package/dist/lib/testing/scenarios/discovery.js.map +1 -1
- package/dist/lib/testing/scenarios/discovery.mjs +2 -2
- package/dist/lib/testing/scenarios/discovery.mjs.map +1 -1
- package/dist/lib/testing/scenarios/edge-cases.js +11 -11
- package/dist/lib/testing/scenarios/edge-cases.js.map +1 -1
- package/dist/lib/testing/scenarios/edge-cases.mjs +11 -11
- package/dist/lib/testing/scenarios/edge-cases.mjs.map +1 -1
- package/dist/lib/testing/scenarios/governance.d.ts.map +1 -1
- package/dist/lib/testing/scenarios/governance.js +16 -22
- package/dist/lib/testing/scenarios/governance.js.map +1 -1
- package/dist/lib/testing/scenarios/governance.mjs +16 -22
- package/dist/lib/testing/scenarios/governance.mjs.map +1 -1
- package/dist/lib/testing/scenarios/media-buy.js +5 -5
- package/dist/lib/testing/scenarios/media-buy.js.map +1 -1
- package/dist/lib/testing/scenarios/media-buy.mjs +5 -5
- package/dist/lib/testing/scenarios/media-buy.mjs.map +1 -1
- package/dist/lib/testing/scenarios/schema-compliance.js +1 -1
- package/dist/lib/testing/scenarios/schema-compliance.js.map +1 -1
- package/dist/lib/testing/scenarios/schema-compliance.mjs +1 -1
- package/dist/lib/testing/scenarios/schema-compliance.mjs.map +1 -1
- package/dist/lib/testing/scenarios/trusted-match.d.ts.map +1 -1
- package/dist/lib/testing/scenarios/trusted-match.js +4 -3
- package/dist/lib/testing/scenarios/trusted-match.js.map +1 -1
- package/dist/lib/testing/scenarios/trusted-match.mjs +4 -3
- package/dist/lib/testing/scenarios/trusted-match.mjs.map +1 -1
- package/dist/lib/testing/storyboard/probes.d.mts +6 -0
- package/dist/lib/testing/storyboard/probes.d.ts +6 -0
- package/dist/lib/testing/storyboard/probes.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/probes.js +16 -5
- package/dist/lib/testing/storyboard/probes.js.map +1 -1
- package/dist/lib/testing/storyboard/probes.mjs +16 -5
- package/dist/lib/testing/storyboard/probes.mjs.map +1 -1
- package/dist/lib/testing/storyboard/runner.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/runner.js +6 -2
- package/dist/lib/testing/storyboard/runner.js.map +1 -1
- package/dist/lib/testing/storyboard/runner.mjs +6 -2
- package/dist/lib/testing/storyboard/runner.mjs.map +1 -1
- package/dist/lib/testing/storyboard/shape-drift-hints.d.mts +3 -4
- package/dist/lib/testing/storyboard/shape-drift-hints.d.ts +3 -4
- package/dist/lib/testing/storyboard/shape-drift-hints.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/shape-drift-hints.js +27 -27
- package/dist/lib/testing/storyboard/shape-drift-hints.js.map +1 -1
- package/dist/lib/testing/storyboard/shape-drift-hints.mjs +27 -27
- package/dist/lib/testing/storyboard/shape-drift-hints.mjs.map +1 -1
- package/dist/lib/testing/storyboard/task-map.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/task-map.js +43 -5
- package/dist/lib/testing/storyboard/task-map.js.map +1 -1
- package/dist/lib/testing/storyboard/task-map.mjs +43 -5
- package/dist/lib/testing/storyboard/task-map.mjs.map +1 -1
- package/dist/lib/testing/test-helpers.d.mts +2 -2
- package/dist/lib/testing/test-helpers.d.ts +2 -2
- package/dist/lib/testing/test-helpers.js.map +1 -1
- package/dist/lib/testing/test-helpers.mjs.map +1 -1
- package/dist/lib/testing/types.d.mts +7 -1
- package/dist/lib/testing/types.d.ts +7 -1
- package/dist/lib/testing/types.d.ts.map +1 -1
- package/dist/lib/testing/types.js.map +1 -1
- package/dist/lib/types/adcp.d.mts +14 -14
- package/dist/lib/types/adcp.d.ts +14 -14
- package/dist/lib/types/adcp.d.ts.map +1 -1
- package/dist/lib/types/adcp.js.map +1 -1
- package/dist/lib/types/adcp.mjs.map +1 -1
- package/dist/lib/types/index.d.mts +2 -2
- package/dist/lib/types/index.d.ts +2 -2
- package/dist/lib/types/index.d.ts.map +1 -1
- package/dist/lib/types/index.js.map +1 -1
- package/dist/lib/types/index.mjs.map +1 -1
- package/dist/lib/utils/capabilities.d.mts +6 -0
- package/dist/lib/utils/capabilities.d.ts +6 -0
- package/dist/lib/utils/capabilities.d.ts.map +1 -1
- package/dist/lib/utils/capabilities.js +4 -0
- package/dist/lib/utils/capabilities.js.map +1 -1
- package/dist/lib/utils/capabilities.mjs +4 -0
- package/dist/lib/utils/capabilities.mjs.map +1 -1
- package/dist/lib/utils/creative-delivery.d.mts +52 -10
- package/dist/lib/utils/creative-delivery.d.ts +52 -10
- package/dist/lib/utils/creative-delivery.d.ts.map +1 -1
- package/dist/lib/utils/creative-delivery.js +104 -9
- package/dist/lib/utils/creative-delivery.js.map +1 -1
- package/dist/lib/utils/creative-delivery.mjs +104 -8
- package/dist/lib/utils/creative-delivery.mjs.map +1 -1
- package/dist/lib/utils/format-assets.d.mts +41 -38
- package/dist/lib/utils/format-assets.d.ts +41 -38
- package/dist/lib/utils/format-assets.d.ts.map +1 -1
- package/dist/lib/utils/format-assets.js +3 -1
- package/dist/lib/utils/format-assets.js.map +1 -1
- package/dist/lib/utils/format-assets.mjs +3 -1
- package/dist/lib/utils/format-assets.mjs.map +1 -1
- package/dist/lib/utils/index.d.mts +8 -8
- package/dist/lib/utils/index.d.ts +8 -8
- package/dist/lib/utils/index.d.ts.map +1 -1
- package/dist/lib/utils/index.js +11 -9
- package/dist/lib/utils/index.js.map +1 -1
- package/dist/lib/utils/index.mjs +9 -8
- package/dist/lib/utils/index.mjs.map +1 -1
- package/dist/lib/utils/preview-utils.d.mts +11 -8
- package/dist/lib/utils/preview-utils.d.ts +11 -8
- package/dist/lib/utils/preview-utils.d.ts.map +1 -1
- package/dist/lib/utils/preview-utils.js +4 -4
- package/dist/lib/utils/preview-utils.js.map +1 -1
- package/dist/lib/utils/preview-utils.mjs +3 -3
- package/dist/lib/utils/preview-utils.mjs.map +1 -1
- package/dist/lib/utils/tool-request-schemas.d.mts +2 -2
- package/dist/lib/utils/tool-request-schemas.d.ts +2 -2
- package/dist/lib/v2/projection/aao-reference-formats.json +492 -0
- package/dist/lib/v2/projection/augment-response.d.mts +31 -11
- package/dist/lib/v2/projection/augment-response.d.ts +31 -11
- package/dist/lib/v2/projection/augment-response.d.ts.map +1 -1
- package/dist/lib/v2/projection/augment-response.js +233 -27
- package/dist/lib/v2/projection/augment-response.js.map +1 -1
- package/dist/lib/v2/projection/augment-response.mjs +232 -27
- package/dist/lib/v2/projection/augment-response.mjs.map +1 -1
- package/dist/lib/v2/projection/builders.d.mts +7 -6
- package/dist/lib/v2/projection/builders.d.ts +7 -6
- package/dist/lib/v2/projection/builders.d.ts.map +1 -1
- package/dist/lib/v2/projection/builders.js +11 -8
- package/dist/lib/v2/projection/builders.js.map +1 -1
- package/dist/lib/v2/projection/builders.mjs +9 -6
- package/dist/lib/v2/projection/builders.mjs.map +1 -1
- package/dist/lib/v2/projection/catalog-snapshot.d.mts +47 -0
- package/dist/lib/v2/projection/catalog-snapshot.d.ts +48 -0
- package/dist/lib/v2/projection/catalog-snapshot.d.ts.map +1 -0
- package/dist/lib/v2/projection/catalog-snapshot.js +291 -0
- package/dist/lib/v2/projection/catalog-snapshot.js.map +1 -0
- package/dist/lib/v2/projection/catalog-snapshot.mjs +265 -0
- package/dist/lib/v2/projection/catalog-snapshot.mjs.map +1 -0
- package/dist/lib/v2/projection/catalog.d.mts +23 -2
- package/dist/lib/v2/projection/catalog.d.ts +23 -2
- package/dist/lib/v2/projection/catalog.d.ts.map +1 -1
- package/dist/lib/v2/projection/catalog.js +34 -10
- package/dist/lib/v2/projection/catalog.js.map +1 -1
- package/dist/lib/v2/projection/catalog.mjs +33 -10
- package/dist/lib/v2/projection/catalog.mjs.map +1 -1
- package/dist/lib/v2/projection/creative-delivery.d.mts +157 -0
- package/dist/lib/v2/projection/creative-delivery.d.ts +158 -0
- package/dist/lib/v2/projection/creative-delivery.d.ts.map +1 -0
- package/dist/lib/v2/projection/creative-delivery.js +967 -0
- package/dist/lib/v2/projection/creative-delivery.js.map +1 -0
- package/dist/lib/v2/projection/creative-delivery.mjs +941 -0
- package/dist/lib/v2/projection/creative-delivery.mjs.map +1 -0
- package/dist/lib/v2/projection/index.d.mts +8 -6
- package/dist/lib/v2/projection/index.d.ts +8 -6
- package/dist/lib/v2/projection/index.d.ts.map +1 -1
- package/dist/lib/v2/projection/index.js +26 -4
- package/dist/lib/v2/projection/index.js.map +1 -1
- package/dist/lib/v2/projection/index.mjs +28 -4
- package/dist/lib/v2/projection/index.mjs.map +1 -1
- package/dist/lib/v2/projection/legacy-metadata.d.mts +21 -0
- package/dist/lib/v2/projection/legacy-metadata.d.ts +22 -0
- package/dist/lib/v2/projection/legacy-metadata.d.ts.map +1 -0
- package/dist/lib/v2/projection/legacy-metadata.js +73 -0
- package/dist/lib/v2/projection/legacy-metadata.js.map +1 -0
- package/dist/lib/v2/projection/legacy-metadata.mjs +45 -0
- package/dist/lib/v2/projection/legacy-metadata.mjs.map +1 -0
- package/dist/lib/v2/projection/types.d.mts +29 -23
- package/dist/lib/v2/projection/types.d.ts +29 -23
- package/dist/lib/v2/projection/types.d.ts.map +1 -1
- package/dist/lib/v2/projection/types.js.map +1 -1
- package/dist/lib/v2/projection/v1-to-v2.d.mts +29 -6
- package/dist/lib/v2/projection/v1-to-v2.d.ts +29 -6
- package/dist/lib/v2/projection/v1-to-v2.d.ts.map +1 -1
- package/dist/lib/v2/projection/v1-to-v2.js +218 -9
- package/dist/lib/v2/projection/v1-to-v2.js.map +1 -1
- package/dist/lib/v2/projection/v1-to-v2.mjs +219 -10
- package/dist/lib/v2/projection/v1-to-v2.mjs.map +1 -1
- package/dist/lib/v2/projection/v2-to-v1.d.mts +31 -2
- package/dist/lib/v2/projection/v2-to-v1.d.ts +31 -2
- package/dist/lib/v2/projection/v2-to-v1.d.ts.map +1 -1
- package/dist/lib/v2/projection/v2-to-v1.js +138 -7
- package/dist/lib/v2/projection/v2-to-v1.js.map +1 -1
- package/dist/lib/v2/projection/v2-to-v1.mjs +135 -6
- package/dist/lib/v2/projection/v2-to-v1.mjs.map +1 -1
- package/dist/lib/v2/projection/write-side.d.mts +12 -13
- package/dist/lib/v2/projection/write-side.d.ts +12 -13
- package/dist/lib/v2/projection/write-side.d.ts.map +1 -1
- package/dist/lib/v2/projection/write-side.js +16 -12
- package/dist/lib/v2/projection/write-side.js.map +1 -1
- package/dist/lib/v2/projection/write-side.mjs +16 -12
- package/dist/lib/v2/projection/write-side.mjs.map +1 -1
- package/dist/lib/version.d.mts +3 -3
- package/dist/lib/version.d.ts +3 -3
- package/dist/lib/version.d.ts.map +1 -1
- package/dist/lib/version.js +3 -3
- package/dist/lib/version.js.map +1 -1
- package/dist/lib/version.mjs +3 -3
- package/dist/lib/version.mjs.map +1 -1
- package/docs/guides/BUILD-AN-AGENT.md +7 -2
- package/docs/guides/PREVIEW-ASSET-DURABILITY.md +6 -2
- package/docs/llms.txt +1 -0
- package/examples/README.md +1 -1
- package/examples/batch-preview-test.ts +3 -3
- package/examples/conversation-client.ts +2 -2
- package/examples/debug-preview-response.ts +1 -1
- package/examples/decisioning-platform-broadcast-tv.ts +10 -4
- package/examples/decisioning-platform-mock-seller.ts +16 -9
- package/examples/decisioning-platform-programmatic.ts +18 -7
- package/examples/error-compliant-server.ts +26 -16
- package/examples/generative-creative-demo.ts +7 -19
- package/examples/hello_creative_adapter_ad_server.ts +37 -16
- package/examples/hello_creative_adapter_template.ts +12 -10
- package/examples/hello_seller_adapter_guaranteed.ts +52 -23
- package/examples/hello_seller_adapter_multi_tenant.ts +6 -3
- package/examples/hello_seller_adapter_non_guaranteed.ts +54 -26
- package/examples/hello_seller_adapter_proposal_mode.ts +40 -14
- package/examples/hello_seller_adapter_social.ts +2 -7
- package/examples/inspect-card-formats.ts +1 -1
- package/examples/simple-getting-started.ts +1 -1
- package/examples/test-helpers-demo.ts +1 -1
- package/package.json +2 -2
- package/skills/build-decisioning-creative-template/SKILL.md +11 -11
- package/skills/build-decisioning-platform/advanced/REFERENCE.md +8 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../src/lib/server/decisioning/proposal/lifecycle.ts"],"sourcesContent":["/**\n * Proposal-lifecycle framework helpers — the v1.5 intercept seam.\n *\n * Sits parallel to existing dispatch helpers in `runtime/from-platform.ts`.\n * Framework intercepts at a seam, does its work, dispatches.\n *\n * Public surface (the dispatch path imports these):\n *\n * - {@link enforceProposalExpiry} — D7. Look up the committed proposal,\n * validate `state === 'committed'` and `now <= expires_at + grace`,\n * return the record.\n * - {@link validateCapabilityOverlap} — D4. Walk a buyer's\n * `create_media_buy` / `update_media_buy` request packages against\n * each recipe's `capability_overlap` and reject mismatches with\n * `INVALID_REQUEST`.\n * - {@link validateOverlapSubsetOfWire} — D4 round-4. Validate at\n * `putDraft` time that each recipe's `capability_overlap` axis is a\n * subset of the corresponding wire-declared product capabilities.\n * Mismatches throw `INTERNAL_ERROR` (adopter bug, not buyer bug).\n * - {@link detectFinalizeAction} — pull out the first finalize-action\n * refine entry from a `GetProductsRequest`.\n * - structured-log helpers per § Observability.\n *\n * Ports `adcp-client-python.src/adcp/decisioning/proposal_lifecycle.py`.\n *\n * @public\n * @packageDocumentation\n */\n\nimport { AdcpError } from '../async-outcome';\nimport type { GetProductsRequest, Product } from '../../../types/tools.generated';\nimport type { ProposalRecord, ProposalStore } from './store';\nimport type { Recipe } from './types';\n\n// ---------------------------------------------------------------------------\n// D7 — expires_at enforcement\n// ---------------------------------------------------------------------------\n\n/**\n * Validate a proposal is committed and within its hold window.\n *\n * Three failure modes mapped to spec error codes:\n *\n * - Record not found OR cross-tenant → `PROPOSAL_NOT_FOUND` (correctable).\n * Cross-tenant probes return the same error as missing IDs (no\n * principal-enumeration via id probing).\n * - State !== `'committed'` → `PROPOSAL_NOT_COMMITTED` (correctable).\n * The buyer needs to call `getProducts({ buying_mode: 'refine',\n * refine: [{ action: 'finalize' }] })` first.\n * - Committed but `now > expires_at + grace` → `PROPOSAL_EXPIRED`\n * (correctable per AdCP 3.0.6 — the buyer re-discovers via\n * `get_products` to obtain a fresh proposal).\n *\n * @public\n */\nexport async function enforceProposalExpiry<TRecipe extends Recipe>(\n proposalId: string,\n args: {\n proposalStore: ProposalStore<TRecipe>;\n expectedAccountId: string;\n graceSeconds?: number;\n now?: Date;\n }\n): Promise<ProposalRecord<TRecipe>> {\n const { proposalStore, expectedAccountId, graceSeconds = 0, now } = args;\n const record = await proposalStore.get(proposalId, { expectedAccountId });\n if (!record) {\n throw new AdcpError('PROPOSAL_NOT_FOUND', {\n recovery: 'correctable',\n message:\n `Proposal ${JSON.stringify(proposalId)} not found. The buyer must call get_products ` +\n `with buying_mode='refine' and refine=[{action:'finalize',...}] to obtain a ` +\n `committed proposal_id before referencing it on create_media_buy.`,\n field: 'proposal_id',\n });\n }\n if (record.state !== 'committed') {\n throw new AdcpError('PROPOSAL_NOT_COMMITTED', {\n recovery: 'correctable',\n message:\n `Proposal ${JSON.stringify(proposalId)} is in state ${JSON.stringify(record.state)}; ` +\n `only committed proposals can be accepted via create_media_buy. Call get_products ` +\n `with buying_mode='refine' and action='finalize' first.`,\n field: 'proposal_id',\n });\n }\n if (record.expiresAt) {\n const current = now ?? new Date();\n const deadline = record.expiresAt.getTime() + graceSeconds * 1000;\n if (current.getTime() > deadline) {\n logExpired({\n proposalId,\n accountId: record.accountId,\n now: current,\n expiresAt: record.expiresAt,\n graceSeconds,\n });\n throw new AdcpError('PROPOSAL_EXPIRED', {\n // Per AdCP 3.0.6 schemas/cache/3.0.6/enums/error-code.json,\n // PROPOSAL_EXPIRED is `correctable` — the buyer re-discovers\n // via `get_products` to obtain a fresh proposal (the hold\n // window has lapsed but the buyer can re-request).\n recovery: 'correctable',\n message:\n `Proposal ${JSON.stringify(proposalId)} expired at ${record.expiresAt.toISOString()}; ` +\n `create_media_buy must be called within the inventory hold window. Call get_products ` +\n `with buying_mode='refine' and action='finalize' to request a fresh hold.`,\n field: 'proposal_id',\n });\n }\n }\n return record;\n}\n\n// ---------------------------------------------------------------------------\n// D4 — capability-overlap validation\n// ---------------------------------------------------------------------------\n\ninterface PackageLike {\n product_id?: string;\n pricing_option_id?: string;\n pricing_model?: string;\n delivery_type?: string;\n signal_type?: string;\n targeting_overlay?: Record<string, unknown>;\n /**\n * Adopter-resolved pricing model — the validation reads this when\n * present. Set by the framework when it has resolved\n * `pricing_option_id` against the product's pricing options.\n */\n _resolved_pricing_model?: string;\n _resolved_delivery_type?: string;\n [k: string]: unknown;\n}\n\n/**\n * Pre-adapter validation seam: walk buyer's packages against each recipe's\n * `capability_overlap` and reject mismatches.\n *\n * Called from the framework's `create_media_buy` and `update_media_buy`\n * dispatch paths after recipes are hydrated from the {@link ProposalStore}.\n * Per D4, the framework owns this gate so every adopter doesn't write the\n * same intersection logic.\n *\n * Validation axes (per {@link CapabilityOverlap}):\n *\n * - `pricingModels` — checked against `package._resolved_pricing_model`\n * or `package.pricing_model`.\n * - `targetingDimensions` — checked against `package.targeting_overlay` keys.\n * - `deliveryTypes` — checked against `package._resolved_delivery_type`\n * or `package.delivery_type`.\n * - `signalTypes` — checked against `package.signal_type`.\n *\n * Per the design's undefined-vs-empty-set semantics: `undefined` skips the\n * gate; an explicit `Set` (including the empty set) is enforced.\n *\n * @public\n */\nexport function validateCapabilityOverlap<TRecipe extends Recipe>(args: {\n packages: readonly PackageLike[];\n recipes: ReadonlyMap<string, TRecipe>;\n fieldPathPrefix?: string;\n}): void {\n const { packages, recipes, fieldPathPrefix = 'packages' } = args;\n for (let i = 0; i < packages.length; i++) {\n const pkg = packages[i]!;\n const productId = pkg.product_id;\n if (!productId) continue;\n const recipe = recipes.get(productId);\n if (!recipe || !recipe.capability_overlap) continue;\n const overlap = recipe.capability_overlap;\n\n if (overlap.pricingModels) {\n const requested = pkg._resolved_pricing_model ?? pkg.pricing_model;\n if (requested !== undefined && !overlap.pricingModels.has(String(requested))) {\n throw new AdcpError('INVALID_REQUEST', {\n recovery: 'terminal',\n message:\n `Buyer requested pricing_model=${JSON.stringify(requested)} on package ` +\n `${JSON.stringify(productId)}, but this product's recipe declares ` +\n `capability_overlap.pricingModels=${JSON.stringify([...overlap.pricingModels].sort())}. ` +\n `The seller did not enable that pricing model for this product.`,\n field: `${fieldPathPrefix}[${i}].pricing_option_id`,\n });\n }\n }\n\n if (overlap.targetingDimensions) {\n const overlay = pkg.targeting_overlay;\n const keys = overlay && typeof overlay === 'object' ? Object.keys(overlay) : [];\n const disallowed = keys.filter(k => !overlap.targetingDimensions!.has(k));\n if (disallowed.length > 0) {\n throw new AdcpError('INVALID_REQUEST', {\n recovery: 'terminal',\n message:\n `Buyer requested targeting dimensions ${JSON.stringify(disallowed.sort())} on ` +\n `package ${JSON.stringify(productId)}, but this product's recipe declares ` +\n `capability_overlap.targetingDimensions=` +\n `${JSON.stringify([...overlap.targetingDimensions].sort())}. The seller did not ` +\n `enable those targeting dimensions for this product.`,\n field: `${fieldPathPrefix}[${i}].targeting_overlay`,\n });\n }\n }\n\n if (overlap.deliveryTypes) {\n const delivery = pkg._resolved_delivery_type ?? pkg.delivery_type;\n if (delivery !== undefined && !overlap.deliveryTypes.has(String(delivery))) {\n throw new AdcpError('INVALID_REQUEST', {\n recovery: 'terminal',\n message:\n `Buyer requested delivery_type=${JSON.stringify(delivery)} on package ` +\n `${JSON.stringify(productId)}, but this product's recipe declares ` +\n `capability_overlap.deliveryTypes=${JSON.stringify([...overlap.deliveryTypes].sort())}.`,\n field: `${fieldPathPrefix}[${i}].delivery_type`,\n });\n }\n }\n\n if (overlap.signalTypes) {\n const signalType = pkg.signal_type;\n if (signalType !== undefined && !overlap.signalTypes.has(String(signalType))) {\n throw new AdcpError('INVALID_REQUEST', {\n recovery: 'terminal',\n message:\n `Buyer requested signal_type=${JSON.stringify(signalType)} on package ` +\n `${JSON.stringify(productId)}, but this product's recipe declares ` +\n `capability_overlap.signalTypes=${JSON.stringify([...overlap.signalTypes].sort())}.`,\n field: `${fieldPathPrefix}[${i}].signal_type`,\n });\n }\n }\n }\n}\n\n/**\n * Validate `recipe.capability_overlap` is a subset of the matching product's\n * wire-declared capabilities.\n *\n * Called at `putDraft` time. Mismatches throw `INTERNAL_ERROR` — this is\n * an adopter bug (the manager declared an overlap claiming capabilities the\n * wire shape doesn't advertise), not a buyer bug.\n *\n * @public\n */\nexport function validateOverlapSubsetOfWire<TRecipe extends Recipe>(args: {\n recipes: ReadonlyMap<string, TRecipe>;\n products: readonly Product[];\n}): void {\n const productsById = new Map<string, Product>();\n for (const p of args.products) {\n if (p.product_id) productsById.set(p.product_id, p);\n }\n for (const [productId, recipe] of args.recipes) {\n if (!recipe.capability_overlap) continue;\n const product = productsById.get(productId);\n if (!product) continue; // missing-product is caught elsewhere\n const overlap = recipe.capability_overlap;\n\n if (overlap.pricingModels) {\n const wirePricing = wirePricingModels(product);\n const extras = [...overlap.pricingModels].filter(p => !wirePricing.has(p));\n if (extras.length > 0) {\n throw new AdcpError('INTERNAL_ERROR', {\n recovery: 'terminal',\n message:\n `Recipe for product ${JSON.stringify(productId)} declares ` +\n `capability_overlap.pricingModels=${JSON.stringify([...overlap.pricingModels].sort())} ` +\n `including ${JSON.stringify(extras.sort())}, but the wire product only advertises ` +\n `${JSON.stringify([...wirePricing].sort())}. The recipe's overlap must be a subset ` +\n `of the wire-declared capabilities; adopter declaration is inconsistent with the ` +\n `product shape.`,\n });\n }\n }\n\n if (overlap.deliveryTypes) {\n const wireDelivery = wireDeliveryTypes(product);\n const extras = [...overlap.deliveryTypes].filter(p => !wireDelivery.has(p));\n // Only enforce when the wire declares something — products lacking a\n // delivery_type field shouldn't trip the gate.\n if (extras.length > 0 && wireDelivery.size > 0) {\n throw new AdcpError('INTERNAL_ERROR', {\n recovery: 'terminal',\n message:\n `Recipe for product ${JSON.stringify(productId)} declares ` +\n `capability_overlap.deliveryTypes=${JSON.stringify([...overlap.deliveryTypes].sort())} ` +\n `including ${JSON.stringify(extras.sort())}, but the wire product only advertises ` +\n `${JSON.stringify([...wireDelivery].sort())}.`,\n });\n }\n }\n }\n}\n\nfunction wirePricingModels(product: Product): Set<string> {\n const out = new Set<string>();\n const pricing = (product as { pricing_options?: ReadonlyArray<{ pricing_model?: string }> }).pricing_options;\n if (!pricing) return out;\n for (const opt of pricing) {\n if (opt.pricing_model) out.add(String(opt.pricing_model));\n }\n return out;\n}\n\nfunction wireDeliveryTypes(product: Product): Set<string> {\n const dt = (product as { delivery_type?: string }).delivery_type;\n return dt ? new Set([String(dt)]) : new Set();\n}\n\n// ---------------------------------------------------------------------------\n// Finalize action detection\n// ---------------------------------------------------------------------------\n\n/**\n * Result of {@link detectFinalizeAction}: the index, proposal_id, and\n * optional ask of the first finalize-action refine entry.\n *\n * @public\n */\nexport interface FinalizeActionRef {\n index: number;\n proposalId: string;\n ask?: string;\n}\n\n/**\n * Return the first finalize-action refine entry from a\n * `GetProductsRequest`, or `null` if no finalize entry exists.\n *\n * The index points at the entry's position in `refine[]` so the framework\n * can produce indexed wire field paths (`refine[3].proposal_id`) on\n * rejection — buyers parsing the error get a precise pointer.\n *\n * Per the spec, `buying_mode: 'refine'` carries a `refine[]` array of\n * entries. Each entry has a `scope` (`request` / `product` / `proposal`)\n * and an optional `action` (`include` / `omit` / `finalize`). v1.5 only\n * intercepts `proposal`-scoped entries with `action: 'finalize'`.\n *\n * The framework processes ONE finalize entry per request; if the buyer\n * sends multiple finalize entries, only the first is processed (rest fall\n * through to the standard refine path).\n *\n * @public\n */\nexport function detectFinalizeAction(req: GetProductsRequest): FinalizeActionRef | null {\n const refine = (req as { refine?: ReadonlyArray<Record<string, unknown>> }).refine;\n if (!refine || refine.length === 0) return null;\n for (let index = 0; index < refine.length; index++) {\n const entry = refine[index]!;\n if (entry.scope === 'proposal' && entry.action === 'finalize') {\n const proposalId = entry.proposal_id;\n if (typeof proposalId === 'string' && proposalId.length > 0) {\n const ask = typeof entry.ask === 'string' ? entry.ask : undefined;\n return ask !== undefined ? { index, proposalId, ask } : { index, proposalId };\n }\n }\n }\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Structured logging — § Observability\n// ---------------------------------------------------------------------------\n\n/**\n * Logger-shaped sink for structured proposal-lifecycle events. Defaults to\n * `console.info`-style emission; the dispatch path can pass a typed logger\n * to route through the rest of the framework's logging.\n *\n * @public\n */\nexport interface ProposalLifecycleLogger {\n info(message: string, fields?: Record<string, unknown>): void;\n}\n\nlet logger: ProposalLifecycleLogger = {\n info: (message, fields) => {\n if (fields) {\n console.log(JSON.stringify({ message, ...fields }));\n } else {\n console.log(message);\n }\n },\n};\n\n/**\n * Replace the module-level logger that proposal-lifecycle structured\n * events (`proposal.draft_persisted`, `proposal.finalized`, `proposal.expired`,\n * `proposal.consumed`) emit through. Adopters wire this to their existing\n * logger (pino, bunyan, etc.) so lifecycle events route through the same\n * pipeline as the rest of their server logs. Tests use it to capture\n * structured emissions for assertion.\n *\n * @public\n */\nexport function setProposalLifecycleLogger(next: ProposalLifecycleLogger): void {\n logger = next;\n}\n\n/** `proposal.draft_persisted` event. */\nexport function logDraftPersisted(args: { proposalId: string; accountId: string; recipesCount: number }): void {\n logger.info('proposal.draft_persisted', {\n event: 'proposal.draft_persisted',\n proposal_id: args.proposalId,\n account_id: args.accountId,\n recipes_count: args.recipesCount,\n });\n}\n\n/**\n * `proposal.finalized` event. `path` is `'inline'` or `'handoff'`.\n */\nexport function logFinalizeSucceeded(args: {\n proposalId: string;\n accountId: string;\n expiresAt: Date;\n path: 'inline' | 'handoff';\n}): void {\n logger.info('proposal.finalized', {\n event: 'proposal.finalized',\n proposal_id: args.proposalId,\n account_id: args.accountId,\n expires_at: args.expiresAt.toISOString(),\n path: args.path,\n });\n}\n\n/** `proposal.expired` event. */\nexport function logExpired(args: {\n proposalId: string;\n accountId: string;\n now: Date;\n expiresAt: Date;\n graceSeconds: number;\n}): void {\n logger.info('proposal.expired', {\n event: 'proposal.expired',\n proposal_id: args.proposalId,\n account_id: args.accountId,\n now: args.now.toISOString(),\n expires_at: args.expiresAt.toISOString(),\n grace_seconds: args.graceSeconds,\n });\n}\n\n/** `proposal.consumed` event. */\nexport function logConsumed(args: { proposalId: string; accountId: string; mediaBuyId: string }): void {\n logger.info('proposal.consumed', {\n event: 'proposal.consumed',\n proposal_id: args.proposalId,\n account_id: args.accountId,\n media_buy_id: args.mediaBuyId,\n });\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../../../src/lib/server/decisioning/proposal/lifecycle.ts"],"sourcesContent":["/**\n * Proposal-lifecycle framework helpers — the v1.5 intercept seam.\n *\n * Sits parallel to existing dispatch helpers in `runtime/from-platform.ts`.\n * Framework intercepts at a seam, does its work, dispatches.\n *\n * Public surface (the dispatch path imports these):\n *\n * - {@link enforceProposalExpiry} — D7. Look up the committed proposal,\n * validate `state === 'committed'` and `now <= expires_at + grace`,\n * return the record.\n * - {@link validateCapabilityOverlap} — D4. Walk a buyer's\n * `create_media_buy` / `update_media_buy` request packages against\n * each recipe's `capability_overlap` and reject mismatches with\n * `INVALID_REQUEST`.\n * - {@link validateOverlapSubsetOfWire} — D4 round-4. Validate at\n * `putDraft` time that each recipe's `capability_overlap` axis is a\n * subset of the corresponding wire-declared product capabilities.\n * Mismatches throw `INTERNAL_ERROR` (adopter bug, not buyer bug).\n * - {@link detectFinalizeAction} — pull out the first finalize-action\n * refine entry from a `GetProductsRequest`.\n * - structured-log helpers per § Observability.\n *\n * Ports `adcp-client-python.src/adcp/decisioning/proposal_lifecycle.py`.\n *\n * @public\n * @packageDocumentation\n */\n\nimport { AdcpError } from '../async-outcome';\nimport type { Product } from '../../../types/tools.generated';\nimport type { CanonicalGetProductsRequest } from '../../../v2/projection/creative-delivery';\nimport type { ProposalRecord, ProposalStore } from './store';\nimport type { Recipe } from './types';\n\n// ---------------------------------------------------------------------------\n// D7 — expires_at enforcement\n// ---------------------------------------------------------------------------\n\n/**\n * Validate a proposal is committed and within its hold window.\n *\n * Three failure modes mapped to spec error codes:\n *\n * - Record not found OR cross-tenant → `PROPOSAL_NOT_FOUND` (correctable).\n * Cross-tenant probes return the same error as missing IDs (no\n * principal-enumeration via id probing).\n * - State !== `'committed'` → `PROPOSAL_NOT_COMMITTED` (correctable).\n * The buyer needs to call `getProducts({ buying_mode: 'refine',\n * refine: [{ action: 'finalize' }] })` first.\n * - Committed but `now > expires_at + grace` → `PROPOSAL_EXPIRED`\n * (correctable per AdCP 3.0.6 — the buyer re-discovers via\n * `get_products` to obtain a fresh proposal).\n *\n * @public\n */\nexport async function enforceProposalExpiry<TRecipe extends Recipe>(\n proposalId: string,\n args: {\n proposalStore: ProposalStore<TRecipe>;\n expectedAccountId: string;\n graceSeconds?: number;\n now?: Date;\n }\n): Promise<ProposalRecord<TRecipe>> {\n const { proposalStore, expectedAccountId, graceSeconds = 0, now } = args;\n const record = await proposalStore.get(proposalId, { expectedAccountId });\n if (!record) {\n throw new AdcpError('PROPOSAL_NOT_FOUND', {\n recovery: 'correctable',\n message:\n `Proposal ${JSON.stringify(proposalId)} not found. The buyer must call get_products ` +\n `with buying_mode='refine' and refine=[{action:'finalize',...}] to obtain a ` +\n `committed proposal_id before referencing it on create_media_buy.`,\n field: 'proposal_id',\n });\n }\n if (record.state !== 'committed') {\n throw new AdcpError('PROPOSAL_NOT_COMMITTED', {\n recovery: 'correctable',\n message:\n `Proposal ${JSON.stringify(proposalId)} is in state ${JSON.stringify(record.state)}; ` +\n `only committed proposals can be accepted via create_media_buy. Call get_products ` +\n `with buying_mode='refine' and action='finalize' first.`,\n field: 'proposal_id',\n });\n }\n if (record.expiresAt) {\n const current = now ?? new Date();\n const deadline = record.expiresAt.getTime() + graceSeconds * 1000;\n if (current.getTime() > deadline) {\n logExpired({\n proposalId,\n accountId: record.accountId,\n now: current,\n expiresAt: record.expiresAt,\n graceSeconds,\n });\n throw new AdcpError('PROPOSAL_EXPIRED', {\n // Per AdCP 3.0.6 schemas/cache/3.0.6/enums/error-code.json,\n // PROPOSAL_EXPIRED is `correctable` — the buyer re-discovers\n // via `get_products` to obtain a fresh proposal (the hold\n // window has lapsed but the buyer can re-request).\n recovery: 'correctable',\n message:\n `Proposal ${JSON.stringify(proposalId)} expired at ${record.expiresAt.toISOString()}; ` +\n `create_media_buy must be called within the inventory hold window. Call get_products ` +\n `with buying_mode='refine' and action='finalize' to request a fresh hold.`,\n field: 'proposal_id',\n });\n }\n }\n return record;\n}\n\n// ---------------------------------------------------------------------------\n// D4 — capability-overlap validation\n// ---------------------------------------------------------------------------\n\ninterface PackageLike {\n product_id?: string;\n pricing_option_id?: string;\n pricing_model?: string;\n delivery_type?: string;\n signal_type?: string;\n targeting_overlay?: Record<string, unknown>;\n /**\n * Adopter-resolved pricing model — the validation reads this when\n * present. Set by the framework when it has resolved\n * `pricing_option_id` against the product's pricing options.\n */\n _resolved_pricing_model?: string;\n _resolved_delivery_type?: string;\n [k: string]: unknown;\n}\n\n/**\n * Pre-adapter validation seam: walk buyer's packages against each recipe's\n * `capability_overlap` and reject mismatches.\n *\n * Called from the framework's `create_media_buy` and `update_media_buy`\n * dispatch paths after recipes are hydrated from the {@link ProposalStore}.\n * Per D4, the framework owns this gate so every adopter doesn't write the\n * same intersection logic.\n *\n * Validation axes (per {@link CapabilityOverlap}):\n *\n * - `pricingModels` — checked against `package._resolved_pricing_model`\n * or `package.pricing_model`.\n * - `targetingDimensions` — checked against `package.targeting_overlay` keys.\n * - `deliveryTypes` — checked against `package._resolved_delivery_type`\n * or `package.delivery_type`.\n * - `signalTypes` — checked against `package.signal_type`.\n *\n * Per the design's undefined-vs-empty-set semantics: `undefined` skips the\n * gate; an explicit `Set` (including the empty set) is enforced.\n *\n * @public\n */\nexport function validateCapabilityOverlap<TRecipe extends Recipe>(args: {\n packages: readonly PackageLike[];\n recipes: ReadonlyMap<string, TRecipe>;\n fieldPathPrefix?: string;\n}): void {\n const { packages, recipes, fieldPathPrefix = 'packages' } = args;\n for (let i = 0; i < packages.length; i++) {\n const pkg = packages[i]!;\n const productId = pkg.product_id;\n if (!productId) continue;\n const recipe = recipes.get(productId);\n if (!recipe || !recipe.capability_overlap) continue;\n const overlap = recipe.capability_overlap;\n\n if (overlap.pricingModels) {\n const requested = pkg._resolved_pricing_model ?? pkg.pricing_model;\n if (requested !== undefined && !overlap.pricingModels.has(String(requested))) {\n throw new AdcpError('INVALID_REQUEST', {\n recovery: 'terminal',\n message:\n `Buyer requested pricing_model=${JSON.stringify(requested)} on package ` +\n `${JSON.stringify(productId)}, but this product's recipe declares ` +\n `capability_overlap.pricingModels=${JSON.stringify([...overlap.pricingModels].sort())}. ` +\n `The seller did not enable that pricing model for this product.`,\n field: `${fieldPathPrefix}[${i}].pricing_option_id`,\n });\n }\n }\n\n if (overlap.targetingDimensions) {\n const overlay = pkg.targeting_overlay;\n const keys = overlay && typeof overlay === 'object' ? Object.keys(overlay) : [];\n const disallowed = keys.filter(k => !overlap.targetingDimensions!.has(k));\n if (disallowed.length > 0) {\n throw new AdcpError('INVALID_REQUEST', {\n recovery: 'terminal',\n message:\n `Buyer requested targeting dimensions ${JSON.stringify(disallowed.sort())} on ` +\n `package ${JSON.stringify(productId)}, but this product's recipe declares ` +\n `capability_overlap.targetingDimensions=` +\n `${JSON.stringify([...overlap.targetingDimensions].sort())}. The seller did not ` +\n `enable those targeting dimensions for this product.`,\n field: `${fieldPathPrefix}[${i}].targeting_overlay`,\n });\n }\n }\n\n if (overlap.deliveryTypes) {\n const delivery = pkg._resolved_delivery_type ?? pkg.delivery_type;\n if (delivery !== undefined && !overlap.deliveryTypes.has(String(delivery))) {\n throw new AdcpError('INVALID_REQUEST', {\n recovery: 'terminal',\n message:\n `Buyer requested delivery_type=${JSON.stringify(delivery)} on package ` +\n `${JSON.stringify(productId)}, but this product's recipe declares ` +\n `capability_overlap.deliveryTypes=${JSON.stringify([...overlap.deliveryTypes].sort())}.`,\n field: `${fieldPathPrefix}[${i}].delivery_type`,\n });\n }\n }\n\n if (overlap.signalTypes) {\n const signalType = pkg.signal_type;\n if (signalType !== undefined && !overlap.signalTypes.has(String(signalType))) {\n throw new AdcpError('INVALID_REQUEST', {\n recovery: 'terminal',\n message:\n `Buyer requested signal_type=${JSON.stringify(signalType)} on package ` +\n `${JSON.stringify(productId)}, but this product's recipe declares ` +\n `capability_overlap.signalTypes=${JSON.stringify([...overlap.signalTypes].sort())}.`,\n field: `${fieldPathPrefix}[${i}].signal_type`,\n });\n }\n }\n }\n}\n\n/**\n * Validate `recipe.capability_overlap` is a subset of the matching product's\n * wire-declared capabilities.\n *\n * Called at `putDraft` time. Mismatches throw `INTERNAL_ERROR` — this is\n * an adopter bug (the manager declared an overlap claiming capabilities the\n * wire shape doesn't advertise), not a buyer bug.\n *\n * @public\n */\nexport function validateOverlapSubsetOfWire<TRecipe extends Recipe>(args: {\n recipes: ReadonlyMap<string, TRecipe>;\n products: readonly Product[];\n}): void {\n const productsById = new Map<string, Product>();\n for (const p of args.products) {\n if (p.product_id) productsById.set(p.product_id, p);\n }\n for (const [productId, recipe] of args.recipes) {\n if (!recipe.capability_overlap) continue;\n const product = productsById.get(productId);\n if (!product) continue; // missing-product is caught elsewhere\n const overlap = recipe.capability_overlap;\n\n if (overlap.pricingModels) {\n const wirePricing = wirePricingModels(product);\n const extras = [...overlap.pricingModels].filter(p => !wirePricing.has(p));\n if (extras.length > 0) {\n throw new AdcpError('INTERNAL_ERROR', {\n recovery: 'terminal',\n message:\n `Recipe for product ${JSON.stringify(productId)} declares ` +\n `capability_overlap.pricingModels=${JSON.stringify([...overlap.pricingModels].sort())} ` +\n `including ${JSON.stringify(extras.sort())}, but the wire product only advertises ` +\n `${JSON.stringify([...wirePricing].sort())}. The recipe's overlap must be a subset ` +\n `of the wire-declared capabilities; adopter declaration is inconsistent with the ` +\n `product shape.`,\n });\n }\n }\n\n if (overlap.deliveryTypes) {\n const wireDelivery = wireDeliveryTypes(product);\n const extras = [...overlap.deliveryTypes].filter(p => !wireDelivery.has(p));\n // Only enforce when the wire declares something — products lacking a\n // delivery_type field shouldn't trip the gate.\n if (extras.length > 0 && wireDelivery.size > 0) {\n throw new AdcpError('INTERNAL_ERROR', {\n recovery: 'terminal',\n message:\n `Recipe for product ${JSON.stringify(productId)} declares ` +\n `capability_overlap.deliveryTypes=${JSON.stringify([...overlap.deliveryTypes].sort())} ` +\n `including ${JSON.stringify(extras.sort())}, but the wire product only advertises ` +\n `${JSON.stringify([...wireDelivery].sort())}.`,\n });\n }\n }\n }\n}\n\nfunction wirePricingModels(product: Product): Set<string> {\n const out = new Set<string>();\n const pricing = (product as { pricing_options?: ReadonlyArray<{ pricing_model?: string }> }).pricing_options;\n if (!pricing) return out;\n for (const opt of pricing) {\n if (opt.pricing_model) out.add(String(opt.pricing_model));\n }\n return out;\n}\n\nfunction wireDeliveryTypes(product: Product): Set<string> {\n const dt = (product as { delivery_type?: string }).delivery_type;\n return dt ? new Set([String(dt)]) : new Set();\n}\n\n// ---------------------------------------------------------------------------\n// Finalize action detection\n// ---------------------------------------------------------------------------\n\n/**\n * Result of {@link detectFinalizeAction}: the index, proposal_id, and\n * optional ask of the first finalize-action refine entry.\n *\n * @public\n */\nexport interface FinalizeActionRef {\n index: number;\n proposalId: string;\n ask?: string;\n}\n\n/**\n * Return the first finalize-action refine entry from a\n * `GetProductsRequest`, or `null` if no finalize entry exists.\n *\n * The index points at the entry's position in `refine[]` so the framework\n * can produce indexed wire field paths (`refine[3].proposal_id`) on\n * rejection — buyers parsing the error get a precise pointer.\n *\n * Per the spec, `buying_mode: 'refine'` carries a `refine[]` array of\n * entries. Each entry has a `scope` (`request` / `product` / `proposal`)\n * and an optional `action` (`include` / `omit` / `finalize`). v1.5 only\n * intercepts `proposal`-scoped entries with `action: 'finalize'`.\n *\n * The framework processes ONE finalize entry per request; if the buyer\n * sends multiple finalize entries, only the first is processed (rest fall\n * through to the standard refine path).\n *\n * @public\n */\nexport function detectFinalizeAction(req: CanonicalGetProductsRequest): FinalizeActionRef | null {\n const refine = (req as { refine?: ReadonlyArray<Record<string, unknown>> }).refine;\n if (!refine || refine.length === 0) return null;\n for (let index = 0; index < refine.length; index++) {\n const entry = refine[index]!;\n if (entry.scope === 'proposal' && entry.action === 'finalize') {\n const proposalId = entry.proposal_id;\n if (typeof proposalId === 'string' && proposalId.length > 0) {\n const ask = typeof entry.ask === 'string' ? entry.ask : undefined;\n return ask !== undefined ? { index, proposalId, ask } : { index, proposalId };\n }\n }\n }\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Structured logging — § Observability\n// ---------------------------------------------------------------------------\n\n/**\n * Logger-shaped sink for structured proposal-lifecycle events. Defaults to\n * `console.info`-style emission; the dispatch path can pass a typed logger\n * to route through the rest of the framework's logging.\n *\n * @public\n */\nexport interface ProposalLifecycleLogger {\n info(message: string, fields?: Record<string, unknown>): void;\n}\n\nlet logger: ProposalLifecycleLogger = {\n info: (message, fields) => {\n if (fields) {\n console.log(JSON.stringify({ message, ...fields }));\n } else {\n console.log(message);\n }\n },\n};\n\n/**\n * Replace the module-level logger that proposal-lifecycle structured\n * events (`proposal.draft_persisted`, `proposal.finalized`, `proposal.expired`,\n * `proposal.consumed`) emit through. Adopters wire this to their existing\n * logger (pino, bunyan, etc.) so lifecycle events route through the same\n * pipeline as the rest of their server logs. Tests use it to capture\n * structured emissions for assertion.\n *\n * @public\n */\nexport function setProposalLifecycleLogger(next: ProposalLifecycleLogger): void {\n logger = next;\n}\n\n/** `proposal.draft_persisted` event. */\nexport function logDraftPersisted(args: { proposalId: string; accountId: string; recipesCount: number }): void {\n logger.info('proposal.draft_persisted', {\n event: 'proposal.draft_persisted',\n proposal_id: args.proposalId,\n account_id: args.accountId,\n recipes_count: args.recipesCount,\n });\n}\n\n/**\n * `proposal.finalized` event. `path` is `'inline'` or `'handoff'`.\n */\nexport function logFinalizeSucceeded(args: {\n proposalId: string;\n accountId: string;\n expiresAt: Date;\n path: 'inline' | 'handoff';\n}): void {\n logger.info('proposal.finalized', {\n event: 'proposal.finalized',\n proposal_id: args.proposalId,\n account_id: args.accountId,\n expires_at: args.expiresAt.toISOString(),\n path: args.path,\n });\n}\n\n/** `proposal.expired` event. */\nexport function logExpired(args: {\n proposalId: string;\n accountId: string;\n now: Date;\n expiresAt: Date;\n graceSeconds: number;\n}): void {\n logger.info('proposal.expired', {\n event: 'proposal.expired',\n proposal_id: args.proposalId,\n account_id: args.accountId,\n now: args.now.toISOString(),\n expires_at: args.expiresAt.toISOString(),\n grace_seconds: args.graceSeconds,\n });\n}\n\n/** `proposal.consumed` event. */\nexport function logConsumed(args: { proposalId: string; accountId: string; mediaBuyId: string }): void {\n logger.info('proposal.consumed', {\n event: 'proposal.consumed',\n proposal_id: args.proposalId,\n account_id: args.accountId,\n media_buy_id: args.mediaBuyId,\n });\n}\n"],"mappings":"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","names":[]}
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
*/
|
|
29
29
|
import type { Account } from '../account.mjs';
|
|
30
30
|
import type { RequestContext } from '../context.mjs';
|
|
31
|
-
import type {
|
|
31
|
+
import type { CanonicalGetProductsRequest } from '../../../v2/projection/creative-delivery.mjs';
|
|
32
32
|
import type { ProposalCapabilities, ProposalGetProductsPayload, ProposalManager, ProposalSalesSpecialism, Recipe } from './types.mjs';
|
|
33
33
|
/**
|
|
34
34
|
* Construction options for {@link MockProposalManager}.
|
|
@@ -86,7 +86,7 @@ export declare class MockProposalManager<TRecipe extends Recipe = Recipe, TCtxMe
|
|
|
86
86
|
constructor(options: MockProposalManagerOptions);
|
|
87
87
|
/** The configured mock-server URL — useful for diagnostics. */
|
|
88
88
|
get mockUpstreamUrl(): string;
|
|
89
|
-
getProducts(req:
|
|
90
|
-
refineProducts(req:
|
|
89
|
+
getProducts(req: CanonicalGetProductsRequest, _ctx: RequestContext<Account<TCtxMeta>>): Promise<ProposalGetProductsPayload>;
|
|
90
|
+
refineProducts(req: CanonicalGetProductsRequest, _ctx: RequestContext<Account<TCtxMeta>>): Promise<ProposalGetProductsPayload>;
|
|
91
91
|
private forward;
|
|
92
92
|
}
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
*/
|
|
29
29
|
import type { Account } from '../account';
|
|
30
30
|
import type { RequestContext } from '../context';
|
|
31
|
-
import type {
|
|
31
|
+
import type { CanonicalGetProductsRequest } from '../../../v2/projection/creative-delivery';
|
|
32
32
|
import type { ProposalCapabilities, ProposalGetProductsPayload, ProposalManager, ProposalSalesSpecialism, Recipe } from './types';
|
|
33
33
|
/**
|
|
34
34
|
* Construction options for {@link MockProposalManager}.
|
|
@@ -86,8 +86,8 @@ export declare class MockProposalManager<TRecipe extends Recipe = Recipe, TCtxMe
|
|
|
86
86
|
constructor(options: MockProposalManagerOptions);
|
|
87
87
|
/** The configured mock-server URL — useful for diagnostics. */
|
|
88
88
|
get mockUpstreamUrl(): string;
|
|
89
|
-
getProducts(req:
|
|
90
|
-
refineProducts(req:
|
|
89
|
+
getProducts(req: CanonicalGetProductsRequest, _ctx: RequestContext<Account<TCtxMeta>>): Promise<ProposalGetProductsPayload>;
|
|
90
|
+
refineProducts(req: CanonicalGetProductsRequest, _ctx: RequestContext<Account<TCtxMeta>>): Promise<ProposalGetProductsPayload>;
|
|
91
91
|
private forward;
|
|
92
92
|
}
|
|
93
93
|
//# sourceMappingURL=mock-manager.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mock-manager.d.ts","sourceRoot":"","sources":["../../../../../src/lib/server/decisioning/proposal/mock-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"mock-manager.d.ts","sourceRoot":"","sources":["../../../../../src/lib/server/decisioning/proposal/mock-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,0CAA0C,CAAC;AAC5F,OAAO,KAAK,EACV,oBAAoB,EACpB,0BAA0B,EAC1B,eAAe,EACf,uBAAuB,EACvB,MAAM,EACP,MAAM,SAAS,CAAC;AAEjB;;;;GAIG;AACH,MAAM,WAAW,0BAA0B;IACzC;;;;;;OAMG;IACH,eAAe,EAAE,MAAM,CAAC;IAExB;;;;;OAKG;IACH,eAAe,CAAC,EAAE,uBAAuB,CAAC;IAE1C;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB;;OAEG;IACH,cAAc,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAElD;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;OAGG;IACH,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;CACjC;AAED;;;;;GAKG;AACH,qBAAa,mBAAmB,CAAC,OAAO,SAAS,MAAM,GAAG,MAAM,EAAE,QAAQ,GAAG,OAAO,CAAE,YAAW,eAAe,CAC9G,OAAO,EACP,QAAQ,CACT;IACC,QAAQ,CAAC,YAAY,EAAE,oBAAoB,CAAC;IAC5C,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAC7B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAmC;IAC3D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA0B;gBAExC,OAAO,EAAE,0BAA0B;IAsB/C,+DAA+D;IAC/D,IAAI,eAAe,IAAI,MAAM,CAE5B;IAEK,WAAW,CACf,GAAG,EAAE,2BAA2B,EAChC,IAAI,EAAE,cAAc,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,GACtC,OAAO,CAAC,0BAA0B,CAAC;IAIhC,cAAc,CAClB,GAAG,EAAE,2BAA2B,EAChC,IAAI,EAAE,cAAc,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,GACtC,OAAO,CAAC,0BAA0B,CAAC;YAaxB,OAAO;CA2BtB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../src/lib/server/decisioning/proposal/mock-manager.ts"],"sourcesContent":["/**\n * MockProposalManager — v1 default forwarder.\n *\n * Symmetric with the mock-mode dispatch pattern adopters use to point\n * `DecisioningPlatform` upstreams at a running `bin/adcp.js mock-server`.\n * Adopters who don't yet have proposal logic of their own start with this\n * class pointed at the appropriate mock-server specialism; their first\n * working seller agent runs against the mock fixtures with zero adopter\n * code on the proposal side. They implement their own\n * {@link ProposalManager} subtype incrementally as they replace\n * mock-served slices with real assembly logic.\n *\n * The mock-server lifecycle is **not** managed by the SDK. Adopters or CI\n * start it as needed (`bin/adcp.js mock-server sales-non-guaranteed`) and\n * pass the resulting URL to this class's constructor.\n *\n * Ports `adcp-client-python.src/adcp/decisioning/proposal_manager.py`'s\n * `MockProposalManager` (PR #504).\n *\n * **Runtime requirement**: uses `globalThis.fetch`, which lands as a built-in\n * on Node 20+ (the package declares `\"engines\": { \"node\": \">=20.0.0\" }`).\n * Adopters running on older Node, on Bun without the global, or in any\n * environment without a global `fetch` should pass an explicit `fetch`\n * implementation via the `fetch` constructor option.\n *\n * @public\n * @packageDocumentation\n */\n\nimport type { Account } from '../account';\nimport type { RequestContext } from '../context';\nimport type {
|
|
1
|
+
{"version":3,"sources":["../../../../../src/lib/server/decisioning/proposal/mock-manager.ts"],"sourcesContent":["/**\n * MockProposalManager — v1 default forwarder.\n *\n * Symmetric with the mock-mode dispatch pattern adopters use to point\n * `DecisioningPlatform` upstreams at a running `bin/adcp.js mock-server`.\n * Adopters who don't yet have proposal logic of their own start with this\n * class pointed at the appropriate mock-server specialism; their first\n * working seller agent runs against the mock fixtures with zero adopter\n * code on the proposal side. They implement their own\n * {@link ProposalManager} subtype incrementally as they replace\n * mock-served slices with real assembly logic.\n *\n * The mock-server lifecycle is **not** managed by the SDK. Adopters or CI\n * start it as needed (`bin/adcp.js mock-server sales-non-guaranteed`) and\n * pass the resulting URL to this class's constructor.\n *\n * Ports `adcp-client-python.src/adcp/decisioning/proposal_manager.py`'s\n * `MockProposalManager` (PR #504).\n *\n * **Runtime requirement**: uses `globalThis.fetch`, which lands as a built-in\n * on Node 20+ (the package declares `\"engines\": { \"node\": \">=20.0.0\" }`).\n * Adopters running on older Node, on Bun without the global, or in any\n * environment without a global `fetch` should pass an explicit `fetch`\n * implementation via the `fetch` constructor option.\n *\n * @public\n * @packageDocumentation\n */\n\nimport type { Account } from '../account';\nimport type { RequestContext } from '../context';\nimport type { CanonicalGetProductsRequest } from '../../../v2/projection/creative-delivery';\nimport type {\n ProposalCapabilities,\n ProposalGetProductsPayload,\n ProposalManager,\n ProposalSalesSpecialism,\n Recipe,\n} from './types';\n\n/**\n * Construction options for {@link MockProposalManager}.\n *\n * @public\n */\nexport interface MockProposalManagerOptions {\n /**\n * URL of the running mock-server. The forwarder POSTs\n * `GetProductsRequest` payloads to `${mockUpstreamUrl}/get_products`\n * and (when refine is enabled) `${mockUpstreamUrl}/refine_products`.\n *\n * Required and non-empty.\n */\n mockUpstreamUrl: string;\n\n /**\n * Which sales specialism this mock manager serves. Defaults to\n * `sales-non-guaranteed` (the catalog-style mock-server fixture).\n * Adopters wiring a guaranteed mock pass `sales-guaranteed` so the\n * framework's capability projection matches the fixtures.\n */\n salesSpecialism?: ProposalSalesSpecialism;\n\n /**\n * When true, the manager declares `refine` capability and forwards\n * `buying_mode: 'refine'` requests to `/refine_products`. Default\n * false — the framework falls through to `getProducts` for refine.\n */\n refine?: boolean;\n\n /**\n * Headers forwarded on every mock-server request (e.g. `X-Tenant-Id`).\n */\n defaultHeaders?: Readonly<Record<string, string>>;\n\n /**\n * Per-request timeout in milliseconds. Default 30 seconds.\n */\n timeoutMs?: number;\n\n /**\n * Optional `fetch` override for testing. Defaults to the global\n * `fetch` (Node 18+).\n */\n fetch?: typeof globalThis.fetch;\n}\n\n/**\n * v1 default forwarder. Dispatches `getProducts` / `refineProducts` to\n * a running mock-server.\n *\n * @public\n */\nexport class MockProposalManager<TRecipe extends Recipe = Recipe, TCtxMeta = unknown> implements ProposalManager<\n TRecipe,\n TCtxMeta\n> {\n readonly capabilities: ProposalCapabilities;\n private readonly url: string;\n private readonly headers: Readonly<Record<string, string>>;\n private readonly timeoutMs: number;\n private readonly fetchImpl: typeof globalThis.fetch;\n\n constructor(options: MockProposalManagerOptions) {\n if (!options.mockUpstreamUrl || typeof options.mockUpstreamUrl !== 'string') {\n throw new Error(\n 'MockProposalManager requires a non-empty `mockUpstreamUrl` pointing at a ' +\n 'running `bin/adcp.js mock-server <specialism>` instance.'\n );\n }\n this.capabilities = {\n salesSpecialism: options.salesSpecialism ?? 'sales-non-guaranteed',\n refine: options.refine ?? false,\n };\n // Strip trailing slashes (loop-based, not regex) so we keep\n // `${url}/get_products` clean without a quantifier-and-anchor\n // pattern CodeQL flags as polynomial-ReDoS-adjacent.\n let trimmed = options.mockUpstreamUrl;\n while (trimmed.endsWith('/')) trimmed = trimmed.slice(0, -1);\n this.url = trimmed;\n this.headers = options.defaultHeaders ?? {};\n this.timeoutMs = options.timeoutMs ?? 30_000;\n this.fetchImpl = options.fetch ?? globalThis.fetch;\n }\n\n /** The configured mock-server URL — useful for diagnostics. */\n get mockUpstreamUrl(): string {\n return this.url;\n }\n\n async getProducts(\n req: CanonicalGetProductsRequest,\n _ctx: RequestContext<Account<TCtxMeta>>\n ): Promise<ProposalGetProductsPayload> {\n return this.forward('/get_products', req);\n }\n\n async refineProducts(\n req: CanonicalGetProductsRequest,\n _ctx: RequestContext<Account<TCtxMeta>>\n ): Promise<ProposalGetProductsPayload> {\n if (!this.capabilities.refine) {\n // Adopter wired the manager without refine but the framework\n // dispatched here anyway — surface the inconsistency rather than\n // silently forwarding.\n throw new Error(\n 'MockProposalManager.refineProducts called but capabilities.refine is false. ' +\n 'Pass `refine: true` to the constructor to enable refine forwarding.'\n );\n }\n return this.forward('/refine_products', req);\n }\n\n private async forward(path: string, body: unknown): Promise<ProposalGetProductsPayload> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeoutMs);\n try {\n const response = await this.fetchImpl(`${this.url}${path}`, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n accept: 'application/json',\n ...this.headers,\n },\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n if (!response.ok) {\n const text = await response.text().catch(() => '');\n throw new Error(\n `MockProposalManager: mock-server returned ${response.status} for ${path}` +\n (text ? `: ${text.slice(0, 500)}` : '')\n );\n }\n const json = (await response.json()) as ProposalGetProductsPayload;\n return json;\n } finally {\n clearTimeout(timer);\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AA6FO,MAAM,oBAGX;AAAA,EACS;AAAA,EACQ;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEjB,YAAY,SAAqC;AAC/C,QAAI,CAAC,QAAQ,mBAAmB,OAAO,QAAQ,oBAAoB,UAAU;AAC3E,YAAM,IAAI;AAAA,QACR;AAAA,MAEF;AAAA,IACF;AACA,SAAK,eAAe;AAAA,MAClB,iBAAiB,QAAQ,mBAAmB;AAAA,MAC5C,QAAQ,QAAQ,UAAU;AAAA,IAC5B;AAIA,QAAI,UAAU,QAAQ;AACtB,WAAO,QAAQ,SAAS,GAAG,EAAG,WAAU,QAAQ,MAAM,GAAG,EAAE;AAC3D,SAAK,MAAM;AACX,SAAK,UAAU,QAAQ,kBAAkB,CAAC;AAC1C,SAAK,YAAY,QAAQ,aAAa;AACtC,SAAK,YAAY,QAAQ,SAAS,WAAW;AAAA,EAC/C;AAAA;AAAA,EAGA,IAAI,kBAA0B;AAC5B,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAM,YACJ,KACA,MACqC;AACrC,WAAO,KAAK,QAAQ,iBAAiB,GAAG;AAAA,EAC1C;AAAA,EAEA,MAAM,eACJ,KACA,MACqC;AACrC,QAAI,CAAC,KAAK,aAAa,QAAQ;AAI7B,YAAM,IAAI;AAAA,QACR;AAAA,MAEF;AAAA,IACF;AACA,WAAO,KAAK,QAAQ,oBAAoB,GAAG;AAAA,EAC7C;AAAA,EAEA,MAAc,QAAQ,MAAc,MAAoD;AACtF,UAAM,aAAa,IAAI,gBAAgB;AACvC,UAAM,QAAQ,WAAW,MAAM,WAAW,MAAM,GAAG,KAAK,SAAS;AACjE,QAAI;AACF,YAAM,WAAW,MAAM,KAAK,UAAU,GAAG,KAAK,GAAG,GAAG,IAAI,IAAI;AAAA,QAC1D,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,QAAQ;AAAA,UACR,GAAG,KAAK;AAAA,QACV;AAAA,QACA,MAAM,KAAK,UAAU,IAAI;AAAA,QACzB,QAAQ,WAAW;AAAA,MACrB,CAAC;AACD,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,OAAO,MAAM,SAAS,KAAK,EAAE,MAAM,MAAM,EAAE;AACjD,cAAM,IAAI;AAAA,UACR,6CAA6C,SAAS,MAAM,QAAQ,IAAI,MACrE,OAAO,KAAK,KAAK,MAAM,GAAG,GAAG,CAAC,KAAK;AAAA,QACxC;AAAA,MACF;AACA,YAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,aAAO;AAAA,IACT,UAAE;AACA,mBAAa,KAAK;AAAA,IACpB;AAAA,EACF;AACF;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../src/lib/server/decisioning/proposal/mock-manager.ts"],"sourcesContent":["/**\n * MockProposalManager — v1 default forwarder.\n *\n * Symmetric with the mock-mode dispatch pattern adopters use to point\n * `DecisioningPlatform` upstreams at a running `bin/adcp.js mock-server`.\n * Adopters who don't yet have proposal logic of their own start with this\n * class pointed at the appropriate mock-server specialism; their first\n * working seller agent runs against the mock fixtures with zero adopter\n * code on the proposal side. They implement their own\n * {@link ProposalManager} subtype incrementally as they replace\n * mock-served slices with real assembly logic.\n *\n * The mock-server lifecycle is **not** managed by the SDK. Adopters or CI\n * start it as needed (`bin/adcp.js mock-server sales-non-guaranteed`) and\n * pass the resulting URL to this class's constructor.\n *\n * Ports `adcp-client-python.src/adcp/decisioning/proposal_manager.py`'s\n * `MockProposalManager` (PR #504).\n *\n * **Runtime requirement**: uses `globalThis.fetch`, which lands as a built-in\n * on Node 20+ (the package declares `\"engines\": { \"node\": \">=20.0.0\" }`).\n * Adopters running on older Node, on Bun without the global, or in any\n * environment without a global `fetch` should pass an explicit `fetch`\n * implementation via the `fetch` constructor option.\n *\n * @public\n * @packageDocumentation\n */\n\nimport type { Account } from '../account';\nimport type { RequestContext } from '../context';\nimport type {
|
|
1
|
+
{"version":3,"sources":["../../../../../src/lib/server/decisioning/proposal/mock-manager.ts"],"sourcesContent":["/**\n * MockProposalManager — v1 default forwarder.\n *\n * Symmetric with the mock-mode dispatch pattern adopters use to point\n * `DecisioningPlatform` upstreams at a running `bin/adcp.js mock-server`.\n * Adopters who don't yet have proposal logic of their own start with this\n * class pointed at the appropriate mock-server specialism; their first\n * working seller agent runs against the mock fixtures with zero adopter\n * code on the proposal side. They implement their own\n * {@link ProposalManager} subtype incrementally as they replace\n * mock-served slices with real assembly logic.\n *\n * The mock-server lifecycle is **not** managed by the SDK. Adopters or CI\n * start it as needed (`bin/adcp.js mock-server sales-non-guaranteed`) and\n * pass the resulting URL to this class's constructor.\n *\n * Ports `adcp-client-python.src/adcp/decisioning/proposal_manager.py`'s\n * `MockProposalManager` (PR #504).\n *\n * **Runtime requirement**: uses `globalThis.fetch`, which lands as a built-in\n * on Node 20+ (the package declares `\"engines\": { \"node\": \">=20.0.0\" }`).\n * Adopters running on older Node, on Bun without the global, or in any\n * environment without a global `fetch` should pass an explicit `fetch`\n * implementation via the `fetch` constructor option.\n *\n * @public\n * @packageDocumentation\n */\n\nimport type { Account } from '../account';\nimport type { RequestContext } from '../context';\nimport type { CanonicalGetProductsRequest } from '../../../v2/projection/creative-delivery';\nimport type {\n ProposalCapabilities,\n ProposalGetProductsPayload,\n ProposalManager,\n ProposalSalesSpecialism,\n Recipe,\n} from './types';\n\n/**\n * Construction options for {@link MockProposalManager}.\n *\n * @public\n */\nexport interface MockProposalManagerOptions {\n /**\n * URL of the running mock-server. The forwarder POSTs\n * `GetProductsRequest` payloads to `${mockUpstreamUrl}/get_products`\n * and (when refine is enabled) `${mockUpstreamUrl}/refine_products`.\n *\n * Required and non-empty.\n */\n mockUpstreamUrl: string;\n\n /**\n * Which sales specialism this mock manager serves. Defaults to\n * `sales-non-guaranteed` (the catalog-style mock-server fixture).\n * Adopters wiring a guaranteed mock pass `sales-guaranteed` so the\n * framework's capability projection matches the fixtures.\n */\n salesSpecialism?: ProposalSalesSpecialism;\n\n /**\n * When true, the manager declares `refine` capability and forwards\n * `buying_mode: 'refine'` requests to `/refine_products`. Default\n * false — the framework falls through to `getProducts` for refine.\n */\n refine?: boolean;\n\n /**\n * Headers forwarded on every mock-server request (e.g. `X-Tenant-Id`).\n */\n defaultHeaders?: Readonly<Record<string, string>>;\n\n /**\n * Per-request timeout in milliseconds. Default 30 seconds.\n */\n timeoutMs?: number;\n\n /**\n * Optional `fetch` override for testing. Defaults to the global\n * `fetch` (Node 18+).\n */\n fetch?: typeof globalThis.fetch;\n}\n\n/**\n * v1 default forwarder. Dispatches `getProducts` / `refineProducts` to\n * a running mock-server.\n *\n * @public\n */\nexport class MockProposalManager<TRecipe extends Recipe = Recipe, TCtxMeta = unknown> implements ProposalManager<\n TRecipe,\n TCtxMeta\n> {\n readonly capabilities: ProposalCapabilities;\n private readonly url: string;\n private readonly headers: Readonly<Record<string, string>>;\n private readonly timeoutMs: number;\n private readonly fetchImpl: typeof globalThis.fetch;\n\n constructor(options: MockProposalManagerOptions) {\n if (!options.mockUpstreamUrl || typeof options.mockUpstreamUrl !== 'string') {\n throw new Error(\n 'MockProposalManager requires a non-empty `mockUpstreamUrl` pointing at a ' +\n 'running `bin/adcp.js mock-server <specialism>` instance.'\n );\n }\n this.capabilities = {\n salesSpecialism: options.salesSpecialism ?? 'sales-non-guaranteed',\n refine: options.refine ?? false,\n };\n // Strip trailing slashes (loop-based, not regex) so we keep\n // `${url}/get_products` clean without a quantifier-and-anchor\n // pattern CodeQL flags as polynomial-ReDoS-adjacent.\n let trimmed = options.mockUpstreamUrl;\n while (trimmed.endsWith('/')) trimmed = trimmed.slice(0, -1);\n this.url = trimmed;\n this.headers = options.defaultHeaders ?? {};\n this.timeoutMs = options.timeoutMs ?? 30_000;\n this.fetchImpl = options.fetch ?? globalThis.fetch;\n }\n\n /** The configured mock-server URL — useful for diagnostics. */\n get mockUpstreamUrl(): string {\n return this.url;\n }\n\n async getProducts(\n req: CanonicalGetProductsRequest,\n _ctx: RequestContext<Account<TCtxMeta>>\n ): Promise<ProposalGetProductsPayload> {\n return this.forward('/get_products', req);\n }\n\n async refineProducts(\n req: CanonicalGetProductsRequest,\n _ctx: RequestContext<Account<TCtxMeta>>\n ): Promise<ProposalGetProductsPayload> {\n if (!this.capabilities.refine) {\n // Adopter wired the manager without refine but the framework\n // dispatched here anyway — surface the inconsistency rather than\n // silently forwarding.\n throw new Error(\n 'MockProposalManager.refineProducts called but capabilities.refine is false. ' +\n 'Pass `refine: true` to the constructor to enable refine forwarding.'\n );\n }\n return this.forward('/refine_products', req);\n }\n\n private async forward(path: string, body: unknown): Promise<ProposalGetProductsPayload> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeoutMs);\n try {\n const response = await this.fetchImpl(`${this.url}${path}`, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n accept: 'application/json',\n ...this.headers,\n },\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n if (!response.ok) {\n const text = await response.text().catch(() => '');\n throw new Error(\n `MockProposalManager: mock-server returned ${response.status} for ${path}` +\n (text ? `: ${text.slice(0, 500)}` : '')\n );\n }\n const json = (await response.json()) as ProposalGetProductsPayload;\n return json;\n } finally {\n clearTimeout(timer);\n }\n }\n}\n"],"mappings":"AA6FO,MAAM,oBAGX;AAAA,EACS;AAAA,EACQ;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEjB,YAAY,SAAqC;AAC/C,QAAI,CAAC,QAAQ,mBAAmB,OAAO,QAAQ,oBAAoB,UAAU;AAC3E,YAAM,IAAI;AAAA,QACR;AAAA,MAEF;AAAA,IACF;AACA,SAAK,eAAe;AAAA,MAClB,iBAAiB,QAAQ,mBAAmB;AAAA,MAC5C,QAAQ,QAAQ,UAAU;AAAA,IAC5B;AAIA,QAAI,UAAU,QAAQ;AACtB,WAAO,QAAQ,SAAS,GAAG,EAAG,WAAU,QAAQ,MAAM,GAAG,EAAE;AAC3D,SAAK,MAAM;AACX,SAAK,UAAU,QAAQ,kBAAkB,CAAC;AAC1C,SAAK,YAAY,QAAQ,aAAa;AACtC,SAAK,YAAY,QAAQ,SAAS,WAAW;AAAA,EAC/C;AAAA;AAAA,EAGA,IAAI,kBAA0B;AAC5B,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAM,YACJ,KACA,MACqC;AACrC,WAAO,KAAK,QAAQ,iBAAiB,GAAG;AAAA,EAC1C;AAAA,EAEA,MAAM,eACJ,KACA,MACqC;AACrC,QAAI,CAAC,KAAK,aAAa,QAAQ;AAI7B,YAAM,IAAI;AAAA,QACR;AAAA,MAEF;AAAA,IACF;AACA,WAAO,KAAK,QAAQ,oBAAoB,GAAG;AAAA,EAC7C;AAAA,EAEA,MAAc,QAAQ,MAAc,MAAoD;AACtF,UAAM,aAAa,IAAI,gBAAgB;AACvC,UAAM,QAAQ,WAAW,MAAM,WAAW,MAAM,GAAG,KAAK,SAAS;AACjE,QAAI;AACF,YAAM,WAAW,MAAM,KAAK,UAAU,GAAG,KAAK,GAAG,GAAG,IAAI,IAAI;AAAA,QAC1D,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,QAAQ;AAAA,UACR,GAAG,KAAK;AAAA,QACV;AAAA,QACA,MAAM,KAAK,UAAU,IAAI;AAAA,QACzB,QAAQ,WAAW;AAAA,MACrB,CAAC;AACD,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,OAAO,MAAM,SAAS,KAAK,EAAE,MAAM,MAAM,EAAE;AACjD,cAAM,IAAI;AAAA,UACR,6CAA6C,SAAS,MAAM,QAAQ,IAAI,MACrE,OAAO,KAAK,KAAK,MAAM,GAAG,GAAG,CAAC,KAAK;AAAA,QACxC;AAAA,MACF;AACA,YAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,aAAO;AAAA,IACT,UAAE;AACA,mBAAa,KAAK;AAAA,IACpB;AAAA,EACF;AACF;","names":[]}
|
|
@@ -31,10 +31,14 @@
|
|
|
31
31
|
import type { MaybePromise } from '../../create-adcp-server.mjs';
|
|
32
32
|
import type { Account } from '../account.mjs';
|
|
33
33
|
import type { RequestContext } from '../context.mjs';
|
|
34
|
-
import type {
|
|
34
|
+
import type { GetProductsResponse } from '../../../types/tools.generated.mjs';
|
|
35
|
+
import type { CanonicalCreativeResponse, CanonicalGetProductsRequest, CanonicalProduct } from '../../../v2/projection/creative-delivery.mjs';
|
|
35
36
|
import type { RequireCacheScopeWhenProducts, ServerPayload } from '../../../types/server-payload.mjs';
|
|
36
37
|
import type { TaskHandoff } from '../async-outcome.mjs';
|
|
37
|
-
export type ProposalGetProductsPayload = RequireCacheScopeWhenProducts<ServerPayload<GetProductsResponse
|
|
38
|
+
export type ProposalGetProductsPayload = RequireCacheScopeWhenProducts<Omit<ServerPayload<CanonicalCreativeResponse<GetProductsResponse>>, 'products'> & {
|
|
39
|
+
products?: CanonicalProduct[];
|
|
40
|
+
}>;
|
|
41
|
+
export type LegacyProposalGetProductsPayload = RequireCacheScopeWhenProducts<ServerPayload<GetProductsResponse>>;
|
|
38
42
|
/**
|
|
39
43
|
* Sales specialisms a {@link ProposalManager} can serve. Mirrors the
|
|
40
44
|
* `sales-*` slugs in the spec's specialism enum.
|
|
@@ -250,11 +254,11 @@ export interface FinalizeProposalRequest<TRecipe extends Recipe = Recipe> {
|
|
|
250
254
|
*/
|
|
251
255
|
ask?: string;
|
|
252
256
|
/**
|
|
253
|
-
* The parent
|
|
257
|
+
* The parent canonical get-products request so the adopter sees the full
|
|
254
258
|
* envelope (account, etc.) without the framework projecting fields
|
|
255
259
|
* one-by-one.
|
|
256
260
|
*/
|
|
257
|
-
parentRequest:
|
|
261
|
+
parentRequest: CanonicalGetProductsRequest;
|
|
258
262
|
}
|
|
259
263
|
/**
|
|
260
264
|
* Adopter-returned shape from {@link ProposalManager.finalizeProposal} —
|
|
@@ -352,7 +356,7 @@ export interface ProposalManager<TRecipe extends Recipe = Recipe, TCtxMeta = unk
|
|
|
352
356
|
* buyer drives the finalize transition via subsequent refine calls
|
|
353
357
|
* with `action: 'finalize'`.
|
|
354
358
|
*/
|
|
355
|
-
getProducts(req:
|
|
359
|
+
getProducts(req: CanonicalGetProductsRequest, ctx: Ctx<TCtxMeta>): MaybePromise<ProposalGetProductsPayload>;
|
|
356
360
|
/**
|
|
357
361
|
* Refine-mode iteration on a previous `getProducts` response.
|
|
358
362
|
*
|
|
@@ -372,7 +376,7 @@ export interface ProposalManager<TRecipe extends Recipe = Recipe, TCtxMeta = unk
|
|
|
372
376
|
* see those entries intercepted by the framework before this method
|
|
373
377
|
* is called.
|
|
374
378
|
*/
|
|
375
|
-
refineProducts?(req:
|
|
379
|
+
refineProducts?(req: CanonicalGetProductsRequest, ctx: Ctx<TCtxMeta>): MaybePromise<ProposalGetProductsPayload>;
|
|
376
380
|
/**
|
|
377
381
|
* Commit a draft proposal to firm pricing + inventory hold.
|
|
378
382
|
*
|
|
@@ -31,10 +31,14 @@
|
|
|
31
31
|
import type { MaybePromise } from '../../create-adcp-server';
|
|
32
32
|
import type { Account } from '../account';
|
|
33
33
|
import type { RequestContext } from '../context';
|
|
34
|
-
import type {
|
|
34
|
+
import type { GetProductsResponse } from '../../../types/tools.generated';
|
|
35
|
+
import type { CanonicalCreativeResponse, CanonicalGetProductsRequest, CanonicalProduct } from '../../../v2/projection/creative-delivery';
|
|
35
36
|
import type { RequireCacheScopeWhenProducts, ServerPayload } from '../../../types/server-payload';
|
|
36
37
|
import type { TaskHandoff } from '../async-outcome';
|
|
37
|
-
export type ProposalGetProductsPayload = RequireCacheScopeWhenProducts<ServerPayload<GetProductsResponse
|
|
38
|
+
export type ProposalGetProductsPayload = RequireCacheScopeWhenProducts<Omit<ServerPayload<CanonicalCreativeResponse<GetProductsResponse>>, 'products'> & {
|
|
39
|
+
products?: CanonicalProduct[];
|
|
40
|
+
}>;
|
|
41
|
+
export type LegacyProposalGetProductsPayload = RequireCacheScopeWhenProducts<ServerPayload<GetProductsResponse>>;
|
|
38
42
|
/**
|
|
39
43
|
* Sales specialisms a {@link ProposalManager} can serve. Mirrors the
|
|
40
44
|
* `sales-*` slugs in the spec's specialism enum.
|
|
@@ -250,11 +254,11 @@ export interface FinalizeProposalRequest<TRecipe extends Recipe = Recipe> {
|
|
|
250
254
|
*/
|
|
251
255
|
ask?: string;
|
|
252
256
|
/**
|
|
253
|
-
* The parent
|
|
257
|
+
* The parent canonical get-products request so the adopter sees the full
|
|
254
258
|
* envelope (account, etc.) without the framework projecting fields
|
|
255
259
|
* one-by-one.
|
|
256
260
|
*/
|
|
257
|
-
parentRequest:
|
|
261
|
+
parentRequest: CanonicalGetProductsRequest;
|
|
258
262
|
}
|
|
259
263
|
/**
|
|
260
264
|
* Adopter-returned shape from {@link ProposalManager.finalizeProposal} —
|
|
@@ -352,7 +356,7 @@ export interface ProposalManager<TRecipe extends Recipe = Recipe, TCtxMeta = unk
|
|
|
352
356
|
* buyer drives the finalize transition via subsequent refine calls
|
|
353
357
|
* with `action: 'finalize'`.
|
|
354
358
|
*/
|
|
355
|
-
getProducts(req:
|
|
359
|
+
getProducts(req: CanonicalGetProductsRequest, ctx: Ctx<TCtxMeta>): MaybePromise<ProposalGetProductsPayload>;
|
|
356
360
|
/**
|
|
357
361
|
* Refine-mode iteration on a previous `getProducts` response.
|
|
358
362
|
*
|
|
@@ -372,7 +376,7 @@ export interface ProposalManager<TRecipe extends Recipe = Recipe, TCtxMeta = unk
|
|
|
372
376
|
* see those entries intercepted by the framework before this method
|
|
373
377
|
* is called.
|
|
374
378
|
*/
|
|
375
|
-
refineProducts?(req:
|
|
379
|
+
refineProducts?(req: CanonicalGetProductsRequest, ctx: Ctx<TCtxMeta>): MaybePromise<ProposalGetProductsPayload>;
|
|
376
380
|
/**
|
|
377
381
|
* Commit a draft proposal to firm pricing + inventory hold.
|
|
378
382
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../src/lib/server/decisioning/proposal/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../src/lib/server/decisioning/proposal/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../src/lib/server/decisioning/proposal/types.ts"],"sourcesContent":["/**\n * ProposalManager — primitives for the two-platform composition.\n *\n * The existing `DecisioningPlatform` conflates two concerns: assembling\n * proposals from briefs (`get_products`, refine) vs. executing media buys\n * against an upstream (`create_media_buy`, `update_media_buy`,\n * `get_media_buy_delivery`). The two-platform composition splits them: a\n * separate {@link ProposalManager} handles the proposal side; the\n * `DecisioningPlatform` keeps the execution side. Either platform can be\n * mock-backed independently.\n *\n * Ports the Python primitives shipped in `adcp-client-python` PRs #504\n * (v1) and #550 (v1.5):\n *\n * - {@link ProposalManager} — interface with `getProducts` (required)\n * plus optional `refineProducts` and `finalizeProposal`.\n * - {@link ProposalCapabilities} — sales-axis-scoped capability flags.\n * - {@link Recipe} — typed `recipe_kind`-discriminated base; adopters\n * subclass with their internal-config schema. Rides on\n * `Product.implementation_config` (opaque to the buyer).\n * - {@link CapabilityOverlap} — typed declaration of which wire\n * capabilities the buyer can configure on a product. The framework\n * validates buyer requests against the overlap pre-adapter.\n * - {@link FinalizeProposalRequest} / {@link FinalizeProposalSuccess} —\n * framework-internal shapes for the finalize lifecycle (wired in the\n * v1.5 dispatch helpers).\n *\n * @public\n * @packageDocumentation\n */\n\nimport type { MaybePromise } from '../../create-adcp-server';\nimport type { Account } from '../account';\nimport type { RequestContext } from '../context';\nimport type { GetProductsRequest, GetProductsResponse } from '../../../types/tools.generated';\nimport type { RequireCacheScopeWhenProducts, ServerPayload } from '../../../types/server-payload';\nimport type { TaskHandoff } from '../async-outcome';\n\nexport type ProposalGetProductsPayload = RequireCacheScopeWhenProducts<ServerPayload<GetProductsResponse>>;\n\n// ---------------------------------------------------------------------------\n// Capabilities\n// ---------------------------------------------------------------------------\n\n/**\n * Sales specialisms a {@link ProposalManager} can serve. Mirrors the\n * `sales-*` slugs in the spec's specialism enum.\n *\n * v1 scopes to the two `ProposalManager`-relevant flavours; broader\n * coverage (broadcast-tv, social, proposal-mode, catalog-driven) lands\n * as adopter signal grows.\n *\n * @public\n */\nexport type ProposalSalesSpecialism = 'sales-guaranteed' | 'sales-non-guaranteed';\n\n/**\n * Capability declaration for a {@link ProposalManager}.\n *\n * Sales-axis-scoped: proposal handling is a sales-specialism concern,\n * not a generic platform-wide concept. The `salesSpecialism` field\n * declares which AdCP sales specialism this manager serves; capability\n * flags declare which optional behaviours it supports.\n *\n * The framework reads this declaration at server-construction time to\n * decide which dispatch paths apply (e.g. `refineProducts` is only\n * invoked when `refine` is true; `finalizeProposal` is only invoked\n * when `finalize` is true).\n *\n * @public\n */\nexport interface ProposalCapabilities {\n /**\n * Which AdCP sales specialism this manager serves.\n *\n * `sales-guaranteed` for guaranteed-direct flows with proposal\n * lifecycle (finalize → committed proposal → media buy).\n *\n * `sales-non-guaranteed` for catalog-style flows where `getProducts`\n * returns a static catalog and buyers reference products directly at\n * `create_media_buy`.\n */\n salesSpecialism: ProposalSalesSpecialism;\n\n /**\n * When true, the manager implements {@link ProposalManager.refineProducts}\n * and the framework routes `get_products` requests with\n * `buying_mode: 'refine'` to that method. When false, refine requests\n * fall through to `getProducts` (or surface `UNSUPPORTED_FEATURE` if\n * the manager rejects them).\n */\n refine?: boolean;\n\n /**\n * When true, the manager implements {@link ProposalManager.finalizeProposal}\n * and the framework intercepts `refine[i].action: 'finalize'` entries to\n * commit drafts via the lifecycle helpers. When false, finalize entries\n * pass through to the underlying `getProducts` / `refineProducts` method\n * unchanged.\n */\n finalize?: boolean;\n\n /**\n * Grace window (in seconds) added to a committed proposal's `expires_at`\n * before the framework rejects `create_media_buy` calls referencing the\n * proposal with `PROPOSAL_EXPIRED`. Default 0 (strict).\n */\n expiresAtGraceSeconds?: number;\n\n /**\n * Signal-driven product assembly — the manager constructs products from\n * buyer signals at request time rather than enumerating a static\n * catalogue. Informational in v1.5; future PRs may validate that\n * inventory / signal stores are wired when this flag is set.\n */\n dynamicProducts?: boolean;\n\n /**\n * The manager consults rate cards (per buyer relationship per product)\n * when emitting prices. Informational in v1.5.\n */\n rateCardPricing?: boolean;\n\n /**\n * The manager reserves capacity at proposal time (typical for\n * guaranteed). Informational in v1.5; the `finalize` transition that\n * drives the actual hold is wired via the lifecycle helpers.\n */\n availabilityReservations?: boolean;\n}\n\n/**\n * Validate a {@link ProposalCapabilities} object. Throws when the declaration\n * is malformed (unknown `salesSpecialism`, negative `expiresAtGraceSeconds`).\n * The framework calls this at boot; adopters can call it from their own\n * config-validation code paths to fail fast on misconfiguration.\n *\n * Kept as a plain function rather than a class constructor to match the\n * codebase's preference for plain TS interfaces over runtime classes\n * for adopter-facing types.\n *\n * @public\n */\nexport function validateProposalCapabilities(caps: ProposalCapabilities): void {\n const valid: readonly ProposalSalesSpecialism[] = ['sales-guaranteed', 'sales-non-guaranteed'];\n if (!valid.includes(caps.salesSpecialism)) {\n throw new Error(\n `ProposalCapabilities.salesSpecialism must be one of ${JSON.stringify(valid)}. ` +\n `Got ${JSON.stringify(caps.salesSpecialism)}. v1.5 scopes ProposalManager to the two ` +\n `core sales specialisms; broader specialism support lands in subsequent releases.`\n );\n }\n if (caps.expiresAtGraceSeconds != null && caps.expiresAtGraceSeconds < 0) {\n throw new Error(\n `ProposalCapabilities.expiresAtGraceSeconds must be >= 0; got ${caps.expiresAtGraceSeconds}. ` +\n `The grace window extends the inventory hold past expires_at; negative values would shrink it.`\n );\n }\n}\n\n// ---------------------------------------------------------------------------\n// Recipe + CapabilityOverlap\n// ---------------------------------------------------------------------------\n\n/**\n * Per-product subset of wire capability flags that the buyer can\n * configure on this product.\n *\n * Buyer requests asking for capabilities outside this overlap are\n * rejected by the framework before the adapter sees them (validated by\n * the lifecycle helpers — see `proposal/lifecycle.ts`).\n *\n * Each field is `ReadonlySet<string> | undefined`:\n *\n * - `undefined` → framework does not gate this axis (open).\n * - `ReadonlySet` → buyer choices must be subsets of this set.\n * An empty set means deny-all on this axis.\n *\n * The undefined vs. empty-set distinction matches set intuition:\n * \"no constraint\" is `undefined`; \"allowed set is empty\" is `new Set()`.\n *\n * **Why no extras dict?** v1.5 deliberately omits an `extras` escape\n * hatch (per Python design § D4). Adopters with novel gating needs\n * extend the interface with typed fields; a dict bag leaves no paper\n * trail. If a new axis turns out to be widely useful, it lands as a\n * typed field on `CapabilityOverlap` upstream.\n *\n * @public\n */\nexport interface CapabilityOverlap {\n /**\n * Subset of wire `pricing_models` the buyer can choose. Validated\n * against the matching `PricingOption.pricing_model` on the buyer's\n * package.\n */\n pricingModels?: ReadonlySet<string>;\n\n /**\n * Subset of wire targeting dimensions (`geo`, `device_type`,\n * `language`, etc.). Validated against the keys present on the\n * buyer's `targeting_overlay`.\n */\n targetingDimensions?: ReadonlySet<string>;\n\n /**\n * Subset of `{ guaranteed, non_guaranteed }` the product offers.\n */\n deliveryTypes?: ReadonlySet<string>;\n\n /**\n * If the seller integrates signals, which signal types this product\n * accepts. An empty set means the seller explicitly refuses all\n * signals on this product; `undefined` means no framework gate.\n */\n signalTypes?: ReadonlySet<string>;\n}\n\n/**\n * Base type for typed product `implementation_config` payloads.\n *\n * Adopters declare a discriminated subtype with a literal `recipe_kind`:\n *\n * ```ts\n * interface GAMRecipe extends Recipe {\n * recipe_kind: 'gam';\n * line_item_template_id: string;\n * ad_unit_ids: readonly string[];\n * capability_overlap?: CapabilityOverlap;\n * }\n * ```\n *\n * The kind tag enables router-by-recipe-kind dispatch in the\n * multi-decisioning case (one ProposalManager + many DecisioningPlatforms,\n * each handling a subset of recipe kinds). v1.5 doesn't yet wire that\n * routing — adopters using a single DecisioningPlatform attach recipes\n * freely without registry validation.\n *\n * **The recipe is never on the buyer's wire surface.** It rides inside\n * `Product.implementation_config` (an opaque-to-buyer dict). Buyers treat\n * it as a black box; the framework persists it through the proposal\n * lifecycle so the executing DecisioningPlatform sees a stable view.\n *\n * `capability_overlap` is optional. When present, the framework activates\n * the v1.5 buyer-request validation seam against the declared subsets.\n *\n * @public\n */\nexport interface Recipe {\n /** Adapter-family discriminator. Subtypes narrow with a literal type. */\n recipe_kind: string;\n\n /**\n * Optional typed declaration of which wire capabilities the buyer can\n * configure on this product. `undefined` means no framework gating.\n * An explicit {@link CapabilityOverlap} activates the v1.5 validation.\n */\n capability_overlap?: CapabilityOverlap;\n}\n\n// ---------------------------------------------------------------------------\n// Finalize lifecycle shapes\n// ---------------------------------------------------------------------------\n\n/**\n * Framework-internal request shape passed to {@link ProposalManager.finalizeProposal}.\n *\n * Constructed by the framework dispatcher when a buyer's `get_products`\n * request with `buying_mode: 'refine'` carries a `refine[i].action: 'finalize'`\n * entry. Adopters don't parse the wire envelope; the framework projects.\n *\n * @public\n */\nexport interface FinalizeProposalRequest<TRecipe extends Recipe = Recipe> {\n /**\n * The draft proposal the buyer is asking to finalize. Hydrated from\n * the wire's `refine[i].proposal_id` field.\n */\n proposalId: string;\n\n /**\n * `product_id -> Recipe` mapping pulled from the {@link ProposalStore}\n * draft. The adopter's finalize logic typically lock-prices these and\n * emits the committed proposal.\n */\n recipes: ReadonlyMap<string, TRecipe>;\n\n /**\n * The draft's wire `Proposal` shape (the same payload the adopter\n * returned on the prior `getProducts` / `refineProducts` call).\n * Adopter typically modifies this with locked pricing and returns it\n * on {@link FinalizeProposalSuccess}.\n */\n proposalPayload: Record<string, unknown>;\n\n /**\n * The buyer's per-entry refine `ask` text — what they want finalized.\n * Free-form; adopter consumes.\n */\n ask?: string;\n\n /**\n * The parent {@link GetProductsRequest} so the adopter sees the full\n * envelope (account, etc.) without the framework projecting fields\n * one-by-one.\n */\n parentRequest: GetProductsRequest;\n}\n\n/**\n * Adopter-returned shape from {@link ProposalManager.finalizeProposal} —\n * inline commit.\n *\n * Framework calls `ProposalStore.commit` with these fields before\n * projecting the wire response. The buyer sees the committed `Proposal`\n * with `proposal_status: 'committed'` + `expires_at` populated on the\n * next `get_products` response payload.\n *\n * @public\n */\nexport interface FinalizeProposalSuccess<TRecipe extends Recipe = Recipe> {\n /**\n * The wire `Proposal` shape with locked pricing and\n * `proposal_status: 'committed'`. Adopter typically derives this from\n * {@link FinalizeProposalRequest.proposalPayload} with modifications.\n *\n * **Must be JSON-serializable end-to-end** — non-JSON values won't\n * survive a process restart through a durable {@link ProposalStore}.\n */\n proposal: Record<string, unknown>;\n\n /**\n * Inventory hold deadline. After this (plus the adopter's\n * {@link ProposalCapabilities.expiresAtGraceSeconds} window), the\n * framework rejects `create_media_buy` calls referencing the proposal\n * with `PROPOSAL_EXPIRED`.\n */\n expiresAt: Date;\n\n /**\n * Optional refreshed recipe mapping. Omitting preserves the draft's\n * recipes verbatim. Adopters whose finalize logic mutates recipe\n * fields (e.g. locking a line-item template id) supply a fresh\n * mapping.\n */\n recipes?: ReadonlyMap<string, TRecipe>;\n}\n\n// ---------------------------------------------------------------------------\n// ProposalManager interface\n// ---------------------------------------------------------------------------\n\ntype Ctx<TCtxMeta> = RequestContext<Account<TCtxMeta>>;\n\n/**\n * Assembles proposals from buyer briefs.\n *\n * Reads inventory, signals, rate cards, availability. Produces proposals\n * where each `Product` carries a typed `implementation_config` (a recipe;\n * see {@link Recipe}) that the bound `DecisioningPlatform` consumes at\n * `create_media_buy` time.\n *\n * Methods may be sync or async; the framework awaits whatever is\n * returned. Same convention as `SalesPlatform`.\n *\n * **Required surface:** {@link getProducts}.\n *\n * **Optional surfaces (capability-gated):**\n *\n * - {@link refineProducts} — only invoked when\n * {@link ProposalCapabilities.refine} is true.\n * - {@link finalizeProposal} — only invoked when\n * {@link ProposalCapabilities.finalize} is true. Wired by the\n * framework's lifecycle helpers; intercepts `refine[i].action: 'finalize'`\n * entries before the underlying `getProducts` / `refineProducts` runs.\n *\n * Throw `AdcpError` for buyer-fixable rejection (`BUDGET_TOO_LOW`,\n * `POLICY_VIOLATION`, `UNSUPPORTED_FEATURE`); the framework projects to\n * the wire structured-error envelope.\n *\n * Adopter typing tip: parameterize on your concrete `Recipe` subtype to\n * get end-to-end recipe typing through `ctx.recipes` in the bound\n * `DecisioningPlatform`'s methods:\n *\n * ```ts\n * type MyRecipe = GAMRecipe | KevelRecipe;\n * const manager: ProposalManager<MyRecipe, MyTenantMeta> = { ... };\n * ```\n *\n * @public\n */\nexport interface ProposalManager<TRecipe extends Recipe = Recipe, TCtxMeta = unknown> {\n /**\n * What this ProposalManager can do — sales specialism + capability flags.\n */\n capabilities: ProposalCapabilities;\n\n /**\n * Initial product discovery from a buyer brief.\n *\n * Each returned `Product` SHOULD carry an `implementation_config`\n * matching the bound `DecisioningPlatform`'s recipe schema (see\n * {@link Recipe}). The framework treats `implementation_config` as\n * opaque on the wire; recipe typing is enforced through the\n * `TRecipe` type parameter on the adopter side.\n *\n * For non-guaranteed flows: typically a static catalogue, possibly\n * filtered by buyer brief / signals.\n *\n * For guaranteed flows: typically a brief-driven assembly consulting\n * rate cards + availability. Adopters return draft proposals; the\n * buyer drives the finalize transition via subsequent refine calls\n * with `action: 'finalize'`.\n */\n getProducts(req: GetProductsRequest, ctx: Ctx<TCtxMeta>): MaybePromise<ProposalGetProductsPayload>;\n\n /**\n * Refine-mode iteration on a previous `getProducts` response.\n *\n * Per the spec, refine is a `buying_mode` value on `get_products` —\n * the wire envelope is the same. The framework routes refine requests\n * to this method when:\n *\n * 1. The wired ProposalManager declares `capabilities.refine` = true,\n * 2. The request has `buying_mode === 'refine'`, AND\n * 3. The manager implements this method.\n *\n * Otherwise refine requests fall through to {@link getProducts}.\n *\n * Adopters implementing `refineProducts` without `finalize` support\n * should treat `action: 'finalize'` entries as `UNSUPPORTED_FEATURE`\n * and return a structured error. Adopters with `finalize` support\n * see those entries intercepted by the framework before this method\n * is called.\n */\n refineProducts?(req: GetProductsRequest, ctx: Ctx<TCtxMeta>): MaybePromise<ProposalGetProductsPayload>;\n\n /**\n * Commit a draft proposal to firm pricing + inventory hold.\n *\n * Wired by the framework's lifecycle helpers when:\n *\n * 1. `capabilities.finalize === true`,\n * 2. The buyer's request has `buying_mode === 'refine'` with a\n * `refine[i]` entry of `{ scope: 'proposal', action: 'finalize',\n * proposal_id }`, AND\n * 3. The proposal_id resolves to a DRAFT entry in the\n * {@link ProposalStore}.\n *\n * Return a {@link FinalizeProposalSuccess} for inline commit (the\n * spec-default route), or a `TaskHandoff<FinalizeProposalSuccess>` to\n * promote to a HITL background task. The HITL commit hook is wired\n * in v1.6+; v1.5 inline-only.\n */\n finalizeProposal?(\n req: FinalizeProposalRequest<TRecipe>,\n ctx: Ctx<TCtxMeta>\n ): MaybePromise<FinalizeProposalSuccess<TRecipe> | TaskHandoff<FinalizeProposalSuccess<TRecipe>>>;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AA+IO,SAAS,6BAA6B,MAAkC;AAC7E,QAAM,QAA4C,CAAC,oBAAoB,sBAAsB;AAC7F,MAAI,CAAC,MAAM,SAAS,KAAK,eAAe,GAAG;AACzC,UAAM,IAAI;AAAA,MACR,uDAAuD,KAAK,UAAU,KAAK,CAAC,SACnE,KAAK,UAAU,KAAK,eAAe,CAAC;AAAA,IAE/C;AAAA,EACF;AACA,MAAI,KAAK,yBAAyB,QAAQ,KAAK,wBAAwB,GAAG;AACxE,UAAM,IAAI;AAAA,MACR,gEAAgE,KAAK,qBAAqB;AAAA,IAE5F;AAAA,EACF;AACF;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../../../src/lib/server/decisioning/proposal/types.ts"],"sourcesContent":["/**\n * ProposalManager — primitives for the two-platform composition.\n *\n * The existing `DecisioningPlatform` conflates two concerns: assembling\n * proposals from briefs (`get_products`, refine) vs. executing media buys\n * against an upstream (`create_media_buy`, `update_media_buy`,\n * `get_media_buy_delivery`). The two-platform composition splits them: a\n * separate {@link ProposalManager} handles the proposal side; the\n * `DecisioningPlatform` keeps the execution side. Either platform can be\n * mock-backed independently.\n *\n * Ports the Python primitives shipped in `adcp-client-python` PRs #504\n * (v1) and #550 (v1.5):\n *\n * - {@link ProposalManager} — interface with `getProducts` (required)\n * plus optional `refineProducts` and `finalizeProposal`.\n * - {@link ProposalCapabilities} — sales-axis-scoped capability flags.\n * - {@link Recipe} — typed `recipe_kind`-discriminated base; adopters\n * subclass with their internal-config schema. Rides on\n * `Product.implementation_config` (opaque to the buyer).\n * - {@link CapabilityOverlap} — typed declaration of which wire\n * capabilities the buyer can configure on a product. The framework\n * validates buyer requests against the overlap pre-adapter.\n * - {@link FinalizeProposalRequest} / {@link FinalizeProposalSuccess} —\n * framework-internal shapes for the finalize lifecycle (wired in the\n * v1.5 dispatch helpers).\n *\n * @public\n * @packageDocumentation\n */\n\nimport type { MaybePromise } from '../../create-adcp-server';\nimport type { Account } from '../account';\nimport type { RequestContext } from '../context';\nimport type { GetProductsResponse } from '../../../types/tools.generated';\nimport type {\n CanonicalCreativeResponse,\n CanonicalGetProductsRequest,\n CanonicalProduct,\n} from '../../../v2/projection/creative-delivery';\nimport type { RequireCacheScopeWhenProducts, ServerPayload } from '../../../types/server-payload';\nimport type { TaskHandoff } from '../async-outcome';\n\nexport type ProposalGetProductsPayload = RequireCacheScopeWhenProducts<\n Omit<ServerPayload<CanonicalCreativeResponse<GetProductsResponse>>, 'products'> & { products?: CanonicalProduct[] }\n>;\nexport type LegacyProposalGetProductsPayload = RequireCacheScopeWhenProducts<ServerPayload<GetProductsResponse>>;\n\n// ---------------------------------------------------------------------------\n// Capabilities\n// ---------------------------------------------------------------------------\n\n/**\n * Sales specialisms a {@link ProposalManager} can serve. Mirrors the\n * `sales-*` slugs in the spec's specialism enum.\n *\n * v1 scopes to the two `ProposalManager`-relevant flavours; broader\n * coverage (broadcast-tv, social, proposal-mode, catalog-driven) lands\n * as adopter signal grows.\n *\n * @public\n */\nexport type ProposalSalesSpecialism = 'sales-guaranteed' | 'sales-non-guaranteed';\n\n/**\n * Capability declaration for a {@link ProposalManager}.\n *\n * Sales-axis-scoped: proposal handling is a sales-specialism concern,\n * not a generic platform-wide concept. The `salesSpecialism` field\n * declares which AdCP sales specialism this manager serves; capability\n * flags declare which optional behaviours it supports.\n *\n * The framework reads this declaration at server-construction time to\n * decide which dispatch paths apply (e.g. `refineProducts` is only\n * invoked when `refine` is true; `finalizeProposal` is only invoked\n * when `finalize` is true).\n *\n * @public\n */\nexport interface ProposalCapabilities {\n /**\n * Which AdCP sales specialism this manager serves.\n *\n * `sales-guaranteed` for guaranteed-direct flows with proposal\n * lifecycle (finalize → committed proposal → media buy).\n *\n * `sales-non-guaranteed` for catalog-style flows where `getProducts`\n * returns a static catalog and buyers reference products directly at\n * `create_media_buy`.\n */\n salesSpecialism: ProposalSalesSpecialism;\n\n /**\n * When true, the manager implements {@link ProposalManager.refineProducts}\n * and the framework routes `get_products` requests with\n * `buying_mode: 'refine'` to that method. When false, refine requests\n * fall through to `getProducts` (or surface `UNSUPPORTED_FEATURE` if\n * the manager rejects them).\n */\n refine?: boolean;\n\n /**\n * When true, the manager implements {@link ProposalManager.finalizeProposal}\n * and the framework intercepts `refine[i].action: 'finalize'` entries to\n * commit drafts via the lifecycle helpers. When false, finalize entries\n * pass through to the underlying `getProducts` / `refineProducts` method\n * unchanged.\n */\n finalize?: boolean;\n\n /**\n * Grace window (in seconds) added to a committed proposal's `expires_at`\n * before the framework rejects `create_media_buy` calls referencing the\n * proposal with `PROPOSAL_EXPIRED`. Default 0 (strict).\n */\n expiresAtGraceSeconds?: number;\n\n /**\n * Signal-driven product assembly — the manager constructs products from\n * buyer signals at request time rather than enumerating a static\n * catalogue. Informational in v1.5; future PRs may validate that\n * inventory / signal stores are wired when this flag is set.\n */\n dynamicProducts?: boolean;\n\n /**\n * The manager consults rate cards (per buyer relationship per product)\n * when emitting prices. Informational in v1.5.\n */\n rateCardPricing?: boolean;\n\n /**\n * The manager reserves capacity at proposal time (typical for\n * guaranteed). Informational in v1.5; the `finalize` transition that\n * drives the actual hold is wired via the lifecycle helpers.\n */\n availabilityReservations?: boolean;\n}\n\n/**\n * Validate a {@link ProposalCapabilities} object. Throws when the declaration\n * is malformed (unknown `salesSpecialism`, negative `expiresAtGraceSeconds`).\n * The framework calls this at boot; adopters can call it from their own\n * config-validation code paths to fail fast on misconfiguration.\n *\n * Kept as a plain function rather than a class constructor to match the\n * codebase's preference for plain TS interfaces over runtime classes\n * for adopter-facing types.\n *\n * @public\n */\nexport function validateProposalCapabilities(caps: ProposalCapabilities): void {\n const valid: readonly ProposalSalesSpecialism[] = ['sales-guaranteed', 'sales-non-guaranteed'];\n if (!valid.includes(caps.salesSpecialism)) {\n throw new Error(\n `ProposalCapabilities.salesSpecialism must be one of ${JSON.stringify(valid)}. ` +\n `Got ${JSON.stringify(caps.salesSpecialism)}. v1.5 scopes ProposalManager to the two ` +\n `core sales specialisms; broader specialism support lands in subsequent releases.`\n );\n }\n if (caps.expiresAtGraceSeconds != null && caps.expiresAtGraceSeconds < 0) {\n throw new Error(\n `ProposalCapabilities.expiresAtGraceSeconds must be >= 0; got ${caps.expiresAtGraceSeconds}. ` +\n `The grace window extends the inventory hold past expires_at; negative values would shrink it.`\n );\n }\n}\n\n// ---------------------------------------------------------------------------\n// Recipe + CapabilityOverlap\n// ---------------------------------------------------------------------------\n\n/**\n * Per-product subset of wire capability flags that the buyer can\n * configure on this product.\n *\n * Buyer requests asking for capabilities outside this overlap are\n * rejected by the framework before the adapter sees them (validated by\n * the lifecycle helpers — see `proposal/lifecycle.ts`).\n *\n * Each field is `ReadonlySet<string> | undefined`:\n *\n * - `undefined` → framework does not gate this axis (open).\n * - `ReadonlySet` → buyer choices must be subsets of this set.\n * An empty set means deny-all on this axis.\n *\n * The undefined vs. empty-set distinction matches set intuition:\n * \"no constraint\" is `undefined`; \"allowed set is empty\" is `new Set()`.\n *\n * **Why no extras dict?** v1.5 deliberately omits an `extras` escape\n * hatch (per Python design § D4). Adopters with novel gating needs\n * extend the interface with typed fields; a dict bag leaves no paper\n * trail. If a new axis turns out to be widely useful, it lands as a\n * typed field on `CapabilityOverlap` upstream.\n *\n * @public\n */\nexport interface CapabilityOverlap {\n /**\n * Subset of wire `pricing_models` the buyer can choose. Validated\n * against the matching `PricingOption.pricing_model` on the buyer's\n * package.\n */\n pricingModels?: ReadonlySet<string>;\n\n /**\n * Subset of wire targeting dimensions (`geo`, `device_type`,\n * `language`, etc.). Validated against the keys present on the\n * buyer's `targeting_overlay`.\n */\n targetingDimensions?: ReadonlySet<string>;\n\n /**\n * Subset of `{ guaranteed, non_guaranteed }` the product offers.\n */\n deliveryTypes?: ReadonlySet<string>;\n\n /**\n * If the seller integrates signals, which signal types this product\n * accepts. An empty set means the seller explicitly refuses all\n * signals on this product; `undefined` means no framework gate.\n */\n signalTypes?: ReadonlySet<string>;\n}\n\n/**\n * Base type for typed product `implementation_config` payloads.\n *\n * Adopters declare a discriminated subtype with a literal `recipe_kind`:\n *\n * ```ts\n * interface GAMRecipe extends Recipe {\n * recipe_kind: 'gam';\n * line_item_template_id: string;\n * ad_unit_ids: readonly string[];\n * capability_overlap?: CapabilityOverlap;\n * }\n * ```\n *\n * The kind tag enables router-by-recipe-kind dispatch in the\n * multi-decisioning case (one ProposalManager + many DecisioningPlatforms,\n * each handling a subset of recipe kinds). v1.5 doesn't yet wire that\n * routing — adopters using a single DecisioningPlatform attach recipes\n * freely without registry validation.\n *\n * **The recipe is never on the buyer's wire surface.** It rides inside\n * `Product.implementation_config` (an opaque-to-buyer dict). Buyers treat\n * it as a black box; the framework persists it through the proposal\n * lifecycle so the executing DecisioningPlatform sees a stable view.\n *\n * `capability_overlap` is optional. When present, the framework activates\n * the v1.5 buyer-request validation seam against the declared subsets.\n *\n * @public\n */\nexport interface Recipe {\n /** Adapter-family discriminator. Subtypes narrow with a literal type. */\n recipe_kind: string;\n\n /**\n * Optional typed declaration of which wire capabilities the buyer can\n * configure on this product. `undefined` means no framework gating.\n * An explicit {@link CapabilityOverlap} activates the v1.5 validation.\n */\n capability_overlap?: CapabilityOverlap;\n}\n\n// ---------------------------------------------------------------------------\n// Finalize lifecycle shapes\n// ---------------------------------------------------------------------------\n\n/**\n * Framework-internal request shape passed to {@link ProposalManager.finalizeProposal}.\n *\n * Constructed by the framework dispatcher when a buyer's `get_products`\n * request with `buying_mode: 'refine'` carries a `refine[i].action: 'finalize'`\n * entry. Adopters don't parse the wire envelope; the framework projects.\n *\n * @public\n */\nexport interface FinalizeProposalRequest<TRecipe extends Recipe = Recipe> {\n /**\n * The draft proposal the buyer is asking to finalize. Hydrated from\n * the wire's `refine[i].proposal_id` field.\n */\n proposalId: string;\n\n /**\n * `product_id -> Recipe` mapping pulled from the {@link ProposalStore}\n * draft. The adopter's finalize logic typically lock-prices these and\n * emits the committed proposal.\n */\n recipes: ReadonlyMap<string, TRecipe>;\n\n /**\n * The draft's wire `Proposal` shape (the same payload the adopter\n * returned on the prior `getProducts` / `refineProducts` call).\n * Adopter typically modifies this with locked pricing and returns it\n * on {@link FinalizeProposalSuccess}.\n */\n proposalPayload: Record<string, unknown>;\n\n /**\n * The buyer's per-entry refine `ask` text — what they want finalized.\n * Free-form; adopter consumes.\n */\n ask?: string;\n\n /**\n * The parent canonical get-products request so the adopter sees the full\n * envelope (account, etc.) without the framework projecting fields\n * one-by-one.\n */\n parentRequest: CanonicalGetProductsRequest;\n}\n\n/**\n * Adopter-returned shape from {@link ProposalManager.finalizeProposal} —\n * inline commit.\n *\n * Framework calls `ProposalStore.commit` with these fields before\n * projecting the wire response. The buyer sees the committed `Proposal`\n * with `proposal_status: 'committed'` + `expires_at` populated on the\n * next `get_products` response payload.\n *\n * @public\n */\nexport interface FinalizeProposalSuccess<TRecipe extends Recipe = Recipe> {\n /**\n * The wire `Proposal` shape with locked pricing and\n * `proposal_status: 'committed'`. Adopter typically derives this from\n * {@link FinalizeProposalRequest.proposalPayload} with modifications.\n *\n * **Must be JSON-serializable end-to-end** — non-JSON values won't\n * survive a process restart through a durable {@link ProposalStore}.\n */\n proposal: Record<string, unknown>;\n\n /**\n * Inventory hold deadline. After this (plus the adopter's\n * {@link ProposalCapabilities.expiresAtGraceSeconds} window), the\n * framework rejects `create_media_buy` calls referencing the proposal\n * with `PROPOSAL_EXPIRED`.\n */\n expiresAt: Date;\n\n /**\n * Optional refreshed recipe mapping. Omitting preserves the draft's\n * recipes verbatim. Adopters whose finalize logic mutates recipe\n * fields (e.g. locking a line-item template id) supply a fresh\n * mapping.\n */\n recipes?: ReadonlyMap<string, TRecipe>;\n}\n\n// ---------------------------------------------------------------------------\n// ProposalManager interface\n// ---------------------------------------------------------------------------\n\ntype Ctx<TCtxMeta> = RequestContext<Account<TCtxMeta>>;\n\n/**\n * Assembles proposals from buyer briefs.\n *\n * Reads inventory, signals, rate cards, availability. Produces proposals\n * where each `Product` carries a typed `implementation_config` (a recipe;\n * see {@link Recipe}) that the bound `DecisioningPlatform` consumes at\n * `create_media_buy` time.\n *\n * Methods may be sync or async; the framework awaits whatever is\n * returned. Same convention as `SalesPlatform`.\n *\n * **Required surface:** {@link getProducts}.\n *\n * **Optional surfaces (capability-gated):**\n *\n * - {@link refineProducts} — only invoked when\n * {@link ProposalCapabilities.refine} is true.\n * - {@link finalizeProposal} — only invoked when\n * {@link ProposalCapabilities.finalize} is true. Wired by the\n * framework's lifecycle helpers; intercepts `refine[i].action: 'finalize'`\n * entries before the underlying `getProducts` / `refineProducts` runs.\n *\n * Throw `AdcpError` for buyer-fixable rejection (`BUDGET_TOO_LOW`,\n * `POLICY_VIOLATION`, `UNSUPPORTED_FEATURE`); the framework projects to\n * the wire structured-error envelope.\n *\n * Adopter typing tip: parameterize on your concrete `Recipe` subtype to\n * get end-to-end recipe typing through `ctx.recipes` in the bound\n * `DecisioningPlatform`'s methods:\n *\n * ```ts\n * type MyRecipe = GAMRecipe | KevelRecipe;\n * const manager: ProposalManager<MyRecipe, MyTenantMeta> = { ... };\n * ```\n *\n * @public\n */\nexport interface ProposalManager<TRecipe extends Recipe = Recipe, TCtxMeta = unknown> {\n /**\n * What this ProposalManager can do — sales specialism + capability flags.\n */\n capabilities: ProposalCapabilities;\n\n /**\n * Initial product discovery from a buyer brief.\n *\n * Each returned `Product` SHOULD carry an `implementation_config`\n * matching the bound `DecisioningPlatform`'s recipe schema (see\n * {@link Recipe}). The framework treats `implementation_config` as\n * opaque on the wire; recipe typing is enforced through the\n * `TRecipe` type parameter on the adopter side.\n *\n * For non-guaranteed flows: typically a static catalogue, possibly\n * filtered by buyer brief / signals.\n *\n * For guaranteed flows: typically a brief-driven assembly consulting\n * rate cards + availability. Adopters return draft proposals; the\n * buyer drives the finalize transition via subsequent refine calls\n * with `action: 'finalize'`.\n */\n getProducts(req: CanonicalGetProductsRequest, ctx: Ctx<TCtxMeta>): MaybePromise<ProposalGetProductsPayload>;\n\n /**\n * Refine-mode iteration on a previous `getProducts` response.\n *\n * Per the spec, refine is a `buying_mode` value on `get_products` —\n * the wire envelope is the same. The framework routes refine requests\n * to this method when:\n *\n * 1. The wired ProposalManager declares `capabilities.refine` = true,\n * 2. The request has `buying_mode === 'refine'`, AND\n * 3. The manager implements this method.\n *\n * Otherwise refine requests fall through to {@link getProducts}.\n *\n * Adopters implementing `refineProducts` without `finalize` support\n * should treat `action: 'finalize'` entries as `UNSUPPORTED_FEATURE`\n * and return a structured error. Adopters with `finalize` support\n * see those entries intercepted by the framework before this method\n * is called.\n */\n refineProducts?(req: CanonicalGetProductsRequest, ctx: Ctx<TCtxMeta>): MaybePromise<ProposalGetProductsPayload>;\n\n /**\n * Commit a draft proposal to firm pricing + inventory hold.\n *\n * Wired by the framework's lifecycle helpers when:\n *\n * 1. `capabilities.finalize === true`,\n * 2. The buyer's request has `buying_mode === 'refine'` with a\n * `refine[i]` entry of `{ scope: 'proposal', action: 'finalize',\n * proposal_id }`, AND\n * 3. The proposal_id resolves to a DRAFT entry in the\n * {@link ProposalStore}.\n *\n * Return a {@link FinalizeProposalSuccess} for inline commit (the\n * spec-default route), or a `TaskHandoff<FinalizeProposalSuccess>` to\n * promote to a HITL background task. The HITL commit hook is wired\n * in v1.6+; v1.5 inline-only.\n */\n finalizeProposal?(\n req: FinalizeProposalRequest<TRecipe>,\n ctx: Ctx<TCtxMeta>\n ): MaybePromise<FinalizeProposalSuccess<TRecipe> | TaskHandoff<FinalizeProposalSuccess<TRecipe>>>;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAuJO,SAAS,6BAA6B,MAAkC;AAC7E,QAAM,QAA4C,CAAC,oBAAoB,sBAAsB;AAC7F,MAAI,CAAC,MAAM,SAAS,KAAK,eAAe,GAAG;AACzC,UAAM,IAAI;AAAA,MACR,uDAAuD,KAAK,UAAU,KAAK,CAAC,SACnE,KAAK,UAAU,KAAK,eAAe,CAAC;AAAA,IAE/C;AAAA,EACF;AACA,MAAI,KAAK,yBAAyB,QAAQ,KAAK,wBAAwB,GAAG;AACxE,UAAM,IAAI;AAAA,MACR,gEAAgE,KAAK,qBAAqB;AAAA,IAE5F;AAAA,EACF;AACF;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../src/lib/server/decisioning/proposal/types.ts"],"sourcesContent":["/**\n * ProposalManager — primitives for the two-platform composition.\n *\n * The existing `DecisioningPlatform` conflates two concerns: assembling\n * proposals from briefs (`get_products`, refine) vs. executing media buys\n * against an upstream (`create_media_buy`, `update_media_buy`,\n * `get_media_buy_delivery`). The two-platform composition splits them: a\n * separate {@link ProposalManager} handles the proposal side; the\n * `DecisioningPlatform` keeps the execution side. Either platform can be\n * mock-backed independently.\n *\n * Ports the Python primitives shipped in `adcp-client-python` PRs #504\n * (v1) and #550 (v1.5):\n *\n * - {@link ProposalManager} — interface with `getProducts` (required)\n * plus optional `refineProducts` and `finalizeProposal`.\n * - {@link ProposalCapabilities} — sales-axis-scoped capability flags.\n * - {@link Recipe} — typed `recipe_kind`-discriminated base; adopters\n * subclass with their internal-config schema. Rides on\n * `Product.implementation_config` (opaque to the buyer).\n * - {@link CapabilityOverlap} — typed declaration of which wire\n * capabilities the buyer can configure on a product. The framework\n * validates buyer requests against the overlap pre-adapter.\n * - {@link FinalizeProposalRequest} / {@link FinalizeProposalSuccess} —\n * framework-internal shapes for the finalize lifecycle (wired in the\n * v1.5 dispatch helpers).\n *\n * @public\n * @packageDocumentation\n */\n\nimport type { MaybePromise } from '../../create-adcp-server';\nimport type { Account } from '../account';\nimport type { RequestContext } from '../context';\nimport type { GetProductsRequest, GetProductsResponse } from '../../../types/tools.generated';\nimport type { RequireCacheScopeWhenProducts, ServerPayload } from '../../../types/server-payload';\nimport type { TaskHandoff } from '../async-outcome';\n\nexport type ProposalGetProductsPayload = RequireCacheScopeWhenProducts<ServerPayload<GetProductsResponse>>;\n\n// ---------------------------------------------------------------------------\n// Capabilities\n// ---------------------------------------------------------------------------\n\n/**\n * Sales specialisms a {@link ProposalManager} can serve. Mirrors the\n * `sales-*` slugs in the spec's specialism enum.\n *\n * v1 scopes to the two `ProposalManager`-relevant flavours; broader\n * coverage (broadcast-tv, social, proposal-mode, catalog-driven) lands\n * as adopter signal grows.\n *\n * @public\n */\nexport type ProposalSalesSpecialism = 'sales-guaranteed' | 'sales-non-guaranteed';\n\n/**\n * Capability declaration for a {@link ProposalManager}.\n *\n * Sales-axis-scoped: proposal handling is a sales-specialism concern,\n * not a generic platform-wide concept. The `salesSpecialism` field\n * declares which AdCP sales specialism this manager serves; capability\n * flags declare which optional behaviours it supports.\n *\n * The framework reads this declaration at server-construction time to\n * decide which dispatch paths apply (e.g. `refineProducts` is only\n * invoked when `refine` is true; `finalizeProposal` is only invoked\n * when `finalize` is true).\n *\n * @public\n */\nexport interface ProposalCapabilities {\n /**\n * Which AdCP sales specialism this manager serves.\n *\n * `sales-guaranteed` for guaranteed-direct flows with proposal\n * lifecycle (finalize → committed proposal → media buy).\n *\n * `sales-non-guaranteed` for catalog-style flows where `getProducts`\n * returns a static catalog and buyers reference products directly at\n * `create_media_buy`.\n */\n salesSpecialism: ProposalSalesSpecialism;\n\n /**\n * When true, the manager implements {@link ProposalManager.refineProducts}\n * and the framework routes `get_products` requests with\n * `buying_mode: 'refine'` to that method. When false, refine requests\n * fall through to `getProducts` (or surface `UNSUPPORTED_FEATURE` if\n * the manager rejects them).\n */\n refine?: boolean;\n\n /**\n * When true, the manager implements {@link ProposalManager.finalizeProposal}\n * and the framework intercepts `refine[i].action: 'finalize'` entries to\n * commit drafts via the lifecycle helpers. When false, finalize entries\n * pass through to the underlying `getProducts` / `refineProducts` method\n * unchanged.\n */\n finalize?: boolean;\n\n /**\n * Grace window (in seconds) added to a committed proposal's `expires_at`\n * before the framework rejects `create_media_buy` calls referencing the\n * proposal with `PROPOSAL_EXPIRED`. Default 0 (strict).\n */\n expiresAtGraceSeconds?: number;\n\n /**\n * Signal-driven product assembly — the manager constructs products from\n * buyer signals at request time rather than enumerating a static\n * catalogue. Informational in v1.5; future PRs may validate that\n * inventory / signal stores are wired when this flag is set.\n */\n dynamicProducts?: boolean;\n\n /**\n * The manager consults rate cards (per buyer relationship per product)\n * when emitting prices. Informational in v1.5.\n */\n rateCardPricing?: boolean;\n\n /**\n * The manager reserves capacity at proposal time (typical for\n * guaranteed). Informational in v1.5; the `finalize` transition that\n * drives the actual hold is wired via the lifecycle helpers.\n */\n availabilityReservations?: boolean;\n}\n\n/**\n * Validate a {@link ProposalCapabilities} object. Throws when the declaration\n * is malformed (unknown `salesSpecialism`, negative `expiresAtGraceSeconds`).\n * The framework calls this at boot; adopters can call it from their own\n * config-validation code paths to fail fast on misconfiguration.\n *\n * Kept as a plain function rather than a class constructor to match the\n * codebase's preference for plain TS interfaces over runtime classes\n * for adopter-facing types.\n *\n * @public\n */\nexport function validateProposalCapabilities(caps: ProposalCapabilities): void {\n const valid: readonly ProposalSalesSpecialism[] = ['sales-guaranteed', 'sales-non-guaranteed'];\n if (!valid.includes(caps.salesSpecialism)) {\n throw new Error(\n `ProposalCapabilities.salesSpecialism must be one of ${JSON.stringify(valid)}. ` +\n `Got ${JSON.stringify(caps.salesSpecialism)}. v1.5 scopes ProposalManager to the two ` +\n `core sales specialisms; broader specialism support lands in subsequent releases.`\n );\n }\n if (caps.expiresAtGraceSeconds != null && caps.expiresAtGraceSeconds < 0) {\n throw new Error(\n `ProposalCapabilities.expiresAtGraceSeconds must be >= 0; got ${caps.expiresAtGraceSeconds}. ` +\n `The grace window extends the inventory hold past expires_at; negative values would shrink it.`\n );\n }\n}\n\n// ---------------------------------------------------------------------------\n// Recipe + CapabilityOverlap\n// ---------------------------------------------------------------------------\n\n/**\n * Per-product subset of wire capability flags that the buyer can\n * configure on this product.\n *\n * Buyer requests asking for capabilities outside this overlap are\n * rejected by the framework before the adapter sees them (validated by\n * the lifecycle helpers — see `proposal/lifecycle.ts`).\n *\n * Each field is `ReadonlySet<string> | undefined`:\n *\n * - `undefined` → framework does not gate this axis (open).\n * - `ReadonlySet` → buyer choices must be subsets of this set.\n * An empty set means deny-all on this axis.\n *\n * The undefined vs. empty-set distinction matches set intuition:\n * \"no constraint\" is `undefined`; \"allowed set is empty\" is `new Set()`.\n *\n * **Why no extras dict?** v1.5 deliberately omits an `extras` escape\n * hatch (per Python design § D4). Adopters with novel gating needs\n * extend the interface with typed fields; a dict bag leaves no paper\n * trail. If a new axis turns out to be widely useful, it lands as a\n * typed field on `CapabilityOverlap` upstream.\n *\n * @public\n */\nexport interface CapabilityOverlap {\n /**\n * Subset of wire `pricing_models` the buyer can choose. Validated\n * against the matching `PricingOption.pricing_model` on the buyer's\n * package.\n */\n pricingModels?: ReadonlySet<string>;\n\n /**\n * Subset of wire targeting dimensions (`geo`, `device_type`,\n * `language`, etc.). Validated against the keys present on the\n * buyer's `targeting_overlay`.\n */\n targetingDimensions?: ReadonlySet<string>;\n\n /**\n * Subset of `{ guaranteed, non_guaranteed }` the product offers.\n */\n deliveryTypes?: ReadonlySet<string>;\n\n /**\n * If the seller integrates signals, which signal types this product\n * accepts. An empty set means the seller explicitly refuses all\n * signals on this product; `undefined` means no framework gate.\n */\n signalTypes?: ReadonlySet<string>;\n}\n\n/**\n * Base type for typed product `implementation_config` payloads.\n *\n * Adopters declare a discriminated subtype with a literal `recipe_kind`:\n *\n * ```ts\n * interface GAMRecipe extends Recipe {\n * recipe_kind: 'gam';\n * line_item_template_id: string;\n * ad_unit_ids: readonly string[];\n * capability_overlap?: CapabilityOverlap;\n * }\n * ```\n *\n * The kind tag enables router-by-recipe-kind dispatch in the\n * multi-decisioning case (one ProposalManager + many DecisioningPlatforms,\n * each handling a subset of recipe kinds). v1.5 doesn't yet wire that\n * routing — adopters using a single DecisioningPlatform attach recipes\n * freely without registry validation.\n *\n * **The recipe is never on the buyer's wire surface.** It rides inside\n * `Product.implementation_config` (an opaque-to-buyer dict). Buyers treat\n * it as a black box; the framework persists it through the proposal\n * lifecycle so the executing DecisioningPlatform sees a stable view.\n *\n * `capability_overlap` is optional. When present, the framework activates\n * the v1.5 buyer-request validation seam against the declared subsets.\n *\n * @public\n */\nexport interface Recipe {\n /** Adapter-family discriminator. Subtypes narrow with a literal type. */\n recipe_kind: string;\n\n /**\n * Optional typed declaration of which wire capabilities the buyer can\n * configure on this product. `undefined` means no framework gating.\n * An explicit {@link CapabilityOverlap} activates the v1.5 validation.\n */\n capability_overlap?: CapabilityOverlap;\n}\n\n// ---------------------------------------------------------------------------\n// Finalize lifecycle shapes\n// ---------------------------------------------------------------------------\n\n/**\n * Framework-internal request shape passed to {@link ProposalManager.finalizeProposal}.\n *\n * Constructed by the framework dispatcher when a buyer's `get_products`\n * request with `buying_mode: 'refine'` carries a `refine[i].action: 'finalize'`\n * entry. Adopters don't parse the wire envelope; the framework projects.\n *\n * @public\n */\nexport interface FinalizeProposalRequest<TRecipe extends Recipe = Recipe> {\n /**\n * The draft proposal the buyer is asking to finalize. Hydrated from\n * the wire's `refine[i].proposal_id` field.\n */\n proposalId: string;\n\n /**\n * `product_id -> Recipe` mapping pulled from the {@link ProposalStore}\n * draft. The adopter's finalize logic typically lock-prices these and\n * emits the committed proposal.\n */\n recipes: ReadonlyMap<string, TRecipe>;\n\n /**\n * The draft's wire `Proposal` shape (the same payload the adopter\n * returned on the prior `getProducts` / `refineProducts` call).\n * Adopter typically modifies this with locked pricing and returns it\n * on {@link FinalizeProposalSuccess}.\n */\n proposalPayload: Record<string, unknown>;\n\n /**\n * The buyer's per-entry refine `ask` text — what they want finalized.\n * Free-form; adopter consumes.\n */\n ask?: string;\n\n /**\n * The parent {@link GetProductsRequest} so the adopter sees the full\n * envelope (account, etc.) without the framework projecting fields\n * one-by-one.\n */\n parentRequest: GetProductsRequest;\n}\n\n/**\n * Adopter-returned shape from {@link ProposalManager.finalizeProposal} —\n * inline commit.\n *\n * Framework calls `ProposalStore.commit` with these fields before\n * projecting the wire response. The buyer sees the committed `Proposal`\n * with `proposal_status: 'committed'` + `expires_at` populated on the\n * next `get_products` response payload.\n *\n * @public\n */\nexport interface FinalizeProposalSuccess<TRecipe extends Recipe = Recipe> {\n /**\n * The wire `Proposal` shape with locked pricing and\n * `proposal_status: 'committed'`. Adopter typically derives this from\n * {@link FinalizeProposalRequest.proposalPayload} with modifications.\n *\n * **Must be JSON-serializable end-to-end** — non-JSON values won't\n * survive a process restart through a durable {@link ProposalStore}.\n */\n proposal: Record<string, unknown>;\n\n /**\n * Inventory hold deadline. After this (plus the adopter's\n * {@link ProposalCapabilities.expiresAtGraceSeconds} window), the\n * framework rejects `create_media_buy` calls referencing the proposal\n * with `PROPOSAL_EXPIRED`.\n */\n expiresAt: Date;\n\n /**\n * Optional refreshed recipe mapping. Omitting preserves the draft's\n * recipes verbatim. Adopters whose finalize logic mutates recipe\n * fields (e.g. locking a line-item template id) supply a fresh\n * mapping.\n */\n recipes?: ReadonlyMap<string, TRecipe>;\n}\n\n// ---------------------------------------------------------------------------\n// ProposalManager interface\n// ---------------------------------------------------------------------------\n\ntype Ctx<TCtxMeta> = RequestContext<Account<TCtxMeta>>;\n\n/**\n * Assembles proposals from buyer briefs.\n *\n * Reads inventory, signals, rate cards, availability. Produces proposals\n * where each `Product` carries a typed `implementation_config` (a recipe;\n * see {@link Recipe}) that the bound `DecisioningPlatform` consumes at\n * `create_media_buy` time.\n *\n * Methods may be sync or async; the framework awaits whatever is\n * returned. Same convention as `SalesPlatform`.\n *\n * **Required surface:** {@link getProducts}.\n *\n * **Optional surfaces (capability-gated):**\n *\n * - {@link refineProducts} — only invoked when\n * {@link ProposalCapabilities.refine} is true.\n * - {@link finalizeProposal} — only invoked when\n * {@link ProposalCapabilities.finalize} is true. Wired by the\n * framework's lifecycle helpers; intercepts `refine[i].action: 'finalize'`\n * entries before the underlying `getProducts` / `refineProducts` runs.\n *\n * Throw `AdcpError` for buyer-fixable rejection (`BUDGET_TOO_LOW`,\n * `POLICY_VIOLATION`, `UNSUPPORTED_FEATURE`); the framework projects to\n * the wire structured-error envelope.\n *\n * Adopter typing tip: parameterize on your concrete `Recipe` subtype to\n * get end-to-end recipe typing through `ctx.recipes` in the bound\n * `DecisioningPlatform`'s methods:\n *\n * ```ts\n * type MyRecipe = GAMRecipe | KevelRecipe;\n * const manager: ProposalManager<MyRecipe, MyTenantMeta> = { ... };\n * ```\n *\n * @public\n */\nexport interface ProposalManager<TRecipe extends Recipe = Recipe, TCtxMeta = unknown> {\n /**\n * What this ProposalManager can do — sales specialism + capability flags.\n */\n capabilities: ProposalCapabilities;\n\n /**\n * Initial product discovery from a buyer brief.\n *\n * Each returned `Product` SHOULD carry an `implementation_config`\n * matching the bound `DecisioningPlatform`'s recipe schema (see\n * {@link Recipe}). The framework treats `implementation_config` as\n * opaque on the wire; recipe typing is enforced through the\n * `TRecipe` type parameter on the adopter side.\n *\n * For non-guaranteed flows: typically a static catalogue, possibly\n * filtered by buyer brief / signals.\n *\n * For guaranteed flows: typically a brief-driven assembly consulting\n * rate cards + availability. Adopters return draft proposals; the\n * buyer drives the finalize transition via subsequent refine calls\n * with `action: 'finalize'`.\n */\n getProducts(req: GetProductsRequest, ctx: Ctx<TCtxMeta>): MaybePromise<ProposalGetProductsPayload>;\n\n /**\n * Refine-mode iteration on a previous `getProducts` response.\n *\n * Per the spec, refine is a `buying_mode` value on `get_products` —\n * the wire envelope is the same. The framework routes refine requests\n * to this method when:\n *\n * 1. The wired ProposalManager declares `capabilities.refine` = true,\n * 2. The request has `buying_mode === 'refine'`, AND\n * 3. The manager implements this method.\n *\n * Otherwise refine requests fall through to {@link getProducts}.\n *\n * Adopters implementing `refineProducts` without `finalize` support\n * should treat `action: 'finalize'` entries as `UNSUPPORTED_FEATURE`\n * and return a structured error. Adopters with `finalize` support\n * see those entries intercepted by the framework before this method\n * is called.\n */\n refineProducts?(req: GetProductsRequest, ctx: Ctx<TCtxMeta>): MaybePromise<ProposalGetProductsPayload>;\n\n /**\n * Commit a draft proposal to firm pricing + inventory hold.\n *\n * Wired by the framework's lifecycle helpers when:\n *\n * 1. `capabilities.finalize === true`,\n * 2. The buyer's request has `buying_mode === 'refine'` with a\n * `refine[i]` entry of `{ scope: 'proposal', action: 'finalize',\n * proposal_id }`, AND\n * 3. The proposal_id resolves to a DRAFT entry in the\n * {@link ProposalStore}.\n *\n * Return a {@link FinalizeProposalSuccess} for inline commit (the\n * spec-default route), or a `TaskHandoff<FinalizeProposalSuccess>` to\n * promote to a HITL background task. The HITL commit hook is wired\n * in v1.6+; v1.5 inline-only.\n */\n finalizeProposal?(\n req: FinalizeProposalRequest<TRecipe>,\n ctx: Ctx<TCtxMeta>\n ): MaybePromise<FinalizeProposalSuccess<TRecipe> | TaskHandoff<FinalizeProposalSuccess<TRecipe>>>;\n}\n"],"mappings":"AA+IO,SAAS,6BAA6B,MAAkC;AAC7E,QAAM,QAA4C,CAAC,oBAAoB,sBAAsB;AAC7F,MAAI,CAAC,MAAM,SAAS,KAAK,eAAe,GAAG;AACzC,UAAM,IAAI;AAAA,MACR,uDAAuD,KAAK,UAAU,KAAK,CAAC,SACnE,KAAK,UAAU,KAAK,eAAe,CAAC;AAAA,IAE/C;AAAA,EACF;AACA,MAAI,KAAK,yBAAyB,QAAQ,KAAK,wBAAwB,GAAG;AACxE,UAAM,IAAI;AAAA,MACR,gEAAgE,KAAK,qBAAqB;AAAA,IAE5F;AAAA,EACF;AACF;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../../../src/lib/server/decisioning/proposal/types.ts"],"sourcesContent":["/**\n * ProposalManager — primitives for the two-platform composition.\n *\n * The existing `DecisioningPlatform` conflates two concerns: assembling\n * proposals from briefs (`get_products`, refine) vs. executing media buys\n * against an upstream (`create_media_buy`, `update_media_buy`,\n * `get_media_buy_delivery`). The two-platform composition splits them: a\n * separate {@link ProposalManager} handles the proposal side; the\n * `DecisioningPlatform` keeps the execution side. Either platform can be\n * mock-backed independently.\n *\n * Ports the Python primitives shipped in `adcp-client-python` PRs #504\n * (v1) and #550 (v1.5):\n *\n * - {@link ProposalManager} — interface with `getProducts` (required)\n * plus optional `refineProducts` and `finalizeProposal`.\n * - {@link ProposalCapabilities} — sales-axis-scoped capability flags.\n * - {@link Recipe} — typed `recipe_kind`-discriminated base; adopters\n * subclass with their internal-config schema. Rides on\n * `Product.implementation_config` (opaque to the buyer).\n * - {@link CapabilityOverlap} — typed declaration of which wire\n * capabilities the buyer can configure on a product. The framework\n * validates buyer requests against the overlap pre-adapter.\n * - {@link FinalizeProposalRequest} / {@link FinalizeProposalSuccess} —\n * framework-internal shapes for the finalize lifecycle (wired in the\n * v1.5 dispatch helpers).\n *\n * @public\n * @packageDocumentation\n */\n\nimport type { MaybePromise } from '../../create-adcp-server';\nimport type { Account } from '../account';\nimport type { RequestContext } from '../context';\nimport type { GetProductsResponse } from '../../../types/tools.generated';\nimport type {\n CanonicalCreativeResponse,\n CanonicalGetProductsRequest,\n CanonicalProduct,\n} from '../../../v2/projection/creative-delivery';\nimport type { RequireCacheScopeWhenProducts, ServerPayload } from '../../../types/server-payload';\nimport type { TaskHandoff } from '../async-outcome';\n\nexport type ProposalGetProductsPayload = RequireCacheScopeWhenProducts<\n Omit<ServerPayload<CanonicalCreativeResponse<GetProductsResponse>>, 'products'> & { products?: CanonicalProduct[] }\n>;\nexport type LegacyProposalGetProductsPayload = RequireCacheScopeWhenProducts<ServerPayload<GetProductsResponse>>;\n\n// ---------------------------------------------------------------------------\n// Capabilities\n// ---------------------------------------------------------------------------\n\n/**\n * Sales specialisms a {@link ProposalManager} can serve. Mirrors the\n * `sales-*` slugs in the spec's specialism enum.\n *\n * v1 scopes to the two `ProposalManager`-relevant flavours; broader\n * coverage (broadcast-tv, social, proposal-mode, catalog-driven) lands\n * as adopter signal grows.\n *\n * @public\n */\nexport type ProposalSalesSpecialism = 'sales-guaranteed' | 'sales-non-guaranteed';\n\n/**\n * Capability declaration for a {@link ProposalManager}.\n *\n * Sales-axis-scoped: proposal handling is a sales-specialism concern,\n * not a generic platform-wide concept. The `salesSpecialism` field\n * declares which AdCP sales specialism this manager serves; capability\n * flags declare which optional behaviours it supports.\n *\n * The framework reads this declaration at server-construction time to\n * decide which dispatch paths apply (e.g. `refineProducts` is only\n * invoked when `refine` is true; `finalizeProposal` is only invoked\n * when `finalize` is true).\n *\n * @public\n */\nexport interface ProposalCapabilities {\n /**\n * Which AdCP sales specialism this manager serves.\n *\n * `sales-guaranteed` for guaranteed-direct flows with proposal\n * lifecycle (finalize → committed proposal → media buy).\n *\n * `sales-non-guaranteed` for catalog-style flows where `getProducts`\n * returns a static catalog and buyers reference products directly at\n * `create_media_buy`.\n */\n salesSpecialism: ProposalSalesSpecialism;\n\n /**\n * When true, the manager implements {@link ProposalManager.refineProducts}\n * and the framework routes `get_products` requests with\n * `buying_mode: 'refine'` to that method. When false, refine requests\n * fall through to `getProducts` (or surface `UNSUPPORTED_FEATURE` if\n * the manager rejects them).\n */\n refine?: boolean;\n\n /**\n * When true, the manager implements {@link ProposalManager.finalizeProposal}\n * and the framework intercepts `refine[i].action: 'finalize'` entries to\n * commit drafts via the lifecycle helpers. When false, finalize entries\n * pass through to the underlying `getProducts` / `refineProducts` method\n * unchanged.\n */\n finalize?: boolean;\n\n /**\n * Grace window (in seconds) added to a committed proposal's `expires_at`\n * before the framework rejects `create_media_buy` calls referencing the\n * proposal with `PROPOSAL_EXPIRED`. Default 0 (strict).\n */\n expiresAtGraceSeconds?: number;\n\n /**\n * Signal-driven product assembly — the manager constructs products from\n * buyer signals at request time rather than enumerating a static\n * catalogue. Informational in v1.5; future PRs may validate that\n * inventory / signal stores are wired when this flag is set.\n */\n dynamicProducts?: boolean;\n\n /**\n * The manager consults rate cards (per buyer relationship per product)\n * when emitting prices. Informational in v1.5.\n */\n rateCardPricing?: boolean;\n\n /**\n * The manager reserves capacity at proposal time (typical for\n * guaranteed). Informational in v1.5; the `finalize` transition that\n * drives the actual hold is wired via the lifecycle helpers.\n */\n availabilityReservations?: boolean;\n}\n\n/**\n * Validate a {@link ProposalCapabilities} object. Throws when the declaration\n * is malformed (unknown `salesSpecialism`, negative `expiresAtGraceSeconds`).\n * The framework calls this at boot; adopters can call it from their own\n * config-validation code paths to fail fast on misconfiguration.\n *\n * Kept as a plain function rather than a class constructor to match the\n * codebase's preference for plain TS interfaces over runtime classes\n * for adopter-facing types.\n *\n * @public\n */\nexport function validateProposalCapabilities(caps: ProposalCapabilities): void {\n const valid: readonly ProposalSalesSpecialism[] = ['sales-guaranteed', 'sales-non-guaranteed'];\n if (!valid.includes(caps.salesSpecialism)) {\n throw new Error(\n `ProposalCapabilities.salesSpecialism must be one of ${JSON.stringify(valid)}. ` +\n `Got ${JSON.stringify(caps.salesSpecialism)}. v1.5 scopes ProposalManager to the two ` +\n `core sales specialisms; broader specialism support lands in subsequent releases.`\n );\n }\n if (caps.expiresAtGraceSeconds != null && caps.expiresAtGraceSeconds < 0) {\n throw new Error(\n `ProposalCapabilities.expiresAtGraceSeconds must be >= 0; got ${caps.expiresAtGraceSeconds}. ` +\n `The grace window extends the inventory hold past expires_at; negative values would shrink it.`\n );\n }\n}\n\n// ---------------------------------------------------------------------------\n// Recipe + CapabilityOverlap\n// ---------------------------------------------------------------------------\n\n/**\n * Per-product subset of wire capability flags that the buyer can\n * configure on this product.\n *\n * Buyer requests asking for capabilities outside this overlap are\n * rejected by the framework before the adapter sees them (validated by\n * the lifecycle helpers — see `proposal/lifecycle.ts`).\n *\n * Each field is `ReadonlySet<string> | undefined`:\n *\n * - `undefined` → framework does not gate this axis (open).\n * - `ReadonlySet` → buyer choices must be subsets of this set.\n * An empty set means deny-all on this axis.\n *\n * The undefined vs. empty-set distinction matches set intuition:\n * \"no constraint\" is `undefined`; \"allowed set is empty\" is `new Set()`.\n *\n * **Why no extras dict?** v1.5 deliberately omits an `extras` escape\n * hatch (per Python design § D4). Adopters with novel gating needs\n * extend the interface with typed fields; a dict bag leaves no paper\n * trail. If a new axis turns out to be widely useful, it lands as a\n * typed field on `CapabilityOverlap` upstream.\n *\n * @public\n */\nexport interface CapabilityOverlap {\n /**\n * Subset of wire `pricing_models` the buyer can choose. Validated\n * against the matching `PricingOption.pricing_model` on the buyer's\n * package.\n */\n pricingModels?: ReadonlySet<string>;\n\n /**\n * Subset of wire targeting dimensions (`geo`, `device_type`,\n * `language`, etc.). Validated against the keys present on the\n * buyer's `targeting_overlay`.\n */\n targetingDimensions?: ReadonlySet<string>;\n\n /**\n * Subset of `{ guaranteed, non_guaranteed }` the product offers.\n */\n deliveryTypes?: ReadonlySet<string>;\n\n /**\n * If the seller integrates signals, which signal types this product\n * accepts. An empty set means the seller explicitly refuses all\n * signals on this product; `undefined` means no framework gate.\n */\n signalTypes?: ReadonlySet<string>;\n}\n\n/**\n * Base type for typed product `implementation_config` payloads.\n *\n * Adopters declare a discriminated subtype with a literal `recipe_kind`:\n *\n * ```ts\n * interface GAMRecipe extends Recipe {\n * recipe_kind: 'gam';\n * line_item_template_id: string;\n * ad_unit_ids: readonly string[];\n * capability_overlap?: CapabilityOverlap;\n * }\n * ```\n *\n * The kind tag enables router-by-recipe-kind dispatch in the\n * multi-decisioning case (one ProposalManager + many DecisioningPlatforms,\n * each handling a subset of recipe kinds). v1.5 doesn't yet wire that\n * routing — adopters using a single DecisioningPlatform attach recipes\n * freely without registry validation.\n *\n * **The recipe is never on the buyer's wire surface.** It rides inside\n * `Product.implementation_config` (an opaque-to-buyer dict). Buyers treat\n * it as a black box; the framework persists it through the proposal\n * lifecycle so the executing DecisioningPlatform sees a stable view.\n *\n * `capability_overlap` is optional. When present, the framework activates\n * the v1.5 buyer-request validation seam against the declared subsets.\n *\n * @public\n */\nexport interface Recipe {\n /** Adapter-family discriminator. Subtypes narrow with a literal type. */\n recipe_kind: string;\n\n /**\n * Optional typed declaration of which wire capabilities the buyer can\n * configure on this product. `undefined` means no framework gating.\n * An explicit {@link CapabilityOverlap} activates the v1.5 validation.\n */\n capability_overlap?: CapabilityOverlap;\n}\n\n// ---------------------------------------------------------------------------\n// Finalize lifecycle shapes\n// ---------------------------------------------------------------------------\n\n/**\n * Framework-internal request shape passed to {@link ProposalManager.finalizeProposal}.\n *\n * Constructed by the framework dispatcher when a buyer's `get_products`\n * request with `buying_mode: 'refine'` carries a `refine[i].action: 'finalize'`\n * entry. Adopters don't parse the wire envelope; the framework projects.\n *\n * @public\n */\nexport interface FinalizeProposalRequest<TRecipe extends Recipe = Recipe> {\n /**\n * The draft proposal the buyer is asking to finalize. Hydrated from\n * the wire's `refine[i].proposal_id` field.\n */\n proposalId: string;\n\n /**\n * `product_id -> Recipe` mapping pulled from the {@link ProposalStore}\n * draft. The adopter's finalize logic typically lock-prices these and\n * emits the committed proposal.\n */\n recipes: ReadonlyMap<string, TRecipe>;\n\n /**\n * The draft's wire `Proposal` shape (the same payload the adopter\n * returned on the prior `getProducts` / `refineProducts` call).\n * Adopter typically modifies this with locked pricing and returns it\n * on {@link FinalizeProposalSuccess}.\n */\n proposalPayload: Record<string, unknown>;\n\n /**\n * The buyer's per-entry refine `ask` text — what they want finalized.\n * Free-form; adopter consumes.\n */\n ask?: string;\n\n /**\n * The parent canonical get-products request so the adopter sees the full\n * envelope (account, etc.) without the framework projecting fields\n * one-by-one.\n */\n parentRequest: CanonicalGetProductsRequest;\n}\n\n/**\n * Adopter-returned shape from {@link ProposalManager.finalizeProposal} —\n * inline commit.\n *\n * Framework calls `ProposalStore.commit` with these fields before\n * projecting the wire response. The buyer sees the committed `Proposal`\n * with `proposal_status: 'committed'` + `expires_at` populated on the\n * next `get_products` response payload.\n *\n * @public\n */\nexport interface FinalizeProposalSuccess<TRecipe extends Recipe = Recipe> {\n /**\n * The wire `Proposal` shape with locked pricing and\n * `proposal_status: 'committed'`. Adopter typically derives this from\n * {@link FinalizeProposalRequest.proposalPayload} with modifications.\n *\n * **Must be JSON-serializable end-to-end** — non-JSON values won't\n * survive a process restart through a durable {@link ProposalStore}.\n */\n proposal: Record<string, unknown>;\n\n /**\n * Inventory hold deadline. After this (plus the adopter's\n * {@link ProposalCapabilities.expiresAtGraceSeconds} window), the\n * framework rejects `create_media_buy` calls referencing the proposal\n * with `PROPOSAL_EXPIRED`.\n */\n expiresAt: Date;\n\n /**\n * Optional refreshed recipe mapping. Omitting preserves the draft's\n * recipes verbatim. Adopters whose finalize logic mutates recipe\n * fields (e.g. locking a line-item template id) supply a fresh\n * mapping.\n */\n recipes?: ReadonlyMap<string, TRecipe>;\n}\n\n// ---------------------------------------------------------------------------\n// ProposalManager interface\n// ---------------------------------------------------------------------------\n\ntype Ctx<TCtxMeta> = RequestContext<Account<TCtxMeta>>;\n\n/**\n * Assembles proposals from buyer briefs.\n *\n * Reads inventory, signals, rate cards, availability. Produces proposals\n * where each `Product` carries a typed `implementation_config` (a recipe;\n * see {@link Recipe}) that the bound `DecisioningPlatform` consumes at\n * `create_media_buy` time.\n *\n * Methods may be sync or async; the framework awaits whatever is\n * returned. Same convention as `SalesPlatform`.\n *\n * **Required surface:** {@link getProducts}.\n *\n * **Optional surfaces (capability-gated):**\n *\n * - {@link refineProducts} — only invoked when\n * {@link ProposalCapabilities.refine} is true.\n * - {@link finalizeProposal} — only invoked when\n * {@link ProposalCapabilities.finalize} is true. Wired by the\n * framework's lifecycle helpers; intercepts `refine[i].action: 'finalize'`\n * entries before the underlying `getProducts` / `refineProducts` runs.\n *\n * Throw `AdcpError` for buyer-fixable rejection (`BUDGET_TOO_LOW`,\n * `POLICY_VIOLATION`, `UNSUPPORTED_FEATURE`); the framework projects to\n * the wire structured-error envelope.\n *\n * Adopter typing tip: parameterize on your concrete `Recipe` subtype to\n * get end-to-end recipe typing through `ctx.recipes` in the bound\n * `DecisioningPlatform`'s methods:\n *\n * ```ts\n * type MyRecipe = GAMRecipe | KevelRecipe;\n * const manager: ProposalManager<MyRecipe, MyTenantMeta> = { ... };\n * ```\n *\n * @public\n */\nexport interface ProposalManager<TRecipe extends Recipe = Recipe, TCtxMeta = unknown> {\n /**\n * What this ProposalManager can do — sales specialism + capability flags.\n */\n capabilities: ProposalCapabilities;\n\n /**\n * Initial product discovery from a buyer brief.\n *\n * Each returned `Product` SHOULD carry an `implementation_config`\n * matching the bound `DecisioningPlatform`'s recipe schema (see\n * {@link Recipe}). The framework treats `implementation_config` as\n * opaque on the wire; recipe typing is enforced through the\n * `TRecipe` type parameter on the adopter side.\n *\n * For non-guaranteed flows: typically a static catalogue, possibly\n * filtered by buyer brief / signals.\n *\n * For guaranteed flows: typically a brief-driven assembly consulting\n * rate cards + availability. Adopters return draft proposals; the\n * buyer drives the finalize transition via subsequent refine calls\n * with `action: 'finalize'`.\n */\n getProducts(req: CanonicalGetProductsRequest, ctx: Ctx<TCtxMeta>): MaybePromise<ProposalGetProductsPayload>;\n\n /**\n * Refine-mode iteration on a previous `getProducts` response.\n *\n * Per the spec, refine is a `buying_mode` value on `get_products` —\n * the wire envelope is the same. The framework routes refine requests\n * to this method when:\n *\n * 1. The wired ProposalManager declares `capabilities.refine` = true,\n * 2. The request has `buying_mode === 'refine'`, AND\n * 3. The manager implements this method.\n *\n * Otherwise refine requests fall through to {@link getProducts}.\n *\n * Adopters implementing `refineProducts` without `finalize` support\n * should treat `action: 'finalize'` entries as `UNSUPPORTED_FEATURE`\n * and return a structured error. Adopters with `finalize` support\n * see those entries intercepted by the framework before this method\n * is called.\n */\n refineProducts?(req: CanonicalGetProductsRequest, ctx: Ctx<TCtxMeta>): MaybePromise<ProposalGetProductsPayload>;\n\n /**\n * Commit a draft proposal to firm pricing + inventory hold.\n *\n * Wired by the framework's lifecycle helpers when:\n *\n * 1. `capabilities.finalize === true`,\n * 2. The buyer's request has `buying_mode === 'refine'` with a\n * `refine[i]` entry of `{ scope: 'proposal', action: 'finalize',\n * proposal_id }`, AND\n * 3. The proposal_id resolves to a DRAFT entry in the\n * {@link ProposalStore}.\n *\n * Return a {@link FinalizeProposalSuccess} for inline commit (the\n * spec-default route), or a `TaskHandoff<FinalizeProposalSuccess>` to\n * promote to a HITL background task. The HITL commit hook is wired\n * in v1.6+; v1.5 inline-only.\n */\n finalizeProposal?(\n req: FinalizeProposalRequest<TRecipe>,\n ctx: Ctx<TCtxMeta>\n ): MaybePromise<FinalizeProposalSuccess<TRecipe> | TaskHandoff<FinalizeProposalSuccess<TRecipe>>>;\n}\n"],"mappings":"AAuJO,SAAS,6BAA6B,MAAkC;AAC7E,QAAM,QAA4C,CAAC,oBAAoB,sBAAsB;AAC7F,MAAI,CAAC,MAAM,SAAS,KAAK,eAAe,GAAG;AACzC,UAAM,IAAI;AAAA,MACR,uDAAuD,KAAK,UAAU,KAAK,CAAC,SACnE,KAAK,UAAU,KAAK,eAAe,CAAC;AAAA,IAE/C;AAAA,EACF;AACA,MAAI,KAAK,yBAAyB,QAAQ,KAAK,wBAAwB,GAAG;AACxE,UAAM,IAAI;AAAA,MACR,gEAAgE,KAAK,qBAAqB;AAAA,IAE5F;AAAA,EACF;AACF;","names":[]}
|