@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.
Files changed (464) hide show
  1. package/README.md +19 -3
  2. package/bin/adcp-fuzz.js +3 -1
  3. package/dist/lib/adapters/content-standards-adapter.d.mts +9 -9
  4. package/dist/lib/adapters/content-standards-adapter.d.ts +9 -9
  5. package/dist/lib/adapters/content-standards-adapter.d.ts.map +1 -1
  6. package/dist/lib/adapters/content-standards-adapter.js +22 -22
  7. package/dist/lib/adapters/content-standards-adapter.js.map +1 -1
  8. package/dist/lib/adapters/content-standards-adapter.mjs +18 -18
  9. package/dist/lib/adapters/content-standards-adapter.mjs.map +1 -1
  10. package/dist/lib/adapters/index.d.mts +2 -2
  11. package/dist/lib/adapters/index.d.ts +2 -2
  12. package/dist/lib/adapters/index.d.ts.map +1 -1
  13. package/dist/lib/adapters/index.js +10 -10
  14. package/dist/lib/adapters/index.js.map +1 -1
  15. package/dist/lib/adapters/index.mjs +9 -9
  16. package/dist/lib/adapters/index.mjs.map +1 -1
  17. package/dist/lib/auth/oauth/authorization-required.d.mts +3 -0
  18. package/dist/lib/auth/oauth/authorization-required.d.ts +3 -0
  19. package/dist/lib/auth/oauth/authorization-required.d.ts.map +1 -1
  20. package/dist/lib/auth/oauth/authorization-required.js +12 -9
  21. package/dist/lib/auth/oauth/authorization-required.js.map +1 -1
  22. package/dist/lib/auth/oauth/authorization-required.mjs +12 -9
  23. package/dist/lib/auth/oauth/authorization-required.mjs.map +1 -1
  24. package/dist/lib/auth/oauth/provider-cache.d.mts +7 -0
  25. package/dist/lib/auth/oauth/provider-cache.d.ts +8 -0
  26. package/dist/lib/auth/oauth/provider-cache.d.ts.map +1 -0
  27. package/dist/lib/auth/oauth/provider-cache.js +44 -0
  28. package/dist/lib/auth/oauth/provider-cache.js.map +1 -0
  29. package/dist/lib/auth/oauth/provider-cache.mjs +19 -0
  30. package/dist/lib/auth/oauth/provider-cache.mjs.map +1 -0
  31. package/dist/lib/conformance/invariants/uniformError.js +1 -1
  32. package/dist/lib/conformance/invariants/uniformError.js.map +1 -1
  33. package/dist/lib/conformance/invariants/uniformError.mjs +1 -1
  34. package/dist/lib/conformance/invariants/uniformError.mjs.map +1 -1
  35. package/dist/lib/conformance/runners.js +1 -1
  36. package/dist/lib/conformance/runners.js.map +1 -1
  37. package/dist/lib/conformance/runners.mjs +1 -1
  38. package/dist/lib/conformance/runners.mjs.map +1 -1
  39. package/dist/lib/conformance/seeder.js +5 -5
  40. package/dist/lib/conformance/seeder.js.map +1 -1
  41. package/dist/lib/conformance/seeder.mjs +5 -5
  42. package/dist/lib/conformance/seeder.mjs.map +1 -1
  43. package/dist/lib/core/ADCPMultiAgentClient.d.mts +26 -29
  44. package/dist/lib/core/ADCPMultiAgentClient.d.ts +26 -29
  45. package/dist/lib/core/ADCPMultiAgentClient.d.ts.map +1 -1
  46. package/dist/lib/core/ADCPMultiAgentClient.js +27 -19
  47. package/dist/lib/core/ADCPMultiAgentClient.js.map +1 -1
  48. package/dist/lib/core/ADCPMultiAgentClient.mjs +27 -19
  49. package/dist/lib/core/ADCPMultiAgentClient.mjs.map +1 -1
  50. package/dist/lib/core/AgentClient.d.mts +148 -138
  51. package/dist/lib/core/AgentClient.d.ts +148 -138
  52. package/dist/lib/core/AgentClient.d.ts.map +1 -1
  53. package/dist/lib/core/AgentClient.js +174 -85
  54. package/dist/lib/core/AgentClient.js.map +1 -1
  55. package/dist/lib/core/AgentClient.mjs +174 -85
  56. package/dist/lib/core/AgentClient.mjs.map +1 -1
  57. package/dist/lib/core/AsyncHandler.d.mts +18 -16
  58. package/dist/lib/core/AsyncHandler.d.ts +18 -16
  59. package/dist/lib/core/AsyncHandler.d.ts.map +1 -1
  60. package/dist/lib/core/AsyncHandler.js +22 -1
  61. package/dist/lib/core/AsyncHandler.js.map +1 -1
  62. package/dist/lib/core/AsyncHandler.mjs +22 -1
  63. package/dist/lib/core/AsyncHandler.mjs.map +1 -1
  64. package/dist/lib/core/ConversationTypes.d.mts +6 -2
  65. package/dist/lib/core/ConversationTypes.d.ts +6 -2
  66. package/dist/lib/core/ConversationTypes.d.ts.map +1 -1
  67. package/dist/lib/core/ConversationTypes.js.map +1 -1
  68. package/dist/lib/core/CreativeAgentClient.d.mts +41 -17
  69. package/dist/lib/core/CreativeAgentClient.d.ts +41 -17
  70. package/dist/lib/core/CreativeAgentClient.d.ts.map +1 -1
  71. package/dist/lib/core/CreativeAgentClient.js +39 -12
  72. package/dist/lib/core/CreativeAgentClient.js.map +1 -1
  73. package/dist/lib/core/CreativeAgentClient.mjs +39 -12
  74. package/dist/lib/core/CreativeAgentClient.mjs.map +1 -1
  75. package/dist/lib/core/SingleAgentClient.d.mts +129 -52
  76. package/dist/lib/core/SingleAgentClient.d.ts +129 -52
  77. package/dist/lib/core/SingleAgentClient.d.ts.map +1 -1
  78. package/dist/lib/core/SingleAgentClient.js +1748 -218
  79. package/dist/lib/core/SingleAgentClient.js.map +1 -1
  80. package/dist/lib/core/SingleAgentClient.mjs +1758 -218
  81. package/dist/lib/core/SingleAgentClient.mjs.map +1 -1
  82. package/dist/lib/core/TaskExecutor.d.mts +9 -0
  83. package/dist/lib/core/TaskExecutor.d.ts +9 -0
  84. package/dist/lib/core/TaskExecutor.d.ts.map +1 -1
  85. package/dist/lib/core/TaskExecutor.js +137 -44
  86. package/dist/lib/core/TaskExecutor.js.map +1 -1
  87. package/dist/lib/core/TaskExecutor.mjs +137 -44
  88. package/dist/lib/core/TaskExecutor.mjs.map +1 -1
  89. package/dist/lib/index.d.mts +111 -17
  90. package/dist/lib/index.d.ts +111 -17
  91. package/dist/lib/index.d.ts.map +1 -1
  92. package/dist/lib/index.js +94 -94
  93. package/dist/lib/index.js.map +1 -1
  94. package/dist/lib/index.mjs +93 -94
  95. package/dist/lib/index.mjs.map +1 -1
  96. package/dist/lib/net/ssrf-fetch.d.mts +13 -1
  97. package/dist/lib/net/ssrf-fetch.d.ts +13 -1
  98. package/dist/lib/net/ssrf-fetch.d.ts.map +1 -1
  99. package/dist/lib/net/ssrf-fetch.js +14 -7
  100. package/dist/lib/net/ssrf-fetch.js.map +1 -1
  101. package/dist/lib/net/ssrf-fetch.mjs +14 -7
  102. package/dist/lib/net/ssrf-fetch.mjs.map +1 -1
  103. package/dist/lib/protocols/a2a.d.mts +2 -2
  104. package/dist/lib/protocols/a2a.d.ts +2 -2
  105. package/dist/lib/protocols/a2a.d.ts.map +1 -1
  106. package/dist/lib/protocols/a2a.js +14 -9
  107. package/dist/lib/protocols/a2a.js.map +1 -1
  108. package/dist/lib/protocols/a2a.mjs +14 -9
  109. package/dist/lib/protocols/a2a.mjs.map +1 -1
  110. package/dist/lib/protocols/index.d.mts +20 -3
  111. package/dist/lib/protocols/index.d.ts +20 -3
  112. package/dist/lib/protocols/index.d.ts.map +1 -1
  113. package/dist/lib/protocols/index.js +44 -31
  114. package/dist/lib/protocols/index.js.map +1 -1
  115. package/dist/lib/protocols/index.mjs +44 -32
  116. package/dist/lib/protocols/index.mjs.map +1 -1
  117. package/dist/lib/protocols/mcp-modern.d.ts.map +1 -1
  118. package/dist/lib/protocols/mcp-modern.js +87 -12
  119. package/dist/lib/protocols/mcp-modern.js.map +1 -1
  120. package/dist/lib/protocols/mcp-modern.mjs +89 -12
  121. package/dist/lib/protocols/mcp-modern.mjs.map +1 -1
  122. package/dist/lib/protocols/mcp-tasks.d.mts +3 -0
  123. package/dist/lib/protocols/mcp-tasks.d.ts +3 -0
  124. package/dist/lib/protocols/mcp-tasks.d.ts.map +1 -1
  125. package/dist/lib/protocols/mcp-tasks.js +21 -11
  126. package/dist/lib/protocols/mcp-tasks.js.map +1 -1
  127. package/dist/lib/protocols/mcp-tasks.mjs +21 -11
  128. package/dist/lib/protocols/mcp-tasks.mjs.map +1 -1
  129. package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
  130. package/dist/lib/server/create-adcp-server.d.mts +6 -0
  131. package/dist/lib/server/create-adcp-server.d.ts +6 -0
  132. package/dist/lib/server/create-adcp-server.d.ts.map +1 -1
  133. package/dist/lib/server/create-adcp-server.js +295 -48
  134. package/dist/lib/server/create-adcp-server.js.map +1 -1
  135. package/dist/lib/server/create-adcp-server.mjs +301 -50
  136. package/dist/lib/server/create-adcp-server.mjs.map +1 -1
  137. package/dist/lib/server/decisioning/assembly-helpers.d.mts +27 -13
  138. package/dist/lib/server/decisioning/assembly-helpers.d.ts +27 -13
  139. package/dist/lib/server/decisioning/assembly-helpers.d.ts.map +1 -1
  140. package/dist/lib/server/decisioning/assembly-helpers.js +136 -14
  141. package/dist/lib/server/decisioning/assembly-helpers.js.map +1 -1
  142. package/dist/lib/server/decisioning/assembly-helpers.mjs +134 -13
  143. package/dist/lib/server/decisioning/assembly-helpers.mjs.map +1 -1
  144. package/dist/lib/server/decisioning/index.d.mts +13 -12
  145. package/dist/lib/server/decisioning/index.d.ts +13 -12
  146. package/dist/lib/server/decisioning/index.d.ts.map +1 -1
  147. package/dist/lib/server/decisioning/index.js +6 -0
  148. package/dist/lib/server/decisioning/index.js.map +1 -1
  149. package/dist/lib/server/decisioning/index.mjs +9 -3
  150. package/dist/lib/server/decisioning/index.mjs.map +1 -1
  151. package/dist/lib/server/decisioning/list-helpers.d.mts +13 -3
  152. package/dist/lib/server/decisioning/list-helpers.d.ts +13 -3
  153. package/dist/lib/server/decisioning/list-helpers.d.ts.map +1 -1
  154. package/dist/lib/server/decisioning/list-helpers.js +13 -3
  155. package/dist/lib/server/decisioning/list-helpers.js.map +1 -1
  156. package/dist/lib/server/decisioning/list-helpers.mjs +11 -2
  157. package/dist/lib/server/decisioning/list-helpers.mjs.map +1 -1
  158. package/dist/lib/server/decisioning/manifest-helpers.d.mts +5 -3
  159. package/dist/lib/server/decisioning/manifest-helpers.d.ts +5 -3
  160. package/dist/lib/server/decisioning/manifest-helpers.d.ts.map +1 -1
  161. package/dist/lib/server/decisioning/manifest-helpers.js.map +1 -1
  162. package/dist/lib/server/decisioning/manifest-helpers.mjs.map +1 -1
  163. package/dist/lib/server/decisioning/proposal/dispatch.d.mts +2 -2
  164. package/dist/lib/server/decisioning/proposal/dispatch.d.ts +2 -2
  165. package/dist/lib/server/decisioning/proposal/dispatch.d.ts.map +1 -1
  166. package/dist/lib/server/decisioning/proposal/dispatch.js.map +1 -1
  167. package/dist/lib/server/decisioning/proposal/dispatch.mjs.map +1 -1
  168. package/dist/lib/server/decisioning/proposal/index.d.mts +1 -1
  169. package/dist/lib/server/decisioning/proposal/index.d.ts +1 -1
  170. package/dist/lib/server/decisioning/proposal/index.d.ts.map +1 -1
  171. package/dist/lib/server/decisioning/proposal/index.js.map +1 -1
  172. package/dist/lib/server/decisioning/proposal/index.mjs.map +1 -1
  173. package/dist/lib/server/decisioning/proposal/lifecycle.d.mts +3 -2
  174. package/dist/lib/server/decisioning/proposal/lifecycle.d.ts +3 -2
  175. package/dist/lib/server/decisioning/proposal/lifecycle.d.ts.map +1 -1
  176. package/dist/lib/server/decisioning/proposal/lifecycle.js.map +1 -1
  177. package/dist/lib/server/decisioning/proposal/lifecycle.mjs.map +1 -1
  178. package/dist/lib/server/decisioning/proposal/mock-manager.d.mts +3 -3
  179. package/dist/lib/server/decisioning/proposal/mock-manager.d.ts +3 -3
  180. package/dist/lib/server/decisioning/proposal/mock-manager.d.ts.map +1 -1
  181. package/dist/lib/server/decisioning/proposal/mock-manager.js.map +1 -1
  182. package/dist/lib/server/decisioning/proposal/mock-manager.mjs.map +1 -1
  183. package/dist/lib/server/decisioning/proposal/types.d.mts +10 -6
  184. package/dist/lib/server/decisioning/proposal/types.d.ts +10 -6
  185. package/dist/lib/server/decisioning/proposal/types.d.ts.map +1 -1
  186. package/dist/lib/server/decisioning/proposal/types.js.map +1 -1
  187. package/dist/lib/server/decisioning/proposal/types.mjs.map +1 -1
  188. package/dist/lib/server/decisioning/runtime/from-platform.d.mts +21 -2
  189. package/dist/lib/server/decisioning/runtime/from-platform.d.ts +21 -2
  190. package/dist/lib/server/decisioning/runtime/from-platform.d.ts.map +1 -1
  191. package/dist/lib/server/decisioning/runtime/from-platform.js +754 -81
  192. package/dist/lib/server/decisioning/runtime/from-platform.js.map +1 -1
  193. package/dist/lib/server/decisioning/runtime/from-platform.mjs +763 -81
  194. package/dist/lib/server/decisioning/runtime/from-platform.mjs.map +1 -1
  195. package/dist/lib/server/decisioning/specialisms/brand-rights.d.mts +9 -9
  196. package/dist/lib/server/decisioning/specialisms/brand-rights.d.ts +9 -9
  197. package/dist/lib/server/decisioning/specialisms/brand-rights.d.ts.map +1 -1
  198. package/dist/lib/server/decisioning/specialisms/brand-rights.js.map +1 -1
  199. package/dist/lib/server/decisioning/specialisms/content-standards.d.mts +16 -16
  200. package/dist/lib/server/decisioning/specialisms/content-standards.d.ts +16 -16
  201. package/dist/lib/server/decisioning/specialisms/content-standards.d.ts.map +1 -1
  202. package/dist/lib/server/decisioning/specialisms/content-standards.js.map +1 -1
  203. package/dist/lib/server/decisioning/specialisms/creative-ad-server.d.mts +15 -13
  204. package/dist/lib/server/decisioning/specialisms/creative-ad-server.d.ts +15 -13
  205. package/dist/lib/server/decisioning/specialisms/creative-ad-server.d.ts.map +1 -1
  206. package/dist/lib/server/decisioning/specialisms/creative-ad-server.js.map +1 -1
  207. package/dist/lib/server/decisioning/specialisms/creative.d.mts +12 -11
  208. package/dist/lib/server/decisioning/specialisms/creative.d.ts +12 -11
  209. package/dist/lib/server/decisioning/specialisms/creative.d.ts.map +1 -1
  210. package/dist/lib/server/decisioning/specialisms/creative.js.map +1 -1
  211. package/dist/lib/server/decisioning/specialisms/sales.d.mts +25 -15
  212. package/dist/lib/server/decisioning/specialisms/sales.d.ts +25 -15
  213. package/dist/lib/server/decisioning/specialisms/sales.d.ts.map +1 -1
  214. package/dist/lib/server/decisioning/specialisms/sales.js.map +1 -1
  215. package/dist/lib/server/index.d.mts +3 -3
  216. package/dist/lib/server/index.d.ts +3 -3
  217. package/dist/lib/server/index.d.ts.map +1 -1
  218. package/dist/lib/server/index.js +72 -72
  219. package/dist/lib/server/index.js.map +1 -1
  220. package/dist/lib/server/index.mjs +36 -36
  221. package/dist/lib/server/index.mjs.map +1 -1
  222. package/dist/lib/server/legacy/v5/index.d.mts +1 -0
  223. package/dist/lib/server/legacy/v5/index.d.ts +1 -0
  224. package/dist/lib/server/legacy/v5/index.d.ts.map +1 -1
  225. package/dist/lib/server/legacy/v5/index.js +74 -1
  226. package/dist/lib/server/legacy/v5/index.js.map +1 -1
  227. package/dist/lib/server/legacy/v5/index.mjs +75 -1
  228. package/dist/lib/server/legacy/v5/index.mjs.map +1 -1
  229. package/dist/lib/server/responses.d.mts +2 -2
  230. package/dist/lib/server/responses.d.ts +2 -2
  231. package/dist/lib/server/responses.js.map +1 -1
  232. package/dist/lib/server/responses.mjs.map +1 -1
  233. package/dist/lib/testing/client.d.ts.map +1 -1
  234. package/dist/lib/testing/client.js +13 -5
  235. package/dist/lib/testing/client.js.map +1 -1
  236. package/dist/lib/testing/client.mjs +13 -5
  237. package/dist/lib/testing/client.mjs.map +1 -1
  238. package/dist/lib/testing/compliance/comply.d.mts +1 -1
  239. package/dist/lib/testing/compliance/comply.d.ts +1 -1
  240. package/dist/lib/testing/compliance/comply.d.ts.map +1 -1
  241. package/dist/lib/testing/compliance/comply.js +16 -6
  242. package/dist/lib/testing/compliance/comply.js.map +1 -1
  243. package/dist/lib/testing/compliance/comply.mjs +16 -6
  244. package/dist/lib/testing/compliance/comply.mjs.map +1 -1
  245. package/dist/lib/testing/scenarios/brand-rights.js +6 -6
  246. package/dist/lib/testing/scenarios/brand-rights.js.map +1 -1
  247. package/dist/lib/testing/scenarios/brand-rights.mjs +6 -6
  248. package/dist/lib/testing/scenarios/brand-rights.mjs.map +1 -1
  249. package/dist/lib/testing/scenarios/creative.js +7 -7
  250. package/dist/lib/testing/scenarios/creative.js.map +1 -1
  251. package/dist/lib/testing/scenarios/creative.mjs +7 -7
  252. package/dist/lib/testing/scenarios/creative.mjs.map +1 -1
  253. package/dist/lib/testing/scenarios/discovery.js +2 -2
  254. package/dist/lib/testing/scenarios/discovery.js.map +1 -1
  255. package/dist/lib/testing/scenarios/discovery.mjs +2 -2
  256. package/dist/lib/testing/scenarios/discovery.mjs.map +1 -1
  257. package/dist/lib/testing/scenarios/edge-cases.js +11 -11
  258. package/dist/lib/testing/scenarios/edge-cases.js.map +1 -1
  259. package/dist/lib/testing/scenarios/edge-cases.mjs +11 -11
  260. package/dist/lib/testing/scenarios/edge-cases.mjs.map +1 -1
  261. package/dist/lib/testing/scenarios/governance.d.ts.map +1 -1
  262. package/dist/lib/testing/scenarios/governance.js +16 -22
  263. package/dist/lib/testing/scenarios/governance.js.map +1 -1
  264. package/dist/lib/testing/scenarios/governance.mjs +16 -22
  265. package/dist/lib/testing/scenarios/governance.mjs.map +1 -1
  266. package/dist/lib/testing/scenarios/media-buy.js +5 -5
  267. package/dist/lib/testing/scenarios/media-buy.js.map +1 -1
  268. package/dist/lib/testing/scenarios/media-buy.mjs +5 -5
  269. package/dist/lib/testing/scenarios/media-buy.mjs.map +1 -1
  270. package/dist/lib/testing/scenarios/schema-compliance.js +1 -1
  271. package/dist/lib/testing/scenarios/schema-compliance.js.map +1 -1
  272. package/dist/lib/testing/scenarios/schema-compliance.mjs +1 -1
  273. package/dist/lib/testing/scenarios/schema-compliance.mjs.map +1 -1
  274. package/dist/lib/testing/scenarios/trusted-match.d.ts.map +1 -1
  275. package/dist/lib/testing/scenarios/trusted-match.js +4 -3
  276. package/dist/lib/testing/scenarios/trusted-match.js.map +1 -1
  277. package/dist/lib/testing/scenarios/trusted-match.mjs +4 -3
  278. package/dist/lib/testing/scenarios/trusted-match.mjs.map +1 -1
  279. package/dist/lib/testing/storyboard/probes.d.mts +6 -0
  280. package/dist/lib/testing/storyboard/probes.d.ts +6 -0
  281. package/dist/lib/testing/storyboard/probes.d.ts.map +1 -1
  282. package/dist/lib/testing/storyboard/probes.js +16 -5
  283. package/dist/lib/testing/storyboard/probes.js.map +1 -1
  284. package/dist/lib/testing/storyboard/probes.mjs +16 -5
  285. package/dist/lib/testing/storyboard/probes.mjs.map +1 -1
  286. package/dist/lib/testing/storyboard/runner.d.ts.map +1 -1
  287. package/dist/lib/testing/storyboard/runner.js +6 -2
  288. package/dist/lib/testing/storyboard/runner.js.map +1 -1
  289. package/dist/lib/testing/storyboard/runner.mjs +6 -2
  290. package/dist/lib/testing/storyboard/runner.mjs.map +1 -1
  291. package/dist/lib/testing/storyboard/shape-drift-hints.d.mts +3 -4
  292. package/dist/lib/testing/storyboard/shape-drift-hints.d.ts +3 -4
  293. package/dist/lib/testing/storyboard/shape-drift-hints.d.ts.map +1 -1
  294. package/dist/lib/testing/storyboard/shape-drift-hints.js +27 -27
  295. package/dist/lib/testing/storyboard/shape-drift-hints.js.map +1 -1
  296. package/dist/lib/testing/storyboard/shape-drift-hints.mjs +27 -27
  297. package/dist/lib/testing/storyboard/shape-drift-hints.mjs.map +1 -1
  298. package/dist/lib/testing/storyboard/task-map.d.ts.map +1 -1
  299. package/dist/lib/testing/storyboard/task-map.js +43 -5
  300. package/dist/lib/testing/storyboard/task-map.js.map +1 -1
  301. package/dist/lib/testing/storyboard/task-map.mjs +43 -5
  302. package/dist/lib/testing/storyboard/task-map.mjs.map +1 -1
  303. package/dist/lib/testing/test-helpers.d.mts +2 -2
  304. package/dist/lib/testing/test-helpers.d.ts +2 -2
  305. package/dist/lib/testing/test-helpers.js.map +1 -1
  306. package/dist/lib/testing/test-helpers.mjs.map +1 -1
  307. package/dist/lib/testing/types.d.mts +7 -1
  308. package/dist/lib/testing/types.d.ts +7 -1
  309. package/dist/lib/testing/types.d.ts.map +1 -1
  310. package/dist/lib/testing/types.js.map +1 -1
  311. package/dist/lib/types/adcp.d.mts +14 -14
  312. package/dist/lib/types/adcp.d.ts +14 -14
  313. package/dist/lib/types/adcp.d.ts.map +1 -1
  314. package/dist/lib/types/adcp.js.map +1 -1
  315. package/dist/lib/types/adcp.mjs.map +1 -1
  316. package/dist/lib/types/index.d.mts +2 -2
  317. package/dist/lib/types/index.d.ts +2 -2
  318. package/dist/lib/types/index.d.ts.map +1 -1
  319. package/dist/lib/types/index.js.map +1 -1
  320. package/dist/lib/types/index.mjs.map +1 -1
  321. package/dist/lib/utils/capabilities.d.mts +6 -0
  322. package/dist/lib/utils/capabilities.d.ts +6 -0
  323. package/dist/lib/utils/capabilities.d.ts.map +1 -1
  324. package/dist/lib/utils/capabilities.js +4 -0
  325. package/dist/lib/utils/capabilities.js.map +1 -1
  326. package/dist/lib/utils/capabilities.mjs +4 -0
  327. package/dist/lib/utils/capabilities.mjs.map +1 -1
  328. package/dist/lib/utils/creative-delivery.d.mts +52 -10
  329. package/dist/lib/utils/creative-delivery.d.ts +52 -10
  330. package/dist/lib/utils/creative-delivery.d.ts.map +1 -1
  331. package/dist/lib/utils/creative-delivery.js +104 -9
  332. package/dist/lib/utils/creative-delivery.js.map +1 -1
  333. package/dist/lib/utils/creative-delivery.mjs +104 -8
  334. package/dist/lib/utils/creative-delivery.mjs.map +1 -1
  335. package/dist/lib/utils/format-assets.d.mts +41 -38
  336. package/dist/lib/utils/format-assets.d.ts +41 -38
  337. package/dist/lib/utils/format-assets.d.ts.map +1 -1
  338. package/dist/lib/utils/format-assets.js +3 -1
  339. package/dist/lib/utils/format-assets.js.map +1 -1
  340. package/dist/lib/utils/format-assets.mjs +3 -1
  341. package/dist/lib/utils/format-assets.mjs.map +1 -1
  342. package/dist/lib/utils/index.d.mts +8 -8
  343. package/dist/lib/utils/index.d.ts +8 -8
  344. package/dist/lib/utils/index.d.ts.map +1 -1
  345. package/dist/lib/utils/index.js +11 -9
  346. package/dist/lib/utils/index.js.map +1 -1
  347. package/dist/lib/utils/index.mjs +9 -8
  348. package/dist/lib/utils/index.mjs.map +1 -1
  349. package/dist/lib/utils/preview-utils.d.mts +11 -8
  350. package/dist/lib/utils/preview-utils.d.ts +11 -8
  351. package/dist/lib/utils/preview-utils.d.ts.map +1 -1
  352. package/dist/lib/utils/preview-utils.js +4 -4
  353. package/dist/lib/utils/preview-utils.js.map +1 -1
  354. package/dist/lib/utils/preview-utils.mjs +3 -3
  355. package/dist/lib/utils/preview-utils.mjs.map +1 -1
  356. package/dist/lib/utils/tool-request-schemas.d.mts +2 -2
  357. package/dist/lib/utils/tool-request-schemas.d.ts +2 -2
  358. package/dist/lib/v2/projection/aao-reference-formats.json +492 -0
  359. package/dist/lib/v2/projection/augment-response.d.mts +31 -11
  360. package/dist/lib/v2/projection/augment-response.d.ts +31 -11
  361. package/dist/lib/v2/projection/augment-response.d.ts.map +1 -1
  362. package/dist/lib/v2/projection/augment-response.js +233 -27
  363. package/dist/lib/v2/projection/augment-response.js.map +1 -1
  364. package/dist/lib/v2/projection/augment-response.mjs +232 -27
  365. package/dist/lib/v2/projection/augment-response.mjs.map +1 -1
  366. package/dist/lib/v2/projection/builders.d.mts +7 -6
  367. package/dist/lib/v2/projection/builders.d.ts +7 -6
  368. package/dist/lib/v2/projection/builders.d.ts.map +1 -1
  369. package/dist/lib/v2/projection/builders.js +11 -8
  370. package/dist/lib/v2/projection/builders.js.map +1 -1
  371. package/dist/lib/v2/projection/builders.mjs +9 -6
  372. package/dist/lib/v2/projection/builders.mjs.map +1 -1
  373. package/dist/lib/v2/projection/catalog-snapshot.d.mts +47 -0
  374. package/dist/lib/v2/projection/catalog-snapshot.d.ts +48 -0
  375. package/dist/lib/v2/projection/catalog-snapshot.d.ts.map +1 -0
  376. package/dist/lib/v2/projection/catalog-snapshot.js +291 -0
  377. package/dist/lib/v2/projection/catalog-snapshot.js.map +1 -0
  378. package/dist/lib/v2/projection/catalog-snapshot.mjs +265 -0
  379. package/dist/lib/v2/projection/catalog-snapshot.mjs.map +1 -0
  380. package/dist/lib/v2/projection/catalog.d.mts +23 -2
  381. package/dist/lib/v2/projection/catalog.d.ts +23 -2
  382. package/dist/lib/v2/projection/catalog.d.ts.map +1 -1
  383. package/dist/lib/v2/projection/catalog.js +34 -10
  384. package/dist/lib/v2/projection/catalog.js.map +1 -1
  385. package/dist/lib/v2/projection/catalog.mjs +33 -10
  386. package/dist/lib/v2/projection/catalog.mjs.map +1 -1
  387. package/dist/lib/v2/projection/creative-delivery.d.mts +157 -0
  388. package/dist/lib/v2/projection/creative-delivery.d.ts +158 -0
  389. package/dist/lib/v2/projection/creative-delivery.d.ts.map +1 -0
  390. package/dist/lib/v2/projection/creative-delivery.js +967 -0
  391. package/dist/lib/v2/projection/creative-delivery.js.map +1 -0
  392. package/dist/lib/v2/projection/creative-delivery.mjs +941 -0
  393. package/dist/lib/v2/projection/creative-delivery.mjs.map +1 -0
  394. package/dist/lib/v2/projection/index.d.mts +8 -6
  395. package/dist/lib/v2/projection/index.d.ts +8 -6
  396. package/dist/lib/v2/projection/index.d.ts.map +1 -1
  397. package/dist/lib/v2/projection/index.js +26 -4
  398. package/dist/lib/v2/projection/index.js.map +1 -1
  399. package/dist/lib/v2/projection/index.mjs +28 -4
  400. package/dist/lib/v2/projection/index.mjs.map +1 -1
  401. package/dist/lib/v2/projection/legacy-metadata.d.mts +21 -0
  402. package/dist/lib/v2/projection/legacy-metadata.d.ts +22 -0
  403. package/dist/lib/v2/projection/legacy-metadata.d.ts.map +1 -0
  404. package/dist/lib/v2/projection/legacy-metadata.js +73 -0
  405. package/dist/lib/v2/projection/legacy-metadata.js.map +1 -0
  406. package/dist/lib/v2/projection/legacy-metadata.mjs +45 -0
  407. package/dist/lib/v2/projection/legacy-metadata.mjs.map +1 -0
  408. package/dist/lib/v2/projection/types.d.mts +29 -23
  409. package/dist/lib/v2/projection/types.d.ts +29 -23
  410. package/dist/lib/v2/projection/types.d.ts.map +1 -1
  411. package/dist/lib/v2/projection/types.js.map +1 -1
  412. package/dist/lib/v2/projection/v1-to-v2.d.mts +29 -6
  413. package/dist/lib/v2/projection/v1-to-v2.d.ts +29 -6
  414. package/dist/lib/v2/projection/v1-to-v2.d.ts.map +1 -1
  415. package/dist/lib/v2/projection/v1-to-v2.js +218 -9
  416. package/dist/lib/v2/projection/v1-to-v2.js.map +1 -1
  417. package/dist/lib/v2/projection/v1-to-v2.mjs +219 -10
  418. package/dist/lib/v2/projection/v1-to-v2.mjs.map +1 -1
  419. package/dist/lib/v2/projection/v2-to-v1.d.mts +31 -2
  420. package/dist/lib/v2/projection/v2-to-v1.d.ts +31 -2
  421. package/dist/lib/v2/projection/v2-to-v1.d.ts.map +1 -1
  422. package/dist/lib/v2/projection/v2-to-v1.js +138 -7
  423. package/dist/lib/v2/projection/v2-to-v1.js.map +1 -1
  424. package/dist/lib/v2/projection/v2-to-v1.mjs +135 -6
  425. package/dist/lib/v2/projection/v2-to-v1.mjs.map +1 -1
  426. package/dist/lib/v2/projection/write-side.d.mts +12 -13
  427. package/dist/lib/v2/projection/write-side.d.ts +12 -13
  428. package/dist/lib/v2/projection/write-side.d.ts.map +1 -1
  429. package/dist/lib/v2/projection/write-side.js +16 -12
  430. package/dist/lib/v2/projection/write-side.js.map +1 -1
  431. package/dist/lib/v2/projection/write-side.mjs +16 -12
  432. package/dist/lib/v2/projection/write-side.mjs.map +1 -1
  433. package/dist/lib/version.d.mts +3 -3
  434. package/dist/lib/version.d.ts +3 -3
  435. package/dist/lib/version.d.ts.map +1 -1
  436. package/dist/lib/version.js +3 -3
  437. package/dist/lib/version.js.map +1 -1
  438. package/dist/lib/version.mjs +3 -3
  439. package/dist/lib/version.mjs.map +1 -1
  440. package/docs/guides/BUILD-AN-AGENT.md +7 -2
  441. package/docs/guides/PREVIEW-ASSET-DURABILITY.md +6 -2
  442. package/docs/llms.txt +1 -0
  443. package/examples/README.md +1 -1
  444. package/examples/batch-preview-test.ts +3 -3
  445. package/examples/conversation-client.ts +2 -2
  446. package/examples/debug-preview-response.ts +1 -1
  447. package/examples/decisioning-platform-broadcast-tv.ts +10 -4
  448. package/examples/decisioning-platform-mock-seller.ts +16 -9
  449. package/examples/decisioning-platform-programmatic.ts +18 -7
  450. package/examples/error-compliant-server.ts +26 -16
  451. package/examples/generative-creative-demo.ts +7 -19
  452. package/examples/hello_creative_adapter_ad_server.ts +37 -16
  453. package/examples/hello_creative_adapter_template.ts +12 -10
  454. package/examples/hello_seller_adapter_guaranteed.ts +52 -23
  455. package/examples/hello_seller_adapter_multi_tenant.ts +6 -3
  456. package/examples/hello_seller_adapter_non_guaranteed.ts +54 -26
  457. package/examples/hello_seller_adapter_proposal_mode.ts +40 -14
  458. package/examples/hello_seller_adapter_social.ts +2 -7
  459. package/examples/inspect-card-formats.ts +1 -1
  460. package/examples/simple-getting-started.ts +1 -1
  461. package/examples/test-helpers-demo.ts +1 -1
  462. package/package.json +2 -2
  463. package/skills/build-decisioning-creative-template/SKILL.md +11 -11
  464. package/skills/build-decisioning-platform/advanced/REFERENCE.md +8 -2
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../src/lib/server/decisioning/proposal/dispatch.ts"],"sourcesContent":["/**\n * Proposal-dispatch — the v1.5 framework intercept seams.\n *\n * Pure functions that the runtime calls from the wire-handler shims\n * (`getProducts`, `createMediaBuy`, `updateMediaBuy`,\n * `getMediaBuyDelivery`). Each helper short-circuits when no manager /\n * store is wired for the request, so the v1 path is preserved\n * byte-for-byte.\n *\n * Five integration points (parity with `adcp-client-python.src/adcp/decisioning/proposal_dispatch.py`):\n *\n * - {@link maybeInterceptFinalize} — `getProducts` shim. Detects\n * `refine[i].action: 'finalize'`, calls the manager's\n * `finalizeProposal`, commits the proposal, returns the wire\n * response. Returns `null` when no finalize entry / no\n * finalize-capable manager.\n * - {@link maybePersistDraftAfterGetProducts} — `getProducts` shim\n * (post-call). Walks the response's `proposals[]`, validates\n * `overlap ⊆ wire`, persists each as a draft.\n * - {@link maybeReserveProposalForCreateMediaBuy} — `createMediaBuy`\n * shim. When `proposal_id` is set, validates expiry + capability\n * overlap, reserves the proposal (`COMMITTED → CONSUMING`), returns\n * the recipes map for `ctx.recipes` hydration.\n * - {@link finalizeProposalConsumption} — `createMediaBuy` shim\n * (post-success). Promotes `CONSUMING → CONSUMED` and records the\n * `mediaBuyId` back-reference.\n * - {@link releaseProposalReservation} — `createMediaBuy` shim\n * (failure rollback). Restores `CONSUMING → COMMITTED`.\n * - {@link maybeHydrateRecipesForMediaBuyId} —\n * `updateMediaBuy` / `getMediaBuyDelivery` shims. Reverse-index\n * hydration via `getByMediaBuyId`. Re-runs capability-overlap\n * validation per Resolutions §5.\n *\n * @public\n * @packageDocumentation\n */\n\nimport { AdcpError, isTaskHandoff, type TaskHandoff } from '../async-outcome';\nimport type { GetProductsRequest, Product, Proposal } from '../../../types/tools.generated';\nimport {\n detectFinalizeAction,\n enforceProposalExpiry,\n logConsumed,\n logDraftPersisted,\n logFinalizeSucceeded,\n validateCapabilityOverlap,\n validateOverlapSubsetOfWire,\n} from './lifecycle';\nimport type { ProposalRecord, ProposalStore } from './store';\nimport type {\n FinalizeProposalRequest,\n FinalizeProposalSuccess,\n ProposalGetProductsPayload,\n ProposalManager,\n Recipe,\n} from './types';\n\n// ---------------------------------------------------------------------------\n// Capability detection\n// ---------------------------------------------------------------------------\n\n/**\n * Per Resolutions §7: framework detects finalize via method-presence +\n * `capabilities.finalize` flag. Adopters opt in by both implementing\n * AND declaring; flipping only one half is an adopter bug.\n */\nfunction hasFinalizeCapability<TRecipe extends Recipe, TCtxMeta>(\n manager: ProposalManager<TRecipe, TCtxMeta> | undefined\n): boolean {\n if (!manager) return false;\n if (!manager.capabilities.finalize) return false;\n return typeof manager.finalizeProposal === 'function';\n}\n\n// ---------------------------------------------------------------------------\n// getProducts — finalize interception\n// ---------------------------------------------------------------------------\n\n/**\n * Result of {@link maybeInterceptFinalize}.\n *\n * - `kind: 'intercepted'` — adopter's `finalizeProposal` was called.\n * Caller threads `result` and `project` through the framework's\n * standard `routeIfHandoff` so the projection callback fires\n * synchronously for sync `FinalizeProposalSuccess` returns AND\n * inside the background task for `TaskHandoff<FinalizeProposalSuccess>`\n * returns. The projection commits the proposal store, emits the\n * `proposal.finalized` log, and shapes the wire response.\n * - `kind: 'pass'` — no finalize entry / no finalize-capable manager;\n * caller continues with the standard `getProducts` dispatch.\n *\n * No special-case handling: finalize HITL inherits the framework's\n * task-lifecycle posture (cancellation, restart-via-durable-store,\n * deadline, webhook delivery) from `routeIfHandoff`, the same machinery\n * that drives `createMediaBuy` HITL and `syncCreatives` HITL.\n *\n * @public\n */\nexport type FinalizeInterceptResult<TRecipe extends Recipe = Recipe> =\n | {\n kind: 'intercepted';\n result: FinalizeProposalSuccess<TRecipe> | TaskHandoff<FinalizeProposalSuccess<TRecipe>>;\n project: (success: FinalizeProposalSuccess<TRecipe>) => Promise<ProposalGetProductsPayload>;\n }\n | { kind: 'pass' };\n\n/**\n * Intercept `buying_mode: 'refine'` requests carrying a\n * `refine[i].action: 'finalize'` entry. Hydrates the draft, calls the\n * manager's `finalizeProposal`, and returns the raw adopter result + a\n * projection callback. The runtime threads them through the framework's\n * standard `routeIfHandoff` so:\n *\n * - Sync `FinalizeProposalSuccess` returns: projection runs inline,\n * buyer sees the committed proposal in the response.\n * - HITL `TaskHandoff<FinalizeProposalSuccess>` returns: framework\n * returns the Submitted envelope to the buyer, runs the adopter's\n * handoff fn in background; projection runs when the handoff\n * resolves, commits the proposal, projects the terminal task\n * artifact.\n *\n * Same machinery as `createMediaBuy` HITL — finalize inherits whatever\n * cancellation, restart-via-durable-store, deadline, and webhook\n * delivery semantics the framework's task lifecycle provides for every\n * other unified-hybrid tool. No special-case wrapper.\n *\n * @public\n */\nexport async function maybeInterceptFinalize<TRecipe extends Recipe, TCtxMeta>(args: {\n request: GetProductsRequest;\n manager: ProposalManager<TRecipe, TCtxMeta> | undefined;\n store: ProposalStore<TRecipe> | undefined;\n ctx: { account: { id: string } } & Record<string, unknown>;\n}): Promise<FinalizeInterceptResult<TRecipe>> {\n const { request, manager, store, ctx } = args;\n const finalizeEntry = detectFinalizeAction(request);\n if (!finalizeEntry) return { kind: 'pass' };\n if (!hasFinalizeCapability(manager) || !store) return { kind: 'pass' };\n\n const fieldPath = `refine[${finalizeEntry.index}].proposal_id`;\n const accountId = ctx.account.id;\n const record = await store.get(finalizeEntry.proposalId, { expectedAccountId: accountId });\n if (!record) {\n throw new AdcpError('PROPOSAL_NOT_FOUND', {\n recovery: 'correctable',\n message:\n `Proposal ${JSON.stringify(finalizeEntry.proposalId)} not found. The buyer must call ` +\n `get_products with buying_mode='brief' or 'refine' to obtain a draft proposal_id ` +\n `before finalizing it.`,\n field: fieldPath,\n });\n }\n if (record.state !== 'draft') {\n throw new AdcpError('PROPOSAL_NOT_COMMITTED', {\n recovery: 'correctable',\n message:\n `Proposal ${JSON.stringify(finalizeEntry.proposalId)} is in state ` +\n `${JSON.stringify(record.state)}; only draft proposals can be finalized. ` +\n `Already-committed proposals should be accepted via create_media_buy(proposal_id=...) ` +\n `directly.`,\n field: fieldPath,\n });\n }\n\n const finalizeReq: FinalizeProposalRequest<TRecipe> = {\n proposalId: finalizeEntry.proposalId,\n recipes: record.recipes,\n proposalPayload: record.proposalPayload,\n parentRequest: request,\n ...(finalizeEntry.ask !== undefined && { ask: finalizeEntry.ask }),\n };\n\n // manager + finalizeProposal are non-undefined per hasFinalizeCapability.\n const result = await manager!.finalizeProposal!(finalizeReq, ctx as never);\n // Pre-determine the path label by checking whether the adopter\n // returned a TaskHandoff up-front. `routeIfHandoff` runs `project`\n // synchronously for the sync arm and inside the background task for\n // the handoff arm; either way, we want the log to reflect which arm\n // the adopter chose.\n const path: 'inline' | 'handoff' = isTaskHandoff(result) ? 'handoff' : 'inline';\n const finalizeProposalId = finalizeEntry.proposalId;\n\n const project = async (success: FinalizeProposalSuccess<TRecipe>): Promise<ProposalGetProductsPayload> => {\n if (!isFinalizeSuccess<TRecipe>(success)) {\n throw new AdcpError('INTERNAL_ERROR', {\n recovery: 'terminal',\n message:\n `finalizeProposal resolved to an unexpected shape; expected FinalizeProposalSuccess ` +\n `with 'proposal' and 'expiresAt' fields.`,\n });\n }\n await store.commit(finalizeProposalId, {\n expiresAt: success.expiresAt,\n proposalPayload: success.proposal,\n });\n logFinalizeSucceeded({\n proposalId: finalizeProposalId,\n accountId,\n expiresAt: success.expiresAt,\n path,\n });\n return projectFinalizeResponse({\n request,\n committedProposal: success.proposal,\n finalizeProposalId,\n });\n };\n\n return { kind: 'intercepted', result, project };\n}\n\nfunction isFinalizeSuccess<TRecipe extends Recipe>(v: unknown): v is FinalizeProposalSuccess<TRecipe> {\n if (!v || typeof v !== 'object') return false;\n const r = v as Record<string, unknown>;\n return r.proposal != null && r.expiresAt instanceof Date;\n}\n\nfunction projectFinalizeResponse(args: {\n request: GetProductsRequest;\n committedProposal: Record<string, unknown>;\n finalizeProposalId: string;\n}): ProposalGetProductsPayload {\n const refineEntries = (args.request as { refine?: ReadonlyArray<Record<string, unknown>> }).refine;\n const refinementApplied: Array<Record<string, unknown>> = [];\n if (refineEntries) {\n for (const entry of refineEntries) {\n const scope = entry.scope;\n if (scope === 'proposal') {\n refinementApplied.push({\n scope: 'proposal',\n proposal_id: typeof entry.proposal_id === 'string' ? entry.proposal_id : args.finalizeProposalId,\n status: 'applied',\n });\n } else if (scope === 'product') {\n refinementApplied.push({\n scope: 'product',\n product_id: typeof entry.product_id === 'string' ? entry.product_id : '',\n status: 'applied',\n });\n } else {\n refinementApplied.push({ scope: 'request', status: 'applied' });\n }\n }\n }\n // **Decision: `products: []` on the finalize response by design.** The\n // committed proposal carries `allocations[]` keyed by `product_id`;\n // the buyer either has the products from the prior `brief_with_proposals`\n // step (the natural flow) or fetches by id. Echoing the products here\n // would require the framework to either re-emit from the persisted\n // draft (extra wire bytes the buyer already has) or call back into the\n // adopter (extra round-trip to upstream). Buyers who explicitly want\n // products on the finalize response can fetch them via a follow-up\n // `get_products({ product_ids: [...] })` keyed off\n // `proposals[0].allocations[].product_id`.\n return {\n cache_scope: 'account',\n products: [],\n proposals: [args.committedProposal as unknown as Proposal],\n refinement_applied: refinementApplied,\n } as unknown as ProposalGetProductsPayload;\n}\n\n// ---------------------------------------------------------------------------\n// getProducts / refineProducts — post-call draft persistence\n// ---------------------------------------------------------------------------\n\n/**\n * Persist proposals returned by `getProducts` / `refineProducts` as\n * drafts in the wired {@link ProposalStore}. Validates `overlap ⊆ wire`\n * for any returned recipes before persisting.\n *\n * Quietly returns when no store is wired, no `proposals[]` in the\n * response, or no typed recipes attached to products.\n *\n * @public\n */\nexport async function maybePersistDraftAfterGetProducts<TRecipe extends Recipe>(args: {\n response: ProposalGetProductsPayload;\n store: ProposalStore<TRecipe> | undefined;\n ctx: { account: { id: string } };\n}): Promise<void> {\n const { response, store, ctx } = args;\n if (!store) return;\n const proposals = (response as { proposals?: ReadonlyArray<Proposal> }).proposals;\n if (!proposals || proposals.length === 0) return;\n const products = (response as { products?: ReadonlyArray<Product> }).products ?? [];\n\n for (const proposal of proposals) {\n const proposalId = (proposal as { proposal_id?: string }).proposal_id;\n if (!proposalId) continue;\n const proposalPayload = toPlainObject(proposal);\n const recipes = collectRecipesFromProducts<TRecipe>(products, proposalPayload);\n if (recipes.size > 0) {\n validateOverlapSubsetOfWire({ recipes, products });\n }\n await store.putDraft({\n proposalId,\n accountId: ctx.account.id,\n recipes,\n proposalPayload,\n });\n logDraftPersisted({\n proposalId,\n accountId: ctx.account.id,\n recipesCount: recipes.size,\n });\n }\n}\n\nfunction collectRecipesFromProducts<TRecipe extends Recipe>(\n products: readonly Product[],\n proposalPayload: Record<string, unknown>\n): Map<string, TRecipe> {\n // Filter to products referenced by the proposal's allocations[] (per\n // spec) or legacy products[] string array when present.\n let referenced: Set<string> | null = null;\n const allocations = proposalPayload.allocations;\n if (Array.isArray(allocations)) {\n referenced = new Set(\n allocations\n .map((a: unknown) => (a as { product_id?: string }).product_id)\n .filter((id): id is string => typeof id === 'string')\n );\n } else if (Array.isArray(proposalPayload.products)) {\n referenced = new Set((proposalPayload.products as unknown[]).filter((p): p is string => typeof p === 'string'));\n }\n\n const recipes = new Map<string, TRecipe>();\n for (const product of products) {\n const productId = product.product_id;\n if (!productId) continue;\n if (referenced && !referenced.has(productId)) continue;\n const implConfig = (product as { implementation_config?: unknown }).implementation_config;\n if (isRecipe(implConfig)) {\n recipes.set(productId, implConfig as TRecipe);\n }\n }\n return recipes;\n}\n\nfunction isRecipe(v: unknown): boolean {\n return v != null && typeof v === 'object' && typeof (v as { recipe_kind?: unknown }).recipe_kind === 'string';\n}\n\nfunction toPlainObject(v: unknown): Record<string, unknown> {\n if (v == null || typeof v !== 'object') return {};\n // Deep clone via JSON to drop class-instance prototypes — the stored\n // payload must round-trip through a durable backing.\n return JSON.parse(JSON.stringify(v)) as Record<string, unknown>;\n}\n\n// ---------------------------------------------------------------------------\n// createMediaBuy — recipe hydration on proposal_id\n// ---------------------------------------------------------------------------\n\n/**\n * Result of {@link maybeReserveProposalForCreateMediaBuy}.\n *\n * - `record` — the reserved {@link ProposalRecord}. Caller threads\n * `recipes` onto `ctx.recipes` and calls\n * {@link finalizeProposalConsumption} on adapter success or\n * {@link releaseProposalReservation} on failure.\n * - `null` — request has no `proposal_id`, no store wired, or the\n * framework didn't intercept (v1 path).\n *\n * @public\n */\nexport type ReservedProposal<TRecipe extends Recipe> = ProposalRecord<TRecipe>;\n\n/**\n * Reserve the proposal for consumption and hydrate `ctx.recipes`.\n *\n * Atomic CAS via {@link ProposalStore.tryReserveConsumption} — the\n * proposal transitions `COMMITTED → CONSUMING` before the adapter runs.\n * Two parallel `createMediaBuy(proposal_id=X)` calls cannot both\n * reserve; the loser raises `PROPOSAL_NOT_COMMITTED`.\n *\n * Validates per § D7 (expiry) BEFORE reserving — an expired proposal\n * surfaces `PROPOSAL_EXPIRED` without flipping the state.\n *\n * @public\n */\nexport async function maybeReserveProposalForCreateMediaBuy<TRecipe extends Recipe, TCtxMeta>(args: {\n request: { proposal_id?: string; packages?: ReadonlyArray<unknown> } & Record<string, unknown>;\n manager: ProposalManager<TRecipe, TCtxMeta> | undefined;\n store: ProposalStore<TRecipe> | undefined;\n ctx: { account: { id: string } };\n now?: Date;\n}): Promise<ReservedProposal<TRecipe> | null> {\n const { request, manager, store, ctx, now } = args;\n const proposalId = request.proposal_id;\n if (!proposalId || !store) return null;\n const graceSeconds = manager?.capabilities.expiresAtGraceSeconds ?? 0;\n\n // Expiry check BEFORE reserving — keep the buyer's slot uncommitted\n // when telling them they're expired.\n await enforceProposalExpiry(proposalId, {\n proposalStore: store,\n expectedAccountId: ctx.account.id,\n graceSeconds,\n ...(now && { now }),\n });\n\n const reserved = await store.tryReserveConsumption(proposalId, {\n expectedAccountId: ctx.account.id,\n });\n\n // Capability-overlap gate per D4. Buyer's packages may be empty when\n // proposal_id is set (the spec allows the seller to derive packages\n // from allocations); skip the gate in that case.\n const packages = request.packages;\n if (packages && Array.isArray(packages) && packages.length > 0) {\n validateCapabilityOverlap({\n packages: packages as never,\n recipes: reserved.recipes,\n });\n }\n\n return reserved;\n}\n\n/**\n * Promote `CONSUMING → CONSUMED` after the adapter's `createMediaBuy`\n * succeeded.\n *\n * @public\n */\nexport async function finalizeProposalConsumption<TRecipe extends Recipe>(args: {\n store: ProposalStore<TRecipe> | undefined;\n record: ReservedProposal<TRecipe>;\n mediaBuyId: string;\n}): Promise<void> {\n const { store, record, mediaBuyId } = args;\n if (!store) return;\n await store.finalizeConsumption(record.proposalId, {\n mediaBuyId,\n expectedAccountId: record.accountId,\n });\n logConsumed({\n proposalId: record.proposalId,\n accountId: record.accountId,\n mediaBuyId,\n });\n}\n\n/**\n * Roll back the reservation: `CONSUMING → COMMITTED`. Best-effort.\n *\n * @public\n */\nexport async function releaseProposalReservation<TRecipe extends Recipe>(args: {\n store: ProposalStore<TRecipe> | undefined;\n record: ReservedProposal<TRecipe>;\n logger?: { warn(message: string): void };\n}): Promise<void> {\n const { store, record, logger } = args;\n if (!store) return;\n try {\n await store.releaseConsumption(record.proposalId, {\n expectedAccountId: record.accountId,\n });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n logger?.warn(\n `[adcp/decisioning] failed to release consumption reservation for proposal ` +\n `${record.proposalId}: ${message}. The record may stay in CONSUMING until eviction.`\n );\n }\n}\n\n// ---------------------------------------------------------------------------\n// updateMediaBuy / getMediaBuyDelivery — reverse-index recipe hydration\n// ---------------------------------------------------------------------------\n\n/**\n * Hydrate `ctx.recipes` for post-acceptance buy operations via the\n * `getByMediaBuyId` reverse-index. Returns the record (so the caller\n * can re-validate overlap if a packages-shaped patch is provided);\n * returns `null` when no proposal backs this buy.\n *\n * Per Resolutions §5: re-validates capability overlap on every call\n * with packages.\n *\n * @public\n */\nexport async function maybeHydrateRecipesForMediaBuyId<TRecipe extends Recipe>(args: {\n mediaBuyId: string | undefined;\n store: ProposalStore<TRecipe> | undefined;\n ctx: { account: { id: string } };\n packages?: ReadonlyArray<unknown>;\n}): Promise<ProposalRecord<TRecipe> | null> {\n const { mediaBuyId, store, ctx, packages } = args;\n if (!mediaBuyId || !store) return null;\n const record = await store.getByMediaBuyId(mediaBuyId, {\n expectedAccountId: ctx.account.id,\n });\n if (!record) return null;\n if (packages && Array.isArray(packages) && packages.length > 0) {\n validateCapabilityOverlap({\n packages: packages as never,\n recipes: record.recipes,\n });\n }\n return record;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../../../src/lib/server/decisioning/proposal/dispatch.ts"],"sourcesContent":["/**\n * Proposal-dispatch — the v1.5 framework intercept seams.\n *\n * Pure functions that the runtime calls from the wire-handler shims\n * (`getProducts`, `createMediaBuy`, `updateMediaBuy`,\n * `getMediaBuyDelivery`). Each helper short-circuits when no manager /\n * store is wired for the request, so the v1 path is preserved\n * byte-for-byte.\n *\n * Five integration points (parity with `adcp-client-python.src/adcp/decisioning/proposal_dispatch.py`):\n *\n * - {@link maybeInterceptFinalize} — `getProducts` shim. Detects\n * `refine[i].action: 'finalize'`, calls the manager's\n * `finalizeProposal`, commits the proposal, returns the wire\n * response. Returns `null` when no finalize entry / no\n * finalize-capable manager.\n * - {@link maybePersistDraftAfterGetProducts} — `getProducts` shim\n * (post-call). Walks the response's `proposals[]`, validates\n * `overlap ⊆ wire`, persists each as a draft.\n * - {@link maybeReserveProposalForCreateMediaBuy} — `createMediaBuy`\n * shim. When `proposal_id` is set, validates expiry + capability\n * overlap, reserves the proposal (`COMMITTED → CONSUMING`), returns\n * the recipes map for `ctx.recipes` hydration.\n * - {@link finalizeProposalConsumption} — `createMediaBuy` shim\n * (post-success). Promotes `CONSUMING → CONSUMED` and records the\n * `mediaBuyId` back-reference.\n * - {@link releaseProposalReservation} — `createMediaBuy` shim\n * (failure rollback). Restores `CONSUMING → COMMITTED`.\n * - {@link maybeHydrateRecipesForMediaBuyId} —\n * `updateMediaBuy` / `getMediaBuyDelivery` shims. Reverse-index\n * hydration via `getByMediaBuyId`. Re-runs capability-overlap\n * validation per Resolutions §5.\n *\n * @public\n * @packageDocumentation\n */\n\nimport { AdcpError, isTaskHandoff, type TaskHandoff } from '../async-outcome';\nimport type { Product, Proposal } from '../../../types/tools.generated';\nimport type { CanonicalGetProductsRequest } from '../../../v2/projection/creative-delivery';\nimport {\n detectFinalizeAction,\n enforceProposalExpiry,\n logConsumed,\n logDraftPersisted,\n logFinalizeSucceeded,\n validateCapabilityOverlap,\n validateOverlapSubsetOfWire,\n} from './lifecycle';\nimport type { ProposalRecord, ProposalStore } from './store';\nimport type {\n FinalizeProposalRequest,\n FinalizeProposalSuccess,\n ProposalGetProductsPayload,\n ProposalManager,\n Recipe,\n} from './types';\n\n// ---------------------------------------------------------------------------\n// Capability detection\n// ---------------------------------------------------------------------------\n\n/**\n * Per Resolutions §7: framework detects finalize via method-presence +\n * `capabilities.finalize` flag. Adopters opt in by both implementing\n * AND declaring; flipping only one half is an adopter bug.\n */\nfunction hasFinalizeCapability<TRecipe extends Recipe, TCtxMeta>(\n manager: ProposalManager<TRecipe, TCtxMeta> | undefined\n): boolean {\n if (!manager) return false;\n if (!manager.capabilities.finalize) return false;\n return typeof manager.finalizeProposal === 'function';\n}\n\n// ---------------------------------------------------------------------------\n// getProducts — finalize interception\n// ---------------------------------------------------------------------------\n\n/**\n * Result of {@link maybeInterceptFinalize}.\n *\n * - `kind: 'intercepted'` — adopter's `finalizeProposal` was called.\n * Caller threads `result` and `project` through the framework's\n * standard `routeIfHandoff` so the projection callback fires\n * synchronously for sync `FinalizeProposalSuccess` returns AND\n * inside the background task for `TaskHandoff<FinalizeProposalSuccess>`\n * returns. The projection commits the proposal store, emits the\n * `proposal.finalized` log, and shapes the wire response.\n * - `kind: 'pass'` — no finalize entry / no finalize-capable manager;\n * caller continues with the standard `getProducts` dispatch.\n *\n * No special-case handling: finalize HITL inherits the framework's\n * task-lifecycle posture (cancellation, restart-via-durable-store,\n * deadline, webhook delivery) from `routeIfHandoff`, the same machinery\n * that drives `createMediaBuy` HITL and `syncCreatives` HITL.\n *\n * @public\n */\nexport type FinalizeInterceptResult<TRecipe extends Recipe = Recipe> =\n | {\n kind: 'intercepted';\n result: FinalizeProposalSuccess<TRecipe> | TaskHandoff<FinalizeProposalSuccess<TRecipe>>;\n project: (success: FinalizeProposalSuccess<TRecipe>) => Promise<ProposalGetProductsPayload>;\n }\n | { kind: 'pass' };\n\n/**\n * Intercept `buying_mode: 'refine'` requests carrying a\n * `refine[i].action: 'finalize'` entry. Hydrates the draft, calls the\n * manager's `finalizeProposal`, and returns the raw adopter result + a\n * projection callback. The runtime threads them through the framework's\n * standard `routeIfHandoff` so:\n *\n * - Sync `FinalizeProposalSuccess` returns: projection runs inline,\n * buyer sees the committed proposal in the response.\n * - HITL `TaskHandoff<FinalizeProposalSuccess>` returns: framework\n * returns the Submitted envelope to the buyer, runs the adopter's\n * handoff fn in background; projection runs when the handoff\n * resolves, commits the proposal, projects the terminal task\n * artifact.\n *\n * Same machinery as `createMediaBuy` HITL — finalize inherits whatever\n * cancellation, restart-via-durable-store, deadline, and webhook\n * delivery semantics the framework's task lifecycle provides for every\n * other unified-hybrid tool. No special-case wrapper.\n *\n * @public\n */\nexport async function maybeInterceptFinalize<TRecipe extends Recipe, TCtxMeta>(args: {\n request: CanonicalGetProductsRequest;\n manager: ProposalManager<TRecipe, TCtxMeta> | undefined;\n store: ProposalStore<TRecipe> | undefined;\n ctx: { account: { id: string } } & Record<string, unknown>;\n}): Promise<FinalizeInterceptResult<TRecipe>> {\n const { request, manager, store, ctx } = args;\n const finalizeEntry = detectFinalizeAction(request);\n if (!finalizeEntry) return { kind: 'pass' };\n if (!hasFinalizeCapability(manager) || !store) return { kind: 'pass' };\n\n const fieldPath = `refine[${finalizeEntry.index}].proposal_id`;\n const accountId = ctx.account.id;\n const record = await store.get(finalizeEntry.proposalId, { expectedAccountId: accountId });\n if (!record) {\n throw new AdcpError('PROPOSAL_NOT_FOUND', {\n recovery: 'correctable',\n message:\n `Proposal ${JSON.stringify(finalizeEntry.proposalId)} not found. The buyer must call ` +\n `get_products with buying_mode='brief' or 'refine' to obtain a draft proposal_id ` +\n `before finalizing it.`,\n field: fieldPath,\n });\n }\n if (record.state !== 'draft') {\n throw new AdcpError('PROPOSAL_NOT_COMMITTED', {\n recovery: 'correctable',\n message:\n `Proposal ${JSON.stringify(finalizeEntry.proposalId)} is in state ` +\n `${JSON.stringify(record.state)}; only draft proposals can be finalized. ` +\n `Already-committed proposals should be accepted via create_media_buy(proposal_id=...) ` +\n `directly.`,\n field: fieldPath,\n });\n }\n\n const finalizeReq: FinalizeProposalRequest<TRecipe> = {\n proposalId: finalizeEntry.proposalId,\n recipes: record.recipes,\n proposalPayload: record.proposalPayload,\n parentRequest: request,\n ...(finalizeEntry.ask !== undefined && { ask: finalizeEntry.ask }),\n };\n\n // manager + finalizeProposal are non-undefined per hasFinalizeCapability.\n const result = await manager!.finalizeProposal!(finalizeReq, ctx as never);\n // Pre-determine the path label by checking whether the adopter\n // returned a TaskHandoff up-front. `routeIfHandoff` runs `project`\n // synchronously for the sync arm and inside the background task for\n // the handoff arm; either way, we want the log to reflect which arm\n // the adopter chose.\n const path: 'inline' | 'handoff' = isTaskHandoff(result) ? 'handoff' : 'inline';\n const finalizeProposalId = finalizeEntry.proposalId;\n\n const project = async (success: FinalizeProposalSuccess<TRecipe>): Promise<ProposalGetProductsPayload> => {\n if (!isFinalizeSuccess<TRecipe>(success)) {\n throw new AdcpError('INTERNAL_ERROR', {\n recovery: 'terminal',\n message:\n `finalizeProposal resolved to an unexpected shape; expected FinalizeProposalSuccess ` +\n `with 'proposal' and 'expiresAt' fields.`,\n });\n }\n await store.commit(finalizeProposalId, {\n expiresAt: success.expiresAt,\n proposalPayload: success.proposal,\n });\n logFinalizeSucceeded({\n proposalId: finalizeProposalId,\n accountId,\n expiresAt: success.expiresAt,\n path,\n });\n return projectFinalizeResponse({\n request,\n committedProposal: success.proposal,\n finalizeProposalId,\n });\n };\n\n return { kind: 'intercepted', result, project };\n}\n\nfunction isFinalizeSuccess<TRecipe extends Recipe>(v: unknown): v is FinalizeProposalSuccess<TRecipe> {\n if (!v || typeof v !== 'object') return false;\n const r = v as Record<string, unknown>;\n return r.proposal != null && r.expiresAt instanceof Date;\n}\n\nfunction projectFinalizeResponse(args: {\n request: CanonicalGetProductsRequest;\n committedProposal: Record<string, unknown>;\n finalizeProposalId: string;\n}): ProposalGetProductsPayload {\n const refineEntries = (args.request as { refine?: ReadonlyArray<Record<string, unknown>> }).refine;\n const refinementApplied: Array<Record<string, unknown>> = [];\n if (refineEntries) {\n for (const entry of refineEntries) {\n const scope = entry.scope;\n if (scope === 'proposal') {\n refinementApplied.push({\n scope: 'proposal',\n proposal_id: typeof entry.proposal_id === 'string' ? entry.proposal_id : args.finalizeProposalId,\n status: 'applied',\n });\n } else if (scope === 'product') {\n refinementApplied.push({\n scope: 'product',\n product_id: typeof entry.product_id === 'string' ? entry.product_id : '',\n status: 'applied',\n });\n } else {\n refinementApplied.push({ scope: 'request', status: 'applied' });\n }\n }\n }\n // **Decision: `products: []` on the finalize response by design.** The\n // committed proposal carries `allocations[]` keyed by `product_id`;\n // the buyer either has the products from the prior `brief_with_proposals`\n // step (the natural flow) or fetches by id. Echoing the products here\n // would require the framework to either re-emit from the persisted\n // draft (extra wire bytes the buyer already has) or call back into the\n // adopter (extra round-trip to upstream). Buyers who explicitly want\n // products on the finalize response can fetch them via a follow-up\n // `get_products({ product_ids: [...] })` keyed off\n // `proposals[0].allocations[].product_id`.\n return {\n cache_scope: 'account',\n products: [],\n proposals: [args.committedProposal as unknown as Proposal],\n refinement_applied: refinementApplied,\n } as unknown as ProposalGetProductsPayload;\n}\n\n// ---------------------------------------------------------------------------\n// getProducts / refineProducts — post-call draft persistence\n// ---------------------------------------------------------------------------\n\n/**\n * Persist proposals returned by `getProducts` / `refineProducts` as\n * drafts in the wired {@link ProposalStore}. Validates `overlap ⊆ wire`\n * for any returned recipes before persisting.\n *\n * Quietly returns when no store is wired, no `proposals[]` in the\n * response, or no typed recipes attached to products.\n *\n * @public\n */\nexport async function maybePersistDraftAfterGetProducts<TRecipe extends Recipe>(args: {\n response: ProposalGetProductsPayload;\n store: ProposalStore<TRecipe> | undefined;\n ctx: { account: { id: string } };\n}): Promise<void> {\n const { response, store, ctx } = args;\n if (!store) return;\n const proposals = (response as { proposals?: ReadonlyArray<Proposal> }).proposals;\n if (!proposals || proposals.length === 0) return;\n const products = (response as { products?: ReadonlyArray<Product> }).products ?? [];\n\n for (const proposal of proposals) {\n const proposalId = (proposal as { proposal_id?: string }).proposal_id;\n if (!proposalId) continue;\n const proposalPayload = toPlainObject(proposal);\n const recipes = collectRecipesFromProducts<TRecipe>(products, proposalPayload);\n if (recipes.size > 0) {\n validateOverlapSubsetOfWire({ recipes, products });\n }\n await store.putDraft({\n proposalId,\n accountId: ctx.account.id,\n recipes,\n proposalPayload,\n });\n logDraftPersisted({\n proposalId,\n accountId: ctx.account.id,\n recipesCount: recipes.size,\n });\n }\n}\n\nfunction collectRecipesFromProducts<TRecipe extends Recipe>(\n products: readonly Product[],\n proposalPayload: Record<string, unknown>\n): Map<string, TRecipe> {\n // Filter to products referenced by the proposal's allocations[] (per\n // spec) or legacy products[] string array when present.\n let referenced: Set<string> | null = null;\n const allocations = proposalPayload.allocations;\n if (Array.isArray(allocations)) {\n referenced = new Set(\n allocations\n .map((a: unknown) => (a as { product_id?: string }).product_id)\n .filter((id): id is string => typeof id === 'string')\n );\n } else if (Array.isArray(proposalPayload.products)) {\n referenced = new Set((proposalPayload.products as unknown[]).filter((p): p is string => typeof p === 'string'));\n }\n\n const recipes = new Map<string, TRecipe>();\n for (const product of products) {\n const productId = product.product_id;\n if (!productId) continue;\n if (referenced && !referenced.has(productId)) continue;\n const implConfig = (product as { implementation_config?: unknown }).implementation_config;\n if (isRecipe(implConfig)) {\n recipes.set(productId, implConfig as TRecipe);\n }\n }\n return recipes;\n}\n\nfunction isRecipe(v: unknown): boolean {\n return v != null && typeof v === 'object' && typeof (v as { recipe_kind?: unknown }).recipe_kind === 'string';\n}\n\nfunction toPlainObject(v: unknown): Record<string, unknown> {\n if (v == null || typeof v !== 'object') return {};\n // Deep clone via JSON to drop class-instance prototypes — the stored\n // payload must round-trip through a durable backing.\n return JSON.parse(JSON.stringify(v)) as Record<string, unknown>;\n}\n\n// ---------------------------------------------------------------------------\n// createMediaBuy — recipe hydration on proposal_id\n// ---------------------------------------------------------------------------\n\n/**\n * Result of {@link maybeReserveProposalForCreateMediaBuy}.\n *\n * - `record` — the reserved {@link ProposalRecord}. Caller threads\n * `recipes` onto `ctx.recipes` and calls\n * {@link finalizeProposalConsumption} on adapter success or\n * {@link releaseProposalReservation} on failure.\n * - `null` — request has no `proposal_id`, no store wired, or the\n * framework didn't intercept (v1 path).\n *\n * @public\n */\nexport type ReservedProposal<TRecipe extends Recipe> = ProposalRecord<TRecipe>;\n\n/**\n * Reserve the proposal for consumption and hydrate `ctx.recipes`.\n *\n * Atomic CAS via {@link ProposalStore.tryReserveConsumption} — the\n * proposal transitions `COMMITTED → CONSUMING` before the adapter runs.\n * Two parallel `createMediaBuy(proposal_id=X)` calls cannot both\n * reserve; the loser raises `PROPOSAL_NOT_COMMITTED`.\n *\n * Validates per § D7 (expiry) BEFORE reserving — an expired proposal\n * surfaces `PROPOSAL_EXPIRED` without flipping the state.\n *\n * @public\n */\nexport async function maybeReserveProposalForCreateMediaBuy<TRecipe extends Recipe, TCtxMeta>(args: {\n request: { proposal_id?: string; packages?: ReadonlyArray<unknown> } & Record<string, unknown>;\n manager: ProposalManager<TRecipe, TCtxMeta> | undefined;\n store: ProposalStore<TRecipe> | undefined;\n ctx: { account: { id: string } };\n now?: Date;\n}): Promise<ReservedProposal<TRecipe> | null> {\n const { request, manager, store, ctx, now } = args;\n const proposalId = request.proposal_id;\n if (!proposalId || !store) return null;\n const graceSeconds = manager?.capabilities.expiresAtGraceSeconds ?? 0;\n\n // Expiry check BEFORE reserving — keep the buyer's slot uncommitted\n // when telling them they're expired.\n await enforceProposalExpiry(proposalId, {\n proposalStore: store,\n expectedAccountId: ctx.account.id,\n graceSeconds,\n ...(now && { now }),\n });\n\n const reserved = await store.tryReserveConsumption(proposalId, {\n expectedAccountId: ctx.account.id,\n });\n\n // Capability-overlap gate per D4. Buyer's packages may be empty when\n // proposal_id is set (the spec allows the seller to derive packages\n // from allocations); skip the gate in that case.\n const packages = request.packages;\n if (packages && Array.isArray(packages) && packages.length > 0) {\n validateCapabilityOverlap({\n packages: packages as never,\n recipes: reserved.recipes,\n });\n }\n\n return reserved;\n}\n\n/**\n * Promote `CONSUMING → CONSUMED` after the adapter's `createMediaBuy`\n * succeeded.\n *\n * @public\n */\nexport async function finalizeProposalConsumption<TRecipe extends Recipe>(args: {\n store: ProposalStore<TRecipe> | undefined;\n record: ReservedProposal<TRecipe>;\n mediaBuyId: string;\n}): Promise<void> {\n const { store, record, mediaBuyId } = args;\n if (!store) return;\n await store.finalizeConsumption(record.proposalId, {\n mediaBuyId,\n expectedAccountId: record.accountId,\n });\n logConsumed({\n proposalId: record.proposalId,\n accountId: record.accountId,\n mediaBuyId,\n });\n}\n\n/**\n * Roll back the reservation: `CONSUMING → COMMITTED`. Best-effort.\n *\n * @public\n */\nexport async function releaseProposalReservation<TRecipe extends Recipe>(args: {\n store: ProposalStore<TRecipe> | undefined;\n record: ReservedProposal<TRecipe>;\n logger?: { warn(message: string): void };\n}): Promise<void> {\n const { store, record, logger } = args;\n if (!store) return;\n try {\n await store.releaseConsumption(record.proposalId, {\n expectedAccountId: record.accountId,\n });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n logger?.warn(\n `[adcp/decisioning] failed to release consumption reservation for proposal ` +\n `${record.proposalId}: ${message}. The record may stay in CONSUMING until eviction.`\n );\n }\n}\n\n// ---------------------------------------------------------------------------\n// updateMediaBuy / getMediaBuyDelivery — reverse-index recipe hydration\n// ---------------------------------------------------------------------------\n\n/**\n * Hydrate `ctx.recipes` for post-acceptance buy operations via the\n * `getByMediaBuyId` reverse-index. Returns the record (so the caller\n * can re-validate overlap if a packages-shaped patch is provided);\n * returns `null` when no proposal backs this buy.\n *\n * Per Resolutions §5: re-validates capability overlap on every call\n * with packages.\n *\n * @public\n */\nexport async function maybeHydrateRecipesForMediaBuyId<TRecipe extends Recipe>(args: {\n mediaBuyId: string | undefined;\n store: ProposalStore<TRecipe> | undefined;\n ctx: { account: { id: string } };\n packages?: ReadonlyArray<unknown>;\n}): Promise<ProposalRecord<TRecipe> | null> {\n const { mediaBuyId, store, ctx, packages } = args;\n if (!mediaBuyId || !store) return null;\n const record = await store.getByMediaBuyId(mediaBuyId, {\n expectedAccountId: ctx.account.id,\n });\n if (!record) return null;\n if (packages && Array.isArray(packages) && packages.length > 0) {\n validateCapabilityOverlap({\n packages: packages as never,\n recipes: record.recipes,\n });\n }\n return record;\n}\n"],"mappings":"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","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../src/lib/server/decisioning/proposal/dispatch.ts"],"sourcesContent":["/**\n * Proposal-dispatch — the v1.5 framework intercept seams.\n *\n * Pure functions that the runtime calls from the wire-handler shims\n * (`getProducts`, `createMediaBuy`, `updateMediaBuy`,\n * `getMediaBuyDelivery`). Each helper short-circuits when no manager /\n * store is wired for the request, so the v1 path is preserved\n * byte-for-byte.\n *\n * Five integration points (parity with `adcp-client-python.src/adcp/decisioning/proposal_dispatch.py`):\n *\n * - {@link maybeInterceptFinalize} — `getProducts` shim. Detects\n * `refine[i].action: 'finalize'`, calls the manager's\n * `finalizeProposal`, commits the proposal, returns the wire\n * response. Returns `null` when no finalize entry / no\n * finalize-capable manager.\n * - {@link maybePersistDraftAfterGetProducts} — `getProducts` shim\n * (post-call). Walks the response's `proposals[]`, validates\n * `overlap ⊆ wire`, persists each as a draft.\n * - {@link maybeReserveProposalForCreateMediaBuy} — `createMediaBuy`\n * shim. When `proposal_id` is set, validates expiry + capability\n * overlap, reserves the proposal (`COMMITTED → CONSUMING`), returns\n * the recipes map for `ctx.recipes` hydration.\n * - {@link finalizeProposalConsumption} — `createMediaBuy` shim\n * (post-success). Promotes `CONSUMING → CONSUMED` and records the\n * `mediaBuyId` back-reference.\n * - {@link releaseProposalReservation} — `createMediaBuy` shim\n * (failure rollback). Restores `CONSUMING → COMMITTED`.\n * - {@link maybeHydrateRecipesForMediaBuyId} —\n * `updateMediaBuy` / `getMediaBuyDelivery` shims. Reverse-index\n * hydration via `getByMediaBuyId`. Re-runs capability-overlap\n * validation per Resolutions §5.\n *\n * @public\n * @packageDocumentation\n */\n\nimport { AdcpError, isTaskHandoff, type TaskHandoff } from '../async-outcome';\nimport type { GetProductsRequest, Product, Proposal } from '../../../types/tools.generated';\nimport {\n detectFinalizeAction,\n enforceProposalExpiry,\n logConsumed,\n logDraftPersisted,\n logFinalizeSucceeded,\n validateCapabilityOverlap,\n validateOverlapSubsetOfWire,\n} from './lifecycle';\nimport type { ProposalRecord, ProposalStore } from './store';\nimport type {\n FinalizeProposalRequest,\n FinalizeProposalSuccess,\n ProposalGetProductsPayload,\n ProposalManager,\n Recipe,\n} from './types';\n\n// ---------------------------------------------------------------------------\n// Capability detection\n// ---------------------------------------------------------------------------\n\n/**\n * Per Resolutions §7: framework detects finalize via method-presence +\n * `capabilities.finalize` flag. Adopters opt in by both implementing\n * AND declaring; flipping only one half is an adopter bug.\n */\nfunction hasFinalizeCapability<TRecipe extends Recipe, TCtxMeta>(\n manager: ProposalManager<TRecipe, TCtxMeta> | undefined\n): boolean {\n if (!manager) return false;\n if (!manager.capabilities.finalize) return false;\n return typeof manager.finalizeProposal === 'function';\n}\n\n// ---------------------------------------------------------------------------\n// getProducts — finalize interception\n// ---------------------------------------------------------------------------\n\n/**\n * Result of {@link maybeInterceptFinalize}.\n *\n * - `kind: 'intercepted'` — adopter's `finalizeProposal` was called.\n * Caller threads `result` and `project` through the framework's\n * standard `routeIfHandoff` so the projection callback fires\n * synchronously for sync `FinalizeProposalSuccess` returns AND\n * inside the background task for `TaskHandoff<FinalizeProposalSuccess>`\n * returns. The projection commits the proposal store, emits the\n * `proposal.finalized` log, and shapes the wire response.\n * - `kind: 'pass'` — no finalize entry / no finalize-capable manager;\n * caller continues with the standard `getProducts` dispatch.\n *\n * No special-case handling: finalize HITL inherits the framework's\n * task-lifecycle posture (cancellation, restart-via-durable-store,\n * deadline, webhook delivery) from `routeIfHandoff`, the same machinery\n * that drives `createMediaBuy` HITL and `syncCreatives` HITL.\n *\n * @public\n */\nexport type FinalizeInterceptResult<TRecipe extends Recipe = Recipe> =\n | {\n kind: 'intercepted';\n result: FinalizeProposalSuccess<TRecipe> | TaskHandoff<FinalizeProposalSuccess<TRecipe>>;\n project: (success: FinalizeProposalSuccess<TRecipe>) => Promise<ProposalGetProductsPayload>;\n }\n | { kind: 'pass' };\n\n/**\n * Intercept `buying_mode: 'refine'` requests carrying a\n * `refine[i].action: 'finalize'` entry. Hydrates the draft, calls the\n * manager's `finalizeProposal`, and returns the raw adopter result + a\n * projection callback. The runtime threads them through the framework's\n * standard `routeIfHandoff` so:\n *\n * - Sync `FinalizeProposalSuccess` returns: projection runs inline,\n * buyer sees the committed proposal in the response.\n * - HITL `TaskHandoff<FinalizeProposalSuccess>` returns: framework\n * returns the Submitted envelope to the buyer, runs the adopter's\n * handoff fn in background; projection runs when the handoff\n * resolves, commits the proposal, projects the terminal task\n * artifact.\n *\n * Same machinery as `createMediaBuy` HITL — finalize inherits whatever\n * cancellation, restart-via-durable-store, deadline, and webhook\n * delivery semantics the framework's task lifecycle provides for every\n * other unified-hybrid tool. No special-case wrapper.\n *\n * @public\n */\nexport async function maybeInterceptFinalize<TRecipe extends Recipe, TCtxMeta>(args: {\n request: GetProductsRequest;\n manager: ProposalManager<TRecipe, TCtxMeta> | undefined;\n store: ProposalStore<TRecipe> | undefined;\n ctx: { account: { id: string } } & Record<string, unknown>;\n}): Promise<FinalizeInterceptResult<TRecipe>> {\n const { request, manager, store, ctx } = args;\n const finalizeEntry = detectFinalizeAction(request);\n if (!finalizeEntry) return { kind: 'pass' };\n if (!hasFinalizeCapability(manager) || !store) return { kind: 'pass' };\n\n const fieldPath = `refine[${finalizeEntry.index}].proposal_id`;\n const accountId = ctx.account.id;\n const record = await store.get(finalizeEntry.proposalId, { expectedAccountId: accountId });\n if (!record) {\n throw new AdcpError('PROPOSAL_NOT_FOUND', {\n recovery: 'correctable',\n message:\n `Proposal ${JSON.stringify(finalizeEntry.proposalId)} not found. The buyer must call ` +\n `get_products with buying_mode='brief' or 'refine' to obtain a draft proposal_id ` +\n `before finalizing it.`,\n field: fieldPath,\n });\n }\n if (record.state !== 'draft') {\n throw new AdcpError('PROPOSAL_NOT_COMMITTED', {\n recovery: 'correctable',\n message:\n `Proposal ${JSON.stringify(finalizeEntry.proposalId)} is in state ` +\n `${JSON.stringify(record.state)}; only draft proposals can be finalized. ` +\n `Already-committed proposals should be accepted via create_media_buy(proposal_id=...) ` +\n `directly.`,\n field: fieldPath,\n });\n }\n\n const finalizeReq: FinalizeProposalRequest<TRecipe> = {\n proposalId: finalizeEntry.proposalId,\n recipes: record.recipes,\n proposalPayload: record.proposalPayload,\n parentRequest: request,\n ...(finalizeEntry.ask !== undefined && { ask: finalizeEntry.ask }),\n };\n\n // manager + finalizeProposal are non-undefined per hasFinalizeCapability.\n const result = await manager!.finalizeProposal!(finalizeReq, ctx as never);\n // Pre-determine the path label by checking whether the adopter\n // returned a TaskHandoff up-front. `routeIfHandoff` runs `project`\n // synchronously for the sync arm and inside the background task for\n // the handoff arm; either way, we want the log to reflect which arm\n // the adopter chose.\n const path: 'inline' | 'handoff' = isTaskHandoff(result) ? 'handoff' : 'inline';\n const finalizeProposalId = finalizeEntry.proposalId;\n\n const project = async (success: FinalizeProposalSuccess<TRecipe>): Promise<ProposalGetProductsPayload> => {\n if (!isFinalizeSuccess<TRecipe>(success)) {\n throw new AdcpError('INTERNAL_ERROR', {\n recovery: 'terminal',\n message:\n `finalizeProposal resolved to an unexpected shape; expected FinalizeProposalSuccess ` +\n `with 'proposal' and 'expiresAt' fields.`,\n });\n }\n await store.commit(finalizeProposalId, {\n expiresAt: success.expiresAt,\n proposalPayload: success.proposal,\n });\n logFinalizeSucceeded({\n proposalId: finalizeProposalId,\n accountId,\n expiresAt: success.expiresAt,\n path,\n });\n return projectFinalizeResponse({\n request,\n committedProposal: success.proposal,\n finalizeProposalId,\n });\n };\n\n return { kind: 'intercepted', result, project };\n}\n\nfunction isFinalizeSuccess<TRecipe extends Recipe>(v: unknown): v is FinalizeProposalSuccess<TRecipe> {\n if (!v || typeof v !== 'object') return false;\n const r = v as Record<string, unknown>;\n return r.proposal != null && r.expiresAt instanceof Date;\n}\n\nfunction projectFinalizeResponse(args: {\n request: GetProductsRequest;\n committedProposal: Record<string, unknown>;\n finalizeProposalId: string;\n}): ProposalGetProductsPayload {\n const refineEntries = (args.request as { refine?: ReadonlyArray<Record<string, unknown>> }).refine;\n const refinementApplied: Array<Record<string, unknown>> = [];\n if (refineEntries) {\n for (const entry of refineEntries) {\n const scope = entry.scope;\n if (scope === 'proposal') {\n refinementApplied.push({\n scope: 'proposal',\n proposal_id: typeof entry.proposal_id === 'string' ? entry.proposal_id : args.finalizeProposalId,\n status: 'applied',\n });\n } else if (scope === 'product') {\n refinementApplied.push({\n scope: 'product',\n product_id: typeof entry.product_id === 'string' ? entry.product_id : '',\n status: 'applied',\n });\n } else {\n refinementApplied.push({ scope: 'request', status: 'applied' });\n }\n }\n }\n // **Decision: `products: []` on the finalize response by design.** The\n // committed proposal carries `allocations[]` keyed by `product_id`;\n // the buyer either has the products from the prior `brief_with_proposals`\n // step (the natural flow) or fetches by id. Echoing the products here\n // would require the framework to either re-emit from the persisted\n // draft (extra wire bytes the buyer already has) or call back into the\n // adopter (extra round-trip to upstream). Buyers who explicitly want\n // products on the finalize response can fetch them via a follow-up\n // `get_products({ product_ids: [...] })` keyed off\n // `proposals[0].allocations[].product_id`.\n return {\n cache_scope: 'account',\n products: [],\n proposals: [args.committedProposal as unknown as Proposal],\n refinement_applied: refinementApplied,\n } as unknown as ProposalGetProductsPayload;\n}\n\n// ---------------------------------------------------------------------------\n// getProducts / refineProducts — post-call draft persistence\n// ---------------------------------------------------------------------------\n\n/**\n * Persist proposals returned by `getProducts` / `refineProducts` as\n * drafts in the wired {@link ProposalStore}. Validates `overlap ⊆ wire`\n * for any returned recipes before persisting.\n *\n * Quietly returns when no store is wired, no `proposals[]` in the\n * response, or no typed recipes attached to products.\n *\n * @public\n */\nexport async function maybePersistDraftAfterGetProducts<TRecipe extends Recipe>(args: {\n response: ProposalGetProductsPayload;\n store: ProposalStore<TRecipe> | undefined;\n ctx: { account: { id: string } };\n}): Promise<void> {\n const { response, store, ctx } = args;\n if (!store) return;\n const proposals = (response as { proposals?: ReadonlyArray<Proposal> }).proposals;\n if (!proposals || proposals.length === 0) return;\n const products = (response as { products?: ReadonlyArray<Product> }).products ?? [];\n\n for (const proposal of proposals) {\n const proposalId = (proposal as { proposal_id?: string }).proposal_id;\n if (!proposalId) continue;\n const proposalPayload = toPlainObject(proposal);\n const recipes = collectRecipesFromProducts<TRecipe>(products, proposalPayload);\n if (recipes.size > 0) {\n validateOverlapSubsetOfWire({ recipes, products });\n }\n await store.putDraft({\n proposalId,\n accountId: ctx.account.id,\n recipes,\n proposalPayload,\n });\n logDraftPersisted({\n proposalId,\n accountId: ctx.account.id,\n recipesCount: recipes.size,\n });\n }\n}\n\nfunction collectRecipesFromProducts<TRecipe extends Recipe>(\n products: readonly Product[],\n proposalPayload: Record<string, unknown>\n): Map<string, TRecipe> {\n // Filter to products referenced by the proposal's allocations[] (per\n // spec) or legacy products[] string array when present.\n let referenced: Set<string> | null = null;\n const allocations = proposalPayload.allocations;\n if (Array.isArray(allocations)) {\n referenced = new Set(\n allocations\n .map((a: unknown) => (a as { product_id?: string }).product_id)\n .filter((id): id is string => typeof id === 'string')\n );\n } else if (Array.isArray(proposalPayload.products)) {\n referenced = new Set((proposalPayload.products as unknown[]).filter((p): p is string => typeof p === 'string'));\n }\n\n const recipes = new Map<string, TRecipe>();\n for (const product of products) {\n const productId = product.product_id;\n if (!productId) continue;\n if (referenced && !referenced.has(productId)) continue;\n const implConfig = (product as { implementation_config?: unknown }).implementation_config;\n if (isRecipe(implConfig)) {\n recipes.set(productId, implConfig as TRecipe);\n }\n }\n return recipes;\n}\n\nfunction isRecipe(v: unknown): boolean {\n return v != null && typeof v === 'object' && typeof (v as { recipe_kind?: unknown }).recipe_kind === 'string';\n}\n\nfunction toPlainObject(v: unknown): Record<string, unknown> {\n if (v == null || typeof v !== 'object') return {};\n // Deep clone via JSON to drop class-instance prototypes — the stored\n // payload must round-trip through a durable backing.\n return JSON.parse(JSON.stringify(v)) as Record<string, unknown>;\n}\n\n// ---------------------------------------------------------------------------\n// createMediaBuy — recipe hydration on proposal_id\n// ---------------------------------------------------------------------------\n\n/**\n * Result of {@link maybeReserveProposalForCreateMediaBuy}.\n *\n * - `record` — the reserved {@link ProposalRecord}. Caller threads\n * `recipes` onto `ctx.recipes` and calls\n * {@link finalizeProposalConsumption} on adapter success or\n * {@link releaseProposalReservation} on failure.\n * - `null` — request has no `proposal_id`, no store wired, or the\n * framework didn't intercept (v1 path).\n *\n * @public\n */\nexport type ReservedProposal<TRecipe extends Recipe> = ProposalRecord<TRecipe>;\n\n/**\n * Reserve the proposal for consumption and hydrate `ctx.recipes`.\n *\n * Atomic CAS via {@link ProposalStore.tryReserveConsumption} — the\n * proposal transitions `COMMITTED → CONSUMING` before the adapter runs.\n * Two parallel `createMediaBuy(proposal_id=X)` calls cannot both\n * reserve; the loser raises `PROPOSAL_NOT_COMMITTED`.\n *\n * Validates per § D7 (expiry) BEFORE reserving — an expired proposal\n * surfaces `PROPOSAL_EXPIRED` without flipping the state.\n *\n * @public\n */\nexport async function maybeReserveProposalForCreateMediaBuy<TRecipe extends Recipe, TCtxMeta>(args: {\n request: { proposal_id?: string; packages?: ReadonlyArray<unknown> } & Record<string, unknown>;\n manager: ProposalManager<TRecipe, TCtxMeta> | undefined;\n store: ProposalStore<TRecipe> | undefined;\n ctx: { account: { id: string } };\n now?: Date;\n}): Promise<ReservedProposal<TRecipe> | null> {\n const { request, manager, store, ctx, now } = args;\n const proposalId = request.proposal_id;\n if (!proposalId || !store) return null;\n const graceSeconds = manager?.capabilities.expiresAtGraceSeconds ?? 0;\n\n // Expiry check BEFORE reserving — keep the buyer's slot uncommitted\n // when telling them they're expired.\n await enforceProposalExpiry(proposalId, {\n proposalStore: store,\n expectedAccountId: ctx.account.id,\n graceSeconds,\n ...(now && { now }),\n });\n\n const reserved = await store.tryReserveConsumption(proposalId, {\n expectedAccountId: ctx.account.id,\n });\n\n // Capability-overlap gate per D4. Buyer's packages may be empty when\n // proposal_id is set (the spec allows the seller to derive packages\n // from allocations); skip the gate in that case.\n const packages = request.packages;\n if (packages && Array.isArray(packages) && packages.length > 0) {\n validateCapabilityOverlap({\n packages: packages as never,\n recipes: reserved.recipes,\n });\n }\n\n return reserved;\n}\n\n/**\n * Promote `CONSUMING → CONSUMED` after the adapter's `createMediaBuy`\n * succeeded.\n *\n * @public\n */\nexport async function finalizeProposalConsumption<TRecipe extends Recipe>(args: {\n store: ProposalStore<TRecipe> | undefined;\n record: ReservedProposal<TRecipe>;\n mediaBuyId: string;\n}): Promise<void> {\n const { store, record, mediaBuyId } = args;\n if (!store) return;\n await store.finalizeConsumption(record.proposalId, {\n mediaBuyId,\n expectedAccountId: record.accountId,\n });\n logConsumed({\n proposalId: record.proposalId,\n accountId: record.accountId,\n mediaBuyId,\n });\n}\n\n/**\n * Roll back the reservation: `CONSUMING → COMMITTED`. Best-effort.\n *\n * @public\n */\nexport async function releaseProposalReservation<TRecipe extends Recipe>(args: {\n store: ProposalStore<TRecipe> | undefined;\n record: ReservedProposal<TRecipe>;\n logger?: { warn(message: string): void };\n}): Promise<void> {\n const { store, record, logger } = args;\n if (!store) return;\n try {\n await store.releaseConsumption(record.proposalId, {\n expectedAccountId: record.accountId,\n });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n logger?.warn(\n `[adcp/decisioning] failed to release consumption reservation for proposal ` +\n `${record.proposalId}: ${message}. The record may stay in CONSUMING until eviction.`\n );\n }\n}\n\n// ---------------------------------------------------------------------------\n// updateMediaBuy / getMediaBuyDelivery — reverse-index recipe hydration\n// ---------------------------------------------------------------------------\n\n/**\n * Hydrate `ctx.recipes` for post-acceptance buy operations via the\n * `getByMediaBuyId` reverse-index. Returns the record (so the caller\n * can re-validate overlap if a packages-shaped patch is provided);\n * returns `null` when no proposal backs this buy.\n *\n * Per Resolutions §5: re-validates capability overlap on every call\n * with packages.\n *\n * @public\n */\nexport async function maybeHydrateRecipesForMediaBuyId<TRecipe extends Recipe>(args: {\n mediaBuyId: string | undefined;\n store: ProposalStore<TRecipe> | undefined;\n ctx: { account: { id: string } };\n packages?: ReadonlyArray<unknown>;\n}): Promise<ProposalRecord<TRecipe> | null> {\n const { mediaBuyId, store, ctx, packages } = args;\n if (!mediaBuyId || !store) return null;\n const record = await store.getByMediaBuyId(mediaBuyId, {\n expectedAccountId: ctx.account.id,\n });\n if (!record) return null;\n if (packages && Array.isArray(packages) && packages.length > 0) {\n validateCapabilityOverlap({\n packages: packages as never,\n recipes: record.recipes,\n });\n }\n return record;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../../../src/lib/server/decisioning/proposal/dispatch.ts"],"sourcesContent":["/**\n * Proposal-dispatch — the v1.5 framework intercept seams.\n *\n * Pure functions that the runtime calls from the wire-handler shims\n * (`getProducts`, `createMediaBuy`, `updateMediaBuy`,\n * `getMediaBuyDelivery`). Each helper short-circuits when no manager /\n * store is wired for the request, so the v1 path is preserved\n * byte-for-byte.\n *\n * Five integration points (parity with `adcp-client-python.src/adcp/decisioning/proposal_dispatch.py`):\n *\n * - {@link maybeInterceptFinalize} — `getProducts` shim. Detects\n * `refine[i].action: 'finalize'`, calls the manager's\n * `finalizeProposal`, commits the proposal, returns the wire\n * response. Returns `null` when no finalize entry / no\n * finalize-capable manager.\n * - {@link maybePersistDraftAfterGetProducts} — `getProducts` shim\n * (post-call). Walks the response's `proposals[]`, validates\n * `overlap ⊆ wire`, persists each as a draft.\n * - {@link maybeReserveProposalForCreateMediaBuy} — `createMediaBuy`\n * shim. When `proposal_id` is set, validates expiry + capability\n * overlap, reserves the proposal (`COMMITTED → CONSUMING`), returns\n * the recipes map for `ctx.recipes` hydration.\n * - {@link finalizeProposalConsumption} — `createMediaBuy` shim\n * (post-success). Promotes `CONSUMING → CONSUMED` and records the\n * `mediaBuyId` back-reference.\n * - {@link releaseProposalReservation} — `createMediaBuy` shim\n * (failure rollback). Restores `CONSUMING → COMMITTED`.\n * - {@link maybeHydrateRecipesForMediaBuyId} —\n * `updateMediaBuy` / `getMediaBuyDelivery` shims. Reverse-index\n * hydration via `getByMediaBuyId`. Re-runs capability-overlap\n * validation per Resolutions §5.\n *\n * @public\n * @packageDocumentation\n */\n\nimport { AdcpError, isTaskHandoff, type TaskHandoff } from '../async-outcome';\nimport type { Product, Proposal } from '../../../types/tools.generated';\nimport type { CanonicalGetProductsRequest } from '../../../v2/projection/creative-delivery';\nimport {\n detectFinalizeAction,\n enforceProposalExpiry,\n logConsumed,\n logDraftPersisted,\n logFinalizeSucceeded,\n validateCapabilityOverlap,\n validateOverlapSubsetOfWire,\n} from './lifecycle';\nimport type { ProposalRecord, ProposalStore } from './store';\nimport type {\n FinalizeProposalRequest,\n FinalizeProposalSuccess,\n ProposalGetProductsPayload,\n ProposalManager,\n Recipe,\n} from './types';\n\n// ---------------------------------------------------------------------------\n// Capability detection\n// ---------------------------------------------------------------------------\n\n/**\n * Per Resolutions §7: framework detects finalize via method-presence +\n * `capabilities.finalize` flag. Adopters opt in by both implementing\n * AND declaring; flipping only one half is an adopter bug.\n */\nfunction hasFinalizeCapability<TRecipe extends Recipe, TCtxMeta>(\n manager: ProposalManager<TRecipe, TCtxMeta> | undefined\n): boolean {\n if (!manager) return false;\n if (!manager.capabilities.finalize) return false;\n return typeof manager.finalizeProposal === 'function';\n}\n\n// ---------------------------------------------------------------------------\n// getProducts — finalize interception\n// ---------------------------------------------------------------------------\n\n/**\n * Result of {@link maybeInterceptFinalize}.\n *\n * - `kind: 'intercepted'` — adopter's `finalizeProposal` was called.\n * Caller threads `result` and `project` through the framework's\n * standard `routeIfHandoff` so the projection callback fires\n * synchronously for sync `FinalizeProposalSuccess` returns AND\n * inside the background task for `TaskHandoff<FinalizeProposalSuccess>`\n * returns. The projection commits the proposal store, emits the\n * `proposal.finalized` log, and shapes the wire response.\n * - `kind: 'pass'` — no finalize entry / no finalize-capable manager;\n * caller continues with the standard `getProducts` dispatch.\n *\n * No special-case handling: finalize HITL inherits the framework's\n * task-lifecycle posture (cancellation, restart-via-durable-store,\n * deadline, webhook delivery) from `routeIfHandoff`, the same machinery\n * that drives `createMediaBuy` HITL and `syncCreatives` HITL.\n *\n * @public\n */\nexport type FinalizeInterceptResult<TRecipe extends Recipe = Recipe> =\n | {\n kind: 'intercepted';\n result: FinalizeProposalSuccess<TRecipe> | TaskHandoff<FinalizeProposalSuccess<TRecipe>>;\n project: (success: FinalizeProposalSuccess<TRecipe>) => Promise<ProposalGetProductsPayload>;\n }\n | { kind: 'pass' };\n\n/**\n * Intercept `buying_mode: 'refine'` requests carrying a\n * `refine[i].action: 'finalize'` entry. Hydrates the draft, calls the\n * manager's `finalizeProposal`, and returns the raw adopter result + a\n * projection callback. The runtime threads them through the framework's\n * standard `routeIfHandoff` so:\n *\n * - Sync `FinalizeProposalSuccess` returns: projection runs inline,\n * buyer sees the committed proposal in the response.\n * - HITL `TaskHandoff<FinalizeProposalSuccess>` returns: framework\n * returns the Submitted envelope to the buyer, runs the adopter's\n * handoff fn in background; projection runs when the handoff\n * resolves, commits the proposal, projects the terminal task\n * artifact.\n *\n * Same machinery as `createMediaBuy` HITL — finalize inherits whatever\n * cancellation, restart-via-durable-store, deadline, and webhook\n * delivery semantics the framework's task lifecycle provides for every\n * other unified-hybrid tool. No special-case wrapper.\n *\n * @public\n */\nexport async function maybeInterceptFinalize<TRecipe extends Recipe, TCtxMeta>(args: {\n request: CanonicalGetProductsRequest;\n manager: ProposalManager<TRecipe, TCtxMeta> | undefined;\n store: ProposalStore<TRecipe> | undefined;\n ctx: { account: { id: string } } & Record<string, unknown>;\n}): Promise<FinalizeInterceptResult<TRecipe>> {\n const { request, manager, store, ctx } = args;\n const finalizeEntry = detectFinalizeAction(request);\n if (!finalizeEntry) return { kind: 'pass' };\n if (!hasFinalizeCapability(manager) || !store) return { kind: 'pass' };\n\n const fieldPath = `refine[${finalizeEntry.index}].proposal_id`;\n const accountId = ctx.account.id;\n const record = await store.get(finalizeEntry.proposalId, { expectedAccountId: accountId });\n if (!record) {\n throw new AdcpError('PROPOSAL_NOT_FOUND', {\n recovery: 'correctable',\n message:\n `Proposal ${JSON.stringify(finalizeEntry.proposalId)} not found. The buyer must call ` +\n `get_products with buying_mode='brief' or 'refine' to obtain a draft proposal_id ` +\n `before finalizing it.`,\n field: fieldPath,\n });\n }\n if (record.state !== 'draft') {\n throw new AdcpError('PROPOSAL_NOT_COMMITTED', {\n recovery: 'correctable',\n message:\n `Proposal ${JSON.stringify(finalizeEntry.proposalId)} is in state ` +\n `${JSON.stringify(record.state)}; only draft proposals can be finalized. ` +\n `Already-committed proposals should be accepted via create_media_buy(proposal_id=...) ` +\n `directly.`,\n field: fieldPath,\n });\n }\n\n const finalizeReq: FinalizeProposalRequest<TRecipe> = {\n proposalId: finalizeEntry.proposalId,\n recipes: record.recipes,\n proposalPayload: record.proposalPayload,\n parentRequest: request,\n ...(finalizeEntry.ask !== undefined && { ask: finalizeEntry.ask }),\n };\n\n // manager + finalizeProposal are non-undefined per hasFinalizeCapability.\n const result = await manager!.finalizeProposal!(finalizeReq, ctx as never);\n // Pre-determine the path label by checking whether the adopter\n // returned a TaskHandoff up-front. `routeIfHandoff` runs `project`\n // synchronously for the sync arm and inside the background task for\n // the handoff arm; either way, we want the log to reflect which arm\n // the adopter chose.\n const path: 'inline' | 'handoff' = isTaskHandoff(result) ? 'handoff' : 'inline';\n const finalizeProposalId = finalizeEntry.proposalId;\n\n const project = async (success: FinalizeProposalSuccess<TRecipe>): Promise<ProposalGetProductsPayload> => {\n if (!isFinalizeSuccess<TRecipe>(success)) {\n throw new AdcpError('INTERNAL_ERROR', {\n recovery: 'terminal',\n message:\n `finalizeProposal resolved to an unexpected shape; expected FinalizeProposalSuccess ` +\n `with 'proposal' and 'expiresAt' fields.`,\n });\n }\n await store.commit(finalizeProposalId, {\n expiresAt: success.expiresAt,\n proposalPayload: success.proposal,\n });\n logFinalizeSucceeded({\n proposalId: finalizeProposalId,\n accountId,\n expiresAt: success.expiresAt,\n path,\n });\n return projectFinalizeResponse({\n request,\n committedProposal: success.proposal,\n finalizeProposalId,\n });\n };\n\n return { kind: 'intercepted', result, project };\n}\n\nfunction isFinalizeSuccess<TRecipe extends Recipe>(v: unknown): v is FinalizeProposalSuccess<TRecipe> {\n if (!v || typeof v !== 'object') return false;\n const r = v as Record<string, unknown>;\n return r.proposal != null && r.expiresAt instanceof Date;\n}\n\nfunction projectFinalizeResponse(args: {\n request: CanonicalGetProductsRequest;\n committedProposal: Record<string, unknown>;\n finalizeProposalId: string;\n}): ProposalGetProductsPayload {\n const refineEntries = (args.request as { refine?: ReadonlyArray<Record<string, unknown>> }).refine;\n const refinementApplied: Array<Record<string, unknown>> = [];\n if (refineEntries) {\n for (const entry of refineEntries) {\n const scope = entry.scope;\n if (scope === 'proposal') {\n refinementApplied.push({\n scope: 'proposal',\n proposal_id: typeof entry.proposal_id === 'string' ? entry.proposal_id : args.finalizeProposalId,\n status: 'applied',\n });\n } else if (scope === 'product') {\n refinementApplied.push({\n scope: 'product',\n product_id: typeof entry.product_id === 'string' ? entry.product_id : '',\n status: 'applied',\n });\n } else {\n refinementApplied.push({ scope: 'request', status: 'applied' });\n }\n }\n }\n // **Decision: `products: []` on the finalize response by design.** The\n // committed proposal carries `allocations[]` keyed by `product_id`;\n // the buyer either has the products from the prior `brief_with_proposals`\n // step (the natural flow) or fetches by id. Echoing the products here\n // would require the framework to either re-emit from the persisted\n // draft (extra wire bytes the buyer already has) or call back into the\n // adopter (extra round-trip to upstream). Buyers who explicitly want\n // products on the finalize response can fetch them via a follow-up\n // `get_products({ product_ids: [...] })` keyed off\n // `proposals[0].allocations[].product_id`.\n return {\n cache_scope: 'account',\n products: [],\n proposals: [args.committedProposal as unknown as Proposal],\n refinement_applied: refinementApplied,\n } as unknown as ProposalGetProductsPayload;\n}\n\n// ---------------------------------------------------------------------------\n// getProducts / refineProducts — post-call draft persistence\n// ---------------------------------------------------------------------------\n\n/**\n * Persist proposals returned by `getProducts` / `refineProducts` as\n * drafts in the wired {@link ProposalStore}. Validates `overlap ⊆ wire`\n * for any returned recipes before persisting.\n *\n * Quietly returns when no store is wired, no `proposals[]` in the\n * response, or no typed recipes attached to products.\n *\n * @public\n */\nexport async function maybePersistDraftAfterGetProducts<TRecipe extends Recipe>(args: {\n response: ProposalGetProductsPayload;\n store: ProposalStore<TRecipe> | undefined;\n ctx: { account: { id: string } };\n}): Promise<void> {\n const { response, store, ctx } = args;\n if (!store) return;\n const proposals = (response as { proposals?: ReadonlyArray<Proposal> }).proposals;\n if (!proposals || proposals.length === 0) return;\n const products = (response as { products?: ReadonlyArray<Product> }).products ?? [];\n\n for (const proposal of proposals) {\n const proposalId = (proposal as { proposal_id?: string }).proposal_id;\n if (!proposalId) continue;\n const proposalPayload = toPlainObject(proposal);\n const recipes = collectRecipesFromProducts<TRecipe>(products, proposalPayload);\n if (recipes.size > 0) {\n validateOverlapSubsetOfWire({ recipes, products });\n }\n await store.putDraft({\n proposalId,\n accountId: ctx.account.id,\n recipes,\n proposalPayload,\n });\n logDraftPersisted({\n proposalId,\n accountId: ctx.account.id,\n recipesCount: recipes.size,\n });\n }\n}\n\nfunction collectRecipesFromProducts<TRecipe extends Recipe>(\n products: readonly Product[],\n proposalPayload: Record<string, unknown>\n): Map<string, TRecipe> {\n // Filter to products referenced by the proposal's allocations[] (per\n // spec) or legacy products[] string array when present.\n let referenced: Set<string> | null = null;\n const allocations = proposalPayload.allocations;\n if (Array.isArray(allocations)) {\n referenced = new Set(\n allocations\n .map((a: unknown) => (a as { product_id?: string }).product_id)\n .filter((id): id is string => typeof id === 'string')\n );\n } else if (Array.isArray(proposalPayload.products)) {\n referenced = new Set((proposalPayload.products as unknown[]).filter((p): p is string => typeof p === 'string'));\n }\n\n const recipes = new Map<string, TRecipe>();\n for (const product of products) {\n const productId = product.product_id;\n if (!productId) continue;\n if (referenced && !referenced.has(productId)) continue;\n const implConfig = (product as { implementation_config?: unknown }).implementation_config;\n if (isRecipe(implConfig)) {\n recipes.set(productId, implConfig as TRecipe);\n }\n }\n return recipes;\n}\n\nfunction isRecipe(v: unknown): boolean {\n return v != null && typeof v === 'object' && typeof (v as { recipe_kind?: unknown }).recipe_kind === 'string';\n}\n\nfunction toPlainObject(v: unknown): Record<string, unknown> {\n if (v == null || typeof v !== 'object') return {};\n // Deep clone via JSON to drop class-instance prototypes — the stored\n // payload must round-trip through a durable backing.\n return JSON.parse(JSON.stringify(v)) as Record<string, unknown>;\n}\n\n// ---------------------------------------------------------------------------\n// createMediaBuy — recipe hydration on proposal_id\n// ---------------------------------------------------------------------------\n\n/**\n * Result of {@link maybeReserveProposalForCreateMediaBuy}.\n *\n * - `record` — the reserved {@link ProposalRecord}. Caller threads\n * `recipes` onto `ctx.recipes` and calls\n * {@link finalizeProposalConsumption} on adapter success or\n * {@link releaseProposalReservation} on failure.\n * - `null` — request has no `proposal_id`, no store wired, or the\n * framework didn't intercept (v1 path).\n *\n * @public\n */\nexport type ReservedProposal<TRecipe extends Recipe> = ProposalRecord<TRecipe>;\n\n/**\n * Reserve the proposal for consumption and hydrate `ctx.recipes`.\n *\n * Atomic CAS via {@link ProposalStore.tryReserveConsumption} — the\n * proposal transitions `COMMITTED → CONSUMING` before the adapter runs.\n * Two parallel `createMediaBuy(proposal_id=X)` calls cannot both\n * reserve; the loser raises `PROPOSAL_NOT_COMMITTED`.\n *\n * Validates per § D7 (expiry) BEFORE reserving — an expired proposal\n * surfaces `PROPOSAL_EXPIRED` without flipping the state.\n *\n * @public\n */\nexport async function maybeReserveProposalForCreateMediaBuy<TRecipe extends Recipe, TCtxMeta>(args: {\n request: { proposal_id?: string; packages?: ReadonlyArray<unknown> } & Record<string, unknown>;\n manager: ProposalManager<TRecipe, TCtxMeta> | undefined;\n store: ProposalStore<TRecipe> | undefined;\n ctx: { account: { id: string } };\n now?: Date;\n}): Promise<ReservedProposal<TRecipe> | null> {\n const { request, manager, store, ctx, now } = args;\n const proposalId = request.proposal_id;\n if (!proposalId || !store) return null;\n const graceSeconds = manager?.capabilities.expiresAtGraceSeconds ?? 0;\n\n // Expiry check BEFORE reserving — keep the buyer's slot uncommitted\n // when telling them they're expired.\n await enforceProposalExpiry(proposalId, {\n proposalStore: store,\n expectedAccountId: ctx.account.id,\n graceSeconds,\n ...(now && { now }),\n });\n\n const reserved = await store.tryReserveConsumption(proposalId, {\n expectedAccountId: ctx.account.id,\n });\n\n // Capability-overlap gate per D4. Buyer's packages may be empty when\n // proposal_id is set (the spec allows the seller to derive packages\n // from allocations); skip the gate in that case.\n const packages = request.packages;\n if (packages && Array.isArray(packages) && packages.length > 0) {\n validateCapabilityOverlap({\n packages: packages as never,\n recipes: reserved.recipes,\n });\n }\n\n return reserved;\n}\n\n/**\n * Promote `CONSUMING → CONSUMED` after the adapter's `createMediaBuy`\n * succeeded.\n *\n * @public\n */\nexport async function finalizeProposalConsumption<TRecipe extends Recipe>(args: {\n store: ProposalStore<TRecipe> | undefined;\n record: ReservedProposal<TRecipe>;\n mediaBuyId: string;\n}): Promise<void> {\n const { store, record, mediaBuyId } = args;\n if (!store) return;\n await store.finalizeConsumption(record.proposalId, {\n mediaBuyId,\n expectedAccountId: record.accountId,\n });\n logConsumed({\n proposalId: record.proposalId,\n accountId: record.accountId,\n mediaBuyId,\n });\n}\n\n/**\n * Roll back the reservation: `CONSUMING → COMMITTED`. Best-effort.\n *\n * @public\n */\nexport async function releaseProposalReservation<TRecipe extends Recipe>(args: {\n store: ProposalStore<TRecipe> | undefined;\n record: ReservedProposal<TRecipe>;\n logger?: { warn(message: string): void };\n}): Promise<void> {\n const { store, record, logger } = args;\n if (!store) return;\n try {\n await store.releaseConsumption(record.proposalId, {\n expectedAccountId: record.accountId,\n });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n logger?.warn(\n `[adcp/decisioning] failed to release consumption reservation for proposal ` +\n `${record.proposalId}: ${message}. The record may stay in CONSUMING until eviction.`\n );\n }\n}\n\n// ---------------------------------------------------------------------------\n// updateMediaBuy / getMediaBuyDelivery — reverse-index recipe hydration\n// ---------------------------------------------------------------------------\n\n/**\n * Hydrate `ctx.recipes` for post-acceptance buy operations via the\n * `getByMediaBuyId` reverse-index. Returns the record (so the caller\n * can re-validate overlap if a packages-shaped patch is provided);\n * returns `null` when no proposal backs this buy.\n *\n * Per Resolutions §5: re-validates capability overlap on every call\n * with packages.\n *\n * @public\n */\nexport async function maybeHydrateRecipesForMediaBuyId<TRecipe extends Recipe>(args: {\n mediaBuyId: string | undefined;\n store: ProposalStore<TRecipe> | undefined;\n ctx: { account: { id: string } };\n packages?: ReadonlyArray<unknown>;\n}): Promise<ProposalRecord<TRecipe> | null> {\n const { mediaBuyId, store, ctx, packages } = args;\n if (!mediaBuyId || !store) return null;\n const record = await store.getByMediaBuyId(mediaBuyId, {\n expectedAccountId: ctx.account.id,\n });\n if (!record) return null;\n if (packages && Array.isArray(packages) && packages.length > 0) {\n validateCapabilityOverlap({\n packages: packages as never,\n recipes: record.recipes,\n });\n }\n return record;\n}\n"],"mappings":"AAqCA,SAAS,WAAW,qBAAuC;AAG3D;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAmBP,SAAS,sBACP,SACS;AACT,MAAI,CAAC,QAAS,QAAO;AACrB,MAAI,CAAC,QAAQ,aAAa,SAAU,QAAO;AAC3C,SAAO,OAAO,QAAQ,qBAAqB;AAC7C;AAwDA,eAAsB,uBAAyD,MAKjC;AAC5C,QAAM,EAAE,SAAS,SAAS,OAAO,IAAI,IAAI;AACzC,QAAM,gBAAgB,qBAAqB,OAAO;AAClD,MAAI,CAAC,cAAe,QAAO,EAAE,MAAM,OAAO;AAC1C,MAAI,CAAC,sBAAsB,OAAO,KAAK,CAAC,MAAO,QAAO,EAAE,MAAM,OAAO;AAErE,QAAM,YAAY,UAAU,cAAc,KAAK;AAC/C,QAAM,YAAY,IAAI,QAAQ;AAC9B,QAAM,SAAS,MAAM,MAAM,IAAI,cAAc,YAAY,EAAE,mBAAmB,UAAU,CAAC;AACzF,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI,UAAU,sBAAsB;AAAA,MACxC,UAAU;AAAA,MACV,SACE,YAAY,KAAK,UAAU,cAAc,UAAU,CAAC;AAAA,MAGtD,OAAO;AAAA,IACT,CAAC;AAAA,EACH;AACA,MAAI,OAAO,UAAU,SAAS;AAC5B,UAAM,IAAI,UAAU,0BAA0B;AAAA,MAC5C,UAAU;AAAA,MACV,SACE,YAAY,KAAK,UAAU,cAAc,UAAU,CAAC,gBACjD,KAAK,UAAU,OAAO,KAAK,CAAC;AAAA,MAGjC,OAAO;AAAA,IACT,CAAC;AAAA,EACH;AAEA,QAAM,cAAgD;AAAA,IACpD,YAAY,cAAc;AAAA,IAC1B,SAAS,OAAO;AAAA,IAChB,iBAAiB,OAAO;AAAA,IACxB,eAAe;AAAA,IACf,GAAI,cAAc,QAAQ,UAAa,EAAE,KAAK,cAAc,IAAI;AAAA,EAClE;AAGA,QAAM,SAAS,MAAM,QAAS,iBAAkB,aAAa,GAAY;AAMzE,QAAM,OAA6B,cAAc,MAAM,IAAI,YAAY;AACvE,QAAM,qBAAqB,cAAc;AAEzC,QAAM,UAAU,OAAO,YAAmF;AACxG,QAAI,CAAC,kBAA2B,OAAO,GAAG;AACxC,YAAM,IAAI,UAAU,kBAAkB;AAAA,QACpC,UAAU;AAAA,QACV,SACE;AAAA,MAEJ,CAAC;AAAA,IACH;AACA,UAAM,MAAM,OAAO,oBAAoB;AAAA,MACrC,WAAW,QAAQ;AAAA,MACnB,iBAAiB,QAAQ;AAAA,IAC3B,CAAC;AACD,yBAAqB;AAAA,MACnB,YAAY;AAAA,MACZ;AAAA,MACA,WAAW,QAAQ;AAAA,MACnB;AAAA,IACF,CAAC;AACD,WAAO,wBAAwB;AAAA,MAC7B;AAAA,MACA,mBAAmB,QAAQ;AAAA,MAC3B;AAAA,IACF,CAAC;AAAA,EACH;AAEA,SAAO,EAAE,MAAM,eAAe,QAAQ,QAAQ;AAChD;AAEA,SAAS,kBAA0C,GAAmD;AACpG,MAAI,CAAC,KAAK,OAAO,MAAM,SAAU,QAAO;AACxC,QAAM,IAAI;AACV,SAAO,EAAE,YAAY,QAAQ,EAAE,qBAAqB;AACtD;AAEA,SAAS,wBAAwB,MAIF;AAC7B,QAAM,gBAAiB,KAAK,QAAgE;AAC5F,QAAM,oBAAoD,CAAC;AAC3D,MAAI,eAAe;AACjB,eAAW,SAAS,eAAe;AACjC,YAAM,QAAQ,MAAM;AACpB,UAAI,UAAU,YAAY;AACxB,0BAAkB,KAAK;AAAA,UACrB,OAAO;AAAA,UACP,aAAa,OAAO,MAAM,gBAAgB,WAAW,MAAM,cAAc,KAAK;AAAA,UAC9E,QAAQ;AAAA,QACV,CAAC;AAAA,MACH,WAAW,UAAU,WAAW;AAC9B,0BAAkB,KAAK;AAAA,UACrB,OAAO;AAAA,UACP,YAAY,OAAO,MAAM,eAAe,WAAW,MAAM,aAAa;AAAA,UACtE,QAAQ;AAAA,QACV,CAAC;AAAA,MACH,OAAO;AACL,0BAAkB,KAAK,EAAE,OAAO,WAAW,QAAQ,UAAU,CAAC;AAAA,MAChE;AAAA,IACF;AAAA,EACF;AAWA,SAAO;AAAA,IACL,aAAa;AAAA,IACb,UAAU,CAAC;AAAA,IACX,WAAW,CAAC,KAAK,iBAAwC;AAAA,IACzD,oBAAoB;AAAA,EACtB;AACF;AAgBA,eAAsB,kCAA0D,MAI9D;AAChB,QAAM,EAAE,UAAU,OAAO,IAAI,IAAI;AACjC,MAAI,CAAC,MAAO;AACZ,QAAM,YAAa,SAAqD;AACxE,MAAI,CAAC,aAAa,UAAU,WAAW,EAAG;AAC1C,QAAM,WAAY,SAAmD,YAAY,CAAC;AAElF,aAAW,YAAY,WAAW;AAChC,UAAM,aAAc,SAAsC;AAC1D,QAAI,CAAC,WAAY;AACjB,UAAM,kBAAkB,cAAc,QAAQ;AAC9C,UAAM,UAAU,2BAAoC,UAAU,eAAe;AAC7E,QAAI,QAAQ,OAAO,GAAG;AACpB,kCAA4B,EAAE,SAAS,SAAS,CAAC;AAAA,IACnD;AACA,UAAM,MAAM,SAAS;AAAA,MACnB;AAAA,MACA,WAAW,IAAI,QAAQ;AAAA,MACvB;AAAA,MACA;AAAA,IACF,CAAC;AACD,sBAAkB;AAAA,MAChB;AAAA,MACA,WAAW,IAAI,QAAQ;AAAA,MACvB,cAAc,QAAQ;AAAA,IACxB,CAAC;AAAA,EACH;AACF;AAEA,SAAS,2BACP,UACA,iBACsB;AAGtB,MAAI,aAAiC;AACrC,QAAM,cAAc,gBAAgB;AACpC,MAAI,MAAM,QAAQ,WAAW,GAAG;AAC9B,iBAAa,IAAI;AAAA,MACf,YACG,IAAI,CAAC,MAAgB,EAA8B,UAAU,EAC7D,OAAO,CAAC,OAAqB,OAAO,OAAO,QAAQ;AAAA,IACxD;AAAA,EACF,WAAW,MAAM,QAAQ,gBAAgB,QAAQ,GAAG;AAClD,iBAAa,IAAI,IAAK,gBAAgB,SAAuB,OAAO,CAAC,MAAmB,OAAO,MAAM,QAAQ,CAAC;AAAA,EAChH;AAEA,QAAM,UAAU,oBAAI,IAAqB;AACzC,aAAW,WAAW,UAAU;AAC9B,UAAM,YAAY,QAAQ;AAC1B,QAAI,CAAC,UAAW;AAChB,QAAI,cAAc,CAAC,WAAW,IAAI,SAAS,EAAG;AAC9C,UAAM,aAAc,QAAgD;AACpE,QAAI,SAAS,UAAU,GAAG;AACxB,cAAQ,IAAI,WAAW,UAAqB;AAAA,IAC9C;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,SAAS,GAAqB;AACrC,SAAO,KAAK,QAAQ,OAAO,MAAM,YAAY,OAAQ,EAAgC,gBAAgB;AACvG;AAEA,SAAS,cAAc,GAAqC;AAC1D,MAAI,KAAK,QAAQ,OAAO,MAAM,SAAU,QAAO,CAAC;AAGhD,SAAO,KAAK,MAAM,KAAK,UAAU,CAAC,CAAC;AACrC;AAiCA,eAAsB,sCAAwE,MAMhD;AAC5C,QAAM,EAAE,SAAS,SAAS,OAAO,KAAK,IAAI,IAAI;AAC9C,QAAM,aAAa,QAAQ;AAC3B,MAAI,CAAC,cAAc,CAAC,MAAO,QAAO;AAClC,QAAM,eAAe,SAAS,aAAa,yBAAyB;AAIpE,QAAM,sBAAsB,YAAY;AAAA,IACtC,eAAe;AAAA,IACf,mBAAmB,IAAI,QAAQ;AAAA,IAC/B;AAAA,IACA,GAAI,OAAO,EAAE,IAAI;AAAA,EACnB,CAAC;AAED,QAAM,WAAW,MAAM,MAAM,sBAAsB,YAAY;AAAA,IAC7D,mBAAmB,IAAI,QAAQ;AAAA,EACjC,CAAC;AAKD,QAAM,WAAW,QAAQ;AACzB,MAAI,YAAY,MAAM,QAAQ,QAAQ,KAAK,SAAS,SAAS,GAAG;AAC9D,8BAA0B;AAAA,MACxB;AAAA,MACA,SAAS,SAAS;AAAA,IACpB,CAAC;AAAA,EACH;AAEA,SAAO;AACT;AAQA,eAAsB,4BAAoD,MAIxD;AAChB,QAAM,EAAE,OAAO,QAAQ,WAAW,IAAI;AACtC,MAAI,CAAC,MAAO;AACZ,QAAM,MAAM,oBAAoB,OAAO,YAAY;AAAA,IACjD;AAAA,IACA,mBAAmB,OAAO;AAAA,EAC5B,CAAC;AACD,cAAY;AAAA,IACV,YAAY,OAAO;AAAA,IACnB,WAAW,OAAO;AAAA,IAClB;AAAA,EACF,CAAC;AACH;AAOA,eAAsB,2BAAmD,MAIvD;AAChB,QAAM,EAAE,OAAO,QAAQ,OAAO,IAAI;AAClC,MAAI,CAAC,MAAO;AACZ,MAAI;AACF,UAAM,MAAM,mBAAmB,OAAO,YAAY;AAAA,MAChD,mBAAmB,OAAO;AAAA,IAC5B,CAAC;AAAA,EACH,SAAS,KAAK;AACZ,UAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,YAAQ;AAAA,MACN,6EACK,OAAO,UAAU,KAAK,OAAO;AAAA,IACpC;AAAA,EACF;AACF;AAiBA,eAAsB,iCAAyD,MAKnC;AAC1C,QAAM,EAAE,YAAY,OAAO,KAAK,SAAS,IAAI;AAC7C,MAAI,CAAC,cAAc,CAAC,MAAO,QAAO;AAClC,QAAM,SAAS,MAAM,MAAM,gBAAgB,YAAY;AAAA,IACrD,mBAAmB,IAAI,QAAQ;AAAA,EACjC,CAAC;AACD,MAAI,CAAC,OAAQ,QAAO;AACpB,MAAI,YAAY,MAAM,QAAQ,QAAQ,KAAK,SAAS,SAAS,GAAG;AAC9D,8BAA0B;AAAA,MACxB;AAAA,MACA,SAAS,OAAO;AAAA,IAClB,CAAC;AAAA,EACH;AACA,SAAO;AACT;","names":[]}
@@ -8,7 +8,7 @@
8
8
  * @public
