@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.
Files changed (288) hide show
  1. package/README.md +45 -7
  2. package/dist/lib/compliance-fixtures/index.d.ts +1 -1
  3. package/dist/lib/compliance-fixtures/index.js +1 -1
  4. package/dist/lib/core/AgentClient.d.ts.map +1 -1
  5. package/dist/lib/core/SingleAgentClient.d.ts.map +1 -1
  6. package/dist/lib/core/SingleAgentClient.js +15 -0
  7. package/dist/lib/core/SingleAgentClient.js.map +1 -1
  8. package/dist/lib/core/TaskExecutor.d.ts +7 -0
  9. package/dist/lib/core/TaskExecutor.d.ts.map +1 -1
  10. package/dist/lib/core/TaskExecutor.js +9 -2
  11. package/dist/lib/core/TaskExecutor.js.map +1 -1
  12. package/dist/lib/index.d.ts +1 -1
  13. package/dist/lib/index.d.ts.map +1 -1
  14. package/dist/lib/index.js +7 -8
  15. package/dist/lib/index.js.map +1 -1
  16. package/dist/lib/schemas/index.d.ts +1 -1
  17. package/dist/lib/schemas/index.js +1 -1
  18. package/dist/lib/server/create-adcp-server.d.ts +129 -11
  19. package/dist/lib/server/create-adcp-server.d.ts.map +1 -1
  20. package/dist/lib/server/create-adcp-server.js +112 -2
  21. package/dist/lib/server/create-adcp-server.js.map +1 -1
  22. package/dist/lib/server/ctx-metadata/backends/memory.d.ts +27 -0
  23. package/dist/lib/server/ctx-metadata/backends/memory.d.ts.map +1 -0
  24. package/dist/lib/server/ctx-metadata/backends/memory.js +72 -0
  25. package/dist/lib/server/ctx-metadata/backends/memory.js.map +1 -0
  26. package/dist/lib/server/ctx-metadata/backends/pg.d.ts +62 -0
  27. package/dist/lib/server/ctx-metadata/backends/pg.d.ts.map +1 -0
  28. package/dist/lib/server/ctx-metadata/backends/pg.js +145 -0
  29. package/dist/lib/server/ctx-metadata/backends/pg.js.map +1 -0
  30. package/dist/lib/server/ctx-metadata/index.d.ts +15 -0
  31. package/dist/lib/server/ctx-metadata/index.d.ts.map +1 -0
  32. package/dist/lib/server/ctx-metadata/index.js +28 -0
  33. package/dist/lib/server/ctx-metadata/index.js.map +1 -0
  34. package/dist/lib/server/ctx-metadata/store.d.ts +177 -0
  35. package/dist/lib/server/ctx-metadata/store.d.ts.map +1 -0
  36. package/dist/lib/server/ctx-metadata/store.js +327 -0
  37. package/dist/lib/server/ctx-metadata/store.js.map +1 -0
  38. package/dist/lib/server/ctx-metadata/wire-shape.d.ts +55 -0
  39. package/dist/lib/server/ctx-metadata/wire-shape.d.ts.map +1 -0
  40. package/dist/lib/server/ctx-metadata/wire-shape.js +121 -0
  41. package/dist/lib/server/ctx-metadata/wire-shape.js.map +1 -0
  42. package/dist/lib/server/decisioning/account.d.ts +309 -0
  43. package/dist/lib/server/decisioning/account.d.ts.map +1 -0
  44. package/dist/lib/server/decisioning/account.js +102 -0
  45. package/dist/lib/server/decisioning/account.js.map +1 -0
  46. package/dist/lib/server/decisioning/admin-router.d.ts +75 -0
  47. package/dist/lib/server/decisioning/admin-router.d.ts.map +1 -0
  48. package/dist/lib/server/decisioning/admin-router.js +120 -0
  49. package/dist/lib/server/decisioning/admin-router.js.map +1 -0
  50. package/dist/lib/server/decisioning/assembly-helpers.d.ts +204 -0
  51. package/dist/lib/server/decisioning/assembly-helpers.d.ts.map +1 -0
  52. package/dist/lib/server/decisioning/assembly-helpers.js +173 -0
  53. package/dist/lib/server/decisioning/assembly-helpers.js.map +1 -0
  54. package/dist/lib/server/decisioning/async-outcome.d.ts +154 -0
  55. package/dist/lib/server/decisioning/async-outcome.d.ts.map +1 -0
  56. package/dist/lib/server/decisioning/async-outcome.js +239 -0
  57. package/dist/lib/server/decisioning/async-outcome.js.map +1 -0
  58. package/dist/lib/server/decisioning/capabilities.d.ts +251 -0
  59. package/dist/lib/server/decisioning/capabilities.d.ts.map +1 -0
  60. package/dist/lib/server/decisioning/capabilities.js +16 -0
  61. package/dist/lib/server/decisioning/capabilities.js.map +1 -0
  62. package/dist/lib/server/decisioning/context.d.ts +212 -0
  63. package/dist/lib/server/decisioning/context.d.ts.map +1 -0
  64. package/dist/lib/server/decisioning/context.js +26 -0
  65. package/dist/lib/server/decisioning/context.js.map +1 -0
  66. package/dist/lib/server/decisioning/errors-typed.d.ts +104 -0
  67. package/dist/lib/server/decisioning/errors-typed.d.ts.map +1 -0
  68. package/dist/lib/server/decisioning/errors-typed.js +304 -0
  69. package/dist/lib/server/decisioning/errors-typed.js.map +1 -0
  70. package/dist/lib/server/decisioning/helpers.d.ts +131 -0
  71. package/dist/lib/server/decisioning/helpers.d.ts.map +1 -0
  72. package/dist/lib/server/decisioning/helpers.js +134 -0
  73. package/dist/lib/server/decisioning/helpers.js.map +1 -0
  74. package/dist/lib/server/decisioning/index.d.ts +46 -0
  75. package/dist/lib/server/decisioning/index.d.ts.map +1 -0
  76. package/dist/lib/server/decisioning/index.js +120 -0
  77. package/dist/lib/server/decisioning/index.js.map +1 -0
  78. package/dist/lib/server/decisioning/list-helpers.d.ts +53 -0
  79. package/dist/lib/server/decisioning/list-helpers.d.ts.map +1 -0
  80. package/dist/lib/server/decisioning/list-helpers.js +96 -0
  81. package/dist/lib/server/decisioning/list-helpers.js.map +1 -0
  82. package/dist/lib/server/decisioning/manifest-helpers.d.ts +56 -0
  83. package/dist/lib/server/decisioning/manifest-helpers.d.ts.map +1 -0
  84. package/dist/lib/server/decisioning/manifest-helpers.js +78 -0
  85. package/dist/lib/server/decisioning/manifest-helpers.js.map +1 -0
  86. package/dist/lib/server/decisioning/pagination.d.ts +21 -0
  87. package/dist/lib/server/decisioning/pagination.d.ts.map +1 -0
  88. package/dist/lib/server/decisioning/pagination.js +12 -0
  89. package/dist/lib/server/decisioning/pagination.js.map +1 -0
  90. package/dist/lib/server/decisioning/platform.d.ts +188 -0
  91. package/dist/lib/server/decisioning/platform.d.ts.map +1 -0
  92. package/dist/lib/server/decisioning/platform.js +19 -0
  93. package/dist/lib/server/decisioning/platform.js.map +1 -0
  94. package/dist/lib/server/decisioning/runtime/from-platform.d.ts +510 -0
  95. package/dist/lib/server/decisioning/runtime/from-platform.d.ts.map +1 -0
  96. package/dist/lib/server/decisioning/runtime/from-platform.js +2196 -0
  97. package/dist/lib/server/decisioning/runtime/from-platform.js.map +1 -0
  98. package/dist/lib/server/decisioning/runtime/postgres-task-registry.d.ts +114 -0
  99. package/dist/lib/server/decisioning/runtime/postgres-task-registry.d.ts.map +1 -0
  100. package/dist/lib/server/decisioning/runtime/postgres-task-registry.js +247 -0
  101. package/dist/lib/server/decisioning/runtime/postgres-task-registry.js.map +1 -0
  102. package/dist/lib/server/decisioning/runtime/protocol-for-tool.d.ts +32 -0
  103. package/dist/lib/server/decisioning/runtime/protocol-for-tool.d.ts.map +1 -0
  104. package/dist/lib/server/decisioning/runtime/protocol-for-tool.js +127 -0
  105. package/dist/lib/server/decisioning/runtime/protocol-for-tool.js.map +1 -0
  106. package/dist/lib/server/decisioning/runtime/task-registry.d.ts +105 -0
  107. package/dist/lib/server/decisioning/runtime/task-registry.d.ts.map +1 -0
  108. package/dist/lib/server/decisioning/runtime/task-registry.js +96 -0
  109. package/dist/lib/server/decisioning/runtime/task-registry.js.map +1 -0
  110. package/dist/lib/server/decisioning/runtime/to-context.d.ts +54 -0
  111. package/dist/lib/server/decisioning/runtime/to-context.d.ts.map +1 -0
  112. package/dist/lib/server/decisioning/runtime/to-context.js +166 -0
  113. package/dist/lib/server/decisioning/runtime/to-context.js.map +1 -0
  114. package/dist/lib/server/decisioning/runtime/validate-platform.d.ts +20 -0
  115. package/dist/lib/server/decisioning/runtime/validate-platform.d.ts.map +1 -0
  116. package/dist/lib/server/decisioning/runtime/validate-platform.js +93 -0
  117. package/dist/lib/server/decisioning/runtime/validate-platform.js.map +1 -0
  118. package/dist/lib/server/decisioning/specialisms/audiences.d.ts +72 -0
  119. package/dist/lib/server/decisioning/specialisms/audiences.d.ts.map +1 -0
  120. package/dist/lib/server/decisioning/specialisms/audiences.js +15 -0
  121. package/dist/lib/server/decisioning/specialisms/audiences.js.map +1 -0
  122. package/dist/lib/server/decisioning/specialisms/brand-rights.d.ts +92 -0
  123. package/dist/lib/server/decisioning/specialisms/brand-rights.d.ts.map +1 -0
  124. package/dist/lib/server/decisioning/specialisms/brand-rights.js +28 -0
  125. package/dist/lib/server/decisioning/specialisms/brand-rights.js.map +1 -0
  126. package/dist/lib/server/decisioning/specialisms/campaign-governance.d.ts +67 -0
  127. package/dist/lib/server/decisioning/specialisms/campaign-governance.d.ts.map +1 -0
  128. package/dist/lib/server/decisioning/specialisms/campaign-governance.js +31 -0
  129. package/dist/lib/server/decisioning/specialisms/campaign-governance.js.map +1 -0
  130. package/dist/lib/server/decisioning/specialisms/content-standards.d.ts +78 -0
  131. package/dist/lib/server/decisioning/specialisms/content-standards.d.ts.map +1 -0
  132. package/dist/lib/server/decisioning/specialisms/content-standards.js +35 -0
  133. package/dist/lib/server/decisioning/specialisms/content-standards.js.map +1 -0
  134. package/dist/lib/server/decisioning/specialisms/creative-ad-server.d.ts +81 -0
  135. package/dist/lib/server/decisioning/specialisms/creative-ad-server.d.ts.map +1 -0
  136. package/dist/lib/server/decisioning/specialisms/creative-ad-server.js +28 -0
  137. package/dist/lib/server/decisioning/specialisms/creative-ad-server.js.map +1 -0
  138. package/dist/lib/server/decisioning/specialisms/creative.d.ts +144 -0
  139. package/dist/lib/server/decisioning/specialisms/creative.d.ts.map +1 -0
  140. package/dist/lib/server/decisioning/specialisms/creative.js +19 -0
  141. package/dist/lib/server/decisioning/specialisms/creative.js.map +1 -0
  142. package/dist/lib/server/decisioning/specialisms/lists.d.ts +61 -0
  143. package/dist/lib/server/decisioning/specialisms/lists.d.ts.map +1 -0
  144. package/dist/lib/server/decisioning/specialisms/lists.js +30 -0
  145. package/dist/lib/server/decisioning/specialisms/lists.js.map +1 -0
  146. package/dist/lib/server/decisioning/specialisms/sales.d.ts +163 -0
  147. package/dist/lib/server/decisioning/specialisms/sales.d.ts.map +1 -0
  148. package/dist/lib/server/decisioning/specialisms/sales.js +64 -0
  149. package/dist/lib/server/decisioning/specialisms/sales.js.map +1 -0
  150. package/dist/lib/server/decisioning/specialisms/signals.d.ts +64 -0
  151. package/dist/lib/server/decisioning/specialisms/signals.d.ts.map +1 -0
  152. package/dist/lib/server/decisioning/specialisms/signals.js +28 -0
  153. package/dist/lib/server/decisioning/specialisms/signals.js.map +1 -0
  154. package/dist/lib/server/decisioning/start-time.d.ts +76 -0
  155. package/dist/lib/server/decisioning/start-time.d.ts.map +1 -0
  156. package/dist/lib/server/decisioning/start-time.js +81 -0
  157. package/dist/lib/server/decisioning/start-time.js.map +1 -0
  158. package/dist/lib/server/decisioning/status-changes.d.ts +165 -0
  159. package/dist/lib/server/decisioning/status-changes.d.ts.map +1 -0
  160. package/dist/lib/server/decisioning/status-changes.js +131 -0
  161. package/dist/lib/server/decisioning/status-changes.js.map +1 -0
  162. package/dist/lib/server/decisioning/status-mappers.d.ts +46 -0
  163. package/dist/lib/server/decisioning/status-mappers.d.ts.map +1 -0
  164. package/dist/lib/server/decisioning/status-mappers.js +46 -0
  165. package/dist/lib/server/decisioning/status-mappers.js.map +1 -0
  166. package/dist/lib/server/decisioning/tenant-registry.d.ts +289 -0
  167. package/dist/lib/server/decisioning/tenant-registry.d.ts.map +1 -0
  168. package/dist/lib/server/decisioning/tenant-registry.js +503 -0
  169. package/dist/lib/server/decisioning/tenant-registry.js.map +1 -0
  170. package/dist/lib/server/express-adapter.d.ts +1 -1
  171. package/dist/lib/server/express-adapter.js +1 -1
  172. package/dist/lib/server/governance.d.ts +1 -1
  173. package/dist/lib/server/governance.js +1 -1
  174. package/dist/lib/server/idempotency/store.d.ts +1 -1
  175. package/dist/lib/server/idempotency/store.js +1 -1
  176. package/dist/lib/server/index.d.ts +9 -2
  177. package/dist/lib/server/index.d.ts.map +1 -1
  178. package/dist/lib/server/index.js +79 -4
  179. package/dist/lib/server/index.js.map +1 -1
  180. package/dist/lib/server/legacy/v5/index.d.ts +38 -0
  181. package/dist/lib/server/legacy/v5/index.d.ts.map +1 -0
  182. package/dist/lib/server/legacy/v5/index.js +60 -0
  183. package/dist/lib/server/legacy/v5/index.js.map +1 -0
  184. package/dist/lib/server/normalize-errors.d.ts +88 -0
  185. package/dist/lib/server/normalize-errors.d.ts.map +1 -0
  186. package/dist/lib/server/normalize-errors.js +146 -0
  187. package/dist/lib/server/normalize-errors.js.map +1 -0
  188. package/dist/lib/server/pick-safe-details.d.ts +90 -0
  189. package/dist/lib/server/pick-safe-details.d.ts.map +1 -0
  190. package/dist/lib/server/pick-safe-details.js +148 -0
  191. package/dist/lib/server/pick-safe-details.js.map +1 -0
  192. package/dist/lib/server/postgres-state-store.d.ts +1 -1
  193. package/dist/lib/server/postgres-state-store.js +1 -1
  194. package/dist/lib/server/responses.d.ts +38 -0
  195. package/dist/lib/server/responses.d.ts.map +1 -1
  196. package/dist/lib/server/responses.js +38 -0
  197. package/dist/lib/server/responses.js.map +1 -1
  198. package/dist/lib/server/state-store.d.ts +1 -1
  199. package/dist/lib/server/state-store.js +1 -1
  200. package/dist/lib/server/test-controller.d.ts +10 -3
  201. package/dist/lib/server/test-controller.d.ts.map +1 -1
  202. package/dist/lib/server/test-controller.js +10 -3
  203. package/dist/lib/server/test-controller.js.map +1 -1
  204. package/dist/lib/testing/comply-controller.d.ts +47 -1
  205. package/dist/lib/testing/comply-controller.d.ts.map +1 -1
  206. package/dist/lib/testing/comply-controller.js +11 -4
  207. package/dist/lib/testing/comply-controller.js.map +1 -1
  208. package/dist/lib/testing/index.d.ts +1 -1
  209. package/dist/lib/testing/index.d.ts.map +1 -1
  210. package/dist/lib/testing/index.js.map +1 -1
  211. package/dist/lib/testing/personas/index.d.ts +143 -0
  212. package/dist/lib/testing/personas/index.d.ts.map +1 -0
  213. package/dist/lib/testing/personas/index.js +190 -0
  214. package/dist/lib/testing/personas/index.js.map +1 -0
  215. package/dist/lib/testing/storyboard/index.d.ts +1 -1
  216. package/dist/lib/testing/storyboard/index.d.ts.map +1 -1
  217. package/dist/lib/testing/storyboard/index.js +3 -2
  218. package/dist/lib/testing/storyboard/index.js.map +1 -1
  219. package/dist/lib/testing/storyboard/runner.d.ts +13 -0
  220. package/dist/lib/testing/storyboard/runner.d.ts.map +1 -1
  221. package/dist/lib/testing/storyboard/runner.js +179 -7
  222. package/dist/lib/testing/storyboard/runner.js.map +1 -1
  223. package/dist/lib/types/asset-instances.d.ts +1 -0
  224. package/dist/lib/types/asset-instances.d.ts.map +1 -1
  225. package/dist/lib/types/core.generated.d.ts +203 -98
  226. package/dist/lib/types/core.generated.d.ts.map +1 -1
  227. package/dist/lib/types/core.generated.js +1 -1
  228. package/dist/lib/types/index.d.ts +1 -0
  229. package/dist/lib/types/index.d.ts.map +1 -1
  230. package/dist/lib/types/index.js.map +1 -1
  231. package/dist/lib/types/schemas.generated.d.ts +599 -159
  232. package/dist/lib/types/schemas.generated.d.ts.map +1 -1
  233. package/dist/lib/types/schemas.generated.js +175 -94
  234. package/dist/lib/types/schemas.generated.js.map +1 -1
  235. package/dist/lib/types/tools.generated.d.ts +315 -46
  236. package/dist/lib/types/tools.generated.d.ts.map +1 -1
  237. package/dist/lib/utils/capabilities.d.ts +1 -1
  238. package/dist/lib/utils/capabilities.d.ts.map +1 -1
  239. package/dist/lib/utils/capabilities.js +6 -0
  240. package/dist/lib/utils/capabilities.js.map +1 -1
  241. package/dist/lib/validation/schema-validator.d.ts +13 -0
  242. package/dist/lib/validation/schema-validator.d.ts.map +1 -1
  243. package/dist/lib/validation/schema-validator.js +240 -3
  244. package/dist/lib/validation/schema-validator.js.map +1 -1
  245. package/dist/lib/version.d.ts +3 -3
  246. package/dist/lib/version.d.ts.map +1 -1
  247. package/dist/lib/version.js +3 -3
  248. package/dist/lib/version.js.map +1 -1
  249. package/docs/guides/BUILD-AN-AGENT.md +30 -5
  250. package/docs/llms.txt +28 -17
  251. package/examples/README.md +3 -1
  252. package/examples/decisioning-platform-broadcast-tv.ts +300 -0
  253. package/examples/decisioning-platform-identity-graph.ts +214 -0
  254. package/examples/decisioning-platform-mock-seller.ts +332 -0
  255. package/examples/decisioning-platform-multi-tenant.ts +128 -0
  256. package/examples/decisioning-platform-programmatic.ts +254 -0
  257. package/examples/signals-agent.ts +1 -1
  258. package/package.json +13 -2
  259. package/skills/build-brand-rights-agent/SKILL.md +10 -3
  260. package/skills/build-creative-agent/SKILL.md +94 -64
  261. package/skills/build-decisioning-creative-template/SKILL.md +554 -0
  262. package/skills/build-decisioning-platform/SKILL.md +304 -0
  263. package/skills/build-decisioning-platform/advanced/BRAND-RIGHTS.md +25 -0
  264. package/skills/build-decisioning-platform/advanced/COMPLIANCE.md +23 -0
  265. package/skills/build-decisioning-platform/advanced/GOVERNANCE.md +24 -0
  266. package/skills/build-decisioning-platform/advanced/HITL.md +34 -0
  267. package/skills/build-decisioning-platform/advanced/IDEMPOTENCY.md +52 -0
  268. package/skills/build-decisioning-platform/advanced/MULTI-TENANT.md +47 -0
  269. package/skills/build-decisioning-platform/advanced/OAUTH.md +22 -0
  270. package/skills/build-decisioning-platform/advanced/REFERENCE.md +991 -0
  271. package/skills/build-decisioning-platform/advanced/SANDBOX.md +24 -0
  272. package/skills/build-decisioning-platform/advanced/STATE-MACHINE.md +52 -0
  273. package/skills/build-decisioning-signal-marketplace/SKILL.md +269 -0
  274. package/skills/build-generative-seller-agent/SKILL.md +89 -53
  275. package/skills/build-governance-agent/SKILL.md +76 -45
  276. package/skills/build-retail-media-agent/SKILL.md +87 -62
  277. package/skills/build-seller-agent/SKILL.md +384 -255
  278. package/skills/build-seller-agent/deployment.md +5 -3
  279. package/skills/build-seller-agent/specialisms/audience-sync.md +0 -2
  280. package/skills/build-seller-agent/specialisms/sales-broadcast-tv.md +0 -2
  281. package/skills/build-seller-agent/specialisms/sales-guaranteed.md +0 -2
  282. package/skills/build-seller-agent/specialisms/sales-non-guaranteed.md +0 -2
  283. package/skills/build-seller-agent/specialisms/sales-proposal-mode.md +0 -2
  284. package/skills/build-seller-agent/specialisms/sales-social.md +0 -2
  285. package/skills/build-seller-agent/specialisms/signed-requests.md +0 -2
  286. package/skills/build-si-agent/SKILL.md +40 -32
  287. package/skills/build-signals-agent/SKILL.md +139 -92
  288. 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
+ }