@adcp/sdk 5.25.1 → 6.0.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 +45 -7
- package/dist/lib/compliance-fixtures/index.d.ts +1 -1
- package/dist/lib/compliance-fixtures/index.js +1 -1
- package/dist/lib/core/AgentClient.d.ts.map +1 -1
- package/dist/lib/core/SingleAgentClient.d.ts.map +1 -1
- package/dist/lib/core/SingleAgentClient.js +15 -0
- package/dist/lib/core/SingleAgentClient.js.map +1 -1
- package/dist/lib/core/TaskExecutor.d.ts +7 -0
- package/dist/lib/core/TaskExecutor.d.ts.map +1 -1
- package/dist/lib/core/TaskExecutor.js +9 -2
- package/dist/lib/core/TaskExecutor.js.map +1 -1
- package/dist/lib/index.d.ts +1 -1
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/index.js +7 -8
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/schemas/index.d.ts +1 -1
- package/dist/lib/schemas/index.js +1 -1
- package/dist/lib/server/create-adcp-server.d.ts +129 -11
- package/dist/lib/server/create-adcp-server.d.ts.map +1 -1
- package/dist/lib/server/create-adcp-server.js +112 -2
- package/dist/lib/server/create-adcp-server.js.map +1 -1
- package/dist/lib/server/ctx-metadata/backends/memory.d.ts +27 -0
- package/dist/lib/server/ctx-metadata/backends/memory.d.ts.map +1 -0
- package/dist/lib/server/ctx-metadata/backends/memory.js +72 -0
- package/dist/lib/server/ctx-metadata/backends/memory.js.map +1 -0
- package/dist/lib/server/ctx-metadata/backends/pg.d.ts +62 -0
- package/dist/lib/server/ctx-metadata/backends/pg.d.ts.map +1 -0
- package/dist/lib/server/ctx-metadata/backends/pg.js +145 -0
- package/dist/lib/server/ctx-metadata/backends/pg.js.map +1 -0
- package/dist/lib/server/ctx-metadata/index.d.ts +15 -0
- package/dist/lib/server/ctx-metadata/index.d.ts.map +1 -0
- package/dist/lib/server/ctx-metadata/index.js +28 -0
- package/dist/lib/server/ctx-metadata/index.js.map +1 -0
- package/dist/lib/server/ctx-metadata/store.d.ts +177 -0
- package/dist/lib/server/ctx-metadata/store.d.ts.map +1 -0
- package/dist/lib/server/ctx-metadata/store.js +327 -0
- package/dist/lib/server/ctx-metadata/store.js.map +1 -0
- package/dist/lib/server/ctx-metadata/wire-shape.d.ts +55 -0
- package/dist/lib/server/ctx-metadata/wire-shape.d.ts.map +1 -0
- package/dist/lib/server/ctx-metadata/wire-shape.js +121 -0
- package/dist/lib/server/ctx-metadata/wire-shape.js.map +1 -0
- package/dist/lib/server/decisioning/account.d.ts +309 -0
- package/dist/lib/server/decisioning/account.d.ts.map +1 -0
- package/dist/lib/server/decisioning/account.js +102 -0
- package/dist/lib/server/decisioning/account.js.map +1 -0
- package/dist/lib/server/decisioning/admin-router.d.ts +75 -0
- package/dist/lib/server/decisioning/admin-router.d.ts.map +1 -0
- package/dist/lib/server/decisioning/admin-router.js +120 -0
- package/dist/lib/server/decisioning/admin-router.js.map +1 -0
- package/dist/lib/server/decisioning/assembly-helpers.d.ts +204 -0
- package/dist/lib/server/decisioning/assembly-helpers.d.ts.map +1 -0
- package/dist/lib/server/decisioning/assembly-helpers.js +173 -0
- package/dist/lib/server/decisioning/assembly-helpers.js.map +1 -0
- package/dist/lib/server/decisioning/async-outcome.d.ts +154 -0
- package/dist/lib/server/decisioning/async-outcome.d.ts.map +1 -0
- package/dist/lib/server/decisioning/async-outcome.js +239 -0
- package/dist/lib/server/decisioning/async-outcome.js.map +1 -0
- package/dist/lib/server/decisioning/capabilities.d.ts +251 -0
- package/dist/lib/server/decisioning/capabilities.d.ts.map +1 -0
- package/dist/lib/server/decisioning/capabilities.js +16 -0
- package/dist/lib/server/decisioning/capabilities.js.map +1 -0
- package/dist/lib/server/decisioning/context.d.ts +212 -0
- package/dist/lib/server/decisioning/context.d.ts.map +1 -0
- package/dist/lib/server/decisioning/context.js +26 -0
- package/dist/lib/server/decisioning/context.js.map +1 -0
- package/dist/lib/server/decisioning/errors-typed.d.ts +104 -0
- package/dist/lib/server/decisioning/errors-typed.d.ts.map +1 -0
- package/dist/lib/server/decisioning/errors-typed.js +304 -0
- package/dist/lib/server/decisioning/errors-typed.js.map +1 -0
- package/dist/lib/server/decisioning/helpers.d.ts +131 -0
- package/dist/lib/server/decisioning/helpers.d.ts.map +1 -0
- package/dist/lib/server/decisioning/helpers.js +134 -0
- package/dist/lib/server/decisioning/helpers.js.map +1 -0
- package/dist/lib/server/decisioning/index.d.ts +46 -0
- package/dist/lib/server/decisioning/index.d.ts.map +1 -0
- package/dist/lib/server/decisioning/index.js +120 -0
- package/dist/lib/server/decisioning/index.js.map +1 -0
- package/dist/lib/server/decisioning/list-helpers.d.ts +53 -0
- package/dist/lib/server/decisioning/list-helpers.d.ts.map +1 -0
- package/dist/lib/server/decisioning/list-helpers.js +96 -0
- package/dist/lib/server/decisioning/list-helpers.js.map +1 -0
- package/dist/lib/server/decisioning/manifest-helpers.d.ts +56 -0
- package/dist/lib/server/decisioning/manifest-helpers.d.ts.map +1 -0
- package/dist/lib/server/decisioning/manifest-helpers.js +78 -0
- package/dist/lib/server/decisioning/manifest-helpers.js.map +1 -0
- package/dist/lib/server/decisioning/pagination.d.ts +21 -0
- package/dist/lib/server/decisioning/pagination.d.ts.map +1 -0
- package/dist/lib/server/decisioning/pagination.js +12 -0
- package/dist/lib/server/decisioning/pagination.js.map +1 -0
- package/dist/lib/server/decisioning/platform.d.ts +188 -0
- package/dist/lib/server/decisioning/platform.d.ts.map +1 -0
- package/dist/lib/server/decisioning/platform.js +19 -0
- package/dist/lib/server/decisioning/platform.js.map +1 -0
- package/dist/lib/server/decisioning/runtime/from-platform.d.ts +510 -0
- package/dist/lib/server/decisioning/runtime/from-platform.d.ts.map +1 -0
- package/dist/lib/server/decisioning/runtime/from-platform.js +2196 -0
- package/dist/lib/server/decisioning/runtime/from-platform.js.map +1 -0
- package/dist/lib/server/decisioning/runtime/postgres-task-registry.d.ts +114 -0
- package/dist/lib/server/decisioning/runtime/postgres-task-registry.d.ts.map +1 -0
- package/dist/lib/server/decisioning/runtime/postgres-task-registry.js +247 -0
- package/dist/lib/server/decisioning/runtime/postgres-task-registry.js.map +1 -0
- package/dist/lib/server/decisioning/runtime/protocol-for-tool.d.ts +32 -0
- package/dist/lib/server/decisioning/runtime/protocol-for-tool.d.ts.map +1 -0
- package/dist/lib/server/decisioning/runtime/protocol-for-tool.js +127 -0
- package/dist/lib/server/decisioning/runtime/protocol-for-tool.js.map +1 -0
- package/dist/lib/server/decisioning/runtime/task-registry.d.ts +105 -0
- package/dist/lib/server/decisioning/runtime/task-registry.d.ts.map +1 -0
- package/dist/lib/server/decisioning/runtime/task-registry.js +96 -0
- package/dist/lib/server/decisioning/runtime/task-registry.js.map +1 -0
- package/dist/lib/server/decisioning/runtime/to-context.d.ts +54 -0
- package/dist/lib/server/decisioning/runtime/to-context.d.ts.map +1 -0
- package/dist/lib/server/decisioning/runtime/to-context.js +166 -0
- package/dist/lib/server/decisioning/runtime/to-context.js.map +1 -0
- package/dist/lib/server/decisioning/runtime/validate-platform.d.ts +20 -0
- package/dist/lib/server/decisioning/runtime/validate-platform.d.ts.map +1 -0
- package/dist/lib/server/decisioning/runtime/validate-platform.js +93 -0
- package/dist/lib/server/decisioning/runtime/validate-platform.js.map +1 -0
- package/dist/lib/server/decisioning/specialisms/audiences.d.ts +72 -0
- package/dist/lib/server/decisioning/specialisms/audiences.d.ts.map +1 -0
- package/dist/lib/server/decisioning/specialisms/audiences.js +15 -0
- package/dist/lib/server/decisioning/specialisms/audiences.js.map +1 -0
- package/dist/lib/server/decisioning/specialisms/brand-rights.d.ts +92 -0
- package/dist/lib/server/decisioning/specialisms/brand-rights.d.ts.map +1 -0
- package/dist/lib/server/decisioning/specialisms/brand-rights.js +28 -0
- package/dist/lib/server/decisioning/specialisms/brand-rights.js.map +1 -0
- package/dist/lib/server/decisioning/specialisms/campaign-governance.d.ts +67 -0
- package/dist/lib/server/decisioning/specialisms/campaign-governance.d.ts.map +1 -0
- package/dist/lib/server/decisioning/specialisms/campaign-governance.js +31 -0
- package/dist/lib/server/decisioning/specialisms/campaign-governance.js.map +1 -0
- package/dist/lib/server/decisioning/specialisms/content-standards.d.ts +78 -0
- package/dist/lib/server/decisioning/specialisms/content-standards.d.ts.map +1 -0
- package/dist/lib/server/decisioning/specialisms/content-standards.js +35 -0
- package/dist/lib/server/decisioning/specialisms/content-standards.js.map +1 -0
- package/dist/lib/server/decisioning/specialisms/creative-ad-server.d.ts +81 -0
- package/dist/lib/server/decisioning/specialisms/creative-ad-server.d.ts.map +1 -0
- package/dist/lib/server/decisioning/specialisms/creative-ad-server.js +28 -0
- package/dist/lib/server/decisioning/specialisms/creative-ad-server.js.map +1 -0
- package/dist/lib/server/decisioning/specialisms/creative.d.ts +144 -0
- package/dist/lib/server/decisioning/specialisms/creative.d.ts.map +1 -0
- package/dist/lib/server/decisioning/specialisms/creative.js +19 -0
- package/dist/lib/server/decisioning/specialisms/creative.js.map +1 -0
- package/dist/lib/server/decisioning/specialisms/lists.d.ts +61 -0
- package/dist/lib/server/decisioning/specialisms/lists.d.ts.map +1 -0
- package/dist/lib/server/decisioning/specialisms/lists.js +30 -0
- package/dist/lib/server/decisioning/specialisms/lists.js.map +1 -0
- package/dist/lib/server/decisioning/specialisms/sales.d.ts +163 -0
- package/dist/lib/server/decisioning/specialisms/sales.d.ts.map +1 -0
- package/dist/lib/server/decisioning/specialisms/sales.js +64 -0
- package/dist/lib/server/decisioning/specialisms/sales.js.map +1 -0
- package/dist/lib/server/decisioning/specialisms/signals.d.ts +64 -0
- package/dist/lib/server/decisioning/specialisms/signals.d.ts.map +1 -0
- package/dist/lib/server/decisioning/specialisms/signals.js +28 -0
- package/dist/lib/server/decisioning/specialisms/signals.js.map +1 -0
- package/dist/lib/server/decisioning/start-time.d.ts +76 -0
- package/dist/lib/server/decisioning/start-time.d.ts.map +1 -0
- package/dist/lib/server/decisioning/start-time.js +81 -0
- package/dist/lib/server/decisioning/start-time.js.map +1 -0
- package/dist/lib/server/decisioning/status-changes.d.ts +165 -0
- package/dist/lib/server/decisioning/status-changes.d.ts.map +1 -0
- package/dist/lib/server/decisioning/status-changes.js +131 -0
- package/dist/lib/server/decisioning/status-changes.js.map +1 -0
- package/dist/lib/server/decisioning/status-mappers.d.ts +46 -0
- package/dist/lib/server/decisioning/status-mappers.d.ts.map +1 -0
- package/dist/lib/server/decisioning/status-mappers.js +46 -0
- package/dist/lib/server/decisioning/status-mappers.js.map +1 -0
- package/dist/lib/server/decisioning/tenant-registry.d.ts +289 -0
- package/dist/lib/server/decisioning/tenant-registry.d.ts.map +1 -0
- package/dist/lib/server/decisioning/tenant-registry.js +503 -0
- package/dist/lib/server/decisioning/tenant-registry.js.map +1 -0
- package/dist/lib/server/express-adapter.d.ts +1 -1
- package/dist/lib/server/express-adapter.js +1 -1
- package/dist/lib/server/governance.d.ts +1 -1
- package/dist/lib/server/governance.js +1 -1
- package/dist/lib/server/idempotency/store.d.ts +1 -1
- package/dist/lib/server/idempotency/store.js +1 -1
- package/dist/lib/server/index.d.ts +9 -2
- package/dist/lib/server/index.d.ts.map +1 -1
- package/dist/lib/server/index.js +79 -4
- package/dist/lib/server/index.js.map +1 -1
- package/dist/lib/server/legacy/v5/index.d.ts +38 -0
- package/dist/lib/server/legacy/v5/index.d.ts.map +1 -0
- package/dist/lib/server/legacy/v5/index.js +60 -0
- package/dist/lib/server/legacy/v5/index.js.map +1 -0
- package/dist/lib/server/normalize-errors.d.ts +88 -0
- package/dist/lib/server/normalize-errors.d.ts.map +1 -0
- package/dist/lib/server/normalize-errors.js +146 -0
- package/dist/lib/server/normalize-errors.js.map +1 -0
- package/dist/lib/server/pick-safe-details.d.ts +90 -0
- package/dist/lib/server/pick-safe-details.d.ts.map +1 -0
- package/dist/lib/server/pick-safe-details.js +148 -0
- package/dist/lib/server/pick-safe-details.js.map +1 -0
- package/dist/lib/server/postgres-state-store.d.ts +1 -1
- package/dist/lib/server/postgres-state-store.js +1 -1
- package/dist/lib/server/responses.d.ts +38 -0
- package/dist/lib/server/responses.d.ts.map +1 -1
- package/dist/lib/server/responses.js +38 -0
- package/dist/lib/server/responses.js.map +1 -1
- package/dist/lib/server/state-store.d.ts +1 -1
- package/dist/lib/server/state-store.js +1 -1
- package/dist/lib/server/test-controller.d.ts +10 -3
- package/dist/lib/server/test-controller.d.ts.map +1 -1
- package/dist/lib/server/test-controller.js +10 -3
- package/dist/lib/server/test-controller.js.map +1 -1
- package/dist/lib/testing/comply-controller.d.ts +47 -1
- package/dist/lib/testing/comply-controller.d.ts.map +1 -1
- package/dist/lib/testing/comply-controller.js +11 -4
- package/dist/lib/testing/comply-controller.js.map +1 -1
- package/dist/lib/testing/index.d.ts +1 -1
- package/dist/lib/testing/index.d.ts.map +1 -1
- package/dist/lib/testing/index.js.map +1 -1
- package/dist/lib/testing/personas/index.d.ts +143 -0
- package/dist/lib/testing/personas/index.d.ts.map +1 -0
- package/dist/lib/testing/personas/index.js +190 -0
- package/dist/lib/testing/personas/index.js.map +1 -0
- package/dist/lib/testing/storyboard/index.d.ts +1 -1
- package/dist/lib/testing/storyboard/index.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/index.js +3 -2
- package/dist/lib/testing/storyboard/index.js.map +1 -1
- package/dist/lib/testing/storyboard/runner.d.ts +13 -0
- package/dist/lib/testing/storyboard/runner.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/runner.js +179 -7
- package/dist/lib/testing/storyboard/runner.js.map +1 -1
- package/dist/lib/types/asset-instances.d.ts +1 -0
- package/dist/lib/types/asset-instances.d.ts.map +1 -1
- package/dist/lib/types/core.generated.d.ts +203 -98
- package/dist/lib/types/core.generated.d.ts.map +1 -1
- package/dist/lib/types/core.generated.js +1 -1
- package/dist/lib/types/index.d.ts +1 -0
- package/dist/lib/types/index.d.ts.map +1 -1
- package/dist/lib/types/index.js.map +1 -1
- package/dist/lib/types/schemas.generated.d.ts +599 -159
- package/dist/lib/types/schemas.generated.d.ts.map +1 -1
- package/dist/lib/types/schemas.generated.js +175 -94
- package/dist/lib/types/schemas.generated.js.map +1 -1
- package/dist/lib/types/tools.generated.d.ts +315 -46
- package/dist/lib/types/tools.generated.d.ts.map +1 -1
- package/dist/lib/utils/capabilities.d.ts +1 -1
- package/dist/lib/utils/capabilities.d.ts.map +1 -1
- package/dist/lib/utils/capabilities.js +6 -0
- package/dist/lib/utils/capabilities.js.map +1 -1
- package/dist/lib/validation/schema-validator.d.ts +13 -0
- package/dist/lib/validation/schema-validator.d.ts.map +1 -1
- package/dist/lib/validation/schema-validator.js +240 -3
- package/dist/lib/validation/schema-validator.js.map +1 -1
- 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/docs/guides/BUILD-AN-AGENT.md +30 -5
- package/docs/llms.txt +28 -17
- package/examples/README.md +3 -1
- package/examples/decisioning-platform-broadcast-tv.ts +300 -0
- package/examples/decisioning-platform-identity-graph.ts +214 -0
- package/examples/decisioning-platform-mock-seller.ts +332 -0
- package/examples/decisioning-platform-multi-tenant.ts +128 -0
- package/examples/decisioning-platform-programmatic.ts +254 -0
- package/examples/signals-agent.ts +1 -1
- package/package.json +13 -2
- package/skills/build-brand-rights-agent/SKILL.md +10 -3
- package/skills/build-creative-agent/SKILL.md +94 -64
- package/skills/build-decisioning-creative-template/SKILL.md +554 -0
- package/skills/build-decisioning-platform/SKILL.md +304 -0
- package/skills/build-decisioning-platform/advanced/BRAND-RIGHTS.md +25 -0
- package/skills/build-decisioning-platform/advanced/COMPLIANCE.md +23 -0
- package/skills/build-decisioning-platform/advanced/GOVERNANCE.md +24 -0
- package/skills/build-decisioning-platform/advanced/HITL.md +34 -0
- package/skills/build-decisioning-platform/advanced/IDEMPOTENCY.md +52 -0
- package/skills/build-decisioning-platform/advanced/MULTI-TENANT.md +47 -0
- package/skills/build-decisioning-platform/advanced/OAUTH.md +22 -0
- package/skills/build-decisioning-platform/advanced/REFERENCE.md +991 -0
- package/skills/build-decisioning-platform/advanced/SANDBOX.md +24 -0
- package/skills/build-decisioning-platform/advanced/STATE-MACHINE.md +52 -0
- package/skills/build-decisioning-signal-marketplace/SKILL.md +269 -0
- package/skills/build-generative-seller-agent/SKILL.md +89 -53
- package/skills/build-governance-agent/SKILL.md +76 -45
- package/skills/build-retail-media-agent/SKILL.md +87 -62
- package/skills/build-seller-agent/SKILL.md +384 -255
- package/skills/build-seller-agent/deployment.md +5 -3
- package/skills/build-seller-agent/specialisms/audience-sync.md +0 -2
- package/skills/build-seller-agent/specialisms/sales-broadcast-tv.md +0 -2
- package/skills/build-seller-agent/specialisms/sales-guaranteed.md +0 -2
- package/skills/build-seller-agent/specialisms/sales-non-guaranteed.md +0 -2
- package/skills/build-seller-agent/specialisms/sales-proposal-mode.md +0 -2
- package/skills/build-seller-agent/specialisms/sales-social.md +0 -2
- package/skills/build-seller-agent/specialisms/signed-requests.md +0 -2
- package/skills/build-si-agent/SKILL.md +40 -32
- package/skills/build-signals-agent/SKILL.md +139 -92
- package/skills/call-adcp-agent.previous/SKILL.md +5 -0
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MockHybridSeller — worked example for the v6.0 DecisioningPlatform
|
|
3
|
+
* runtime under the unified hybrid shape.
|
|
4
|
+
*
|
|
5
|
+
* One method per tool. The same `createMediaBuy` returns either:
|
|
6
|
+
*
|
|
7
|
+
* - The wire success arm directly (sync fast path) — buyer gets
|
|
8
|
+
* `media_buy_id` on the immediate response. No polling.
|
|
9
|
+
* - `ctx.handoffToTask(fn)` (HITL slow path) — buyer gets
|
|
10
|
+
* `{ status: 'submitted', task_id }`, framework runs `fn` in
|
|
11
|
+
* background; `fn`'s return value becomes the task's terminal
|
|
12
|
+
* artifact, `throw AdcpError` becomes the terminal error.
|
|
13
|
+
*
|
|
14
|
+
* Adopter branches per call on whatever signal determines the path
|
|
15
|
+
* (product type, buyer pre-approval, amount thresholds, etc.). Hybrid
|
|
16
|
+
* sellers — programmatic remnant + guaranteed inventory in one tenant —
|
|
17
|
+
* are the canonical case. Pure-sync adopters never call
|
|
18
|
+
* `ctx.handoffToTask`; pure-HITL adopters always call it. Same
|
|
19
|
+
* signature handles all three deployment shapes.
|
|
20
|
+
*
|
|
21
|
+
* Patterns demonstrated:
|
|
22
|
+
*
|
|
23
|
+
* 1. **Sync fast path** — `MockHybridSeller.createMediaBuy` returns
|
|
24
|
+
* `CreateMediaBuySuccess` directly when `req.buyer_ref` is
|
|
25
|
+
* pre-approved. No `tasks_get` polling needed.
|
|
26
|
+
* 2. **HITL slow path** — when not pre-approved, returns
|
|
27
|
+
* `ctx.handoffToTask(...)` and the trafficker-review work runs
|
|
28
|
+
* in background.
|
|
29
|
+
* 3. **Per-creative review** — `syncCreatives` returns mixed
|
|
30
|
+
* `approved` + `pending_review` rows in one response. Or hands
|
|
31
|
+
* off the whole batch to background review when needed.
|
|
32
|
+
* 4. **Multi-error pre-flight rejection** — adopter throws one
|
|
33
|
+
* `AdcpError` carrying all validation failures in `details.errors`.
|
|
34
|
+
*
|
|
35
|
+
* This file doubles as integration tests in
|
|
36
|
+
* `test/server-decisioning-mock-seller.test.js`.
|
|
37
|
+
*
|
|
38
|
+
* @see `docs/proposals/decisioning-platform-v2-hitl-split.md`
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
import {
|
|
42
|
+
AdcpError,
|
|
43
|
+
createAdcpServerFromPlatform,
|
|
44
|
+
type DecisioningPlatform,
|
|
45
|
+
type SalesPlatform,
|
|
46
|
+
type AccountStore,
|
|
47
|
+
type AdcpStructuredError,
|
|
48
|
+
type SyncCreativesRow,
|
|
49
|
+
} from '@adcp/sdk/server';
|
|
50
|
+
import type {
|
|
51
|
+
GetProductsRequest,
|
|
52
|
+
GetProductsResponse,
|
|
53
|
+
CreateMediaBuyRequest,
|
|
54
|
+
CreateMediaBuySuccess,
|
|
55
|
+
UpdateMediaBuyRequest,
|
|
56
|
+
UpdateMediaBuySuccess,
|
|
57
|
+
GetMediaBuyDeliveryRequest,
|
|
58
|
+
GetMediaBuyDeliveryResponse,
|
|
59
|
+
CreativeAsset,
|
|
60
|
+
AccountReference,
|
|
61
|
+
} from '@adcp/sdk/types';
|
|
62
|
+
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
// Shared config + state
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
|
|
67
|
+
export interface MockSellerConfig {
|
|
68
|
+
/** Threshold (CPM) below which media buys are auto-rejected. */
|
|
69
|
+
floorCpm: number;
|
|
70
|
+
/** Simulated trafficker review duration (ms) — only used by HITL variant. */
|
|
71
|
+
approvalDurationMs: number;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
interface MockSellerMeta {
|
|
75
|
+
network_id: string;
|
|
76
|
+
advertiser_id: string;
|
|
77
|
+
[key: string]: unknown;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
type MockMediaBuy = CreateMediaBuySuccess;
|
|
81
|
+
|
|
82
|
+
const DEFAULT_CONFIG: MockSellerConfig = {
|
|
83
|
+
floorCpm: 1.0,
|
|
84
|
+
approvalDurationMs: 100,
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
// Shared cross-variant helpers — preflight, account store, syncCreatives,
|
|
88
|
+
// getProducts. Variant classes only differ on createMediaBuy{,Task}.
|
|
89
|
+
|
|
90
|
+
function makeAccounts(): AccountStore<MockSellerMeta> {
|
|
91
|
+
return {
|
|
92
|
+
// Multi-tenant: MockSeller accepts buyer-supplied account_id refs.
|
|
93
|
+
// Single-tenant adopters declare resolution: 'derived' instead and
|
|
94
|
+
// ignore `ref`. See SKILL § "Account resolution".
|
|
95
|
+
resolution: 'explicit',
|
|
96
|
+
resolve: async (ref: AccountReference) => {
|
|
97
|
+
const id = 'account_id' in ref ? ref.account_id : 'mock_acc_1';
|
|
98
|
+
return {
|
|
99
|
+
id,
|
|
100
|
+
name: 'Mock Account',
|
|
101
|
+
status: 'active',
|
|
102
|
+
operator: 'mockseller.example.com',
|
|
103
|
+
ctx_metadata: { network_id: 'mock_network', advertiser_id: 'mock_advertiser' },
|
|
104
|
+
authInfo: { kind: 'api_key' },
|
|
105
|
+
};
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function preflight(req: CreateMediaBuyRequest, config: MockSellerConfig): AdcpStructuredError[] {
|
|
111
|
+
const errors: AdcpStructuredError[] = [];
|
|
112
|
+
const totalBudget =
|
|
113
|
+
typeof req.total_budget === 'number' ? req.total_budget : ((req.total_budget as { amount?: number })?.amount ?? 0);
|
|
114
|
+
|
|
115
|
+
if (totalBudget < config.floorCpm * 1000) {
|
|
116
|
+
errors.push({
|
|
117
|
+
code: 'BUDGET_TOO_LOW',
|
|
118
|
+
recovery: 'correctable',
|
|
119
|
+
message: `total_budget below floor (${config.floorCpm} CPM × 1000 imp)`,
|
|
120
|
+
field: 'total_budget',
|
|
121
|
+
suggestion: `Raise total_budget to at least ${config.floorCpm * 1000}`,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const packages = (req as { packages?: unknown[] }).packages ?? [];
|
|
126
|
+
if (packages.length === 0) {
|
|
127
|
+
errors.push({
|
|
128
|
+
code: 'INVALID_REQUEST',
|
|
129
|
+
recovery: 'correctable',
|
|
130
|
+
message: 'packages must be non-empty',
|
|
131
|
+
field: 'packages',
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return errors;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function rejectPreflight(errors: AdcpStructuredError[]): never {
|
|
139
|
+
throw new AdcpError('INVALID_REQUEST', {
|
|
140
|
+
recovery: 'correctable',
|
|
141
|
+
message: errors[0]!.message,
|
|
142
|
+
field: errors[0]!.field,
|
|
143
|
+
details: { errors },
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const SHARED_GET_PRODUCTS = async (_req: GetProductsRequest): Promise<GetProductsResponse> => ({
|
|
148
|
+
products: [
|
|
149
|
+
{
|
|
150
|
+
product_id: 'prod_premium_video',
|
|
151
|
+
name: 'Premium Video',
|
|
152
|
+
description: 'Pre-roll video on premium inventory',
|
|
153
|
+
delivery_type: 'non_guaranteed',
|
|
154
|
+
format_ids: [{ id: 'video_15s', agent_url: 'https://example.com/creative-agent/mcp' }],
|
|
155
|
+
publisher_properties: [{ publisher_domain: 'publisher.example.com', selection_type: 'all' }],
|
|
156
|
+
pricing_options: [
|
|
157
|
+
{
|
|
158
|
+
pricing_option_id: 'cpm_12_50',
|
|
159
|
+
pricing_model: 'cpm',
|
|
160
|
+
fixed_price: 12.5,
|
|
161
|
+
currency: 'USD',
|
|
162
|
+
},
|
|
163
|
+
],
|
|
164
|
+
reporting_capabilities: {
|
|
165
|
+
available_reporting_frequencies: ['hourly', 'daily'],
|
|
166
|
+
expected_delay_minutes: 30,
|
|
167
|
+
timezone: 'UTC',
|
|
168
|
+
supports_webhooks: false,
|
|
169
|
+
available_metrics: [],
|
|
170
|
+
date_range_support: 'date_range',
|
|
171
|
+
},
|
|
172
|
+
},
|
|
173
|
+
],
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
const SHARED_SYNC_CREATIVES = async (creatives: CreativeAsset[]): Promise<SyncCreativesRow[]> => {
|
|
177
|
+
return creatives.map(c => {
|
|
178
|
+
const id = (c as { creative_id?: string }).creative_id ?? `cr_${Math.random()}`;
|
|
179
|
+
const needsReview = (c as { format_id?: { id?: string } }).format_id?.id?.startsWith('video_');
|
|
180
|
+
return {
|
|
181
|
+
creative_id: id,
|
|
182
|
+
action: 'created',
|
|
183
|
+
status: needsReview ? 'pending_review' : 'approved',
|
|
184
|
+
};
|
|
185
|
+
});
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
const SHARED_GET_MEDIA_BUY_DELIVERY = async (
|
|
189
|
+
filter: GetMediaBuyDeliveryRequest
|
|
190
|
+
): Promise<GetMediaBuyDeliveryResponse> => ({
|
|
191
|
+
currency: 'USD',
|
|
192
|
+
reporting_period: {
|
|
193
|
+
start: filter.start_date ?? '2026-04-01',
|
|
194
|
+
end: filter.end_date ?? '2026-04-30',
|
|
195
|
+
},
|
|
196
|
+
media_buy_deliveries: [],
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
// ---------------------------------------------------------------------------
|
|
200
|
+
// MockHybridSeller — unified hybrid shape (sync fast path + HITL slow path)
|
|
201
|
+
// ---------------------------------------------------------------------------
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Whether a request takes the fast (sync) path or the slow (HITL) path
|
|
205
|
+
* is encoded in the request itself — `buyer_ref: 'pre_approved'` skips
|
|
206
|
+
* trafficker review; everything else hands off to background.
|
|
207
|
+
*
|
|
208
|
+
* Real-world signal would be a join against the seller's pre-approved
|
|
209
|
+
* buyer list, an amount threshold, or a product type check. The shape
|
|
210
|
+
* is the same: branch in the method body, return `Success` directly OR
|
|
211
|
+
* return `ctx.handoffToTask(fn)`.
|
|
212
|
+
*/
|
|
213
|
+
function isPreApprovedBuyer(req: CreateMediaBuyRequest): boolean {
|
|
214
|
+
return (req as { buyer_ref?: string }).buyer_ref === 'pre_approved';
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export class MockHybridSeller implements DecisioningPlatform<MockSellerConfig, MockSellerMeta> {
|
|
218
|
+
private mediaBuys = new Map<string, MockMediaBuy>();
|
|
219
|
+
|
|
220
|
+
capabilities = {
|
|
221
|
+
specialisms: ['sales-non-guaranteed'] as const,
|
|
222
|
+
creative_agents: [{ agent_url: 'https://example.com/creative-agent/mcp' }],
|
|
223
|
+
channels: ['display', 'olv'] as const,
|
|
224
|
+
pricingModels: ['cpm'] as const,
|
|
225
|
+
config: { ...DEFAULT_CONFIG } satisfies MockSellerConfig,
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
statusMappers = {};
|
|
229
|
+
accounts = makeAccounts();
|
|
230
|
+
|
|
231
|
+
sales: SalesPlatform<MockSellerMeta> = {
|
|
232
|
+
getProducts: SHARED_GET_PRODUCTS,
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Unified hybrid shape. Pre-approved buyers get the wire `Success` arm
|
|
236
|
+
* directly (`media_buy_id` + `packages` on the immediate response, no
|
|
237
|
+
* polling). Everything else hands off to a background task — buyer
|
|
238
|
+
* sees `{ status: 'submitted', task_id }`, framework runs the
|
|
239
|
+
* trafficker-review work, and the task's terminal artifact lands on
|
|
240
|
+
* `tasks_get` / webhook delivery.
|
|
241
|
+
*
|
|
242
|
+
* Same method, dynamic decision per call. Buyer pattern-matches on
|
|
243
|
+
* the response shape — predictable per request (deterministic given
|
|
244
|
+
* the buyer_ref / products / amount), dynamic per call.
|
|
245
|
+
*/
|
|
246
|
+
createMediaBuy: (req, ctx) => {
|
|
247
|
+
// Pre-flight runs sync regardless of path — bad requests reject
|
|
248
|
+
// before allocating a task id.
|
|
249
|
+
const errors = preflight(req, this.capabilities.config);
|
|
250
|
+
if (errors.length > 0) rejectPreflight(errors);
|
|
251
|
+
|
|
252
|
+
// Fast path: pre-approved buyer → return Success directly.
|
|
253
|
+
if (isPreApprovedBuyer(req)) {
|
|
254
|
+
const buyId = `mb_${Date.now()}_${Math.floor(Math.random() * 1000)}`;
|
|
255
|
+
const buy: MockMediaBuy = {
|
|
256
|
+
media_buy_id: buyId,
|
|
257
|
+
status: 'pending_creatives',
|
|
258
|
+
confirmed_at: new Date().toISOString(),
|
|
259
|
+
packages: [],
|
|
260
|
+
};
|
|
261
|
+
this.mediaBuys.set(buyId, buy);
|
|
262
|
+
return Promise.resolve(buy);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Slow path: hand off to background task.
|
|
266
|
+
return Promise.resolve(
|
|
267
|
+
ctx.handoffToTask(async taskCtx => {
|
|
268
|
+
void taskCtx; // taskCtx.id available if you need to persist it
|
|
269
|
+
// Trafficker review window
|
|
270
|
+
await new Promise(r => setTimeout(r, this.capabilities.config.approvalDurationMs));
|
|
271
|
+
|
|
272
|
+
const buyId = `mb_${Date.now()}_${Math.floor(Math.random() * 1000)}`;
|
|
273
|
+
const buy: MockMediaBuy = {
|
|
274
|
+
media_buy_id: buyId,
|
|
275
|
+
status: 'active',
|
|
276
|
+
confirmed_at: new Date().toISOString(),
|
|
277
|
+
packages: [],
|
|
278
|
+
};
|
|
279
|
+
this.mediaBuys.set(buyId, buy);
|
|
280
|
+
return buy;
|
|
281
|
+
})
|
|
282
|
+
);
|
|
283
|
+
},
|
|
284
|
+
|
|
285
|
+
updateMediaBuy: async (buyId: string, patch: UpdateMediaBuyRequest): Promise<UpdateMediaBuySuccess> => {
|
|
286
|
+
const existing = this.mediaBuys.get(buyId);
|
|
287
|
+
if (!existing) {
|
|
288
|
+
throw new AdcpError('MEDIA_BUY_NOT_FOUND', {
|
|
289
|
+
recovery: 'terminal',
|
|
290
|
+
message: `media buy ${buyId} not found`,
|
|
291
|
+
field: 'media_buy_id',
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
if (patch.paused === true) existing.status = 'paused';
|
|
295
|
+
if (patch.paused === false && existing.status === 'paused') existing.status = 'active';
|
|
296
|
+
return { media_buy_id: existing.media_buy_id, status: existing.status };
|
|
297
|
+
},
|
|
298
|
+
|
|
299
|
+
syncCreatives: SHARED_SYNC_CREATIVES,
|
|
300
|
+
getMediaBuyDelivery: SHARED_GET_MEDIA_BUY_DELIVERY,
|
|
301
|
+
// Required on SalesPlatform — empty-array stub (mock seller doesn't
|
|
302
|
+
// persist buys across runs; getMediaBuys returns nothing).
|
|
303
|
+
getMediaBuys: async () => ({ media_buys: [] }),
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// ---------------------------------------------------------------------------
|
|
308
|
+
// Merge-seam demonstration: v6 platform + v5 leftover handlers
|
|
309
|
+
// ---------------------------------------------------------------------------
|
|
310
|
+
//
|
|
311
|
+
// Adopters migrating from v5.x's `createAdcpServer({ mediaBuy: { ... } })`
|
|
312
|
+
// don't have to rewrite all of it. `createAdcpServerFromPlatform` accepts
|
|
313
|
+
// v5-style handler entries on `opts` that fill gaps the v6 specialism
|
|
314
|
+
// interfaces don't yet model (e.g., `listCreativeFormats`,
|
|
315
|
+
// `providePerformanceFeedback`, content-standards CRUD).
|
|
316
|
+
//
|
|
317
|
+
// The seam logs collisions so v6.x silently shadowing your override is
|
|
318
|
+
// loud, not silent. Set `mergeSeam: 'strict'` in CI for migration safety.
|
|
319
|
+
|
|
320
|
+
export function buildHybridServerExample(platform: MockHybridSeller) {
|
|
321
|
+
return createAdcpServerFromPlatform(platform, {
|
|
322
|
+
name: 'mock-hybrid',
|
|
323
|
+
version: '0.0.1',
|
|
324
|
+
validation: { requests: 'off', responses: 'off' },
|
|
325
|
+
mergeSeam: 'strict',
|
|
326
|
+
mediaBuy: {
|
|
327
|
+
// v5 leftover — listCreativeFormats isn't on SalesPlatform v1.0
|
|
328
|
+
// (deferred to rc.1). Custom handler here fills the gap until then.
|
|
329
|
+
listCreativeFormats: async () => ({ formats: [] }),
|
|
330
|
+
},
|
|
331
|
+
});
|
|
332
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multi-tenant deployment — one process serving many advertiser-tenants
|
|
3
|
+
* across different hosts with mixed shapes.
|
|
4
|
+
*
|
|
5
|
+
* Composes `createTenantRegistry()` with the framework's existing
|
|
6
|
+
* host-routed `serve()` factory: the registry holds host → tenant config,
|
|
7
|
+
* the factory resolves the tenant per request and returns its server.
|
|
8
|
+
*
|
|
9
|
+
* Each tenant has its own DecisioningPlatform impl, signing key, and
|
|
10
|
+
* health state. One bad tenant (JWKS mismatch, brand.json malformed) is
|
|
11
|
+
* disabled in isolation — the rest keep serving.
|
|
12
|
+
*
|
|
13
|
+
* @see `docs/proposals/decisioning-platform-v2-hitl-split.md`
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import {
|
|
17
|
+
createTenantRegistry,
|
|
18
|
+
type TenantRegistry,
|
|
19
|
+
type TenantSigningKey,
|
|
20
|
+
type DecisioningAdcpServer,
|
|
21
|
+
} from '@adcp/sdk/server';
|
|
22
|
+
import { BroadcastTvSeller } from './decisioning-platform-broadcast-tv';
|
|
23
|
+
import { ProgrammaticSeller } from './decisioning-platform-programmatic';
|
|
24
|
+
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
// 🔴 PRODUCTION: REPLACE WITH KMS-BACKED LOADER
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
//
|
|
29
|
+
// The fixtures below are DUMMY values — the `n` modulus is a placeholder
|
|
30
|
+
// string, not a valid RSA modulus. They exist so the example file
|
|
31
|
+
// typechecks and the test harness can mock the JWKS validator.
|
|
32
|
+
//
|
|
33
|
+
// In a real multi-tenant deployment these MUST come from a key-management
|
|
34
|
+
// system (HashiCorp Vault, AWS KMS, GCP Secret Manager) — never commit
|
|
35
|
+
// real signing material to source. The TenantRegistry's `pending` health
|
|
36
|
+
// state will refuse to serve a tenant whose JWKS validation fails, but
|
|
37
|
+
// shipping placeholder keys to a non-test environment is still a
|
|
38
|
+
// production incident waiting to happen.
|
|
39
|
+
//
|
|
40
|
+
// See `skills/build-decisioning-platform/SKILL.md` § "Multi-tenant
|
|
41
|
+
// hosting (TenantRegistry)" for the production wiring pattern.
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
const TENANT_KEYS: Record<string, TenantSigningKey> = {
|
|
44
|
+
acme_tv: {
|
|
45
|
+
keyId: 'acme_tv-2026-04',
|
|
46
|
+
publicJwk: { kty: 'RSA', n: 'pub_modulus_acme', e: 'AQAB' },
|
|
47
|
+
privateJwk: { kty: 'RSA', n: 'pub_modulus_acme', e: 'AQAB', d: 'priv_exp_acme' },
|
|
48
|
+
},
|
|
49
|
+
zenith_programmatic: {
|
|
50
|
+
keyId: 'zenith-2026-04',
|
|
51
|
+
publicJwk: { kty: 'RSA', n: 'pub_modulus_zenith', e: 'AQAB' },
|
|
52
|
+
privateJwk: { kty: 'RSA', n: 'pub_modulus_zenith', e: 'AQAB', d: 'priv_exp_zenith' },
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Build a registry seeded with two tenants of mixed shapes:
|
|
58
|
+
*
|
|
59
|
+
* - `acme_tv` hosts a `BroadcastTvSeller` (HITL — `*Task` variants).
|
|
60
|
+
* - `zenith_programmatic` hosts a `ProgrammaticSeller` (sync + status-change).
|
|
61
|
+
*
|
|
62
|
+
* Buyers reach each at their own agentUrl; the framework dispatches based
|
|
63
|
+
* on `ctx.host` resolved from `X-Forwarded-Host` (proxied) or the request
|
|
64
|
+
* `Host` header (direct).
|
|
65
|
+
*/
|
|
66
|
+
export function buildMultiTenantRegistry(): TenantRegistry {
|
|
67
|
+
const registry = createTenantRegistry({
|
|
68
|
+
defaultServerOptions: {
|
|
69
|
+
name: 'multi-tenant-host',
|
|
70
|
+
version: '0.0.1',
|
|
71
|
+
validation: { requests: 'strict', responses: 'strict' },
|
|
72
|
+
},
|
|
73
|
+
autoValidate: true,
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
registry.register('acme_tv', {
|
|
77
|
+
agentUrl: 'https://acme-tv.example.com',
|
|
78
|
+
signingKey: TENANT_KEYS.acme_tv!,
|
|
79
|
+
platform: new BroadcastTvSeller(),
|
|
80
|
+
label: 'Acme Broadcast TV',
|
|
81
|
+
serverOptions: {
|
|
82
|
+
name: 'acme-tv',
|
|
83
|
+
version: '1.0.0',
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
registry.register('zenith_programmatic', {
|
|
88
|
+
agentUrl: 'https://zenith.example.com',
|
|
89
|
+
signingKey: TENANT_KEYS.zenith_programmatic!,
|
|
90
|
+
platform: new ProgrammaticSeller(),
|
|
91
|
+
label: 'Zenith Programmatic',
|
|
92
|
+
serverOptions: {
|
|
93
|
+
name: 'zenith-programmatic',
|
|
94
|
+
version: '1.0.0',
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
return registry;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Factory the caller passes to `serve(createAgent, options)`. Resolves the
|
|
103
|
+
* tenant by `ctx.host`, returns its `AdcpServer`. Throws `UnknownHostError`-
|
|
104
|
+
* compatible behavior when no tenant matches — the framework projects this
|
|
105
|
+
* to the standard 404/unknown-host wire response.
|
|
106
|
+
*
|
|
107
|
+
* In a real deployment this is wired alongside `serve()`:
|
|
108
|
+
*
|
|
109
|
+
* ```ts
|
|
110
|
+
* import { serve } from '@adcp/sdk/server';
|
|
111
|
+
* const registry = buildMultiTenantRegistry();
|
|
112
|
+
* serve(makeMultiTenantFactory(registry), { ... });
|
|
113
|
+
* ```
|
|
114
|
+
*
|
|
115
|
+
* Disabled tenants resolve to null → factory throws → framework returns
|
|
116
|
+
* SERVICE_UNAVAILABLE. Healthy and unverified tenants both serve normally;
|
|
117
|
+
* unverified tenants log a warning per request so operators know which
|
|
118
|
+
* are awaiting validation.
|
|
119
|
+
*/
|
|
120
|
+
export function makeMultiTenantFactory(registry: TenantRegistry): (ctx: { host: string }) => DecisioningAdcpServer {
|
|
121
|
+
return ctx => {
|
|
122
|
+
const resolved = registry.resolveByHost(ctx.host);
|
|
123
|
+
if (!resolved) {
|
|
124
|
+
throw new Error(`No tenant registered for host '${ctx.host}'`);
|
|
125
|
+
}
|
|
126
|
+
return resolved.server;
|
|
127
|
+
};
|
|
128
|
+
}
|