9
9
  * @packageDocumentation
10
10
  */
11
- export type { ProposalManager, ProposalCapabilities, ProposalSalesSpecialism, Recipe, CapabilityOverlap, FinalizeProposalRequest, FinalizeProposalSuccess, } from './types.mjs';
11
+ export type { ProposalManager, ProposalCapabilities, ProposalSalesSpecialism, Recipe, CapabilityOverlap, FinalizeProposalRequest, FinalizeProposalSuccess, ProposalGetProductsPayload, LegacyProposalGetProductsPayload, } from './types.mjs';
12
12
  export { validateProposalCapabilities } from './types.mjs';
13
13
  export type { ProposalState, ProposalRecord, ProposalStore, InMemoryProposalStoreOptions } from './store.mjs';
14
14
  export { InMemoryProposalStore } from './store.mjs';
@@ -8,7 +8,7 @@
8
8
  * @public
9
9
  * @packageDocumentation
10
10
  */
11
- export type { ProposalManager, ProposalCapabilities, ProposalSalesSpecialism, Recipe, CapabilityOverlap, FinalizeProposalRequest, FinalizeProposalSuccess, } from './types';
11
+ export type { ProposalManager, ProposalCapabilities, ProposalSalesSpecialism, Recipe, CapabilityOverlap, FinalizeProposalRequest, FinalizeProposalSuccess, ProposalGetProductsPayload, LegacyProposalGetProductsPayload, } from './types';
12
12
  export { validateProposalCapabilities } from './types';
13
13
  export type { ProposalState, ProposalRecord, ProposalStore, InMemoryProposalStoreOptions } from './store';
14
14
  export { InMemoryProposalStore } from './store';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/lib/server/decisioning/proposal/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,YAAY,EACV,eAAe,EACf,oBAAoB,EACpB,uBAAuB,EACvB,MAAM,EACN,iBAAiB,EACjB,uBAAuB,EACvB,uBAAuB,GACxB,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,4BAA4B,EAAE,MAAM,SAAS,CAAC;AAEvD,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,aAAa,EAAE,4BAA4B,EAAE,MAAM,SAAS,CAAC;AAE1G,OAAO,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAEhD,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AACrD,YAAY,EAAE,0BAA0B,EAAE,MAAM,gBAAgB,CAAC;AAEjE,OAAO,EACL,qBAAqB,EACrB,yBAAyB,EACzB,2BAA2B,EAC3B,oBAAoB,EACpB,0BAA0B,EAC1B,iBAAiB,EACjB,oBAAoB,EACpB,UAAU,EACV,WAAW,GACZ,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAE9E,OAAO,EACL,sBAAsB,EACtB,iCAAiC,EACjC,qCAAqC,EACrC,2BAA2B,EAC3B,0BAA0B,EAC1B,gCAAgC,GACjC,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/lib/server/decisioning/proposal/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,YAAY,EACV,eAAe,EACf,oBAAoB,EACpB,uBAAuB,EACvB,MAAM,EACN,iBAAiB,EACjB,uBAAuB,EACvB,uBAAuB,EACvB,0BAA0B,EAC1B,gCAAgC,GACjC,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,4BAA4B,EAAE,MAAM,SAAS,CAAC;AAEvD,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,aAAa,EAAE,4BAA4B,EAAE,MAAM,SAAS,CAAC;AAE1G,OAAO,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAEhD,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AACrD,YAAY,EAAE,0BAA0B,EAAE,MAAM,gBAAgB,CAAC;AAEjE,OAAO,EACL,qBAAqB,EACrB,yBAAyB,EACzB,2BAA2B,EAC3B,oBAAoB,EACpB,0BAA0B,EAC1B,iBAAiB,EACjB,oBAAoB,EACpB,UAAU,EACV,WAAW,GACZ,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAE9E,OAAO,EACL,sBAAsB,EACtB,iCAAiC,EACjC,qCAAqC,EACrC,2BAA2B,EAC3B,0BAA0B,EAC1B,gCAAgC,GACjC,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../src/lib/server/decisioning/proposal/index.ts"],"sourcesContent":["/**\n * ProposalManager — primitives for the two-platform composition.\n *\n * Splits proposal assembly (`get_products`, refine, finalize) from media-buy\n * execution (`create_media_buy`, lifecycle), mirroring `adcp-client-python`'s\n * v1.5 ProposalManager. Either side can be mock-backed independently.\n *\n * @public\n * @packageDocumentation\n */\n\nexport type {\n ProposalManager,\n ProposalCapabilities,\n ProposalSalesSpecialism,\n Recipe,\n CapabilityOverlap,\n FinalizeProposalRequest,\n FinalizeProposalSuccess,\n} from './types';\n\nexport { validateProposalCapabilities } from './types';\n\nexport type { ProposalState, ProposalRecord, ProposalStore, InMemoryProposalStoreOptions } from './store';\n\nexport { InMemoryProposalStore } from './store';\n\nexport { MockProposalManager } from './mock-manager';\nexport type { MockProposalManagerOptions } from './mock-manager';\n\nexport {\n enforceProposalExpiry,\n validateCapabilityOverlap,\n validateOverlapSubsetOfWire,\n detectFinalizeAction,\n setProposalLifecycleLogger,\n logDraftPersisted,\n logFinalizeSucceeded,\n logExpired,\n logConsumed,\n} from './lifecycle';\nexport type { FinalizeActionRef, ProposalLifecycleLogger } from './lifecycle';\n\nexport {\n maybeInterceptFinalize,\n maybePersistDraftAfterGetProducts,\n maybeReserveProposalForCreateMediaBuy,\n finalizeProposalConsumption,\n releaseProposalReservation,\n maybeHydrateRecipesForMediaBuyId,\n} from './dispatch';\nexport type { FinalizeInterceptResult, ReservedProposal } from './dispatch';\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAqBA,mBAA6C;AAI7C,mBAAsC;AAEtC,0BAAoC;AAGpC,uBAUO;AAGP,sBAOO;","names":[]}
1
+ {"version":3,"sources":["../../../../../src/lib/server/decisioning/proposal/index.ts"],"sourcesContent":["/**\n * ProposalManager — primitives for the two-platform composition.\n *\n * Splits proposal assembly (`get_products`, refine, finalize) from media-buy\n * execution (`create_media_buy`, lifecycle), mirroring `adcp-client-python`'s\n * v1.5 ProposalManager. Either side can be mock-backed independently.\n *\n * @public\n * @packageDocumentation\n */\n\nexport type {\n ProposalManager,\n ProposalCapabilities,\n ProposalSalesSpecialism,\n Recipe,\n CapabilityOverlap,\n FinalizeProposalRequest,\n FinalizeProposalSuccess,\n ProposalGetProductsPayload,\n LegacyProposalGetProductsPayload,\n} from './types';\n\nexport { validateProposalCapabilities } from './types';\n\nexport type { ProposalState, ProposalRecord, ProposalStore, InMemoryProposalStoreOptions } from './store';\n\nexport { InMemoryProposalStore } from './store';\n\nexport { MockProposalManager } from './mock-manager';\nexport type { MockProposalManagerOptions } from './mock-manager';\n\nexport {\n enforceProposalExpiry,\n validateCapabilityOverlap,\n validateOverlapSubsetOfWire,\n detectFinalizeAction,\n setProposalLifecycleLogger,\n logDraftPersisted,\n logFinalizeSucceeded,\n logExpired,\n logConsumed,\n} from './lifecycle';\nexport type { FinalizeActionRef, ProposalLifecycleLogger } from './lifecycle';\n\nexport {\n maybeInterceptFinalize,\n maybePersistDraftAfterGetProducts,\n maybeReserveProposalForCreateMediaBuy,\n finalizeProposalConsumption,\n releaseProposalReservation,\n maybeHydrateRecipesForMediaBuyId,\n} from './dispatch';\nexport type { FinalizeInterceptResult, ReservedProposal } from './dispatch';\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAuBA,mBAA6C;AAI7C,mBAAsC;AAEtC,0BAAoC;AAGpC,uBAUO;AAGP,sBAOO;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../src/lib/server/decisioning/proposal/index.ts"],"sourcesContent":["/**\n * ProposalManager — primitives for the two-platform composition.\n *\n * Splits proposal assembly (`get_products`, refine, finalize) from media-buy\n * execution (`create_media_buy`, lifecycle), mirroring `adcp-client-python`'s\n * v1.5 ProposalManager. Either side can be mock-backed independently.\n *\n * @public\n * @packageDocumentation\n */\n\nexport type {\n ProposalManager,\n ProposalCapabilities,\n ProposalSalesSpecialism,\n Recipe,\n CapabilityOverlap,\n FinalizeProposalRequest,\n FinalizeProposalSuccess,\n} from './types';\n\nexport { validateProposalCapabilities } from './types';\n\nexport type { ProposalState, ProposalRecord, ProposalStore, InMemoryProposalStoreOptions } from './store';\n\nexport { InMemoryProposalStore } from './store';\n\nexport { MockProposalManager } from './mock-manager';\nexport type { MockProposalManagerOptions } from './mock-manager';\n\nexport {\n enforceProposalExpiry,\n validateCapabilityOverlap,\n validateOverlapSubsetOfWire,\n detectFinalizeAction,\n setProposalLifecycleLogger,\n logDraftPersisted,\n logFinalizeSucceeded,\n logExpired,\n logConsumed,\n} from './lifecycle';\nexport type { FinalizeActionRef, ProposalLifecycleLogger } from './lifecycle';\n\nexport {\n maybeInterceptFinalize,\n maybePersistDraftAfterGetProducts,\n maybeReserveProposalForCreateMediaBuy,\n finalizeProposalConsumption,\n releaseProposalReservation,\n maybeHydrateRecipesForMediaBuyId,\n} from './dispatch';\nexport type { FinalizeInterceptResult, ReservedProposal } from './dispatch';\n"],"mappings":"AAqBA,SAAS,oCAAoC;AAI7C,SAAS,6BAA6B;AAEtC,SAAS,2BAA2B;AAGpC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAGP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;","names":[]}
1
+ {"version":3,"sources":["../../../../../src/lib/server/decisioning/proposal/index.ts"],"sourcesContent":["/**\n * ProposalManager — primitives for the two-platform composition.\n *\n * Splits proposal assembly (`get_products`, refine, finalize) from media-buy\n * execution (`create_media_buy`, lifecycle), mirroring `adcp-client-python`'s\n * v1.5 ProposalManager. Either side can be mock-backed independently.\n *\n * @public\n * @packageDocumentation\n */\n\nexport type {\n ProposalManager,\n ProposalCapabilities,\n ProposalSalesSpecialism,\n Recipe,\n CapabilityOverlap,\n FinalizeProposalRequest,\n FinalizeProposalSuccess,\n ProposalGetProductsPayload,\n LegacyProposalGetProductsPayload,\n} from './types';\n\nexport { validateProposalCapabilities } from './types';\n\nexport type { ProposalState, ProposalRecord, ProposalStore, InMemoryProposalStoreOptions } from './store';\n\nexport { InMemoryProposalStore } from './store';\n\nexport { MockProposalManager } from './mock-manager';\nexport type { MockProposalManagerOptions } from './mock-manager';\n\nexport {\n enforceProposalExpiry,\n validateCapabilityOverlap,\n validateOverlapSubsetOfWire,\n detectFinalizeAction,\n setProposalLifecycleLogger,\n logDraftPersisted,\n logFinalizeSucceeded,\n logExpired,\n logConsumed,\n} from './lifecycle';\nexport type { FinalizeActionRef, ProposalLifecycleLogger } from './lifecycle';\n\nexport {\n maybeInterceptFinalize,\n maybePersistDraftAfterGetProducts,\n maybeReserveProposalForCreateMediaBuy,\n finalizeProposalConsumption,\n releaseProposalReservation,\n maybeHydrateRecipesForMediaBuyId,\n} from './dispatch';\nexport type { FinalizeInterceptResult, ReservedProposal } from './dispatch';\n"],"mappings":"AAuBA,SAAS,oCAAoC;AAI7C,SAAS,6BAA6B;AAEtC,SAAS,2BAA2B;AAGpC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAGP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;","names":[]}
@@ -26,7 +26,8 @@
26
26
  * @public
27
27
  * @packageDocumentation
28
28
  */
29
- import type { GetProductsRequest, Product } from '../../../types/tools.generated.mjs';
29
+ import type { Product } from '../../../types/tools.generated.mjs';
30
+ import type { CanonicalGetProductsRequest } from '../../../v2/projection/creative-delivery.mjs';
30
31
  import type { ProposalRecord, ProposalStore } from './store.mjs';
31
32
  import type { Recipe } from './types.mjs';
32
33
  /**
@@ -140,7 +141,7 @@ export interface FinalizeActionRef {
140
141
  *
141
142
  * @public
142
143
  */
143
- export declare function detectFinalizeAction(req: GetProductsRequest): FinalizeActionRef | null;
144
+ export declare function detectFinalizeAction(req: CanonicalGetProductsRequest): FinalizeActionRef | null;
144
145
  /**
145
146
  * Logger-shaped sink for structured proposal-lifecycle events. Defaults to
146
147
  * `console.info`-style emission; the dispatch path can pass a typed logger
@@ -26,7 +26,8 @@
26
26
  * @public
27
27
  * @packageDocumentation
28
28
  */
29
- import type { GetProductsRequest, Product } from '../../../types/tools.generated';
29
+ import type { Product } from '../../../types/tools.generated';
30
+ import type { CanonicalGetProductsRequest } from '../../../v2/projection/creative-delivery';
30
31
  import type { ProposalRecord, ProposalStore } from './store';
31
32
  import type { Recipe } from './types';
32
33
  /**
@@ -140,7 +141,7 @@ export interface FinalizeActionRef {
140
141
  *
141
142
  * @public
142
143
  */
143
- export declare function detectFinalizeAction(req: GetProductsRequest): FinalizeActionRef | null;
144
+ export declare function detectFinalizeAction(req: CanonicalGetProductsRequest): FinalizeActionRef | null;
144
145
  /**
145
146
  * Logger-shaped sink for structured proposal-lifecycle events. Defaults to
146
147
  * `console.info`-style emission; the dispatch path can pass a typed logger
@@ -1 +1 @@
1
- {"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../../../../src/lib/server/decisioning/proposal/lifecycle.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../../../../src/lib/server/decisioning/proposal/lifecycle.ts"],"names":[],"mappings":"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"}
@@ -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":[]}