@tangle-network/agent-app 0.43.56 → 0.43.58

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 (352) hide show
  1. package/dist/DesignCanvas-UNL34LS2.js +12 -0
  2. package/dist/DesignCanvasEditor-FJIVYOA2.js +13 -0
  3. package/dist/IntakeInterview-3ZIQMZS6.js +8 -0
  4. package/dist/InvitationsPanel-CJ7DR4F7.js +8 -0
  5. package/dist/MembersPanel-BB4OD2KB.js +8 -0
  6. package/dist/{TimelineEditor-I35SJJAJ.js → TimelineEditor-2ECKSU6P.js} +3 -3
  7. package/dist/{VaultPane-DrQ_tH6c.d.ts → VaultPane-BDP6SB_K.d.ts} +1 -0
  8. package/dist/{access-BZ81Btt5.d.ts → access-490O6aOU.d.ts} +16 -1
  9. package/dist/app-auth/index.d.ts +6 -1
  10. package/dist/app-auth/index.js +3 -3
  11. package/dist/app-auth/index.js.map +1 -1
  12. package/dist/{apply-Gk4raT2j.d.ts → apply-6wlMOLf8.d.ts} +30 -1
  13. package/dist/assets/index.d.ts +41 -0
  14. package/dist/assets/index.js +1 -1
  15. package/dist/assistant/index.d.ts +26 -6
  16. package/dist/assistant/index.js +9 -9
  17. package/dist/assistant/index.js.map +1 -1
  18. package/dist/{attachment-validation-D0csjCvz.d.ts → attachment-validation-tPRp-PP1.d.ts} +7 -1
  19. package/dist/{auth-DuptSkWh.d.ts → auth-_FU8w01b.d.ts} +4 -1
  20. package/dist/billing/index.d.ts +8 -0
  21. package/dist/billing/index.js +1 -1
  22. package/dist/brand/index.d.ts +2 -0
  23. package/dist/brand/index.js.map +1 -1
  24. package/dist/brand-extraction/index.d.ts +3 -0
  25. package/dist/brand-extraction/index.js.map +1 -1
  26. package/dist/catalog/index.d.ts +2 -0
  27. package/dist/catalog/index.js +1 -1
  28. package/dist/chat-routes/index.d.ts +41 -10
  29. package/dist/chat-routes/index.js +15 -15
  30. package/dist/chat-routes/index.js.map +1 -1
  31. package/dist/chat-store/index.d.ts +18 -4
  32. package/dist/chat-store/index.js +4 -4
  33. package/dist/chat-store/index.js.map +1 -1
  34. package/dist/{chunk-H5FM75MR.js → chunk-22GLESKE.js} +3 -3
  35. package/dist/chunk-22GLESKE.js.map +1 -0
  36. package/dist/{chunk-WEBBJBDH.js → chunk-2DRYTJHI.js} +1 -1
  37. package/dist/chunk-2DRYTJHI.js.map +1 -0
  38. package/dist/{chunk-7EVZUIHW.js → chunk-3EJ6SFJI.js} +1 -1
  39. package/dist/chunk-3EJ6SFJI.js.map +1 -0
  40. package/dist/{chunk-HRJJH3RX.js → chunk-3OFQFW5K.js} +4 -4
  41. package/dist/chunk-3OFQFW5K.js.map +1 -0
  42. package/dist/{chunk-7SUPA2YT.js → chunk-4KKXZQPT.js} +2 -2
  43. package/dist/chunk-4KKXZQPT.js.map +1 -0
  44. package/dist/{chunk-5PTGEJZL.js → chunk-5SD6HT2V.js} +1 -1
  45. package/dist/chunk-5SD6HT2V.js.map +1 -0
  46. package/dist/{chunk-7C64WKAH.js → chunk-5SXS3YAB.js} +2 -2
  47. package/dist/{chunk-L2TG5DBW.js → chunk-5TESA4SN.js} +1 -1
  48. package/dist/chunk-5TESA4SN.js.map +1 -0
  49. package/dist/{chunk-63CE7FEZ.js → chunk-6XIAPIW6.js} +1 -1
  50. package/dist/chunk-6XIAPIW6.js.map +1 -0
  51. package/dist/{chunk-MFRCM32T.js → chunk-7CTIUCQ4.js} +1 -1
  52. package/dist/chunk-7CTIUCQ4.js.map +1 -0
  53. package/dist/{chunk-6KVH5SP5.js → chunk-7YW6EBDX.js} +2 -2
  54. package/dist/chunk-7YW6EBDX.js.map +1 -0
  55. package/dist/{chunk-ZXNXAQAH.js → chunk-AP2UXKUK.js} +1 -1
  56. package/dist/chunk-AP2UXKUK.js.map +1 -0
  57. package/dist/{chunk-K63OWUDJ.js → chunk-BPSV5AA2.js} +2 -2
  58. package/dist/chunk-BPSV5AA2.js.map +1 -0
  59. package/dist/{chunk-MFI4ZM7W.js → chunk-BT4QFCTB.js} +1 -1
  60. package/dist/chunk-BT4QFCTB.js.map +1 -0
  61. package/dist/{chunk-ZYBWGSAZ.js → chunk-BVKQKGRK.js} +1 -1
  62. package/dist/chunk-BVKQKGRK.js.map +1 -0
  63. package/dist/{chunk-RVUISYUE.js → chunk-C3NSB66C.js} +7 -7
  64. package/dist/chunk-C3NSB66C.js.map +1 -0
  65. package/dist/{chunk-BQV42AFK.js → chunk-CCVWR33L.js} +1 -1
  66. package/dist/chunk-CCVWR33L.js.map +1 -0
  67. package/dist/{chunk-MCJSS6SM.js → chunk-CQZSAR77.js} +1 -1
  68. package/dist/chunk-CQZSAR77.js.map +1 -0
  69. package/dist/{chunk-3SVAA3MA.js → chunk-DJ4VJIH5.js} +1 -1
  70. package/dist/chunk-DJ4VJIH5.js.map +1 -0
  71. package/dist/{chunk-XKBIYSSM.js → chunk-DTSPS2HV.js} +3 -3
  72. package/dist/chunk-DTSPS2HV.js.map +1 -0
  73. package/dist/{chunk-4QN75UHX.js → chunk-E7N67FYI.js} +8 -8
  74. package/dist/chunk-E7N67FYI.js.map +1 -0
  75. package/dist/{chunk-2QI7XV2T.js → chunk-FBVLEGEG.js} +1 -1
  76. package/dist/{chunk-2QI7XV2T.js.map → chunk-FBVLEGEG.js.map} +1 -1
  77. package/dist/{chunk-5RMIUJDI.js → chunk-FKCB5FTD.js} +1 -1
  78. package/dist/chunk-FKCB5FTD.js.map +1 -0
  79. package/dist/{chunk-EEPJGZJW.js → chunk-GPBRL2XR.js} +1 -1
  80. package/dist/chunk-GPBRL2XR.js.map +1 -0
  81. package/dist/{chunk-G3HCU7TA.js → chunk-GXE6KHTS.js} +1 -1
  82. package/dist/chunk-GXE6KHTS.js.map +1 -0
  83. package/dist/{chunk-65P3HJY3.js → chunk-HCOROIRT.js} +1 -1
  84. package/dist/chunk-HCOROIRT.js.map +1 -0
  85. package/dist/{chunk-54GSK2KK.js → chunk-HYTIHYNV.js} +2 -2
  86. package/dist/chunk-HYTIHYNV.js.map +1 -0
  87. package/dist/{chunk-5MG74GVQ.js → chunk-IG46XCZM.js} +3 -3
  88. package/dist/chunk-IG46XCZM.js.map +1 -0
  89. package/dist/{chunk-ND3XEGBE.js → chunk-JDEGS53O.js} +1 -1
  90. package/dist/chunk-JDEGS53O.js.map +1 -0
  91. package/dist/{chunk-7W5XSTUF.js → chunk-JML7WKWU.js} +1 -1
  92. package/dist/chunk-JML7WKWU.js.map +1 -0
  93. package/dist/{chunk-Q4TKVF3L.js → chunk-JTLYXUEV.js} +1 -1
  94. package/dist/chunk-JTLYXUEV.js.map +1 -0
  95. package/dist/{chunk-CCRVDLV3.js → chunk-JWBZ74TW.js} +2 -2
  96. package/dist/chunk-JWBZ74TW.js.map +1 -0
  97. package/dist/{chunk-ZU5GNSOJ.js → chunk-K3LIPKFV.js} +3 -3
  98. package/dist/chunk-K3LIPKFV.js.map +1 -0
  99. package/dist/{chunk-GQRL6VNU.js → chunk-LGBO24FL.js} +1 -1
  100. package/dist/chunk-LGBO24FL.js.map +1 -0
  101. package/dist/{chunk-SWUVTGMR.js → chunk-MEUNTJL5.js} +3 -3
  102. package/dist/{chunk-SWUVTGMR.js.map → chunk-MEUNTJL5.js.map} +1 -1
  103. package/dist/{chunk-YNALYIQ3.js → chunk-NLSSFLLS.js} +1 -1
  104. package/dist/chunk-NLSSFLLS.js.map +1 -0
  105. package/dist/{chunk-HFC4BTWJ.js → chunk-PTUMBMVH.js} +1 -1
  106. package/dist/chunk-PTUMBMVH.js.map +1 -0
  107. package/dist/{chunk-R4NOKZMD.js → chunk-ROLFDOMJ.js} +3 -3
  108. package/dist/{chunk-NYATNLRK.js → chunk-RT5RKAFX.js} +1 -1
  109. package/dist/chunk-RT5RKAFX.js.map +1 -0
  110. package/dist/{chunk-GNL3MG5J.js → chunk-S564OFTL.js} +2 -2
  111. package/dist/{chunk-GUSCYE3S.js → chunk-TQOFR6XY.js} +1 -1
  112. package/dist/chunk-TQOFR6XY.js.map +1 -0
  113. package/dist/{chunk-47C6PJCW.js → chunk-TZO3SOUL.js} +1 -1
  114. package/dist/chunk-TZO3SOUL.js.map +1 -0
  115. package/dist/{chunk-YGUNTIT5.js → chunk-UKTYKG57.js} +1 -1
  116. package/dist/chunk-UKTYKG57.js.map +1 -0
  117. package/dist/{chunk-23LUHHHN.js → chunk-VCOD5AOR.js} +1 -1
  118. package/dist/chunk-VCOD5AOR.js.map +1 -0
  119. package/dist/{chunk-5I72FRZY.js → chunk-VOJB7MVG.js} +2 -2
  120. package/dist/{chunk-XWJXZ6WE.js → chunk-VTWKH4YU.js} +2 -2
  121. package/dist/chunk-VTWKH4YU.js.map +1 -0
  122. package/dist/{chunk-4K7BZYO7.js → chunk-VWAEWOWK.js} +3 -3
  123. package/dist/chunk-VWAEWOWK.js.map +1 -0
  124. package/dist/{chunk-M2EBUDZ7.js → chunk-VWOPOLVO.js} +2 -2
  125. package/dist/chunk-VWOPOLVO.js.map +1 -0
  126. package/dist/{chunk-BN2USWIR.js → chunk-VYECQAGM.js} +2 -2
  127. package/dist/chunk-VYECQAGM.js.map +1 -0
  128. package/dist/{chunk-SIXYZ2FB.js → chunk-YJMCRXQQ.js} +1 -1
  129. package/dist/chunk-YJMCRXQQ.js.map +1 -0
  130. package/dist/{chunk-PJC4NXPA.js → chunk-YKBDH2UY.js} +3 -3
  131. package/dist/chunk-YKBDH2UY.js.map +1 -0
  132. package/dist/{chunk-XAWFPMAR.js → chunk-YKOM4ZLQ.js} +1 -1
  133. package/dist/chunk-YKOM4ZLQ.js.map +1 -0
  134. package/dist/{chunk-TKVJE63N.js → chunk-YRMLYFVR.js} +3 -3
  135. package/dist/chunk-YRMLYFVR.js.map +1 -0
  136. package/dist/{chunk-USYXHDKE.js → chunk-YZ6KQULN.js} +2 -2
  137. package/dist/{command-stack-B_-WNfZU.d.ts → command-stack-TNPWJCzW.d.ts} +14 -2
  138. package/dist/{completion-Kaqp_tWk.d.ts → completion-CvcNUz07.d.ts} +1 -1
  139. package/dist/config/index.d.ts +1 -1
  140. package/dist/{contract-KfqJh_au.d.ts → contract-TjB68uqA.d.ts} +17 -0
  141. package/dist/design-canvas/drizzle.d.ts +10 -2
  142. package/dist/design-canvas/drizzle.js.map +1 -1
  143. package/dist/design-canvas/index.d.ts +29 -11
  144. package/dist/design-canvas/index.js +1070 -17
  145. package/dist/design-canvas/index.js.map +1 -1
  146. package/dist/design-canvas-react/engine.d.ts +42 -6
  147. package/dist/design-canvas-react/engine.js +4 -4
  148. package/dist/design-canvas-react/index.d.ts +12 -7
  149. package/dist/design-canvas-react/index.js +11 -11
  150. package/dist/design-canvas-react/index.js.map +1 -1
  151. package/dist/design-canvas-react/lazy.d.ts +4 -4
  152. package/dist/design-canvas-react/lazy.js +1 -1
  153. package/dist/durable-chat/index.d.ts +46 -1
  154. package/dist/durable-chat/index.js +3 -3
  155. package/dist/eval/index.d.ts +1 -1
  156. package/dist/{export-presets-DAsU5Wal.d.ts → export-presets-mgVulRaV.d.ts} +10 -1
  157. package/dist/{flow-types-Cb_AblZs.d.ts → flow-types-CqomVAUN.d.ts} +1 -0
  158. package/dist/harness/index.d.ts +5 -0
  159. package/dist/harness/index.js +1 -1
  160. package/dist/index.d.ts +10 -18
  161. package/dist/index.js +30 -231
  162. package/dist/intakes/api.d.ts +3 -2
  163. package/dist/intakes/api.js +3 -3
  164. package/dist/intakes/api.js.map +1 -1
  165. package/dist/intakes/drizzle.d.ts +10 -2
  166. package/dist/intakes/drizzle.js +3 -3
  167. package/dist/intakes/drizzle.js.map +1 -1
  168. package/dist/intakes/index.d.ts +2 -2
  169. package/dist/intakes/index.js +2 -2
  170. package/dist/intakes-react/index.d.ts +2 -1
  171. package/dist/intakes-react/index.js +2 -2
  172. package/dist/intakes-react/lazy.d.ts +2 -1
  173. package/dist/intakes-react/lazy.js +1 -1
  174. package/dist/intakes-react/lazy.js.map +1 -1
  175. package/dist/integrations/index.d.ts +4 -0
  176. package/dist/integrations/index.js +1 -1
  177. package/dist/interactions/index.d.ts +13 -2
  178. package/dist/interactions/index.js +2 -2
  179. package/dist/{invitations-CZ5G2tIn.d.ts → invitations-DoYMvXea.d.ts} +11 -1
  180. package/dist/{invitations-schema-DaMO4hZE.d.ts → invitations-schema-_fI7HkJP.d.ts} +5 -1
  181. package/dist/knowledge/index.d.ts +2 -0
  182. package/dist/knowledge/index.js +1 -1
  183. package/dist/knowledge-loop/index.d.ts +4 -1
  184. package/dist/knowledge-loop/index.js +1 -1
  185. package/dist/{lazy-C7LiVeFt.d.ts → lazy-DrkVyDpe.d.ts} +2 -2
  186. package/dist/{mcp-BHfIoGLW.d.ts → mcp-BjPqQ-3C.d.ts} +4 -2
  187. package/dist/{mcp-rpc-DLw_r9PQ.d.ts → mcp-rpc-BfUOsalw.d.ts} +3 -0
  188. package/dist/missions/index.d.ts +25 -0
  189. package/dist/missions/index.js +1 -1
  190. package/dist/{model-Bzi5i63l.d.ts → model-C8U0HZxL.d.ts} +1 -0
  191. package/dist/{model-dF2h4xT9.d.ts → model-DmdkIteM.d.ts} +19 -0
  192. package/dist/{model-BBwyOuWG.d.ts → model-DnfO_PdJ.d.ts} +25 -1
  193. package/dist/model-resolution/index.d.ts +10 -0
  194. package/dist/model-resolution/index.js.map +1 -1
  195. package/dist/{operations-DLIvvD00.d.ts → operations-ByJ6yXOW.d.ts} +20 -2
  196. package/dist/{parts-1sRYSPjF.d.ts → parts-n9XUon0a.d.ts} +29 -1
  197. package/dist/plans/index.d.ts +6 -0
  198. package/dist/plans/index.js +1 -1
  199. package/dist/platform/index.d.ts +24 -2
  200. package/dist/platform/index.js +3 -3
  201. package/dist/platform/index.js.map +1 -1
  202. package/dist/preflight/index.d.ts +3 -0
  203. package/dist/preflight/index.js +1 -1
  204. package/dist/preset-cloudflare/index.d.ts +4 -1
  205. package/dist/preset-cloudflare/index.js +2 -2
  206. package/dist/redact/index.d.ts +5 -0
  207. package/dist/redact/index.js +1 -1
  208. package/dist/{roles-BC1n4t37.d.ts → roles-B4nU3jDi.d.ts} +12 -0
  209. package/dist/run/index.d.ts +3 -0
  210. package/dist/run/index.js +1 -1
  211. package/dist/run/index.js.map +1 -1
  212. package/dist/runtime/index.d.ts +11 -4
  213. package/dist/runtime/index.js +4 -4
  214. package/dist/sandbox/index.d.ts +96 -3
  215. package/dist/sandbox/index.js +6 -6
  216. package/dist/{sandbox-terminal-BIIC__CP.d.ts → sandbox-terminal-ChNEdHF8.d.ts} +5 -0
  217. package/dist/sequences/drizzle.d.ts +11 -1
  218. package/dist/sequences/drizzle.js +1 -1
  219. package/dist/sequences/drizzle.js.map +1 -1
  220. package/dist/sequences/index.d.ts +32 -7
  221. package/dist/sequences/index.js +1562 -47
  222. package/dist/sequences/index.js.map +1 -1
  223. package/dist/sequences-react/index.d.ts +32 -2
  224. package/dist/sequences-react/index.js +3 -3
  225. package/dist/sequences-react/index.js.map +1 -1
  226. package/dist/{sso-CNOsARMJ.d.ts → sso-Cm00S61u.d.ts} +13 -0
  227. package/dist/{stale-turn-lock-C8Na1cFZ.d.ts → stale-turn-lock-DucQzvXu.d.ts} +2 -0
  228. package/dist/store/index.d.ts +5 -0
  229. package/dist/store/index.js.map +1 -1
  230. package/dist/{store-gckrNq-g.d.ts → store-B7LLlk9p.d.ts} +22 -1
  231. package/dist/{store-CFyxgTlD.d.ts → store-CqfDtnPQ.d.ts} +8 -2
  232. package/dist/stream/index.d.ts +10 -4
  233. package/dist/stream/index.js +4 -4
  234. package/dist/{stream-normalizer-DWvtmY6F.d.ts → stream-normalizer-C3M--r6y.d.ts} +17 -1
  235. package/dist/studio/index.d.ts +37 -0
  236. package/dist/studio/index.js +1 -1
  237. package/dist/studio-react/index.d.ts +3 -0
  238. package/dist/studio-react/index.js +1 -1
  239. package/dist/studio-react/index.js.map +1 -1
  240. package/dist/tangle/index.d.ts +3 -0
  241. package/dist/tangle/index.js +1 -1
  242. package/dist/teams/drizzle.d.ts +8 -4
  243. package/dist/teams/drizzle.js +1 -1
  244. package/dist/teams/drizzle.js.map +1 -1
  245. package/dist/teams/index.d.ts +4 -2
  246. package/dist/teams/index.js +3 -3
  247. package/dist/teams/invitations-api.d.ts +10 -4
  248. package/dist/teams/invitations-api.js +4 -4
  249. package/dist/teams/invitations-api.js.map +1 -1
  250. package/dist/teams/members-api.d.ts +4 -2
  251. package/dist/teams/members-api.js +3 -3
  252. package/dist/teams/resend.d.ts +5 -4
  253. package/dist/teams/resend.js +1 -1
  254. package/dist/teams/resend.js.map +1 -1
  255. package/dist/teams-react/index.d.ts +7 -2
  256. package/dist/teams-react/index.js +3 -3
  257. package/dist/teams-react/lazy.d.ts +5 -2
  258. package/dist/teams-react/lazy.js +2 -2
  259. package/dist/teams-react/lazy.js.map +1 -1
  260. package/dist/theme/index.d.ts +2 -0
  261. package/dist/theme/index.js +1 -1
  262. package/dist/theme/tailwind-preset.d.ts +1 -0
  263. package/dist/theme/tailwind-preset.js.map +1 -1
  264. package/dist/theme-contract/cli.js +1 -1
  265. package/dist/theme-contract/index.d.ts +3 -0
  266. package/dist/theme-contract/index.js +1 -1
  267. package/dist/tools/index.d.ts +13 -6
  268. package/dist/tools/index.js +3 -3
  269. package/dist/trace/index.d.ts +6 -1
  270. package/dist/trace/index.js +2 -2
  271. package/dist/{turn-buffer-C9mEgoop.d.ts → turn-buffer-DGnAPKwa.d.ts} +6 -0
  272. package/dist/turn-stream/index.d.ts +28 -2
  273. package/dist/turn-stream/index.js +1 -1
  274. package/dist/turn-stream/index.js.map +1 -1
  275. package/dist/{types-BEOvc_ue.d.ts → types-CBRyqijY.d.ts} +10 -0
  276. package/dist/vault/index.d.ts +1 -1
  277. package/dist/vault/lazy.d.ts +3 -2
  278. package/dist/vault/lazy.js.map +1 -1
  279. package/dist/web/index.d.ts +4 -0
  280. package/dist/web/index.js +1 -1
  281. package/dist/web-react/index.d.ts +51 -8
  282. package/dist/web-react/index.js +9 -9
  283. package/dist/web-react/terminal.d.ts +2 -2
  284. package/dist/web-react/terminal.js +1 -1
  285. package/package.json +3 -2
  286. package/dist/DesignCanvas-5DJAFDNC.js +0 -12
  287. package/dist/DesignCanvasEditor-XUJZARSN.js +0 -13
  288. package/dist/IntakeInterview-WHVKMNQW.js +0 -8
  289. package/dist/InvitationsPanel-XUUWZBPV.js +0 -8
  290. package/dist/MembersPanel-FOVHWUQ3.js +0 -8
  291. package/dist/chunk-23LUHHHN.js.map +0 -1
  292. package/dist/chunk-3SVAA3MA.js.map +0 -1
  293. package/dist/chunk-47C6PJCW.js.map +0 -1
  294. package/dist/chunk-4K7BZYO7.js.map +0 -1
  295. package/dist/chunk-4QN75UHX.js.map +0 -1
  296. package/dist/chunk-54GSK2KK.js.map +0 -1
  297. package/dist/chunk-5MG74GVQ.js.map +0 -1
  298. package/dist/chunk-5PTGEJZL.js.map +0 -1
  299. package/dist/chunk-5RMIUJDI.js.map +0 -1
  300. package/dist/chunk-63CE7FEZ.js.map +0 -1
  301. package/dist/chunk-65P3HJY3.js.map +0 -1
  302. package/dist/chunk-6KVH5SP5.js.map +0 -1
  303. package/dist/chunk-7EVZUIHW.js.map +0 -1
  304. package/dist/chunk-7SUPA2YT.js.map +0 -1
  305. package/dist/chunk-7W5XSTUF.js.map +0 -1
  306. package/dist/chunk-BN2USWIR.js.map +0 -1
  307. package/dist/chunk-BQV42AFK.js.map +0 -1
  308. package/dist/chunk-CCRVDLV3.js.map +0 -1
  309. package/dist/chunk-EEPJGZJW.js.map +0 -1
  310. package/dist/chunk-G3HCU7TA.js.map +0 -1
  311. package/dist/chunk-GQRL6VNU.js.map +0 -1
  312. package/dist/chunk-GUSCYE3S.js.map +0 -1
  313. package/dist/chunk-H5FM75MR.js.map +0 -1
  314. package/dist/chunk-HFC4BTWJ.js.map +0 -1
  315. package/dist/chunk-HRJJH3RX.js.map +0 -1
  316. package/dist/chunk-K63OWUDJ.js.map +0 -1
  317. package/dist/chunk-L2TG5DBW.js.map +0 -1
  318. package/dist/chunk-M2EBUDZ7.js.map +0 -1
  319. package/dist/chunk-MCJSS6SM.js.map +0 -1
  320. package/dist/chunk-MFI4ZM7W.js.map +0 -1
  321. package/dist/chunk-MFRCM32T.js.map +0 -1
  322. package/dist/chunk-ND3XEGBE.js.map +0 -1
  323. package/dist/chunk-NEOV2NQ3.js +0 -1617
  324. package/dist/chunk-NEOV2NQ3.js.map +0 -1
  325. package/dist/chunk-NYATNLRK.js.map +0 -1
  326. package/dist/chunk-PJC4NXPA.js.map +0 -1
  327. package/dist/chunk-Q4TKVF3L.js.map +0 -1
  328. package/dist/chunk-RVUISYUE.js.map +0 -1
  329. package/dist/chunk-SIXYZ2FB.js.map +0 -1
  330. package/dist/chunk-TKVJE63N.js.map +0 -1
  331. package/dist/chunk-WEBBJBDH.js.map +0 -1
  332. package/dist/chunk-WFMUDX5J.js +0 -1092
  333. package/dist/chunk-WFMUDX5J.js.map +0 -1
  334. package/dist/chunk-XAWFPMAR.js.map +0 -1
  335. package/dist/chunk-XKBIYSSM.js.map +0 -1
  336. package/dist/chunk-XWJXZ6WE.js.map +0 -1
  337. package/dist/chunk-YGUNTIT5.js.map +0 -1
  338. package/dist/chunk-YNALYIQ3.js.map +0 -1
  339. package/dist/chunk-ZU5GNSOJ.js.map +0 -1
  340. package/dist/chunk-ZXNXAQAH.js.map +0 -1
  341. package/dist/chunk-ZYBWGSAZ.js.map +0 -1
  342. /package/dist/{DesignCanvas-5DJAFDNC.js.map → DesignCanvas-UNL34LS2.js.map} +0 -0
  343. /package/dist/{DesignCanvasEditor-XUJZARSN.js.map → DesignCanvasEditor-FJIVYOA2.js.map} +0 -0
  344. /package/dist/{IntakeInterview-WHVKMNQW.js.map → IntakeInterview-3ZIQMZS6.js.map} +0 -0
  345. /package/dist/{InvitationsPanel-XUUWZBPV.js.map → InvitationsPanel-CJ7DR4F7.js.map} +0 -0
  346. /package/dist/{MembersPanel-FOVHWUQ3.js.map → MembersPanel-BB4OD2KB.js.map} +0 -0
  347. /package/dist/{TimelineEditor-I35SJJAJ.js.map → TimelineEditor-2ECKSU6P.js.map} +0 -0
  348. /package/dist/{chunk-7C64WKAH.js.map → chunk-5SXS3YAB.js.map} +0 -0
  349. /package/dist/{chunk-R4NOKZMD.js.map → chunk-ROLFDOMJ.js.map} +0 -0
  350. /package/dist/{chunk-GNL3MG5J.js.map → chunk-S564OFTL.js.map} +0 -0
  351. /package/dist/{chunk-5I72FRZY.js.map → chunk-VOJB7MVG.js.map} +0 -0
  352. /package/dist/{chunk-USYXHDKE.js.map → chunk-YZ6KQULN.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/missions/service.ts","../src/missions/events.ts","../src/missions/engine.ts","../src/missions/plan-parse.ts","../src/missions/agent-activity.ts"],"sourcesContent":["/**\n * Durable mission state — the guarded status machine for a multi-step agent run.\n *\n * A mission is a persisted plan (ordered steps), a cursor (count of completed\n * steps), a cost ledger + budget, and a status machine. This module owns the\n * legal transitions and the guarded mutation surface; it does NOT execute steps\n * (the engine in `./engine` does) and it does NOT own persistence — products\n * implement {@link MissionStorePort} over their own tables. Every state change\n * appends a {@link MissionAuditEvent} so the run timeline is a single durable\n * audit trail.\n *\n * Concurrency contract: a mission MUST be driven by a single serialized owner\n * (a Durable Object, a Cloudflare Workflow, a queue consumer — one per\n * mission). The service is the typed guard layer, not a serializer: every\n * mutation re-reads the record and asks the store for a compare-and-set write\n * guarded on the values it read. When the guard misses, the row changed under\n * us and the caller gets `{ succeeded: false, conflict: true }` — never a\n * silent clobber, never a stale overwrite.\n */\n\nexport type MissionStatus =\n | 'scheduled'\n | 'running'\n | 'paused'\n | 'waiting_approval'\n | 'blocked'\n | 'succeeded'\n | 'failed'\n | 'aborted'\n | 'cancelled'\n\nexport type MissionStepStatus = 'pending' | 'running' | 'waiting_approval' | 'done' | 'failed'\n\nexport interface MissionStep {\n id: string\n /** What the step should accomplish — an intent, never an implementation. */\n intent: string\n /** Product-defined kind label. Labels intent for gating/UX; it never selects\n * a different execution path. */\n kind: string\n status: MissionStepStatus\n /** Count of genuine `* -> running` edges (retries inflate this; idempotent\n * re-asserts do not). */\n attempts: number\n /** One-line live status surfaced on the step row (\"7/15 refs\"). */\n sublabel?: string\n /** Small pointer at the produced artifact (vault path, asset id) — never the\n * full payload. */\n resultRef?: string\n}\n\nexport interface MissionCostLedger {\n tokensIn: number\n tokensOut: number\n costUsd: number\n wallMs: number\n llmCalls: number\n}\n\n/** The durable mission row, shape-normalized. Timestamps are epoch ms. */\nexport interface MissionRecord {\n id: string\n workspaceId: string\n status: MissionStatus\n /** Product-defined origin label ('chat', 'manual', 'cron', …). */\n trigger: string\n summary: string | null\n plan: MissionStep[]\n /** Count of durably-completed steps; the next step to run is `plan[cursor]`. */\n cursor: number\n cost: MissionCostLedger | null\n budgetUsd: number | null\n spentUsd: number\n pauseReason: string | null\n /** The single owning engine's instance id, write-once (see `setEngineRef`). */\n engineRef: string | null\n scheduledAt: number | null\n startedAt: number\n completedAt: number | null\n metadata: Record<string, unknown> | null\n}\n\n/**\n * Discriminated outcome for guarded operations. Callers MUST inspect\n * `succeeded` before reading `value` — illegal transitions and missing rows\n * surface here, never as a throw-and-swallow or a silent no-op. `conflict`\n * distinguishes a lost guarded race (retryable — re-read and re-apply) from a\n * logic rejection (illegal edge, missing step — deterministic, never retried).\n */\nexport type MissionOutcome<T> =\n | { succeeded: true; value: T }\n | { succeeded: false; error: string; conflict: boolean }\n\n/** Fields a guarded write compares against the values the caller read. A SQL\n * implementation compares JSON columns as serialized text\n * (`coalesce(col, 'null') = JSON.stringify(expected)`), matching how the\n * in-memory store compares. An absent field is unguarded. */\nexport interface MissionUpdateGuard {\n status?: MissionStatus\n cursor?: number\n plan?: MissionStep[]\n cost?: MissionCostLedger | null\n metadata?: Record<string, unknown> | null\n /** Guard that no engine has bound yet (the write-once bind). */\n engineRefIsNull?: true\n}\n\n/** Fields a guarded write sets when the guard holds. `null` values are real\n * writes (clear the column), not skips. */\nexport interface MissionUpdatePatch {\n status?: MissionStatus\n pauseReason?: string | null\n summary?: string\n completedAt?: number | null\n plan?: MissionStep[]\n cursor?: number\n cost?: MissionCostLedger\n spentUsd?: number\n metadata?: Record<string, unknown>\n engineRef?: string\n}\n\n/** One audit-trail row. Appended after every committed state change, so an\n * event always denotes a real transition (no phantom rows on rejected or\n * no-op calls). */\nexport interface MissionAuditEvent {\n missionId: string\n workspaceId: string\n level: 'info' | 'warn' | 'error'\n /** Machine-readable transition name ('mission.created', 'mission.step.done',\n * 'mission.cursor', 'mission.cost', 'mission.paused', …). */\n step: string\n message: string\n metadata: Record<string, unknown>\n at: number\n}\n\n/**\n * Persistence seam — the product implements this over its own tables. The\n * invariant the implementation MUST keep: `update` applies `patch` ONLY when\n * every guard field still equals the stored value, and returns `null` when the\n * guard misses. That null is how a concurrent write surfaces as a typed\n * failure instead of a clobber.\n */\nexport interface MissionStorePort {\n load(id: string): Promise<MissionRecord | null>\n /** `extras` are the opaque product-column values from\n * `CreateMissionInput.extras` — write them in the SAME statement as the\n * record (single-write creation) or ignore them if the table has no extra\n * columns. */\n insert(record: MissionRecord, extras?: Record<string, unknown>): Promise<MissionRecord>\n update(id: string, guard: MissionUpdateGuard, patch: MissionUpdatePatch): Promise<MissionRecord | null>\n appendEvent(event: MissionAuditEvent): Promise<void>\n}\n\nconst TERMINAL_STATUSES: ReadonlySet<MissionStatus> = new Set<MissionStatus>([\n 'succeeded',\n 'failed',\n 'aborted',\n 'cancelled',\n])\n\n/** Statuses a mission can never leave — the run is done. */\nexport function isMissionTerminal(status: MissionStatus): boolean {\n return TERMINAL_STATUSES.has(status)\n}\n\n/** The cooperative kill switch: a stop request rides metadata so it survives\n * any status and is honored by the engine before the next side effect. */\nexport function isMissionStopRequested(mission: MissionRecord): boolean {\n return (mission.metadata ?? {}).stopRequested === true\n}\n\n// Legal mission status transitions. A target absent from a source's set is\n// rejected by the guarded helpers. Terminal statuses have no outgoing edges.\nconst MISSION_TRANSITIONS: Record<MissionStatus, ReadonlySet<MissionStatus>> = {\n scheduled: new Set<MissionStatus>(['running', 'cancelled', 'aborted']),\n running: new Set<MissionStatus>([\n 'paused',\n 'waiting_approval',\n 'blocked',\n 'succeeded',\n 'failed',\n 'aborted',\n ]),\n paused: new Set<MissionStatus>(['running', 'aborted', 'cancelled']),\n waiting_approval: new Set<MissionStatus>(['running', 'aborted', 'cancelled']),\n blocked: new Set<MissionStatus>(['running', 'aborted', 'cancelled']),\n succeeded: new Set<MissionStatus>(),\n failed: new Set<MissionStatus>(),\n aborted: new Set<MissionStatus>(),\n cancelled: new Set<MissionStatus>(),\n}\n\n// Legal per-step transitions inside a plan.\nconst STEP_TRANSITIONS: Record<MissionStepStatus, ReadonlySet<MissionStepStatus>> = {\n pending: new Set<MissionStepStatus>(['running', 'waiting_approval', 'failed']),\n running: new Set<MissionStepStatus>(['done', 'failed', 'waiting_approval']),\n waiting_approval: new Set<MissionStepStatus>(['running', 'done', 'failed']),\n done: new Set<MissionStepStatus>([]),\n failed: new Set<MissionStepStatus>(['pending', 'running']),\n}\n\nconst ZERO_LEDGER: MissionCostLedger = {\n tokensIn: 0,\n tokensOut: 0,\n costUsd: 0,\n wallMs: 0,\n llmCalls: 0,\n}\n\nexport interface CreateMissionInput {\n /** Explicit row id. Omit to use the service's id generator. Pass a\n * DETERMINISTIC id (derived from the originating turn) when the caller may\n * re-create the same mission under at-least-once delivery — the duplicate\n * insert then trips the store's uniqueness instead of spawning a second run. */\n id?: string\n workspaceId: string\n /** Becomes the mission summary. */\n title: string\n /** Plan step ids MUST be unique: the owning workflow keys its durable step\n * cache by step id, so a collision would silently replay the wrong result.\n * A duplicate is rejected here (fail loud). */\n plan: MissionStep[]\n budgetUsd?: number | null\n /** Epoch ms. Present → the mission starts `scheduled` instead of `running`. */\n scheduledAt?: number | null\n trigger: string\n /** Caller-defined context stamped onto the record (thread ids, source turn,\n * model). Read back via `mission.metadata`; the engine only reads\n * `stopRequested` from it. */\n metadata?: Record<string, unknown> | null\n /**\n * Opaque product-column values handed VERBATIM to `MissionStorePort.insert`\n * (e.g. a `workflowId` FK on the product's mission table), so creation is a\n * single write — no post-insert stamp. The service never reads, validates,\n * or persists them itself; they exist only on the insert call.\n */\n extras?: Record<string, unknown>\n}\n\nexport interface SetStepStatusPatch {\n sublabel?: string\n resultRef?: string\n error?: string\n /**\n * Spend committed ATOMICALLY with the step transition. Folding the cost into\n * the SAME guarded write makes step completion the per-step idempotency key:\n * a same-status replay (the step is already `done`) short-circuits as a no-op\n * BEFORE the write, so the charge lands exactly once even when an engine\n * RESUME re-dispatches a step whose cost previously committed. `deltaUsd` is\n * the marginal spend; `ledgerDelta` carries the token/wall/llm breakdown.\n */\n cost?: {\n deltaUsd: number\n ledgerDelta?: Partial<MissionCostLedger>\n }\n}\n\nexport interface CompleteMissionInput {\n ok: boolean\n summary?: string\n}\n\nexport interface MissionService {\n createMission(input: CreateMissionInput): Promise<MissionRecord>\n getMission(id: string): Promise<MissionRecord | null>\n /** Bind the executing engine's instance id, write-once from the single\n * owner: re-asserting the same ref is a no-op; binding a DIFFERENT ref over\n * an existing one is rejected so a second owner can never steal the run. */\n setEngineRef(id: string, engineRef: string): Promise<MissionOutcome<MissionRecord>>\n /** Shallow-merge keys into metadata. Guarded on the metadata read, so racing\n * merges surface as conflicts instead of silently dropping keys. */\n mergeMetadata(id: string, patch: Record<string, unknown>): Promise<MissionOutcome<MissionRecord>>\n /** Mutate one plan step's status (+ optional sublabel/resultRef) and append a\n * transition event. Rejects unknown steps and illegal step edges. Does NOT\n * move the cursor — call `advanceCursor` for that. */\n setStepStatus(\n id: string,\n stepId: string,\n status: MissionStepStatus,\n patch?: SetStepStatusPatch,\n ): Promise<MissionOutcome<MissionRecord>>\n /** Move the done-count cursor forward by one. Rejects advancing past the end\n * of the plan so the caller learns the mission has no further work. */\n advanceCursor(id: string): Promise<MissionOutcome<MissionRecord>>\n /** Increment spentUsd and merge a partial ledger into the cumulative ledger.\n * `deltaUsd` is the marginal spend; `ledgerDelta` carries the token/wall/\n * llm-call breakdown for the same unit of work. */\n addCost(\n id: string,\n deltaUsd: number,\n ledgerDelta?: Partial<MissionCostLedger>,\n ): Promise<MissionOutcome<MissionRecord>>\n pause(id: string, reason: string): Promise<MissionOutcome<MissionRecord>>\n resume(id: string): Promise<MissionOutcome<MissionRecord>>\n abort(id: string): Promise<MissionOutcome<MissionRecord>>\n /** Flip one step and the whole mission to waiting_approval together. The\n * mission transition is validated FIRST so an illegal source is rejected\n * without mutating the step — no half-applied state. */\n markWaitingApproval(id: string, stepId: string): Promise<MissionOutcome<MissionRecord>>\n complete(id: string, input: CompleteMissionInput): Promise<MissionOutcome<MissionRecord>>\n}\n\nexport interface MissionServiceOptions {\n store: MissionStorePort\n /** Injectable clock (epoch ms). Default `Date.now`. */\n now?: () => number\n /**\n * Row-id generator when `CreateMissionInput.id` is omitted. Default\n * `crypto.randomUUID()` — a 36-char dashed UUID. Inject your own when your\n * mission table already has an id shape (e.g. 32-hex matching D1 row\n * defaults): the service stamps the generated id verbatim onto the inserted\n * record, so a mismatch with the rest of your schema surfaces only at the\n * product layer. Keep id shape parity here, not in the store.\n */\n generateId?: () => string\n}\n\nfunction rejected<T>(error: string): MissionOutcome<T> {\n return { succeeded: false, error, conflict: false }\n}\n\nfunction lostRace<T>(id: string): MissionOutcome<T> {\n return { succeeded: false, error: `Mission ${id} changed concurrently`, conflict: true }\n}\n\nexport function createMissionService(options: MissionServiceOptions): MissionService {\n const { store } = options\n const now = options.now ?? (() => Date.now())\n const generateId = options.generateId ?? (() => crypto.randomUUID())\n\n async function appendEvent(\n mission: MissionRecord,\n level: MissionAuditEvent['level'],\n step: string,\n message: string,\n metadata: Record<string, unknown> = {},\n ): Promise<void> {\n await store.appendEvent({\n missionId: mission.id,\n workspaceId: mission.workspaceId,\n level,\n step,\n message,\n metadata,\n at: now(),\n })\n }\n\n // Guarded status transition: load → validate against the transition table →\n // CAS-write guarded on the status we read → event. The loser of a racing\n // transition gets a conflict instead of silently violating the machine.\n async function transition(\n id: string,\n to: MissionStatus,\n patch: Omit<MissionUpdatePatch, 'status'> = {},\n eventMeta: Record<string, unknown> = {},\n ): Promise<MissionOutcome<MissionRecord>> {\n const mission = await store.load(id)\n if (!mission) return rejected(`Mission ${id} not found`)\n\n const from = mission.status\n if (TERMINAL_STATUSES.has(from)) {\n return rejected(`Mission ${id} is terminal (${from}); cannot transition to ${to}`)\n }\n // Same-status calls are a true no-op (resume-while-running, idempotent\n // re-assert under at-least-once delivery): no patch re-apply, no phantom\n // event, so an event row always denotes a real state change.\n if (from === to) return { succeeded: true, value: mission }\n if (!MISSION_TRANSITIONS[from].has(to)) {\n return rejected(`Illegal mission transition ${from} -> ${to} for mission ${id}`)\n }\n\n const updated = await store.update(id, { status: from }, { status: to, ...patch })\n if (!updated) return lostRace(id)\n await appendEvent(updated, to === 'failed' ? 'error' : 'info', `mission.${to}`, `Mission ${from} -> ${to}`, {\n from,\n to,\n ...eventMeta,\n })\n return { succeeded: true, value: updated }\n }\n\n const createMission: MissionService['createMission'] = async (input) => {\n const seen = new Set<string>()\n for (const step of input.plan) {\n if (seen.has(step.id)) {\n throw new Error(`Duplicate plan step id \"${step.id}\" — mission plan step ids must be unique`)\n }\n seen.add(step.id)\n }\n\n const scheduledAt = input.scheduledAt ?? null\n const status: MissionStatus = scheduledAt !== null ? 'scheduled' : 'running'\n const plan: MissionStep[] = input.plan.map((step) => ({\n id: step.id,\n intent: step.intent,\n kind: step.kind,\n status: step.status,\n attempts: step.attempts,\n ...(step.sublabel === undefined ? {} : { sublabel: step.sublabel }),\n ...(step.resultRef === undefined ? {} : { resultRef: step.resultRef }),\n }))\n\n const record = await store.insert(\n {\n id: input.id ?? generateId(),\n workspaceId: input.workspaceId,\n status,\n trigger: input.trigger,\n summary: input.title,\n plan,\n cursor: 0,\n cost: { ...ZERO_LEDGER },\n budgetUsd: input.budgetUsd ?? null,\n spentUsd: 0,\n pauseReason: null,\n engineRef: null,\n scheduledAt,\n startedAt: now(),\n completedAt: null,\n metadata: input.metadata ?? null,\n },\n input.extras,\n )\n\n await appendEvent(record, 'info', 'mission.created', `Mission \"${input.title}\" ${status}`, {\n status,\n stepCount: plan.length,\n budgetUsd: input.budgetUsd ?? null,\n scheduledAt,\n })\n return record\n }\n\n const getMission: MissionService['getMission'] = (id) => store.load(id)\n\n const setEngineRef: MissionService['setEngineRef'] = async (id, engineRef) => {\n const mission = await store.load(id)\n if (!mission) return rejected(`Mission ${id} not found`)\n if (TERMINAL_STATUSES.has(mission.status) || isMissionStopRequested(mission)) {\n return rejected(`Mission ${id} is not writable in status ${mission.status}`)\n }\n if (mission.engineRef === engineRef) return { succeeded: true, value: mission }\n if (mission.engineRef !== null) {\n return rejected(`Mission ${id} is already bound to engine ${mission.engineRef}`)\n }\n\n // Guard on the null engineRef we read so two racing owners cannot both\n // bind: the second write misses the guard and reports the lost race —\n // distinct from the deterministic already-bound rejection above.\n const updated = await store.update(id, { engineRefIsNull: true }, { engineRef })\n if (!updated) return lostRace(id)\n await appendEvent(updated, 'info', 'mission.engine', `Engine bound: ${engineRef}`, { engineRef })\n return { succeeded: true, value: updated }\n }\n\n const mergeMetadata: MissionService['mergeMetadata'] = async (id, patch) => {\n const mission = await store.load(id)\n if (!mission) return rejected(`Mission ${id} not found`)\n if (TERMINAL_STATUSES.has(mission.status) || isMissionStopRequested(mission)) {\n return rejected(`Mission ${id} is not writable in status ${mission.status}`)\n }\n\n const updated = await store.update(\n id,\n { metadata: mission.metadata },\n { metadata: { ...(mission.metadata ?? {}), ...patch } },\n )\n if (!updated) return lostRace(id)\n return { succeeded: true, value: updated }\n }\n\n const setStepStatus: MissionService['setStepStatus'] = async (id, stepId, status, patch = {}) => {\n const mission = await store.load(id)\n if (!mission) return rejected(`Mission ${id} not found`)\n\n const plan = mission.plan\n const index = plan.findIndex((step) => step.id === stepId)\n const current = index < 0 ? undefined : plan[index]\n if (!current) return rejected(`Step ${stepId} not found in mission ${id}`)\n\n const sameStatus = current.status === status\n if (!sameStatus && !STEP_TRANSITIONS[current.status].has(status)) {\n return rejected(`Illegal step transition ${current.status} -> ${status} for step ${stepId}`)\n }\n\n // True no-op: re-asserting the same status with no patch field change must\n // not bump attempts or write a duplicate event (at-least-once delivery,\n // retries, and reconnects re-assert state). A patch on the same status\n // still persists, but does not count a new attempt.\n const sublabelChanges = patch.sublabel !== undefined && patch.sublabel !== current.sublabel\n const resultRefChanges = patch.resultRef !== undefined && patch.resultRef !== current.resultRef\n if (sameStatus && !sublabelChanges && !resultRefChanges) {\n return { succeeded: true, value: mission }\n }\n\n // Only a genuine `* -> running` edge counts an attempt; a running->running\n // re-assert (handled as a no-op above) never inflates the counter.\n const nextStep: MissionStep = {\n ...current,\n status,\n attempts: status === 'running' && !sameStatus ? current.attempts + 1 : current.attempts,\n ...(patch.sublabel === undefined ? {} : { sublabel: patch.sublabel }),\n ...(patch.resultRef === undefined ? {} : { resultRef: patch.resultRef }),\n }\n const nextPlan = plan.slice()\n nextPlan[index] = nextStep\n\n // Spend folded into the SAME guarded write as the transition. Because the\n // same-status no-op above returns BEFORE this point, a step that is already\n // `done` never reaches here — so an engine RESUME re-dispatching a committed\n // step cannot charge a second time. The ledger CAS guard preserves the\n // over-spend protection `addCost` carried: an undercounted concurrent merge\n // surfaces as a lost race, never a clobber.\n const cost = patch.cost\n const ledgerDelta = cost?.ledgerDelta\n const base = mission.cost ?? { ...ZERO_LEDGER }\n const nextCost: MissionCostLedger | undefined =\n cost === undefined\n ? undefined\n : {\n tokensIn: base.tokensIn + (ledgerDelta?.tokensIn ?? 0),\n tokensOut: base.tokensOut + (ledgerDelta?.tokensOut ?? 0),\n costUsd: base.costUsd + (ledgerDelta?.costUsd ?? cost.deltaUsd),\n wallMs: base.wallMs + (ledgerDelta?.wallMs ?? 0),\n llmCalls: base.llmCalls + (ledgerDelta?.llmCalls ?? 0),\n }\n\n // The plan write is guarded on the status, plan, metadata, AND (when a cost\n // rides along) the ledger we read: a concurrent stop request (a metadata\n // write), any plan mutation, or a racing spend flips a guard and the loser\n // reports the race instead of clobbering it.\n const updated = await store.update(\n id,\n {\n status: mission.status,\n plan: mission.plan,\n metadata: mission.metadata,\n ...(cost === undefined ? {} : { cost: mission.cost }),\n },\n {\n plan: nextPlan,\n ...(cost === undefined ? {} : { cost: nextCost!, spentUsd: mission.spentUsd + cost.deltaUsd }),\n },\n )\n if (!updated) return lostRace(id)\n await appendEvent(\n updated,\n status === 'failed' ? 'error' : 'info',\n `mission.step.${status}`,\n patch.error ?? `Step ${stepId} (${current.intent}) -> ${status}`,\n {\n stepId,\n from: current.status,\n to: status,\n attempts: nextStep.attempts,\n ...(patch.resultRef ? { resultRef: patch.resultRef } : {}),\n },\n )\n if (cost !== undefined) {\n await appendEvent(updated, 'info', 'mission.cost', `Spent +$${cost.deltaUsd.toFixed(4)}`, {\n deltaUsd: cost.deltaUsd,\n spentUsd: updated.spentUsd,\n budgetUsd: updated.budgetUsd,\n })\n }\n return { succeeded: true, value: updated }\n }\n\n const advanceCursor: MissionService['advanceCursor'] = async (id) => {\n const mission = await store.load(id)\n if (!mission) return rejected(`Mission ${id} not found`)\n\n const next = mission.cursor + 1\n if (next > mission.plan.length) {\n return rejected(`Cursor ${mission.cursor} is already at the end of mission ${id}`)\n }\n\n // Guarded on the cursor (and status) we read: a concurrent advance shifts\n // the cursor, the guard misses, and we report the lost race rather than\n // writing a stale value that drops the other increment.\n const updated = await store.update(\n id,\n { status: mission.status, cursor: mission.cursor },\n { cursor: next },\n )\n if (!updated) return lostRace(id)\n await appendEvent(updated, 'info', 'mission.cursor', `Cursor ${mission.cursor} -> ${updated.cursor}`, {\n from: mission.cursor,\n to: updated.cursor,\n })\n return { succeeded: true, value: updated }\n }\n\n const addCost: MissionService['addCost'] = async (id, deltaUsd, ledgerDelta) => {\n const mission = await store.load(id)\n if (!mission) return rejected(`Mission ${id} not found`)\n\n const base = mission.cost ?? { ...ZERO_LEDGER }\n const nextCost: MissionCostLedger = {\n tokensIn: base.tokensIn + (ledgerDelta?.tokensIn ?? 0),\n tokensOut: base.tokensOut + (ledgerDelta?.tokensOut ?? 0),\n costUsd: base.costUsd + (ledgerDelta?.costUsd ?? deltaUsd),\n wallMs: base.wallMs + (ledgerDelta?.wallMs ?? 0),\n llmCalls: base.llmCalls + (ledgerDelta?.llmCalls ?? 0),\n }\n\n // Guarded on the ledger we read: an undercounted spend is an over-spend\n // against the budget gate, so a concurrent merge must surface as a lost\n // race (retry re-reads and re-merges), never be clobbered.\n const updated = await store.update(\n id,\n { cost: mission.cost },\n { cost: nextCost, spentUsd: mission.spentUsd + deltaUsd },\n )\n if (!updated) return lostRace(id)\n await appendEvent(updated, 'info', 'mission.cost', `Spent +$${deltaUsd.toFixed(4)}`, {\n deltaUsd,\n spentUsd: updated.spentUsd,\n budgetUsd: updated.budgetUsd,\n })\n return { succeeded: true, value: updated }\n }\n\n const markWaitingApproval: MissionService['markWaitingApproval'] = async (id, stepId) => {\n const mission = await store.load(id)\n if (!mission) return rejected(`Mission ${id} not found`)\n if (!MISSION_TRANSITIONS[mission.status].has('waiting_approval')) {\n return rejected(`Illegal mission transition ${mission.status} -> waiting_approval for mission ${id}`)\n }\n\n const stepResult = await setStepStatus(id, stepId, 'waiting_approval')\n if (!stepResult.succeeded) return stepResult\n return transition(id, 'waiting_approval', {}, { stepId })\n }\n\n return {\n createMission,\n getMission,\n setEngineRef,\n mergeMetadata,\n setStepStatus,\n advanceCursor,\n addCost,\n markWaitingApproval,\n pause: (id, reason) => transition(id, 'paused', { pauseReason: reason }),\n resume: (id) => transition(id, 'running', { pauseReason: null }),\n abort: (id) => transition(id, 'aborted', { completedAt: now() }),\n complete: (id, input) =>\n transition(id, input.ok ? 'succeeded' : 'failed', {\n completedAt: now(),\n ...(input.summary === undefined ? {} : { summary: input.summary }),\n }),\n }\n}\n\n// ── in-memory store ──────────────────────────────────────────────────────────\n\nexport interface InMemoryMissionStore extends MissionStorePort {\n /** The full audit trail, append order. */\n events(): MissionAuditEvent[]\n /** Unguarded direct write — simulates a concurrent owner or a crash-shaped\n * state in tests. Production writers go through the guarded `update`. */\n put(record: MissionRecord): void\n}\n\n/**\n * In-memory {@link MissionStorePort} — the portable backend for tests and\n * sandbox/eval shells. Guard comparison uses JSON serialization of the read\n * value, the same contract a SQL implementation honors by comparing stored\n * JSON text. Records are deep-copied on every boundary so callers can never\n * mutate stored state around the guards.\n */\nexport function createInMemoryMissionStore(): InMemoryMissionStore {\n const rows = new Map<string, MissionRecord>()\n const events: MissionAuditEvent[] = []\n\n return {\n async load(id) {\n const record = rows.get(id)\n return record ? structuredClone(record) : null\n },\n async insert(record) {\n if (rows.has(record.id)) throw new Error(`Mission ${record.id} already exists`)\n rows.set(record.id, structuredClone(record))\n return structuredClone(record)\n },\n async update(id, guard, patch) {\n const current = rows.get(id)\n if (!current) return null\n if (guard.status !== undefined && current.status !== guard.status) return null\n if (guard.cursor !== undefined && current.cursor !== guard.cursor) return null\n if (guard.plan !== undefined && JSON.stringify(current.plan) !== JSON.stringify(guard.plan)) return null\n if (guard.cost !== undefined && JSON.stringify(current.cost) !== JSON.stringify(guard.cost)) return null\n if (\n guard.metadata !== undefined &&\n JSON.stringify(current.metadata) !== JSON.stringify(guard.metadata)\n ) {\n return null\n }\n if (guard.engineRefIsNull && current.engineRef !== null) return null\n\n const next: MissionRecord = { ...current }\n if (patch.status !== undefined) next.status = patch.status\n if (patch.pauseReason !== undefined) next.pauseReason = patch.pauseReason\n if (patch.summary !== undefined) next.summary = patch.summary\n if (patch.completedAt !== undefined) next.completedAt = patch.completedAt\n if (patch.plan !== undefined) next.plan = patch.plan\n if (patch.cursor !== undefined) next.cursor = patch.cursor\n if (patch.cost !== undefined) next.cost = patch.cost\n if (patch.spentUsd !== undefined) next.spentUsd = patch.spentUsd\n if (patch.metadata !== undefined) next.metadata = patch.metadata\n if (patch.engineRef !== undefined) next.engineRef = patch.engineRef\n rows.set(id, structuredClone(next))\n return structuredClone(next)\n },\n async appendEvent(event) {\n events.push(structuredClone(event))\n },\n events() {\n return events.map((event) => structuredClone(event))\n },\n put(record) {\n rows.set(record.id, structuredClone(record))\n },\n }\n}\n","/**\n * Shared mission realtime contract — the single source of truth for the typed\n * events the engine BROADCASTS over a live channel and the client REDUCES into\n * live mission state. Server emit and client reduce import the same module so\n * the wire shape can never drift between the two ends.\n *\n * This module is CLIENT-SAFE: no server imports, no platform globals, no DB\n * types. It is pure data + a pure reducer. Keep it that way — a server-only\n * import here would leak into the browser bundle.\n *\n * Sink contract — best-effort UI notification, never load-bearing:\n * - fire-and-forget: the engine never awaits `emit` and a sink failure can\n * never fail a step (the engine wraps every emit). The durable audit-event\n * row is the authoritative timeline; the socket is a convenience.\n * - replay-safe: the engine re-emits on a resume/replay. The reducer below is\n * idempotent + order-tolerant, so a re-sent or duplicated event converges.\n * The sink itself does no dedupe.\n */\n\nimport type { StepAgentActivity } from './agent-activity'\n\nexport interface MissionEventSink {\n emit(event: MissionStreamEvent): void\n}\n\n/** A sink that drops every event — the engine default when no live channel is\n * wired (and the unit-test default). */\nexport const noopEventSink: MissionEventSink = { emit() {} }\n\n/** Workspace-wide channel id missions broadcast on (alongside any per-thread\n * channel the product keys). */\nexport const MISSION_CONTROL_CHANNEL_ID = 'missions'\n\n/** One plan step as it appears on the wire — only what a live UI needs\n * (`sublabel` updates travel separately via `step.updated` so the snapshot\n * stays small). */\nexport interface MissionStreamStep {\n id: string\n intent: string\n kind: string\n status: MissionStreamStepStatus\n}\n\nexport type MissionStreamStepStatus =\n | 'pending'\n | 'running'\n | 'done'\n | 'failed'\n | 'waiting_approval'\n\nexport type MissionStreamStatus =\n | 'scheduled'\n | 'running'\n | 'paused'\n | 'waiting_approval'\n | 'succeeded'\n | 'aborted'\n | 'cancelled'\n | 'failed'\n\n/**\n * Discriminated union of every live mission event. Every member carries\n * `missionId` (one channel may multiplex several missions) and a `type` the\n * client switches on. `at` is the emitter's wall-clock ms — used only for\n * display ordering; the reducer never trusts it for causality.\n */\nexport type MissionStreamEvent =\n | {\n type: 'mission.created'\n missionId: string\n at: number\n title: string\n status?: MissionStreamStatus\n steps: MissionStreamStep[]\n budgetUsd?: number | null\n }\n | { type: 'mission.started'; missionId: string; at: number }\n | { type: 'step.started'; missionId: string; at: number; stepId: string }\n | {\n type: 'step.updated'\n missionId: string\n at: number\n stepId: string\n sublabel?: string\n /**\n * Full CURRENT snapshot of the step's delegated runs — never a delta.\n * The reducer replaces the whole lane (latest snapshot wins by `at`), so\n * emitters re-send everything they know each time and at-least-once /\n * out-of-order delivery converges.\n */\n agentActivity?: StepAgentActivity[]\n }\n | {\n type: 'step.completed'\n missionId: string\n at: number\n stepId: string\n ok: boolean\n reason?: string\n durationMs?: number\n }\n | {\n type: 'cost.updated'\n missionId: string\n at: number\n spentUsd: number\n capUsd?: number | null\n }\n | { type: 'mission.paused'; missionId: string; at: number; reason?: string }\n | { type: 'mission.waiting_approval'; missionId: string; at: number; reason?: string }\n | { type: 'mission.resumed'; missionId: string; at: number }\n | {\n type: 'mission.plan.updated'\n missionId: string\n at: number\n title: string\n steps: MissionStreamStep[]\n budgetUsd?: number | null\n }\n | {\n type: 'mission.completed'\n missionId: string\n at: number\n ok: boolean\n status?: Extract<MissionStreamStatus, 'succeeded' | 'failed' | 'aborted' | 'cancelled'>\n summary?: string\n }\n\n// All mission event `type` discriminants. Used to cheaply reject the non-mission\n// events that share a channel without a full switch.\nconst MISSION_EVENT_TYPES: ReadonlySet<string> = new Set<MissionStreamEvent['type']>([\n 'mission.created',\n 'mission.started',\n 'step.started',\n 'step.updated',\n 'step.completed',\n 'cost.updated',\n 'mission.paused',\n 'mission.waiting_approval',\n 'mission.resumed',\n 'mission.plan.updated',\n 'mission.completed',\n])\n\n/**\n * Reconstruct the flat MissionStreamEvent from a broadcast envelope of shape\n * `{ type, data: { ...missionFields } }` (transports may also stamp routing\n * fields like workspaceId/threadId into `data`). The envelope `type` is the\n * AUTHORITATIVE discriminant set by the server, so it is spread LAST — a\n * payload that happens to carry a top-level `type` inside `data` cannot shadow\n * it and mis-render as a mission event. Non-mission envelopes and malformed\n * payloads return null and are simply skipped, so one channel can carry both\n * streams.\n */\nexport function parseSessionStreamEnvelope(raw: unknown): MissionStreamEvent | null {\n if (!raw || typeof raw !== 'object') return null\n const envelope = raw as { type?: unknown; data?: unknown }\n if (typeof envelope.type !== 'string') return null\n const data = envelope.data && typeof envelope.data === 'object' ? envelope.data : {}\n return asMissionStreamEvent({ ...(data as Record<string, unknown>), type: envelope.type })\n}\n\n/** Narrow an arbitrary channel payload to a MissionStreamEvent. Returns null\n * for non-mission events and anything malformed — the reducer skips those. */\nexport function asMissionStreamEvent(value: unknown): MissionStreamEvent | null {\n if (!value || typeof value !== 'object') return null\n const record = value as Record<string, unknown>\n const type = record.type\n if (typeof type !== 'string' || !MISSION_EVENT_TYPES.has(type)) return null\n if (typeof record.missionId !== 'string' || !record.missionId) return null\n // `at` is best-effort: this narrows the type but does not fill a default —\n // the reducer tolerates a missing `at` and treats it as 0.\n return value as MissionStreamEvent\n}\n\n/** Live per-step view the reducer maintains. `status` only ever moves FORWARD\n * (see STEP_RANK) so a duplicate/out-of-order event can never regress a step\n * from done back to running. */\nexport interface MissionStepState {\n id: string\n intent: string\n kind: string\n status: MissionStreamStepStatus\n sublabel?: string\n reason?: string\n durationMs?: number\n /** Latest delegated-run snapshot for the step (see `step.updated`). */\n agentActivity?: StepAgentActivity[]\n /** The `at` of the snapshot currently held — an older snapshot arriving\n * late never replaces a newer one. */\n agentActivityAt?: number\n}\n\n/** Live per-mission view the reducer folds events into. */\nexport interface MissionState {\n missionId: string\n title?: string\n status: MissionStreamStatus\n steps: MissionStepState[]\n spentUsd: number\n capUsd?: number | null\n pauseReason?: string\n summary?: string\n /** The largest `at` folded so far — purely for display; never gates folding. */\n lastEventAt: number\n /** The largest pause/resume control `at` folded — lets a newer resume beat an\n * older pause that arrives late. */\n lastControlAt?: number\n}\n\n// Monotonic rank for step status. A fold NEVER moves a step to a lower rank, so\n// a late/duplicate `step.started` arriving after `step.completed` is a no-op —\n// the reducer tolerates out-of-order and at-least-once delivery.\nconst STEP_RANK: Record<MissionStreamStepStatus, number> = {\n pending: 0,\n running: 1,\n waiting_approval: 2,\n // done and failed are both TERMINAL for a step; rank them equal-and-highest\n // so neither can be overwritten by the other or regressed to running.\n done: 3,\n failed: 3,\n}\n\n// Monotonic rank for mission status. Same forward-only guarantee: a stray\n// `mission.started` after `mission.completed` cannot un-finish a mission.\nconst MISSION_RANK: Record<MissionStreamStatus, number> = {\n scheduled: 0,\n running: 1,\n paused: 2,\n waiting_approval: 2,\n succeeded: 3,\n aborted: 3,\n cancelled: 3,\n failed: 3,\n}\n\nfunction maxStepStatus(\n current: MissionStreamStepStatus,\n next: MissionStreamStepStatus,\n): MissionStreamStepStatus {\n // Equal rank but different terminal kind (done vs failed): the FIRST terminal\n // wins — a step.completed is authoritative and a later contradicting one is a\n // duplicate-class artifact we ignore.\n if (STEP_RANK[next] <= STEP_RANK[current]) return current\n return next\n}\n\nfunction maxMissionStatus(\n current: MissionStreamStatus,\n next: MissionStreamStatus,\n): MissionStreamStatus {\n if (MISSION_RANK[next] <= MISSION_RANK[current]) return current\n return next\n}\n\nfunction emptyMission(missionId: string): MissionState {\n // Seed at the LOWEST status rank so a fold-in is purely monotonic. A\n // step.started that arrives before mission.created must not pin the mission\n // at `running`: were the create to carry `scheduled`, the reducer would then\n // diverge by arrival order (created-first → scheduled, started-first →\n // running). Seeding `scheduled` makes every status edge climb upward only, so\n // both orders converge to the same state.\n return {\n missionId,\n status: 'scheduled',\n steps: [],\n spentUsd: 0,\n lastEventAt: 0,\n lastControlAt: 0,\n }\n}\n\nfunction stepStateFrom(step: MissionStreamStep): MissionStepState {\n return { id: step.id, intent: step.intent, kind: step.kind, status: step.status }\n}\n\n/**\n * Fold one event into one mission's state. PURE: returns a new state, mutates\n * nothing. Idempotent + order-tolerant — every status move is clamped through\n * the monotonic ranks above, so duplicates and out-of-order delivery converge\n * to the same terminal state regardless of arrival order.\n */\nexport function applyMissionEvent(\n prev: MissionState | undefined,\n event: MissionStreamEvent,\n): MissionState {\n const at = typeof event.at === 'number' ? event.at : 0\n const base = prev ?? emptyMission(event.missionId)\n const lastEventAt = Math.max(base.lastEventAt, at)\n const lastControlAt = base.lastControlAt ?? 0\n\n switch (event.type) {\n case 'mission.created': {\n // The authoritative plan snapshot. Merge rather than overwrite so a\n // late-arriving create (e.g. after the client already saw a step.started\n // from a reconnect race) does not clobber forward step progress.\n const merged = event.steps.map((incoming) => {\n const existing = base.steps.find((s) => s.id === incoming.id)\n if (!existing) return stepStateFrom(incoming)\n return {\n ...existing,\n intent: incoming.intent,\n kind: incoming.kind,\n status: maxStepStatus(existing.status, incoming.status),\n }\n })\n // Keep any steps the client already knew that the snapshot omits (it\n // never should, but never drop known progress).\n for (const known of base.steps) {\n if (!merged.some((s) => s.id === known.id)) merged.push(known)\n }\n return {\n ...base,\n title: event.title || base.title,\n status: maxMissionStatus(base.status, event.status ?? base.status),\n capUsd: event.budgetUsd ?? base.capUsd,\n steps: merged,\n lastEventAt,\n }\n }\n\n case 'mission.started':\n return { ...base, status: maxMissionStatus(base.status, 'running'), lastEventAt }\n\n case 'step.started':\n return {\n ...base,\n steps: upsertStep(base.steps, event.stepId, (step) => ({\n ...step,\n status: maxStepStatus(step.status, 'running'),\n })),\n lastEventAt,\n }\n\n case 'step.updated':\n return {\n ...base,\n steps: upsertStep(base.steps, event.stepId, (step) => ({\n ...step,\n // A sublabel is a live counter (\"7/15\") — always take the latest; it\n // does not move status.\n ...(event.sublabel !== undefined ? { sublabel: event.sublabel } : {}),\n // agentActivity is a full snapshot, replaced wholesale. Guarded by\n // `at` so a stale snapshot delivered late never erases newer rows;\n // an equal-`at` replay rewrites identical content (idempotent).\n ...(event.agentActivity !== undefined && at >= (step.agentActivityAt ?? 0)\n ? { agentActivity: event.agentActivity, agentActivityAt: at }\n : {}),\n })),\n lastEventAt,\n }\n\n case 'step.completed':\n return {\n ...base,\n steps: upsertStep(base.steps, event.stepId, (step) => ({\n ...step,\n status: maxStepStatus(step.status, event.ok ? 'done' : 'failed'),\n ...(event.reason !== undefined ? { reason: event.reason } : {}),\n ...(event.durationMs !== undefined ? { durationMs: event.durationMs } : {}),\n })),\n lastEventAt,\n }\n\n case 'cost.updated':\n return {\n ...base,\n // spentUsd is cumulative and monotonically non-decreasing at the\n // source; clamp so an out-of-order older value never lowers the\n // displayed spend.\n spentUsd: Math.max(base.spentUsd, event.spentUsd),\n capUsd: event.capUsd ?? base.capUsd,\n lastEventAt,\n }\n\n case 'mission.paused':\n if (at <= lastControlAt) return { ...base, lastEventAt }\n return {\n ...base,\n status: maxMissionStatus(base.status, 'paused'),\n ...(event.reason !== undefined ? { pauseReason: event.reason } : {}),\n lastEventAt,\n lastControlAt: Math.max(lastControlAt, at),\n }\n\n case 'mission.waiting_approval':\n if (at <= lastControlAt) return { ...base, lastEventAt }\n return {\n ...base,\n status: maxMissionStatus(base.status, 'waiting_approval'),\n ...(event.reason !== undefined ? { pauseReason: event.reason } : {}),\n lastEventAt,\n lastControlAt: Math.max(lastControlAt, at),\n }\n\n case 'mission.resumed':\n if (at <= lastControlAt) return { ...base, lastEventAt }\n return {\n ...base,\n status: isTerminalStreamStatus(base.status) ? base.status : 'running',\n pauseReason: undefined,\n lastEventAt,\n lastControlAt: Math.max(lastControlAt, at),\n }\n\n case 'mission.plan.updated':\n return {\n ...base,\n title: event.title || base.title,\n capUsd: event.budgetUsd ?? base.capUsd,\n steps: event.steps.map((incoming) => {\n const existing = base.steps.find((s) => s.id === incoming.id)\n if (!existing) return stepStateFrom(incoming)\n return {\n ...stepStateFrom(incoming),\n status: maxStepStatus(existing.status, incoming.status),\n ...(existing.sublabel !== undefined ? { sublabel: existing.sublabel } : {}),\n ...(existing.reason !== undefined ? { reason: existing.reason } : {}),\n ...(existing.durationMs !== undefined ? { durationMs: existing.durationMs } : {}),\n ...(existing.agentActivity !== undefined ? { agentActivity: existing.agentActivity } : {}),\n ...(existing.agentActivityAt !== undefined ? { agentActivityAt: existing.agentActivityAt } : {}),\n }\n }),\n lastEventAt,\n }\n\n case 'mission.completed':\n return {\n ...base,\n status: maxMissionStatus(base.status, event.status ?? (event.ok ? 'succeeded' : 'failed')),\n ...(event.summary !== undefined ? { summary: event.summary } : {}),\n lastEventAt,\n }\n }\n}\n\nfunction isTerminalStreamStatus(status: MissionStreamStatus): boolean {\n return status === 'succeeded' || status === 'failed' || status === 'aborted' || status === 'cancelled'\n}\n\n// Upsert one step by id, applying `patch`. A step unknown to the client (e.g. a\n// step.started arriving before the create snapshot on a reconnect) is created\n// as a minimal `pending` row so progress is never dropped; the create snapshot\n// fills in intent/kind when it arrives.\nfunction upsertStep(\n steps: MissionStepState[],\n stepId: string,\n patch: (step: MissionStepState) => MissionStepState,\n): MissionStepState[] {\n const index = steps.findIndex((s) => s.id === stepId)\n const existing = index < 0 ? undefined : steps[index]\n if (!existing) {\n const placeholder: MissionStepState = {\n id: stepId,\n intent: '',\n kind: '',\n status: 'pending',\n }\n return [...steps, patch(placeholder)]\n }\n const next = steps.slice()\n next[index] = patch(existing)\n return next\n}\n\n/**\n * Merge a loader SEED into the live state for one mission, advancing through\n * the SAME monotonic clamps the event reducer uses. The durable mission row is\n * the authoritative converged state: while the live channel is down the row\n * advances but the frozen live state does not, and nothing re-fires the gap to\n * a reconnecting client. Folding the seed THROUGH the clamps backfills that gap\n * on reconnect while never regressing a more-advanced live value:\n * - a stale seed for a more-advanced live mission is a no-op,\n * - an advanced seed after an outage fills the gap (status/steps/spend move\n * forward to the row's converged state).\n * `live === undefined` (mission unknown to the client) just adopts the seed.\n */\nexport function mergeMissionState(live: MissionState | undefined, seed: MissionState): MissionState {\n if (!live) return seed\n\n // The loader seed carries the authoritative current plan. Rebuild from it,\n // while preserving live progress fields for matching ids and retaining\n // live-only steps that have real progress evidence.\n const steps: MissionStepState[] = []\n for (const seededStep of seed.steps) {\n const current = live.steps.find((s) => s.id === seededStep.id)\n if (!current) {\n steps.push(seededStep)\n continue\n }\n steps.push({\n ...seededStep,\n intent: current.intent || seededStep.intent,\n kind: current.kind || seededStep.kind,\n status: maxStepStatus(current.status, seededStep.status),\n ...(current.sublabel !== undefined ? { sublabel: current.sublabel } : seededStep.sublabel !== undefined ? { sublabel: seededStep.sublabel } : {}),\n ...(current.reason !== undefined ? { reason: current.reason } : seededStep.reason !== undefined ? { reason: seededStep.reason } : {}),\n ...(current.durationMs !== undefined ? { durationMs: current.durationMs } : seededStep.durationMs !== undefined ? { durationMs: seededStep.durationMs } : {}),\n // The newer snapshot wins by its stamped `at`; an unstamped lane (loader\n // seed copied from the settled artifact) only fills an empty live lane.\n ...(mergeActivity(current, seededStep)),\n })\n }\n for (const current of live.steps) {\n if (seed.steps.some((step) => step.id === current.id)) continue\n if (hasStepProgressEvidence(current)) steps.push(current)\n }\n\n const status = mergeSeedMissionStatus(live.status, seed.status, live.lastControlAt ?? 0, seed.lastControlAt ?? 0)\n return {\n ...live,\n title: live.title ?? seed.title,\n status,\n steps,\n spentUsd: Math.max(live.spentUsd, seed.spentUsd),\n capUsd: live.capUsd ?? seed.capUsd,\n pauseReason: status === 'running' || status === 'scheduled' ? undefined : seed.pauseReason ?? live.pauseReason,\n summary: live.summary ?? seed.summary,\n lastEventAt: Math.max(live.lastEventAt, seed.lastEventAt),\n lastControlAt: Math.max(live.lastControlAt ?? 0, seed.lastControlAt ?? 0),\n }\n}\n\nfunction hasStepProgressEvidence(step: MissionStepState): boolean {\n return step.status !== 'pending' ||\n step.sublabel !== undefined ||\n step.reason !== undefined ||\n step.durationMs !== undefined ||\n step.agentActivity !== undefined\n}\n\n// Pick the activity lane to keep when merging a loader seed into live state:\n// the snapshot with the larger `at` wins; ties keep the live side.\nfunction mergeActivity(\n live: MissionStepState,\n seeded: MissionStepState,\n): Pick<MissionStepState, 'agentActivity' | 'agentActivityAt'> {\n const winner = (seeded.agentActivityAt ?? 0) > (live.agentActivityAt ?? 0) ? seeded : live\n if (winner.agentActivity === undefined) {\n const fallback = winner === live ? seeded : live\n if (fallback.agentActivity === undefined) return {}\n return {\n agentActivity: fallback.agentActivity,\n ...(fallback.agentActivityAt !== undefined ? { agentActivityAt: fallback.agentActivityAt } : {}),\n }\n }\n return {\n agentActivity: winner.agentActivity,\n ...(winner.agentActivityAt !== undefined ? { agentActivityAt: winner.agentActivityAt } : {}),\n }\n}\n\nfunction mergeSeedMissionStatus(\n liveStatus: MissionStreamStatus,\n seedStatus: MissionStreamStatus,\n liveControlAt: number,\n seedControlAt: number,\n): MissionStreamStatus {\n if (isTerminalStreamStatus(liveStatus)) return liveStatus\n if (isTerminalStreamStatus(seedStatus)) return seedStatus\n if ((seedStatus === 'paused' || seedStatus === 'waiting_approval') && liveControlAt > seedControlAt) {\n return liveStatus\n }\n if (seedStatus === 'running') return 'running'\n if (seedStatus === 'scheduled' && liveStatus !== 'scheduled') return liveStatus\n return maxMissionStatus(liveStatus, seedStatus)\n}\n\n/**\n * Fold a whole event sequence into a Map<missionId, MissionState>. PURE and\n * order-tolerant: feeding the same events in any order (with duplicates)\n * converges to the same map. `seed` lets a reload start from loader-rehydrated\n * state before live events arrive.\n */\nexport function reduceMissionEvents(\n events: MissionStreamEvent[],\n seed?: Map<string, MissionState>,\n): Map<string, MissionState> {\n const next = new Map<string, MissionState>(seed ?? [])\n for (const event of events) {\n next.set(event.missionId, applyMissionEvent(next.get(event.missionId), event))\n }\n return next\n}\n","/**\n * Mission execution engine — drives one mission's plan to completion under a\n * SINGLE serialized owner (a Cloudflare Workflow, a Durable Object alarm, a\n * queue consumer — one per mission). The owner wraps each `runStep` call in its\n * durable-step primitive (e.g. Workflows `step.do(step.id, …)`) so a completed\n * step's result is persisted and replayed instead of re-run after a mid-run\n * restart. This module holds the logic that must be correct independent of any\n * runtime, so it is injectable and unit-testable with the dispatch mocked.\n *\n * Idempotency is layered, belt-and-suspenders:\n * 1. The owner's durable-step cache replays a completed step's result.\n * 2. `runStep` re-reads the mission first; a step already `done` (with a\n * resultRef) returns the cached pointer WITHOUT re-dispatching — this\n * closes the at-least-once window where a callback re-runs after the\n * side effect committed but before the owner durably recorded it.\n * 3. The cursor advances only after a step is `done`, so a fresh run resumes\n * from `mission.cursor` and never re-touches earlier steps.\n *\n * Seams (the product supplies domain; the engine owns mechanism):\n * - {@link SandboxDispatch} — how a step actually executes.\n * - {@link MissionEngineOptions.estimateStepCostUsd} — per-step USD estimate.\n * - {@link MissionGateOptions.classifyStep} — which steps need approval.\n * - {@link MissionApprovalsPort} — where gate proposals live and how they\n * resolve.\n */\n\nimport type {\n MissionCostLedger,\n MissionOutcome,\n MissionRecord,\n MissionService,\n MissionStatus,\n MissionStep,\n} from './service'\nimport { isMissionStopRequested, isMissionTerminal } from './service'\nimport { noopEventSink, type MissionEventSink, type MissionStreamEvent } from './events'\n\n/**\n * A side-effecting unit of per-step work. The owner supplies the real\n * implementation (e.g. a detached sandbox-session dispatcher); tests supply a\n * mock. MUST return a SMALL pointer — large output is written to the product's\n * storage and only the resultRef is returned.\n */\nexport type SandboxDispatch = (input: SandboxDispatchInput) => Promise<SandboxDispatchResult>\n\nexport interface SandboxDispatchInput {\n mission: MissionRecord\n step: MissionStep\n stepIndex: number\n}\n\nexport interface SandboxDispatchDoneResult {\n kind?: 'done'\n /** Small pointer at the produced artifact/output (vault path, asset id, exec\n * digest). Stored on the step as `resultRef`; never the full payload. */\n resultRef: string\n /** Optional one-line status surfaced on the step row. */\n sublabel?: string\n /** Optional marginal spend for this step. `ledgerDelta` carries platform-\n * reported truth (real token counts, wall time); `deltaUsd` is set ONLY when\n * a provider-authored price is known. Omit fields rather than synthesizing\n * zeros — the engine substitutes its injected per-step estimate for a\n * missing deltaUsd and records that estimate in the ledger. */\n cost?: {\n deltaUsd?: number\n ledgerDelta?: Partial<MissionCostLedger>\n }\n}\n\n/** The dispatched step's detached session is still executing on the platform.\n * The owner sleeps `pollAfterMs` and re-invokes the step; the dispatch is\n * idempotent on the session ref, so the re-invocation settles the same session\n * rather than starting a second run. */\nexport interface SandboxDispatchInProgressResult {\n kind: 'in_progress'\n sessionRef: string\n pollAfterMs: number\n sublabel?: string\n}\n\nexport type SandboxDispatchResult = SandboxDispatchDoneResult | SandboxDispatchInProgressResult\n\n/** Outcome of running a single step. `cached` distinguishes a replay/skip\n * (step was already done) from a fresh execution so the engine and its tests\n * can assert the dispatch was NOT re-invoked. */\nexport type StepOutcome =\n | { kind: 'done'; resultRef: string; cached: boolean }\n | { kind: 'in_progress'; sessionRef: string; pollAfterMs: number; sublabel?: string }\n | { kind: 'skipped-cursor'; reason: string }\n | { kind: 'failed'; error: string; fatal: boolean }\n\n/** Outcome of running the whole plan from the cursor to the end. */\nexport type PlanOutcome =\n | { kind: 'completed'; summary: string }\n | { kind: 'in_progress'; stepId: string; sessionRef: string; pollAfterMs: number; sublabel?: string }\n | { kind: 'failed'; failedStepId: string; error: string }\n | { kind: 'halted'; status: MissionStatus; reason?: string | null }\n | { kind: 'terminal'; status: MissionStatus }\n | { kind: 'not-found' }\n\ntype StepGateOutcome =\n | { kind: 'continue' }\n | { kind: 'halted'; status: MissionStatus; reason: string }\n\nexport interface MissionPlanRunOptions {\n /** Pre-step veto (kill switch, schedule window). A non-null return pauses the\n * mission with that reason before the step's side effect starts. */\n beforeStep?: (mission: MissionRecord, step: MissionStep) => Promise<string | null>\n}\n\n/** Thrown to make the owner's durable-step wrapper retry. The single-owner\n * invariant makes a genuine concurrent change rare (it means another writer\n * touched the row), so retrying — rather than corrupting state by forcing a\n * stale write — is the correct response. Distinct from a task failure, which\n * is recorded on the step. */\nexport class MissionConcurrencyError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'MissionConcurrencyError'\n }\n}\n\n/** Thrown by a {@link SandboxDispatch} for a TRANSIENT failure (platform blip,\n * exec-time network fault) that should be re-attempted. `runStep` RE-THROWS it\n * so the owner engages its bounded retry+backoff; the step is left `running`\n * and the re-dispatch is made idempotent by the cached-done guard. A\n * deterministic failure must be a plain Error instead — that is recorded as a\n * fatal `failed` step and is never retried (no money-burning loop on a\n * deterministic error). */\nexport class RetryableStepError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'RetryableStepError'\n }\n}\n\n/** Resolution states a gate proposal can be in. `approved`/`executed` unblock\n * the gated step; everything else keeps the mission parked. */\nexport type MissionProposalResolution = 'pending' | 'approved' | 'rejected' | 'executed' | 'ignored'\n\nexport type MissionGateKind = 'step' | 'budget' | 'volume'\n\n/** Product classification of one step. Returned by\n * {@link MissionGateOptions.classifyStep}; the matching rules (regexes, intent\n * vocabularies, path allowlists) are product domain and never live here. */\nexport interface StepGateClassification {\n /** Product approval-type label persisted on the proposal ('generate',\n * 'integration_invoke', …). */\n type: string\n /** Counted against the per-mission external-action volume cap. */\n externalAction?: boolean\n estCostUsd?: number | null\n}\n\n/** A gate proposal the engine asks the product to persist. The id is\n * deterministic per (gate, mission, step) — see the `*ProposalId` helpers —\n * so a replay re-finds the same proposal instead of duplicating it. The\n * product composes its own title/description from the structured fields. */\nexport interface MissionGateProposal {\n id: string\n missionId: string\n stepId: string\n gate: MissionGateKind\n mission: MissionRecord\n step: MissionStep\n /** Present for `gate: 'step'` — the classification that triggered the gate. */\n classification?: StepGateClassification\n /** Present for `gate: 'budget'`. */\n budget?: { spentUsd: number; budgetUsd: number; estimatedCostUsd: number }\n /** Present for `gate: 'volume'`. */\n volume?: { externalActionCount: number; cap: number }\n}\n\n/** Approval persistence seam — the product implements this over its own\n * proposal table and resolution flow. */\nexport interface MissionApprovalsPort {\n /** Resolution of the proposal with this id, or null when none exists. */\n findResolution(proposalId: string): Promise<MissionProposalResolution | null>\n /** Persist a new gate proposal (id is deterministic; called at most once per\n * (gate, mission, step) absent a resolution). */\n createProposal(proposal: MissionGateProposal): Promise<void>\n /** Count of this mission's `gate: 'step'` proposals whose classification was\n * `externalAction: true` — the denominator of the volume cap. */\n countExternalActionProposals(missionId: string): Promise<number>\n}\n\nexport interface MissionGateOptions {\n approvals: MissionApprovalsPort\n /** Which steps need human approval, and as what. Return null for an ungated\n * step. The rules are product domain (intent regexes, kind tables). */\n classifyStep: (step: MissionStep) => StepGateClassification | null\n /** Max external-action approvals per mission before an approved override is\n * required to request another. Default 5. */\n externalActionCap?: number\n}\n\nexport interface MissionEngineOptions {\n service: MissionService\n /** Per-step USD estimate. Load-bearing twice: the budget gate parks on it\n * BEFORE a step runs, and the engine records it as the step's spend when the\n * dispatch reports no provider-authored price — using one estimator keeps\n * spend and gate consistent. */\n estimateStepCostUsd: (step: MissionStep) => number\n /** Best-effort live notifier. Fired AFTER each guarded write commits, so a\n * broadcast always reflects persisted state; re-fired on idempotent replays\n * so a reconnecting client converges. Never awaited; a throwing sink can\n * never fail a step. Default: drop everything. */\n sink?: MissionEventSink\n /** Approval gating. Omitted → no classification/volume gates, and a budget\n * overrun pauses the mission (fail closed) instead of parking it\n * waiting_approval behind an override proposal. */\n gates?: MissionGateOptions\n /** Step kinds whose failure does NOT abort the whole mission — enrichment\n * steps the plan can complete without. Every other kind is fatal-on-failure.\n * Default `['optional', 'best-effort']`. */\n nonFatalStepKinds?: readonly string[]\n}\n\nexport interface MissionEngine {\n /** Run exactly one plan step. Idempotent: re-invoking for a step already\n * `done` returns the cached pointer without re-dispatching. A lost guarded\n * race throws {@link MissionConcurrencyError} so the owner's durable-step\n * wrapper retries instead of writing a stale value. */\n runStep(missionId: string, stepId: string, dispatch: SandboxDispatch): Promise<StepOutcome>\n /** Walk the plan from the durable cursor to the end, re-reading the mission\n * between steps so a pause/stop control that lands while a step is running\n * is honored before the next side effect. `runStep` is the owner's boundary:\n * in production `(step) => durableStep.do(step.id, () => engine.runStep(…))`;\n * in tests `engine.runStep` directly. */\n runPlan(\n missionId: string,\n runStep: (step: MissionStep, stepIndex: number) => Promise<StepOutcome>,\n options?: MissionPlanRunOptions,\n ): Promise<PlanOutcome>\n /** Record spend durable-first, live second: the guarded ledger write commits,\n * then the sink sees the new total. A guarded failure returns unchanged. */\n recordCost(\n missionId: string,\n deltaUsd: number,\n ledgerDelta?: Partial<MissionCostLedger>,\n ): Promise<MissionOutcome<MissionRecord>>\n /** Pause durable-first, live second (the paused event fires only on a real\n * edge, not an idempotent re-pause). */\n pauseMission(missionId: string, reason: string): Promise<MissionOutcome<MissionRecord>>\n}\n\nconst DEFAULT_EXTERNAL_ACTION_CAP = 5\nconst DEFAULT_NON_FATAL_STEP_KINDS: readonly string[] = ['optional', 'best-effort']\n\n/** Deterministic proposal id for a step-classification gate. */\nexport function stepGateProposalId(missionId: string, stepId: string): string {\n return `mission-step-gate:${missionId}:${stepId}`\n}\n\n/** Deterministic proposal id for a budget-overrun override. */\nexport function budgetGateProposalId(missionId: string, stepId: string): string {\n return `mission-budget-gate:${missionId}:${stepId}`\n}\n\n/** Deterministic proposal id for an external-action volume-cap override. */\nexport function volumeGateProposalId(missionId: string, stepId: string): string {\n return `mission-volume-gate:${missionId}:${stepId}`\n}\n\n// Emit a live event without letting the sink's failure touch the step path.\n// The engine owns the fire-and-forget guarantee at this boundary: a sink that\n// throws synchronously can NEVER fail a step. The durable audit row is the\n// authoritative timeline; the live channel is a convenience.\nfunction safeEmit(sink: MissionEventSink, event: MissionStreamEvent): void {\n try {\n sink.emit(event)\n } catch {\n // Best-effort UI notification — a broadcast fault never fails the step.\n }\n}\n\nfunction unblocked(resolution: MissionProposalResolution | null): boolean {\n return resolution === 'approved' || resolution === 'executed'\n}\n\n// Map a durable terminal status onto the live `mission.completed` event. Only\n// `succeeded` is ok=true; aborted/cancelled/failed fold to ok=false with the\n// terminal status preserved so the reducer keeps user stops distinct from\n// failures.\nfunction terminalMissionEvent(\n missionId: string,\n status: MissionStatus,\n): Extract<MissionStreamEvent, { type: 'mission.completed' }> {\n const terminal = status === 'succeeded' || status === 'failed' || status === 'aborted' || status === 'cancelled'\n ? status\n : 'failed'\n return {\n type: 'mission.completed',\n missionId,\n at: Date.now(),\n ok: terminal === 'succeeded',\n status: terminal,\n ...(terminal === 'succeeded' ? {} : { summary: `Mission ${status}` }),\n }\n}\n\nexport function createMissionEngine(options: MissionEngineOptions): MissionEngine {\n const { service, estimateStepCostUsd, gates } = options\n const sink = options.sink ?? noopEventSink\n const nonFatalStepKinds = new Set(options.nonFatalStepKinds ?? DEFAULT_NON_FATAL_STEP_KINDS)\n const externalActionCap = gates?.externalActionCap ?? DEFAULT_EXTERNAL_ACTION_CAP\n\n function isFatalStepKind(kind: string): boolean {\n return !nonFatalStepKinds.has(kind)\n }\n\n // A guarded service rejection is one of two things:\n // - a lost race (conflict: true) — THROW so the owner's durable-step\n // wrapper retries; the single owner makes this rare and a retry re-reads\n // fresh state rather than forcing a stale write.\n // - a logic rejection (illegal transition, missing step) — that step is\n // structurally broken; return a fatal `failed` outcome instead of\n // looping a retry forever (no money-burning retry on a deterministic\n // error).\n function rejectStep(failure: { error: string; conflict: boolean }): StepOutcome {\n if (failure.conflict) throw new MissionConcurrencyError(failure.error)\n return { kind: 'failed', error: failure.error, fatal: true }\n }\n\n function isStepCurrentOrFuture(mission: MissionRecord, stepId: string): boolean {\n const index = mission.plan.findIndex((candidate) => candidate.id === stepId)\n return index >= mission.cursor\n }\n\n const recordCost: MissionEngine['recordCost'] = async (missionId, deltaUsd, ledgerDelta) => {\n const recorded = await service.addCost(missionId, deltaUsd, ledgerDelta)\n if (!recorded.succeeded) return recorded\n safeEmit(sink, {\n type: 'cost.updated',\n missionId,\n at: Date.now(),\n spentUsd: recorded.value.spentUsd,\n capUsd: recorded.value.budgetUsd,\n })\n return recorded\n }\n\n const pauseMission: MissionEngine['pauseMission'] = async (missionId, reason) => {\n const before = await service.getMission(missionId)\n const paused = await service.pause(missionId, reason)\n if (!paused.succeeded) return paused\n if (before?.status !== 'paused') {\n safeEmit(sink, {\n type: 'mission.paused',\n missionId,\n at: Date.now(),\n reason: paused.value.pauseReason ?? reason,\n })\n }\n return paused\n }\n\n const runStep: MissionEngine['runStep'] = async (missionId, stepId, dispatch) => {\n const mission = await service.getMission(missionId)\n if (!mission) throw new MissionConcurrencyError(`Mission ${missionId} not found mid-run`)\n\n const stepIndex = mission.plan.findIndex((candidate) => candidate.id === stepId)\n const step = stepIndex < 0 ? undefined : mission.plan[stepIndex]\n if (!step) {\n return { kind: 'skipped-cursor', reason: `Step ${stepId} is no longer in mission plan` }\n }\n if (isMissionStopRequested(mission)) {\n return { kind: 'failed', error: mission.pauseReason ?? 'Mission stop requested', fatal: true }\n }\n if (mission.status !== 'running') {\n return { kind: 'skipped-cursor', reason: mission.pauseReason ?? `Mission is ${mission.status}` }\n }\n\n // (2) Idempotent short-circuit: a step that already reached `done` keeps\n // its resultRef. Return it WITHOUT re-running the side effect. Reconcile\n // the cursor first: a crash in the window between setStepStatus('done')\n // and advanceCursor leaves the cursor one slot behind this done step; a\n // fresh resume would otherwise end with cursor = plan.length - 1. Advance\n // it here (guarded — a lost race throws for the owner to retry) so the\n // cursor stays a true done-count.\n if (step.status === 'done' && step.resultRef) {\n if (stepIndex === mission.cursor) {\n const reconciled = await service.advanceCursor(missionId)\n if (!reconciled.succeeded) return rejectStep(reconciled)\n }\n // Re-emit the terminal event on replay so a client that connected after\n // the step finished still folds it (idempotent at the reducer).\n safeEmit(sink, { type: 'step.completed', missionId, at: Date.now(), stepId, ok: true })\n return { kind: 'done', resultRef: step.resultRef, cached: true }\n }\n\n // (3) The cursor has already moved past this step but it is not `done` —\n // the owner is replaying a step the engine no longer considers active.\n // Skip it rather than re-run; the cursor is the source of truth.\n if (stepIndex < mission.cursor) {\n return { kind: 'skipped-cursor', reason: `Step ${stepId} is behind cursor ${mission.cursor}` }\n }\n\n const startedAt = Date.now()\n if (step.status !== 'running') {\n const running = await service.setStepStatus(missionId, stepId, 'running')\n if (!running.succeeded) return rejectStep(running)\n }\n safeEmit(sink, { type: 'step.started', missionId, at: startedAt, stepId })\n\n let dispatched: SandboxDispatchResult\n try {\n dispatched = await dispatch({ mission, step, stepIndex })\n } catch (error) {\n // A TRANSIENT failure re-throws so the owner engages its bounded\n // retry+backoff: leave the step `running` (the re-dispatch is idempotent\n // via the cached-done guard) and let the owner re-attempt up to its limit.\n if (error instanceof RetryableStepError) throw error\n const latest = await service.getMission(missionId)\n if (\n latest &&\n !isMissionTerminal(latest.status) &&\n !isMissionStopRequested(latest) &&\n !isStepCurrentOrFuture(latest, stepId)\n ) {\n return { kind: 'skipped-cursor', reason: `Step ${stepId} is no longer active` }\n }\n // A deterministic failure is recorded as a fatal `failed` step and is\n // NOT retried — looping a retry on a deterministic error burns money.\n const message = error instanceof Error ? error.message : 'Sandbox dispatch failed'\n const failed = await service.setStepStatus(missionId, stepId, 'failed', { error: message })\n if (!failed.succeeded) return rejectStep(failed)\n safeEmit(sink, {\n type: 'step.completed',\n missionId,\n at: Date.now(),\n stepId,\n ok: false,\n reason: message,\n durationMs: Date.now() - startedAt,\n })\n return { kind: 'failed', error: message, fatal: isFatalStepKind(step.kind) }\n }\n\n const afterDispatch = await service.getMission(missionId)\n if (!afterDispatch) throw new MissionConcurrencyError(`Mission ${missionId} not found after dispatch`)\n if (isMissionTerminal(afterDispatch.status) || isMissionStopRequested(afterDispatch)) {\n return { kind: 'failed', error: afterDispatch.pauseReason ?? 'Mission stop requested', fatal: true }\n }\n if (afterDispatch.status !== 'running') {\n return { kind: 'skipped-cursor', reason: afterDispatch.pauseReason ?? `Mission is ${afterDispatch.status}` }\n }\n if (!isStepCurrentOrFuture(afterDispatch, stepId)) {\n return { kind: 'skipped-cursor', reason: `Step ${stepId} is no longer active` }\n }\n\n // The detached session is still running: surface the elapsed sublabel and\n // hand the poll cadence to the owner. The step row stays `running`.\n if (dispatched.kind === 'in_progress') {\n if (dispatched.sublabel !== undefined) {\n safeEmit(sink, { type: 'step.updated', missionId, at: Date.now(), stepId, sublabel: dispatched.sublabel })\n }\n return {\n kind: 'in_progress',\n sessionRef: dispatched.sessionRef,\n pollAfterMs: dispatched.pollAfterMs,\n ...(dispatched.sublabel === undefined ? {} : { sublabel: dispatched.sublabel }),\n }\n }\n\n // A live counter (\"7/15\") the dispatch surfaced — push it before the\n // terminal event so a long step shows progress, then settles to done.\n if (dispatched.sublabel !== undefined) {\n safeEmit(sink, { type: 'step.updated', missionId, at: Date.now(), stepId, sublabel: dispatched.sublabel })\n }\n\n // USD: provider-authored price when the dispatch reports one, the injected\n // estimate otherwise (the budget gate runs on the same estimate, so spend\n // and gate stay consistent). Token counts/wall time come from the\n // dispatch's ledgerDelta when the platform reported them. The spend is\n // folded into the SAME guarded pending->done write below so step completion\n // is the per-step idempotency key: a RESUME re-dispatching an already-done\n // step short-circuits as a no-op and never charges twice.\n const deltaUsd = dispatched.cost?.deltaUsd ?? estimateStepCostUsd(step)\n const chargeable = deltaUsd > 0 || Boolean(dispatched.cost?.ledgerDelta)\n const spentBefore = afterDispatch.spentUsd\n\n const done = await service.setStepStatus(missionId, stepId, 'done', {\n resultRef: dispatched.resultRef,\n ...(dispatched.sublabel === undefined ? {} : { sublabel: dispatched.sublabel }),\n ...(chargeable\n ? {\n cost: {\n deltaUsd,\n ledgerDelta: {\n costUsd: deltaUsd,\n llmCalls: 1,\n ...(dispatched.cost?.ledgerDelta ?? {}),\n },\n },\n }\n : {}),\n })\n if (!done.succeeded) return rejectStep(done)\n // Emit cost.updated only when the spend actually committed (a real\n // transition, not a replayed no-op that re-asserted an already-done step).\n if (done.value.spentUsd !== spentBefore) {\n safeEmit(sink, {\n type: 'cost.updated',\n missionId,\n at: Date.now(),\n spentUsd: done.value.spentUsd,\n capUsd: done.value.budgetUsd,\n })\n }\n safeEmit(sink, {\n type: 'step.completed',\n missionId,\n at: Date.now(),\n stepId,\n ok: true,\n durationMs: Date.now() - startedAt,\n })\n\n // Advance the cursor only after the step is durably `done`. A racing\n // advance (single owner ⇒ should not happen) surfaces as a retry.\n const advanced = await service.advanceCursor(missionId)\n if (!advanced.succeeded) return rejectStep(advanced)\n\n return { kind: 'done', resultRef: dispatched.resultRef, cached: false }\n }\n\n async function parkForApproval(\n mission: MissionRecord,\n step: MissionStep,\n reason: string,\n ): Promise<StepGateOutcome> {\n const waiting = await service.markWaitingApproval(mission.id, step.id)\n if (!waiting.succeeded) {\n if (waiting.conflict) throw new MissionConcurrencyError(waiting.error)\n return { kind: 'halted', status: mission.status, reason: waiting.error }\n }\n safeEmit(sink, {\n type: 'mission.waiting_approval',\n missionId: mission.id,\n at: Date.now(),\n reason,\n })\n safeEmit(sink, {\n type: 'mission.plan.updated',\n missionId: mission.id,\n at: Date.now(),\n title: waiting.value.summary ?? 'Mission',\n steps: waiting.value.plan.map((candidate) => ({\n id: candidate.id,\n intent: candidate.intent,\n kind: candidate.kind,\n status: candidate.status,\n })),\n budgetUsd: waiting.value.budgetUsd,\n })\n return { kind: 'halted', status: 'waiting_approval', reason }\n }\n\n // A budgeted mission never starts a step whose estimate would push spend\n // past the cap. With an approvals port the overrun parks behind a\n // deterministic override proposal; without one it pauses (fail closed) for a\n // manual budget raise + resume.\n async function enforceBudget(mission: MissionRecord, step: MissionStep): Promise<StepGateOutcome> {\n const capUsd = mission.budgetUsd\n if (capUsd === null) return { kind: 'continue' }\n const estimatedCostUsd = estimateStepCostUsd(step)\n if (estimatedCostUsd <= 0) return { kind: 'continue' }\n // Compare in integer cents: USD amounts accumulate binary-float error (e.g.\n // 0.1 + 0.2 > 0.3), so a raw float compare can spuriously trip the gate at\n // the exact cap. Cent-rounding makes the boundary deterministic.\n if (Math.round((mission.spentUsd + estimatedCostUsd) * 100) <= Math.round(capUsd * 100)) {\n return { kind: 'continue' }\n }\n\n if (!gates) {\n const reason = `Budget cap reached before step ${step.id}: $${mission.spentUsd.toFixed(2)} spent of $${capUsd.toFixed(2)}, next step estimated $${estimatedCostUsd.toFixed(2)}`\n const paused = await pauseMission(mission.id, reason)\n if (!paused.succeeded) {\n if (paused.conflict) throw new MissionConcurrencyError(paused.error)\n return { kind: 'halted', status: mission.status, reason: paused.error }\n }\n return { kind: 'halted', status: paused.value.status, reason }\n }\n\n const proposalId = budgetGateProposalId(mission.id, step.id)\n const resolution = await gates.approvals.findResolution(proposalId)\n if (unblocked(resolution)) return { kind: 'continue' }\n if (resolution === null) {\n await gates.approvals.createProposal({\n id: proposalId,\n missionId: mission.id,\n stepId: step.id,\n gate: 'budget',\n mission,\n step,\n budget: { spentUsd: mission.spentUsd, budgetUsd: capUsd, estimatedCostUsd },\n })\n }\n return parkForApproval(mission, step, `Budget approval required for step ${step.id}`)\n }\n\n async function enforceVolumeCap(mission: MissionRecord, step: MissionStep): Promise<StepGateOutcome> {\n if (!gates) return { kind: 'continue' }\n const overrideId = volumeGateProposalId(mission.id, step.id)\n const override = await gates.approvals.findResolution(overrideId)\n if (unblocked(override)) return { kind: 'continue' }\n\n const externalCount = await gates.approvals.countExternalActionProposals(mission.id)\n if (externalCount < externalActionCap) return { kind: 'continue' }\n\n if (override === null) {\n await gates.approvals.createProposal({\n id: overrideId,\n missionId: mission.id,\n stepId: step.id,\n gate: 'volume',\n mission,\n step,\n volume: { externalActionCount: externalCount, cap: externalActionCap },\n })\n }\n return parkForApproval(mission, step, `External action cap approval required for step ${step.id}`)\n }\n\n async function enforceStepGate(mission: MissionRecord, step: MissionStep): Promise<StepGateOutcome> {\n if (!gates) return { kind: 'continue' }\n const classification = gates.classifyStep(step)\n if (!classification) return { kind: 'continue' }\n\n if (classification.externalAction) {\n const volume = await enforceVolumeCap(mission, step)\n if (volume.kind === 'halted') return volume\n }\n\n const proposalId = stepGateProposalId(mission.id, step.id)\n const resolution = await gates.approvals.findResolution(proposalId)\n if (unblocked(resolution)) return { kind: 'continue' }\n\n if (resolution === null) {\n await gates.approvals.createProposal({\n id: proposalId,\n missionId: mission.id,\n stepId: step.id,\n gate: 'step',\n mission,\n step,\n classification,\n })\n }\n return parkForApproval(mission, step, `Approval required for step ${step.id}`)\n }\n\n const runPlan: MissionEngine['runPlan'] = async (missionId, runStepFn, planOptions = {}) => {\n const mission = await service.getMission(missionId)\n if (!mission) return { kind: 'not-found' }\n if (isMissionTerminal(mission.status)) {\n // Already terminal on entry (a replay after the row finished, or a\n // resume of an aborted/cancelled run). Re-emit the terminal event so a\n // client that connected during an outage still converges off this run.\n // Idempotent at the reducer.\n safeEmit(sink, terminalMissionEvent(missionId, mission.status))\n return { kind: 'terminal', status: mission.status }\n }\n\n // Resume from the durable cursor. Re-read the mission between steps so a\n // pause/stop control that lands while a step is running is honored before\n // the next side effect starts.\n while (true) {\n const currentMission = await service.getMission(missionId)\n if (!currentMission) return { kind: 'not-found' }\n if (isMissionTerminal(currentMission.status)) {\n safeEmit(sink, terminalMissionEvent(missionId, currentMission.status))\n return { kind: 'terminal', status: currentMission.status }\n }\n if (isMissionStopRequested(currentMission)) {\n return {\n kind: 'halted',\n status: currentMission.status,\n reason: currentMission.pauseReason ?? 'Mission stop requested',\n }\n }\n if (currentMission.status !== 'running') {\n return {\n kind: 'halted',\n status: currentMission.status,\n reason: currentMission.pauseReason,\n }\n }\n\n const index = currentMission.cursor\n if (index >= currentMission.plan.length) break\n const step = currentMission.plan[index]\n if (!step) break\n\n const haltReason = await planOptions.beforeStep?.(currentMission, step)\n if (haltReason) {\n const paused = await pauseMission(missionId, haltReason)\n if (!paused.succeeded) throw new MissionConcurrencyError(paused.error)\n return { kind: 'halted', status: paused.value.status, reason: paused.value.pauseReason ?? haltReason }\n }\n\n if (step.status !== 'done') {\n const budget = await enforceBudget(currentMission, step)\n if (budget.kind === 'halted') {\n return { kind: 'halted', status: budget.status, reason: budget.reason }\n }\n const gate = await enforceStepGate(currentMission, step)\n if (gate.kind === 'halted') {\n return { kind: 'halted', status: gate.status, reason: gate.reason }\n }\n }\n\n const outcome = await runStepFn(step, index)\n if (outcome.kind === 'failed' && outcome.fatal) {\n // The owner flips the mission row to `failed` on this outcome; emit the\n // live terminal event here so a watcher sees the failure without\n // waiting for the next loader poll. Idempotent at the reducer.\n safeEmit(sink, {\n type: 'mission.completed',\n missionId,\n at: Date.now(),\n ok: false,\n summary: `Step ${step.id} failed: ${outcome.error}`,\n })\n return { kind: 'failed', failedStepId: step.id, error: outcome.error }\n }\n if (outcome.kind === 'failed' && !outcome.fatal) {\n const advanced = await service.advanceCursor(missionId)\n if (!advanced.succeeded) throw new MissionConcurrencyError(advanced.error)\n }\n if (outcome.kind === 'in_progress') {\n return {\n kind: 'in_progress',\n stepId: step.id,\n sessionRef: outcome.sessionRef,\n pollAfterMs: outcome.pollAfterMs,\n ...(outcome.sublabel === undefined ? {} : { sublabel: outcome.sublabel }),\n }\n }\n // Cursor-skips fall through: another pass already moved past it. The next\n // loop re-reads the authoritative cursor and plan.\n }\n\n const finalMission = await service.getMission(missionId)\n const planLength = finalMission?.plan.length ?? 0\n const summary = `Completed ${planLength} step${planLength === 1 ? '' : 's'}`\n const completed = await service.complete(missionId, { ok: true, summary })\n if (!completed.succeeded) {\n // complete() is guarded; a concurrent terminal transition (abort/cancel)\n // wins the race. Re-read to report the real terminal status rather than\n // claim a completion that did not land.\n const after = await service.getMission(missionId)\n if (after && isMissionTerminal(after.status)) {\n safeEmit(sink, terminalMissionEvent(missionId, after.status))\n return { kind: 'terminal', status: after.status }\n }\n throw new MissionConcurrencyError(completed.error)\n }\n safeEmit(sink, { type: 'mission.completed', missionId, at: Date.now(), ok: true, summary })\n return { kind: 'completed', summary }\n }\n\n return { runStep, runPlan, recordCost, pauseMission }\n}\n","/**\n * Parsing for the agent-authored `:::mission` block — the bridge from a chat\n * prompt contract to the engine's MissionStep[] shape. The block format:\n *\n * :::mission\n * title: <mission title>\n * <id>: <kind> | <intent>\n * :::\n *\n * The allowed kind vocabulary is a PARAMETER — products pass their own list to\n * match their prompt directive; {@link DEFAULT_MISSION_STEP_KINDS} is the\n * default. Kinds label intent for gating and UX; they never select a different\n * execution path.\n */\n\nimport type { MissionStep } from './service'\n\n/** Default step-kind vocabulary. `best-effort` matches the engine's default\n * non-fatal kind (a failure does not abort the mission); the rest are\n * fatal-on-failure agent sub-tasks. */\nexport const DEFAULT_MISSION_STEP_KINDS: readonly string[] = [\n 'research',\n 'generate',\n 'analyze',\n 'write',\n 'best-effort',\n]\n\nexport interface ParsedMissionStep {\n id: string\n kind: string\n intent: string\n}\n\nexport interface ParsedMission {\n title: string\n steps: ParsedMissionStep[]\n}\n\nexport interface ParseMissionBlocksOptions {\n /** Allowed step kinds (lowercase). Default {@link DEFAULT_MISSION_STEP_KINDS}. */\n kinds?: readonly string[]\n}\n\n/**\n * Parse every well-formed `:::mission` block. A block without a title or\n * without at least one valid step yields nothing (it is malformed — never\n * guess a plan from loose prose). Unknown kinds and malformed step lines are\n * dropped; an empty result lets the caller skip the block rather than start an\n * empty mission.\n */\nexport function parseMissionBlocks(\n fullContent: string,\n options: ParseMissionBlocksOptions = {},\n): ParsedMission[] {\n const kinds = new Set(options.kinds ?? DEFAULT_MISSION_STEP_KINDS)\n const missionRegex = /:::mission\\s*\\n([\\s\\S]*?)\\n\\s*:::/g\n const missions: ParsedMission[] = []\n let match\n while ((match = missionRegex.exec(fullContent)) !== null) {\n const body = match[1]\n if (body === undefined) continue\n const parsed = parseMissionBody(body, kinds)\n if (parsed) missions.push(parsed)\n }\n return missions\n}\n\nfunction parseMissionBody(body: string, kinds: ReadonlySet<string>): ParsedMission | null {\n let title: string | null = null\n const steps: ParsedMissionStep[] = []\n for (const rawLine of body.split('\\n')) {\n const line = rawLine.trim()\n if (!line) continue\n const titleMatch = /^title\\s*:\\s*(.+)$/i.exec(line)\n if (titleMatch?.[1] !== undefined) {\n if (title === null) title = titleMatch[1].trim()\n continue\n }\n // `<id>: <kind> | <intent>`\n const stepMatch = /^([A-Za-z0-9][A-Za-z0-9_-]*)\\s*:\\s*([A-Za-z-]+)\\s*\\|\\s*(.+)$/.exec(line)\n if (!stepMatch) continue\n const id = stepMatch[1]?.trim()\n const kind = stepMatch[2]?.trim().toLowerCase()\n const intent = stepMatch[3]?.trim()\n if (!id || !kind || !intent) continue\n if (!kinds.has(kind)) continue\n steps.push({ id, kind, intent })\n }\n if (!title || steps.length === 0) return null\n return { title, steps }\n}\n\n/**\n * Materialize parsed steps into the engine's MissionStep[] shape. Rejects a\n * duplicate step id (fail loud — the owner keys its durable step cache by\n * step id and `createMission` rejects duplicates anyway; catching it here\n * gives a clearer diagnostic). Every step starts `pending` with zero attempts.\n */\nexport function buildAgentMissionPlan(steps: ParsedMissionStep[]): MissionStep[] {\n if (steps.length === 0) throw new Error('mission plan must have at least one step')\n const seen = new Set<string>()\n for (const step of steps) {\n if (seen.has(step.id)) {\n throw new Error(`duplicate mission step id \"${step.id}\" — step ids must be unique`)\n }\n seen.add(step.id)\n }\n return steps.map((step) => ({\n id: step.id,\n intent: step.intent,\n kind: step.kind,\n status: 'pending' as const,\n attempts: 0,\n }))\n}\n","/**\n * Canonical shape for the per-step agent-activity lane — the delegated runs\n * (delegation MCP registry entries) a mission step spawned, journaled onto the\n * step so a live UI can render \"what the agent is actually doing\" under the\n * step row.\n *\n * CLIENT-SAFE, like everything in `./events`: pure data + a validator. The\n * server attaches `StepAgentActivity[]` when a step settles (and live via\n * `step.updated`); the client re-validates with {@link stepAgentActivity}\n * because the value rides loader/JSON boundaries as untyped metadata.\n */\n\nexport interface StepAgentActivity {\n taskId: string\n /** Delegation profile: coder | researcher | ui-auditor | product-defined. */\n tool: string\n status: string\n /** Title-ish excerpt of the delegation's args (goal / question / audit root). */\n detail: string\n costUsd?: number\n durationMs?: number\n /** ISO timestamp the delegation started. */\n startedAt: string\n /** Live loop progress (DelegationProgress.iteration) while the run is in flight. */\n iteration?: number\n /** Live loop progress (DelegationProgress.phase) while the run is in flight. */\n phase?: string\n /** 32-hex trace id when the delegation joined a mission trace (see `/trace`). */\n traceId?: string\n /** 16-hex span id of the delegation's span inside that trace. */\n spanId?: string\n}\n\n/** Step state extended with the activity lane a loader/seed route attaches. */\nexport type WithAgentActivity<Step> = Step & { agentActivity?: StepAgentActivity[] }\n\n/**\n * Re-validate an `agentActivity` lane that crossed a JSON boundary. Entries\n * missing any required field are dropped (a torn row must not crash the lane);\n * optional fields are kept only when well-typed.\n */\nexport function stepAgentActivity(step: object): StepAgentActivity[] {\n const value = (step as { agentActivity?: unknown }).agentActivity\n if (!Array.isArray(value)) return []\n const items: StepAgentActivity[] = []\n for (const entry of value) {\n if (!entry || typeof entry !== 'object') continue\n const record = entry as Record<string, unknown>\n if (\n typeof record.taskId !== 'string' ||\n typeof record.tool !== 'string' ||\n typeof record.status !== 'string' ||\n typeof record.detail !== 'string' ||\n typeof record.startedAt !== 'string'\n ) {\n continue\n }\n items.push({\n taskId: record.taskId,\n tool: record.tool,\n status: record.status,\n detail: record.detail,\n startedAt: record.startedAt,\n ...(typeof record.costUsd === 'number' && Number.isFinite(record.costUsd) ? { costUsd: record.costUsd } : {}),\n ...(typeof record.durationMs === 'number' && Number.isFinite(record.durationMs) ? { durationMs: record.durationMs } : {}),\n ...(typeof record.iteration === 'number' && Number.isFinite(record.iteration) ? { iteration: record.iteration } : {}),\n ...(typeof record.phase === 'string' ? { phase: record.phase } : {}),\n ...(typeof record.traceId === 'string' ? { traceId: record.traceId } : {}),\n ...(typeof record.spanId === 'string' ? { spanId: record.spanId } : {}),\n })\n }\n return items\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/durable-chat/errors.ts","../src/durable-chat/types.ts","../src/durable-chat/memory.ts","../src/durable-chat/interactions.ts","../src/durable-chat/adapters.ts","../src/durable-chat/plan-routes.ts"],"sourcesContent":["export type DurableChatErrorCode =\n | 'DURABLE_CHAT_BAD_REQUEST'\n | 'DURABLE_CHAT_UNAUTHORIZED'\n | 'DURABLE_CHAT_CONFLICT'\n | 'DURABLE_CHAT_UNAVAILABLE'\n | 'DURABLE_CHAT_GONE'\n | 'DURABLE_CHAT_NOT_FOUND'\n\n/** Typed, fail-loud errors for adapters and route seams. */\nexport class DurableChatError extends Error {\n readonly code: DurableChatErrorCode\n readonly status: number\n readonly details?: unknown\n\n constructor(code: DurableChatErrorCode, message: string, status: number, details?: unknown) {\n super(message)\n this.name = 'DurableChatError'\n this.code = code\n this.status = status\n this.details = details\n }\n}\n\nexport class DurableChatConflictError extends DurableChatError {\n constructor(message = 'durable chat state conflict', details?: unknown) {\n super('DURABLE_CHAT_CONFLICT', message, 409, details)\n this.name = 'DurableChatConflictError'\n }\n}\n\nexport class DurableChatUnavailableError extends DurableChatError {\n constructor(message = 'durable chat authority unavailable', details?: unknown) {\n super('DURABLE_CHAT_UNAVAILABLE', message, 503, details)\n this.name = 'DurableChatUnavailableError'\n }\n}\n\nexport class DurableChatGoneError extends DurableChatError {\n constructor(message = 'durable chat plan is gone', details?: unknown) {\n super('DURABLE_CHAT_GONE', message, 410, details)\n this.name = 'DurableChatGoneError'\n }\n}\n","import type {\n ChatInteraction,\n ChatInteractionField,\n ChatInteractionStatus,\n InteractionAnswerValue,\n InteractionAnswers,\n} from '../interactions/contract'\nimport type { ChatPlan, ChatPlanStatus } from '../plans/index'\n\n/** An authorization-derived tenant/thread scope. Consumers should only\n * create this value after authenticating the request; the route never accepts\n * an identity or scope from the request body. */\nexport type DurableChatScope = string & { readonly __durableChatScope: unique symbol }\n\nexport function createDurableChatScope(value: string): DurableChatScope {\n if (typeof value !== 'string' || value.trim() === '') throw new TypeError('durable chat scope must be a non-empty string')\n return value as DurableChatScope\n}\n\nexport function durableChatScopeKey(scope: DurableChatScope): string {\n if (typeof scope !== 'string' || scope.length === 0) throw new TypeError('durable chat scope is required')\n return scope\n}\n\nexport type DurablePlanDecision = 'approved' | 'rejected'\nexport type DurablePlanAuthorityDecision = DurablePlanDecision | 'approve' | 'reject'\n\n/** Projection retained by a durable store. It is intentionally compatible\n * with the browser `/plans` projection and adds no product-specific fields. */\nexport type DurablePlanProjection = ChatPlan & {\n metadata?: Record<string, unknown>\n decidedBy?: string\n}\n\nexport type DurablePlanCommandKey = string\n\nexport type DurablePlanCommandState =\n | 'claimed'\n | 'authority_committed'\n | 'finalized'\n | 'conflicted'\n\nexport interface DurablePlanCommandRecord {\n scope: DurableChatScope\n planId: string\n revision: number\n decision: DurablePlanDecision\n commandKey: DurablePlanCommandKey\n authorityIdempotencyKey: string\n state: DurablePlanCommandState\n claimedAt: string\n authorityResult?: DurablePlanAuthorityResult\n receipt?: DurableFollowUpReceipt\n conflict?: string\n}\n\nexport interface DurablePlanAuthorityCurrentResult {\n /** Authoritative state. `null` means the authority has forgotten the plan. */\n plan: DurablePlanProjection | null\n receipt?: DurableFollowUpReceipt\n}\n\nexport interface DurableFollowUpReceipt {\n /** Stable for a scope + plan + revision + decision. */\n receiptId: string\n planId: string\n revision: number\n decision: DurablePlanDecision\n turnId: string\n state: string\n /** Authority's stable idempotency key, useful when reconciling. */\n authorityIdempotencyKey: string\n}\n\nexport interface DurablePlanAuthorityResult {\n /** Authority's final plan projection. */\n plan: DurablePlanProjection\n /** Sandbox-style follow-up result. */\n followUp: { turnId: string; state: string }\n /** True when the authority served a previously committed decision. */\n idempotent?: boolean\n /** Stable authority operation id, if the provider has one. */\n authorityId?: string\n receipt?: DurableFollowUpReceipt\n}\n\n/** Structural port to Sandbox (or another durable plan authority). */\nexport interface DurablePlanAuthority {\n current(args: {\n scope: DurableChatScope\n planId: string\n revision?: number\n }): Promise<DurablePlanAuthorityCurrentResult | DurablePlanProjection | null>\n decide(args: {\n scope: DurableChatScope\n planId: string\n revision: number\n decision: DurablePlanDecision\n feedback?: string\n idempotencyKey: string\n }): Promise<DurablePlanAuthorityResult>\n}\n\nexport interface DurablePlanEffectRecord {\n effectKey: string\n scope: DurableChatScope\n planId: string\n revision: number\n decision: DurablePlanDecision\n state: 'claimed' | 'completed' | 'error'\n claimedAt: string\n completedAt?: string\n error?: string\n}\n\nexport interface DurablePlanStore {\n getPlanProjection(scope: DurableChatScope, planId: string, revision?: number): Promise<DurablePlanProjection | null>\n putPlanProjection(scope: DurableChatScope, projection: DurablePlanProjection): Promise<void>\n listPlanProjections?(scope: DurableChatScope, planId?: string): Promise<DurablePlanProjection[]>\n getPlanCommand(scope: DurableChatScope, commandKey: DurablePlanCommandKey): Promise<DurablePlanCommandRecord | null>\n claimPlanCommand(scope: DurableChatScope, command: DurablePlanCommandRecord): Promise<\n | { status: 'claimed'; record: DurablePlanCommandRecord }\n | { status: 'existing'; record: DurablePlanCommandRecord }\n | { status: 'conflict'; record?: DurablePlanCommandRecord; reason: string }\n >\n recordPlanAuthorityResult(scope: DurableChatScope, commandKey: DurablePlanCommandKey, result: DurablePlanAuthorityResult, receipt: DurableFollowUpReceipt): Promise<void>\n finalizePlanCommand(scope: DurableChatScope, commandKey: DurablePlanCommandKey): Promise<void>\n getPlanEffect(scope: DurableChatScope, effectKey: string): Promise<DurablePlanEffectRecord | null>\n claimPlanEffect(scope: DurableChatScope, effect: DurablePlanEffectRecord): Promise<\n | { status: 'claimed'; record: DurablePlanEffectRecord }\n | { status: 'existing'; record: DurablePlanEffectRecord }\n >\n completePlanEffect(scope: DurableChatScope, effectKey: string): Promise<void>\n failPlanEffect(scope: DurableChatScope, effectKey: string, error: string): Promise<void>\n\n getInteractionProjection(scope: DurableChatScope, interactionId: string): Promise<DurableInteractionProjection | null>\n upsertInteractionProjection(scope: DurableChatScope, projection: DurableInteractionProjection): Promise<DurableInteractionProjection>\n listInteractionProjections?(scope: DurableChatScope): Promise<DurableInteractionProjection[]>\n getAnswerIntent(scope: DurableChatScope, intentKey: string): Promise<DurableAnswerIntentRecord | null>\n claimAnswerIntent(scope: DurableChatScope, intent: DurableAnswerIntentRecord): Promise<\n | { status: 'claimed'; record: DurableAnswerIntentRecord }\n | { status: 'existing'; record: DurableAnswerIntentRecord }\n | { status: 'conflict'; record?: DurableAnswerIntentRecord; reason: string }\n >\n acknowledgeAnswerIntent(scope: DurableChatScope, intentKey: string, acknowledgement: DurableInteractionAcknowledgement): Promise<void>\n /** Atomically settle the interaction projection (including semantic aliases)\n * from the acknowledged intent and mark the intent finalized. */\n finalizeAnswerIntent(scope: DurableChatScope, intentKey: string, guarantee?: DurableInteractionGuarantee): Promise<void>\n abortAnswerIntent(scope: DurableChatScope, intentKey: string, error: string): Promise<void>\n}\n\n/** Alias used by adapters that store all durable chat state in one port. */\nexport type DurableChatStateStore = DurablePlanStore\nexport type DurablePlanStateStore = DurablePlanStore\nexport type DurablePlanCommandJournal = Pick<DurablePlanStore, 'getPlanCommand' | 'claimPlanCommand' | 'recordPlanAuthorityResult' | 'finalizePlanCommand'>\nexport type DurableAnswerIntentJournal = Pick<DurablePlanStore, 'getAnswerIntent' | 'claimAnswerIntent' | 'acknowledgeAnswerIntent' | 'finalizeAnswerIntent' | 'abortAnswerIntent'>\n\nexport interface DurableInteractionProjection extends ChatInteraction {\n /** Sequence/event identity used to make ask replays idempotent. */\n eventId?: string\n semanticKey?: string\n /** A cancel-before-ask row is a terminal tombstone. */\n tombstone?: boolean\n updatedAt?: string\n}\n\nexport type DurableAnswerIntentState = 'prepared' | 'acknowledged' | 'finalized' | 'aborted'\n\nexport interface DurableAnswerIntentRecord {\n scope: DurableChatScope\n interactionId: string\n attemptKey: string\n intentKey: string\n outcome: 'accepted' | 'declined'\n data?: Record<string, InteractionAnswerValue>\n state: DurableAnswerIntentState\n guarantee?: DurableInteractionGuarantee\n acknowledgement?: DurableInteractionAcknowledgement\n createdAt: string\n finalizedAt?: string\n error?: string\n}\n\nexport interface DurableInteractionAcknowledgement {\n acknowledged: true\n authorityId?: string\n status?: string\n at?: string\n}\n\nexport type DurableInteractionGuarantee = 'reconciled' | 'best-effort'\n\nexport interface DurableInteractionSettlementOptions {\n /** Caller-created and stable across retries/reconnects. */\n attemptKey: string\n guarantee?: DurableInteractionGuarantee\n now?: () => string\n}\n\nexport interface DurableInteractionSettlement {\n prepare(scope: DurableChatScope, interactionId: string, outcome: 'accepted' | 'declined', data?: Record<string, InteractionAnswerValue>): Promise<DurableAnswerIntentRecord>\n acknowledge(scope: DurableChatScope, intentKey: string, acknowledgement?: Omit<DurableInteractionAcknowledgement, 'acknowledged'>): Promise<DurableAnswerIntentRecord>\n finalize(scope: DurableChatScope, intentKey: string): Promise<DurableAnswerIntentRecord>\n abort(scope: DurableChatScope, intentKey: string, error: string): Promise<DurableAnswerIntentRecord>\n reconcile(scope: DurableChatScope, intentKey: string): Promise<DurableAnswerIntentRecord | null>\n}\n\nexport function normalizePlanDecision(value: unknown): DurablePlanDecision | null {\n if (value === 'approved' || value === 'approve') return 'approved'\n if (value === 'rejected' || value === 'reject') return 'rejected'\n return null\n}\n\nexport function planCommandKey(planId: string, revision: number, decision: DurablePlanDecision): string {\n return `plan:${encodeURIComponent(planId)}:${revision}:${decision}`\n}\n\nexport function planAuthorityIdempotencyKey(scope: DurableChatScope, planId: string, revision: number, decision: DurablePlanDecision): string {\n return `durable-plan:${encodeURIComponent(durableChatScopeKey(scope))}:${encodeURIComponent(planId)}:${revision}:${decision}`\n}\n\nexport function planEffectKey(scope: DurableChatScope, planId: string, revision: number, decision: DurablePlanDecision): string {\n return `after-decision:${encodeURIComponent(durableChatScopeKey(scope))}:${encodeURIComponent(planId)}:${revision}:${decision}`\n}\n\nexport function stablePlanReceipt(\n scope: DurableChatScope,\n planId: string,\n revision: number,\n decision: DurablePlanDecision,\n result: Pick<DurablePlanAuthorityResult, 'followUp'>,\n): DurableFollowUpReceipt {\n const authorityIdempotencyKey = planAuthorityIdempotencyKey(scope, planId, revision, decision)\n return {\n receiptId: `receipt:${authorityIdempotencyKey}`,\n planId,\n revision,\n decision,\n turnId: result.followUp.turnId,\n state: result.followUp.state,\n authorityIdempotencyKey,\n }\n}\n","import {\n durableChatScopeKey,\n type DurableAnswerIntentRecord,\n type DurableChatScope,\n type DurableInteractionProjection,\n type DurablePlanAuthorityResult,\n type DurablePlanCommandRecord,\n type DurablePlanCommandKey,\n type DurablePlanEffectRecord,\n type DurablePlanProjection,\n type DurablePlanStore,\n} from './types'\nimport { DurableChatConflictError } from './errors'\nimport { canTransitionPlanStatus } from '../plans/index'\n\n/**\n * Reference adapter for tests and local development. It is intentionally\n * process-local and non-production: there is no locking across processes,\n * transaction, or crash recovery. Production adapters should implement the\n * same port with a database/Workflow primitive and CAS at every claim.\n */\nexport class InMemoryDurableChatStateStore implements DurablePlanStore {\n private readonly plans = new Map<string, DurablePlanProjection>()\n private readonly currentPlans = new Map<string, string>()\n private readonly commands = new Map<string, DurablePlanCommandRecord>()\n private readonly effects = new Map<string, DurablePlanEffectRecord>()\n private readonly interactions = new Map<string, DurableInteractionProjection>()\n private readonly interactionSemantic = new Map<string, string>()\n /** Duplicate event ids point at the canonical semantic interaction so all\n * duplicate asks settle through one terminal row/attempt. */\n private readonly interactionAliases = new Map<string, string>()\n private readonly answerIntents = new Map<string, DurableAnswerIntentRecord>()\n\n private scopePrefix(scope: DurableChatScope): string {\n return `${encodeURIComponent(durableChatScopeKey(scope))}\\u0000`\n }\n private scopedKey(scope: DurableChatScope, ...parts: Array<string | number>): string {\n return `${this.scopePrefix(scope)}${parts.map((part) => encodeURIComponent(String(part))).join('\\u0000')}`\n }\n private planKey(scope: DurableChatScope, planId: string, revision: number): string {\n return this.scopedKey(scope, planId, revision)\n }\n private currentKey(scope: DurableChatScope, planId: string): string {\n return this.scopedKey(scope, planId)\n }\n private commandKey(scope: DurableChatScope, key: string): string {\n return this.scopedKey(scope, key)\n }\n private interactionKey(scope: DurableChatScope, id: string): string {\n return this.scopedKey(scope, id)\n }\n private intentKey(scope: DurableChatScope, key: string): string {\n return this.scopedKey(scope, key)\n }\n private effectKey(scope: DurableChatScope, key: string): string {\n return this.scopedKey(scope, key)\n }\n\n async getPlanProjection(scope: DurableChatScope, planId: string, revision?: number): Promise<DurablePlanProjection | null> {\n const current = revision === undefined ? this.currentPlans.get(this.currentKey(scope, planId)) : undefined\n const key = revision === undefined\n ? current\n : this.planKey(scope, planId, revision)\n return key ? this.plans.get(key) ?? null : null\n }\n\n async listPlanProjections(scope: DurableChatScope, planId?: string): Promise<DurablePlanProjection[]> {\n const prefix = this.scopePrefix(scope)\n return [...this.plans.entries()]\n .filter(([key, projection]) => key.startsWith(prefix) && (!planId || projection.planId === planId))\n .map(([, projection]) => projection)\n .sort((a, b) => a.revision - b.revision)\n }\n\n async putPlanProjection(scope: DurableChatScope, projection: DurablePlanProjection): Promise<void> {\n if (!projection.planId || !Number.isInteger(projection.revision) || projection.revision < 1) {\n throw new TypeError('plan projection requires planId and positive integer revision')\n }\n const key = this.planKey(scope, projection.planId, projection.revision)\n const prior = this.plans.get(key)\n if (prior && JSON.stringify(prior) !== JSON.stringify(projection)) {\n if (\n prior.body !== projection.body ||\n prior.title !== projection.title ||\n prior.submittedAt !== projection.submittedAt\n ) {\n throw new DurableChatConflictError('plan content changed without a new revision')\n }\n if (prior.status === projection.status && !['preparing', 'pending'].includes(prior.status)) {\n throw new DurableChatConflictError('terminal plan projection cannot be rewritten')\n }\n if (!canTransitionPlanStatus(prior.status, projection.status)) {\n throw new DurableChatConflictError('conflicting plan projection for the same revision')\n }\n }\n this.plans.set(key, projection)\n const currentKey = this.currentKey(scope, projection.planId)\n const currentKeyValue = this.currentPlans.get(currentKey)\n const current = currentKeyValue ? this.plans.get(currentKeyValue) : undefined\n if (!current || projection.revision > current.revision) this.currentPlans.set(currentKey, key)\n }\n\n async getPlanCommand(scope: DurableChatScope, commandKey: DurablePlanCommandKey): Promise<DurablePlanCommandRecord | null> {\n return this.commands.get(this.commandKey(scope, commandKey)) ?? null\n }\n\n async claimPlanCommand(scope: DurableChatScope, command: DurablePlanCommandRecord) {\n const key = this.commandKey(scope, command.commandKey)\n const existing = this.commands.get(key)\n if (!existing) {\n const prefix = this.scopePrefix(scope)\n const competing = [...this.commands.entries()].find(([storedKey, candidate]) =>\n storedKey.startsWith(prefix) && candidate.planId === command.planId &&\n candidate.revision === command.revision && candidate.decision !== command.decision,\n )\n if (competing) {\n return { status: 'conflict' as const, record: competing[1], reason: 'competing decision for plan revision' }\n }\n this.commands.set(key, command)\n return { status: 'claimed' as const, record: command }\n }\n if (\n existing.planId === command.planId && existing.revision === command.revision &&\n existing.decision === command.decision\n ) return { status: 'existing' as const, record: existing }\n return { status: 'conflict' as const, record: existing, reason: 'command key is already used by another decision' }\n }\n\n async recordPlanAuthorityResult(scope: DurableChatScope, commandKey: string, result: DurablePlanAuthorityResult, receipt: DurablePlanCommandRecord['receipt']): Promise<void> {\n const key = this.commandKey(scope, commandKey)\n const command = this.commands.get(key)\n if (!command) throw new DurableChatConflictError('cannot record authority result before claiming command')\n command.authorityResult = result\n command.receipt = receipt\n command.state = 'authority_committed'\n }\n\n async finalizePlanCommand(scope: DurableChatScope, commandKey: string): Promise<void> {\n const command = this.commands.get(this.commandKey(scope, commandKey))\n if (!command) throw new DurableChatConflictError('cannot finalize unknown plan command')\n command.state = 'finalized'\n }\n\n async getPlanEffect(scope: DurableChatScope, effectKey: string): Promise<DurablePlanEffectRecord | null> {\n return this.effects.get(this.effectKey(scope, effectKey)) ?? null\n }\n async claimPlanEffect(scope: DurableChatScope, effect: DurablePlanEffectRecord) {\n const key = this.effectKey(scope, effect.effectKey)\n const existing = this.effects.get(key)\n if (existing) return { status: 'existing' as const, record: existing }\n this.effects.set(key, effect)\n return { status: 'claimed' as const, record: effect }\n }\n async completePlanEffect(scope: DurableChatScope, effectKey: string): Promise<void> {\n const effect = this.effects.get(this.effectKey(scope, effectKey))\n if (!effect) throw new DurableChatConflictError('cannot complete unknown plan effect')\n effect.state = 'completed'\n effect.completedAt = new Date().toISOString()\n }\n async failPlanEffect(scope: DurableChatScope, effectKey: string, error: string): Promise<void> {\n const effect = this.effects.get(this.effectKey(scope, effectKey))\n if (!effect) throw new DurableChatConflictError('cannot fail unknown plan effect')\n effect.state = 'error'\n effect.error = error\n }\n\n async getInteractionProjection(scope: DurableChatScope, interactionId: string): Promise<DurableInteractionProjection | null> {\n const directKey = this.interactionKey(scope, interactionId)\n const alias = this.interactionAliases.get(directKey)\n return this.interactions.get(alias ?? directKey) ?? null\n }\n\n async listInteractionProjections(scope: DurableChatScope): Promise<DurableInteractionProjection[]> {\n const prefix = this.scopePrefix(scope)\n return [...this.interactions.entries()]\n .filter(([key]) => key.startsWith(prefix))\n .map(([, value]) => value)\n }\n\n async upsertInteractionProjection(scope: DurableChatScope, projection: DurableInteractionProjection): Promise<DurableInteractionProjection> {\n if (!projection.id) throw new TypeError('interaction projection requires id')\n const key = this.interactionKey(scope, projection.id)\n const prior = this.interactions.get(key)\n const resolvedPrior = prior ?? (() => {\n const alias = this.interactionAliases.get(key)\n return alias ? this.interactions.get(alias) : undefined\n })()\n if (!prior && resolvedPrior) {\n this.interactionAliases.set(key, this.interactionKey(scope, resolvedPrior.id))\n }\n if (resolvedPrior) {\n if (projection.eventId && resolvedPrior.eventId === projection.eventId) return resolvedPrior\n if (resolvedPrior.status !== 'pending') {\n if (\n resolvedPrior.tombstone && resolvedPrior.kind === 'unknown' &&\n projection.status === resolvedPrior.status\n ) {\n const enriched = {\n ...projection,\n id: resolvedPrior.id,\n status: resolvedPrior.status,\n tombstone: true,\n ...(resolvedPrior.cancelReason ? { cancelReason: resolvedPrior.cancelReason } : {}),\n }\n this.interactions.set(this.interactionKey(scope, resolvedPrior.id), enriched)\n return enriched\n }\n if (resolvedPrior.status === projection.status && JSON.stringify(resolvedPrior.answers) === JSON.stringify(projection.answers)) return resolvedPrior\n throw new DurableChatConflictError('interaction already has a conflicting terminal outcome')\n }\n // A terminal event wins over pending. A second terminal event is caught\n // above; this is the accepted-answer/cancel ordering rule.\n if (projection.status !== 'pending') {\n const canonical = resolvedPrior.id === projection.id ? projection : { ...projection, id: resolvedPrior.id }\n this.interactions.set(this.interactionKey(scope, canonical.id), canonical)\n if (resolvedPrior.semanticKey) {\n this.interactionSemantic.delete(this.scopedKey(scope, resolvedPrior.semanticKey))\n }\n return canonical\n }\n return resolvedPrior\n }\n if (projection.semanticKey) {\n const semantic = this.scopedKey(scope, projection.semanticKey)\n const priorId = this.interactionSemantic.get(semantic)\n if (priorId) {\n const canonicalKey = this.interactionKey(scope, priorId)\n this.interactionAliases.set(this.interactionKey(scope, projection.id), canonicalKey)\n return this.interactions.get(canonicalKey) ?? projection\n }\n this.interactionSemantic.set(semantic, projection.id)\n }\n this.interactions.set(key, projection)\n return projection\n }\n\n async getAnswerIntent(scope: DurableChatScope, intentKey: string): Promise<DurableAnswerIntentRecord | null> {\n return this.answerIntents.get(this.intentKey(scope, intentKey)) ?? null\n }\n async claimAnswerIntent(scope: DurableChatScope, intent: DurableAnswerIntentRecord) {\n const key = this.intentKey(scope, intent.intentKey)\n const existing = this.answerIntents.get(key)\n if (!existing) {\n this.answerIntents.set(key, intent)\n return { status: 'claimed' as const, record: intent }\n }\n if (existing.interactionId === intent.interactionId && existing.attemptKey === intent.attemptKey && existing.outcome === intent.outcome && JSON.stringify(existing.data) === JSON.stringify(intent.data)) {\n return { status: 'existing' as const, record: existing }\n }\n return { status: 'conflict' as const, record: existing, reason: 'answer intent key is already used by another answer' }\n }\n async acknowledgeAnswerIntent(scope: DurableChatScope, intentKey: string, acknowledgement: DurableAnswerIntentRecord['acknowledgement']): Promise<void> {\n const intent = this.answerIntents.get(this.intentKey(scope, intentKey))\n if (!intent) throw new DurableChatConflictError('cannot acknowledge unknown answer intent')\n if (intent.state === 'finalized') return\n intent.acknowledgement = acknowledgement\n intent.state = 'acknowledged'\n }\n async finalizeAnswerIntent(scope: DurableChatScope, intentKey: string, guarantee: DurableAnswerIntentRecord['guarantee'] = 'reconciled'): Promise<void> {\n const intent = this.answerIntents.get(this.intentKey(scope, intentKey))\n if (!intent) throw new DurableChatConflictError('cannot finalize unknown answer intent')\n if (intent.state === 'finalized') return\n if (!intent.acknowledgement?.acknowledged) {\n throw new DurableChatConflictError('cannot finalize an answer before authority acknowledgement')\n }\n const projection = await this.getInteractionProjection(scope, intent.interactionId)\n if (!projection) throw new DurableChatConflictError('cannot finalize an answer before its ask projection')\n const status = intent.outcome === 'accepted' ? 'answered' : 'declined'\n await this.upsertInteractionProjection(scope, {\n ...projection,\n status,\n ...(intent.data ? { answers: intent.data } : {}),\n updatedAt: intent.acknowledgement.at ?? new Date().toISOString(),\n })\n intent.state = 'finalized'\n intent.guarantee = guarantee\n intent.finalizedAt = new Date().toISOString()\n }\n async abortAnswerIntent(scope: DurableChatScope, intentKey: string, error: string): Promise<void> {\n const intent = this.answerIntents.get(this.intentKey(scope, intentKey))\n if (!intent) throw new DurableChatConflictError('cannot abort unknown answer intent')\n if (intent.state === 'finalized') return\n intent.state = 'aborted'\n intent.error = error\n }\n}\n\n/** Short aliases retained for adapter authors who call this a durable chat\n * store rather than a state store. Both names refer to the same non-production\n * reference implementation. */\nexport const InMemoryDurableChatStore = InMemoryDurableChatStateStore\nexport function createInMemoryDurableChatStateStore(): InMemoryDurableChatStateStore {\n return new InMemoryDurableChatStateStore()\n}\n","import {\n cancelStatusFor,\n interactionFromWireRequest,\n isSafeInteractionFieldKey,\n parseInteractionAnswers,\n questionInteractionContentSignature,\n type InteractionAnswerValue,\n type InteractionAnswers,\n type InteractionRequestWire,\n} from '../interactions/contract'\nimport { DurableChatConflictError } from './errors'\nimport type {\n DurableAnswerIntentRecord,\n DurableChatScope,\n DurableInteractionAcknowledgement,\n DurableInteractionGuarantee,\n DurableInteractionProjection,\n DurableInteractionSettlement,\n DurableInteractionSettlementOptions,\n DurablePlanStore,\n} from './types'\n\nfunction nowIso(now?: () => string): string {\n return (now ?? (() => new Date().toISOString()))()\n}\n\nfunction intentKey(scope: DurableChatScope, interactionId: string, attemptKey: string): string {\n return `interaction-answer:${encodeURIComponent(scope)}:${encodeURIComponent(interactionId)}:${encodeURIComponent(attemptKey)}`\n}\n\nfunction ensureAttemptKey(attemptKey: string): void {\n if (!attemptKey || typeof attemptKey !== 'string' || attemptKey.length > 512) throw new TypeError('attemptKey must be a non-empty string')\n}\n\nfunction safeAnswers(data: Record<string, InteractionAnswerValue> | undefined): InteractionAnswers | undefined {\n if (data === undefined) return undefined\n for (const key of Object.keys(data)) {\n if (!isSafeInteractionFieldKey(key)) throw new TypeError(`unsafe interaction answer key: ${key}`)\n }\n const parsed = parseInteractionAnswers(data)\n if (!parsed.succeeded) throw new TypeError(parsed.error)\n return parsed.value\n}\n\n/** Apply an ask event. Event ids and semantic signatures make replays safe;\n * a prior cancel creates a tombstone and cannot be resurrected by a late ask. */\nexport async function upsertDurableInteractionAsk(\n store: DurablePlanStore,\n scope: DurableChatScope,\n request: InteractionRequestWire,\n options: { eventId?: string; semanticKey?: string; now?: () => string } = {},\n): Promise<DurableInteractionProjection> {\n const semanticKey = options.semanticKey ?? questionInteractionContentSignature(interactionFromWireRequest(request)) ?? undefined\n const incoming: DurableInteractionProjection = {\n ...interactionFromWireRequest(request),\n ...(options.eventId ? { eventId: options.eventId } : {}),\n ...(semanticKey ? { semanticKey } : {}),\n updatedAt: nowIso(options.now),\n }\n const prior = await store.getInteractionProjection(scope, request.id)\n if (prior?.tombstone) {\n return store.upsertInteractionProjection(scope, {\n ...incoming,\n status: prior.status,\n tombstone: true,\n ...(prior.cancelReason ? { cancelReason: prior.cancelReason } : {}),\n })\n }\n if (prior && prior.status !== 'pending') return prior\n return store.upsertInteractionProjection(scope, incoming)\n}\n\n/** Apply a cancel event. It is valid before the ask arrives and leaves a\n * terminal tombstone so a delayed ask cannot re-open the card. */\nexport async function recordDurableInteractionCancel(\n store: DurablePlanStore,\n scope: DurableChatScope,\n interactionId: string,\n reason?: string,\n options: { eventId?: string; now?: () => string } = {},\n): Promise<DurableInteractionProjection> {\n if (!interactionId) throw new TypeError('interaction id is required')\n const status = cancelStatusFor(reason)\n const prior = await store.getInteractionProjection(scope, interactionId)\n if (prior) {\n if (prior.status === status) return prior\n if (prior.status !== 'pending') throw new DurableChatConflictError('interaction already has a conflicting terminal outcome')\n return store.upsertInteractionProjection(scope, {\n ...prior, status, tombstone: true, ...(reason ? { cancelReason: reason } : {}),\n ...(options.eventId ? { eventId: options.eventId } : {}), updatedAt: nowIso(options.now),\n })\n }\n return store.upsertInteractionProjection(scope, {\n id: interactionId, kind: 'unknown', title: '', fields: [], status,\n tombstone: true, ...(reason ? { cancelReason: reason } : {}),\n ...(options.eventId ? { eventId: options.eventId } : {}), updatedAt: nowIso(options.now),\n })\n}\n\n/** Record an accepted/declined answer in the projection. The terminal\n * transition is intentionally separate from answer-intent acknowledgement so\n * callers can choose reconciled or best-effort delivery. */\nexport async function recordDurableInteractionAnswer(\n store: DurablePlanStore,\n scope: DurableChatScope,\n interactionId: string,\n outcome: 'accepted' | 'declined',\n answers?: Record<string, InteractionAnswerValue>,\n options: { eventId?: string; now?: () => string } = {},\n): Promise<DurableInteractionProjection> {\n const prior = await store.getInteractionProjection(scope, interactionId)\n if (!prior) throw new DurableChatConflictError('cannot answer an interaction before its ask projection')\n const status = outcome === 'accepted' ? 'answered' : 'declined'\n const parsed = safeAnswers(answers)\n if (prior.status === status && JSON.stringify(prior.answers) === JSON.stringify(parsed)) return prior\n if (prior.status !== 'pending') throw new DurableChatConflictError('interaction already has a conflicting terminal outcome')\n return store.upsertInteractionProjection(scope, {\n ...prior, status, ...(parsed ? { answers: parsed } : {}),\n ...(options.eventId ? { eventId: options.eventId } : {}), updatedAt: nowIso(options.now),\n })\n}\n\nexport interface DurableInteractionSettlementFactoryOptions extends DurableInteractionSettlementOptions {\n store: DurablePlanStore\n /** Optional authority lookup used by `reconcile`; returning null leaves the\n * intent prepared for a later durable retry. */\n reconcileAuthority?: (args: {\n scope: DurableChatScope\n intent: DurableAnswerIntentRecord\n }) => Promise<DurableInteractionAcknowledgement | null>\n}\n\n/** Additive answer settlement primitive for wiring into `/interactions`.\n * `attemptKey` belongs to the caller and is never generated from user data. */\nexport function createDurableInteractionSettlement(\n options: DurableInteractionSettlementFactoryOptions,\n): DurableInteractionSettlement {\n const now = options.now ?? (() => new Date().toISOString())\n return {\n async prepare(scope, interactionId, outcome, data) {\n ensureAttemptKey(options.attemptKey)\n const parsed = safeAnswers(data)\n const record: DurableAnswerIntentRecord = {\n scope, interactionId, attemptKey: options.attemptKey,\n intentKey: intentKey(scope, interactionId, options.attemptKey),\n outcome, ...(parsed ? { data: parsed } : {}), state: 'prepared',\n guarantee: options.guarantee ?? 'reconciled', createdAt: now(),\n }\n const result = await options.store.claimAnswerIntent(scope, record)\n if (result.status === 'conflict') throw new DurableChatConflictError(result.reason)\n return result.record\n },\n async acknowledge(scope, key, acknowledgement = {}) {\n const existing = await options.store.getAnswerIntent(scope, key)\n if (!existing) throw new DurableChatConflictError('unknown answer intent')\n const value: DurableInteractionAcknowledgement = {\n acknowledged: true,\n ...(acknowledgement.authorityId ? { authorityId: acknowledgement.authorityId } : {}),\n ...(acknowledgement.status ? { status: acknowledgement.status } : {}),\n at: acknowledgement.at ?? now(),\n }\n await options.store.acknowledgeAnswerIntent(scope, key, value)\n return (await options.store.getAnswerIntent(scope, key))!\n },\n async finalize(scope, key) {\n const existing = await options.store.getAnswerIntent(scope, key)\n if (!existing) throw new DurableChatConflictError('unknown answer intent')\n await options.store.finalizeAnswerIntent(scope, key, options.guarantee ?? 'reconciled')\n return (await options.store.getAnswerIntent(scope, key))!\n },\n async abort(scope, key, error) {\n const existing = await options.store.getAnswerIntent(scope, key)\n if (!existing) throw new DurableChatConflictError('unknown answer intent')\n await options.store.abortAnswerIntent(scope, key, error)\n return (await options.store.getAnswerIntent(scope, key))!\n },\n async reconcile(scope, key) {\n const existing = await options.store.getAnswerIntent(scope, key)\n if (!existing || !options.reconcileAuthority) return existing\n if (existing.state === 'finalized') return existing\n const acknowledgement = await options.reconcileAuthority({ scope, intent: existing })\n if (!acknowledgement) return existing\n await options.store.acknowledgeAnswerIntent(scope, key, {\n acknowledged: true,\n ...(acknowledgement.authorityId ? { authorityId: acknowledgement.authorityId } : {}),\n ...(acknowledgement.status ? { status: acknowledgement.status } : {}),\n ...(acknowledgement.at ? { at: acknowledgement.at } : {}),\n })\n await options.store.finalizeAnswerIntent(scope, key, options.guarantee ?? 'reconciled')\n return (await options.store.getAnswerIntent(scope, key))!\n },\n }\n}\n\n/** Stable key helper exported for products implementing their own settlement\n * loop. */\nexport function durableInteractionIntentKey(scope: DurableChatScope, interactionId: string, attemptKey: string): string {\n ensureAttemptKey(attemptKey)\n return intentKey(scope, interactionId, attemptKey)\n}\n\nexport const applyDurableInteractionAsk = upsertDurableInteractionAsk\nexport const applyDurableInteractionCancel = recordDurableInteractionCancel\nexport const applyDurableInteractionAnswer = recordDurableInteractionAnswer\n","import {\n interactionToPersistedPart,\n parseInteractionCancel,\n parseInteractionRequest,\n type InteractionCancelData,\n type InteractionPersistedPart,\n type InteractionRequestWire,\n} from '../interactions/contract'\nimport { DurableChatConflictError } from './errors'\nimport type {\n DurableInteractionRouteArgs,\n DurableInteractionRoutePersistence,\n} from '../interactions/route'\nimport {\n createDurableInteractionSettlement,\n recordDurableInteractionCancel,\n upsertDurableInteractionAsk,\n} from './interactions'\nimport type {\n DurableAnswerIntentRecord,\n DurableChatScope,\n DurableInteractionAcknowledgement,\n DurableInteractionGuarantee,\n DurableInteractionSettlement,\n DurablePlanStore,\n} from './types'\nimport {\n parsePlanSubmittedEvent,\n persistedPartToPlan,\n planToPersistedPart,\n} from '../plans/index'\n\nexport interface DurableInteractionProjectionAdapter {\n upsertAsk(request: InteractionRequestWire): Promise<void>\n cancel(cancel: InteractionCancelData): Promise<void>\n materialize(): Promise<InteractionPersistedPart[]>\n}\n\n/** Binds an authorized durable scope/store to interaction lifecycle events. */\nexport function createDurableInteractionProjectionAdapter(options: {\n store: DurablePlanStore\n scope: DurableChatScope\n now?: () => string\n}): DurableInteractionProjectionAdapter {\n const observed = new Set<string>()\n return {\n async upsertAsk(request) {\n observed.add(request.id)\n await upsertDurableInteractionAsk(options.store, options.scope, request, { now: options.now })\n },\n async cancel(cancel) {\n observed.add(cancel.id)\n await recordDurableInteractionCancel(\n options.store,\n options.scope,\n cancel.id,\n cancel.reason,\n { now: options.now },\n )\n },\n async materialize() {\n const projections = await Promise.all(\n [...observed].map((id) => options.store.getInteractionProjection(options.scope, id)),\n )\n const canonical = new Map(projections.filter((item) => item !== null).map((item) => [item.id, item]))\n return [...canonical.values()]\n .filter((projection) => projection.kind !== 'unknown')\n .map((projection) => interactionToPersistedPart(\n {\n id: projection.id,\n kind: projection.kind,\n title: projection.title,\n ...(projection.body ? { body: projection.body } : {}),\n answerSpec: { fields: projection.fields },\n },\n projection.status,\n projection.cancelReason,\n projection.answers,\n ))\n },\n }\n}\n\nexport interface DurableChatEventProjection {\n observe(event: unknown): void | Promise<void>\n materialize(): Array<Record<string, unknown>> | Promise<Array<Record<string, unknown>>>\n}\n\nfunction eventRecord(value: unknown): Record<string, unknown> | null {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return null\n const record = value as Record<string, unknown>\n if (record.kind === 'event' && record.event && typeof record.event === 'object') {\n return record.event as Record<string, unknown>\n }\n return record\n}\n\n/** Event projector usable with any `ChatTurnRouteProducer` through\n * `withDurableChatProjection`. It tracks this turn's identities so older\n * thread state is not copied into every assistant message. */\nexport function createDurableChatEventProjection(options: {\n store: DurablePlanStore\n scope: DurableChatScope\n now?: () => string\n}): DurableChatEventProjection {\n const interactions = createDurableInteractionProjectionAdapter(options)\n const plans = new Set<string>()\n return {\n async observe(value) {\n const event = eventRecord(value)\n if (!event || typeof event.type !== 'string') return\n const data = eventRecord(event.data)\n if (event.type === 'interaction') {\n const parsed = parseInteractionRequest(data ?? undefined)\n if (parsed.succeeded) await interactions.upsertAsk(parsed.value)\n return\n }\n if (event.type === 'interaction.cancel') {\n const parsed = parseInteractionCancel(data ?? undefined)\n if (parsed.succeeded) {\n try {\n await interactions.cancel(parsed.value)\n } catch (error) {\n if (!(error instanceof DurableChatConflictError)) throw error\n }\n }\n return\n }\n if (!event.type.startsWith('plan.')) return\n const submitted = parsePlanSubmittedEvent(event)\n const planRecord = eventRecord(data?.plan) ?? eventRecord(eventRecord(event.properties)?.plan)\n const plan = submitted.succeeded\n ? submitted.value\n : planRecord ? persistedPartToPlan({ type: 'plan', ...planRecord }) : null\n if (!plan) return\n plans.add(`${plan.planId}\\u0000${plan.revision}`)\n try {\n await options.store.putPlanProjection(options.scope, plan)\n } catch (error) {\n if (!(error instanceof DurableChatConflictError)) throw error\n }\n },\n async materialize() {\n const interactionParts = await interactions.materialize()\n const planParts = await Promise.all([...plans].map(async (key) => {\n const [planId, rawRevision] = key.split('\\u0000')\n const plan = await options.store.getPlanProjection(options.scope, planId!, Number(rawRevision))\n return plan ? planToPersistedPart(plan) : null\n }))\n return [...interactionParts, ...planParts.filter((part) => part !== null)]\n },\n }\n}\n\nexport interface PreparedDurableInteractionAnswer {\n scope: DurableChatScope\n settlement: DurableInteractionSettlement\n intent: DurableAnswerIntentRecord\n}\n\ninterface DurableInteractionRoutePersistenceBase {\n store: DurablePlanStore\n scope(args: DurableInteractionRouteArgs): DurableChatScope | Promise<DurableChatScope>\n now?: () => string\n}\n\nexport type CreateDurableInteractionRoutePersistenceOptions =\n | (DurableInteractionRoutePersistenceBase & {\n guarantee: 'reconciled'\n reconcileAuthority(args: {\n scope: DurableChatScope\n intent: DurableAnswerIntentRecord\n route: DurableInteractionRouteArgs\n }): Promise<DurableInteractionAcknowledgement | null>\n })\n | (DurableInteractionRoutePersistenceBase & {\n guarantee: 'best-effort'\n reconcileAuthority?: (args: {\n scope: DurableChatScope\n intent: DurableAnswerIntentRecord\n route: DurableInteractionRouteArgs\n }) => Promise<DurableInteractionAcknowledgement | null>\n })\n\n/** Ready-to-use bridge from `/interactions` to the durable state port. Only\n * the reconciled variant may claim crash-safe behavior; best-effort is\n * explicit in both its type and persisted intent. */\nexport function createDurableInteractionRoutePersistence(\n options: CreateDurableInteractionRoutePersistenceOptions,\n): DurableInteractionRoutePersistence<PreparedDurableInteractionAnswer> {\n const guarantee: DurableInteractionGuarantee = options.guarantee\n return {\n guarantee,\n async prepare(args) {\n const scope = await options.scope(args)\n const settlement = createDurableInteractionSettlement({\n store: options.store,\n attemptKey: args.attemptKey,\n guarantee,\n now: options.now,\n ...(options.reconcileAuthority\n ? { reconcileAuthority: ({ intent }) => options.reconcileAuthority!({ scope, intent, route: args }) }\n : {}),\n })\n const intent = await settlement.prepare(\n scope,\n args.answer.id,\n args.answer.outcome,\n args.answer.data,\n )\n return { scope, settlement, intent }\n },\n async reconcile({ prepared }) {\n let intent = await prepared.settlement.reconcile(prepared.scope, prepared.intent.intentKey)\n if (intent?.state === 'acknowledged') {\n intent = await prepared.settlement.finalize(prepared.scope, prepared.intent.intentKey)\n }\n return { settled: intent?.state === 'finalized' }\n },\n async acknowledge({ prepared }) {\n await prepared.settlement.acknowledge(prepared.scope, prepared.intent.intentKey, {\n status: prepared.intent.outcome,\n })\n },\n async finalize({ prepared }) {\n await prepared.settlement.finalize(prepared.scope, prepared.intent.intentKey)\n },\n async fail({ prepared, error }) {\n await prepared.settlement.abort(\n prepared.scope,\n prepared.intent.intentKey,\n error instanceof Error ? error.message : String(error),\n )\n },\n }\n}\n","import {\n DurableChatConflictError,\n DurableChatGoneError,\n DurableChatUnavailableError,\n} from './errors'\nimport {\n durableChatScopeKey,\n normalizePlanDecision,\n planAuthorityIdempotencyKey,\n planCommandKey,\n planEffectKey,\n stablePlanReceipt,\n type DurableChatScope,\n type DurableFollowUpReceipt,\n type DurablePlanAuthority,\n type DurablePlanAuthorityCurrentResult,\n type DurablePlanAuthorityResult,\n type DurablePlanCommandRecord,\n type DurablePlanProjection,\n type DurablePlanStore,\n} from './types'\nimport { canTransitionPlanStatus, planFollowUpTurnId } from '../plans/index'\n\nexport type DurablePlanAuthorization =\n | DurableChatScope\n | { scope: DurableChatScope }\n | Response\n | null\n | undefined\n\nexport interface DurablePlanRouteAuthorizeArgs {\n request: Request\n operation: 'current' | 'decide'\n planId?: string\n}\n\nexport interface DurablePlanRouteOptions {\n store: DurablePlanStore\n authority: DurablePlanAuthority\n /** Authentication and resource authorization. Scope must be derived by the\n * caller from trusted credentials, never from client JSON. */\n authorize: (args: DurablePlanRouteAuthorizeArgs) => Promise<DurablePlanAuthorization> | DurablePlanAuthorization\n /** Required idempotent side effect seam. The route claims `effectKey` before\n * calling it; a product must make the callback safe to retry by that key. */\n afterDecision: (args: {\n scope: DurableChatScope\n plan: DurablePlanProjection\n receipt: DurableFollowUpReceipt\n effectKey: string\n }) => Promise<void> | void\n now?: () => string\n logger?: Pick<Console, 'warn' | 'error'>\n}\n\nexport interface DurablePlanRoutes {\n current(request: Request): Promise<Response>\n decide(request: Request): Promise<Response>\n}\n\ntype ParsedDecision = {\n planId: string\n revision: number\n decision: 'approved' | 'rejected'\n feedback?: string\n}\n\nfunction errorResponse(error: unknown, fallbackCode: string, fallbackStatus: number, fallbackMessage: string): Response {\n if (error instanceof Response) return error\n if (error && typeof error === 'object' && 'status' in error && typeof error.status === 'number') {\n const typed = error as { code?: unknown; message?: unknown; status: number }\n return Response.json(\n { code: typeof typed.code === 'string' ? typed.code : fallbackCode, error: typeof typed.message === 'string' ? typed.message : fallbackMessage },\n { status: typed.status },\n )\n }\n return Response.json({ code: fallbackCode, error: fallbackMessage }, { status: fallbackStatus })\n}\n\nfunction scopeFromAuthorization(value: DurablePlanAuthorization): DurableChatScope | Response {\n if (value instanceof Response) return value\n if (typeof value === 'string' && value) return value\n if (value && typeof value === 'object' && 'scope' in value && value.scope) return value.scope\n return Response.json({ code: 'DURABLE_CHAT_UNAUTHORIZED', error: 'not authorized' }, { status: 401 })\n}\n\nfunction planIdFromUrl(request: Request): string | null {\n const url = new URL(request.url)\n const query = url.searchParams.get('planId') ?? url.searchParams.get('id')\n if (query) return query\n const segments = url.pathname.split('/').filter(Boolean)\n return segments.length > 0 ? decodeURIComponent(segments[segments.length - 1]!) : null\n}\n\nfunction asCurrent(value: DurablePlanAuthorityCurrentResult | DurablePlanProjection | null): DurablePlanAuthorityCurrentResult {\n if (value === null) return { plan: null }\n if ('plan' in value) return value\n return { plan: value }\n}\n\nfunction terminalDecision(plan: DurablePlanProjection | null): 'approved' | 'rejected' | null {\n if (!plan) return null\n return plan.status === 'approved' || plan.status === 'rejected' ? plan.status : null\n}\n\nfunction parseDecisionBody(body: unknown): ParsedDecision | { error: string } {\n if (!body || typeof body !== 'object' || Array.isArray(body)) return { error: 'Invalid JSON body' }\n const record = body as Record<string, unknown>\n const planId = typeof record.planId === 'string' && record.planId\n ? record.planId\n : typeof record.id === 'string' && record.id ? record.id : null\n const revision = record.revision\n const decision = normalizePlanDecision(record.decision ?? record.outcome ?? record.status)\n if (!planId) return { error: 'Missing planId' }\n if (typeof revision !== 'number' || !Number.isInteger(revision) || revision < 1) return { error: 'revision must be a positive integer' }\n if (!decision) return { error: 'decision must be approved or rejected' }\n if (record.feedback !== undefined && typeof record.feedback !== 'string') return { error: 'feedback must be a string' }\n return { planId, revision, decision, ...(typeof record.feedback === 'string' ? { feedback: record.feedback } : {}) }\n}\n\nfunction withReceipt(result: DurablePlanAuthorityResult, scope: DurableChatScope, parsed: ParsedDecision): DurablePlanAuthorityResult & { receipt: DurableFollowUpReceipt } {\n const receipt = result.receipt ?? stablePlanReceipt(scope, parsed.planId, parsed.revision, parsed.decision, result)\n return { ...result, receipt }\n}\n\nfunction recoverReceipt(\n scope: DurableChatScope,\n plan: DurablePlanProjection,\n decision: 'approved' | 'rejected',\n): DurableFollowUpReceipt {\n return stablePlanReceipt(scope, plan.planId, plan.revision, decision, {\n followUp: { turnId: planFollowUpTurnId(plan.planId, decision), state: 'unknown' },\n })\n}\n\nexport function createDurablePlanRoutes(options: DurablePlanRouteOptions): DurablePlanRoutes {\n const now = options.now ?? (() => new Date().toISOString())\n const logger = options.logger ?? console\n\n async function authorize(request: Request, operation: 'current' | 'decide', planId?: string): Promise<DurableChatScope | Response> {\n try {\n return scopeFromAuthorization(await options.authorize({ request, operation, ...(planId ? { planId } : {}) }))\n } catch (error) {\n return errorResponse(error, 'DURABLE_CHAT_UNAUTHORIZED', 401, 'not authorized')\n }\n }\n\n async function runEffect(\n scope: DurableChatScope,\n plan: DurablePlanProjection,\n receipt: DurableFollowUpReceipt,\n ): Promise<boolean> {\n const effectKey = planEffectKey(scope, plan.planId, plan.revision, receipt.decision)\n try {\n const existing = await options.store.getPlanEffect(scope, effectKey)\n if (existing?.state === 'completed') return true\n const claim = await options.store.claimPlanEffect(scope, {\n effectKey, scope, planId: plan.planId, revision: plan.revision,\n decision: receipt.decision, state: 'claimed', claimedAt: now(),\n })\n if (claim.status === 'existing' && claim.record.state === 'completed') return true\n await options.afterDecision({ scope, plan, receipt, effectKey })\n await options.store.completePlanEffect(scope, effectKey)\n return true\n } catch (error) {\n await options.store.failPlanEffect(scope, effectKey, error instanceof Error ? error.message : String(error)).catch(() => undefined)\n logger.warn('[durable-chat] afterDecision failed:', error)\n return false\n }\n }\n\n async function current(request: Request): Promise<Response> {\n const planId = planIdFromUrl(request)\n if (!planId) return Response.json({ code: 'DURABLE_CHAT_BAD_REQUEST', error: 'Missing planId' }, { status: 400 })\n const scope = await authorize(request, 'current', planId)\n if (scope instanceof Response) return scope\n const local = await options.store.getPlanProjection(scope, planId)\n let authoritative: DurablePlanAuthorityCurrentResult\n try {\n authoritative = asCurrent(await options.authority.current({ scope, planId }))\n } catch (error) {\n // A known terminal local record is safe to replay while authority is\n // temporarily down; pending records must not pretend to be committed.\n const decision = terminalDecision(local)\n if (decision) {\n const command = await options.store.getPlanCommand(scope, planCommandKey(planId, local!.revision, decision))\n if (command?.receipt) {\n const effectComplete = await runEffect(scope, command.authorityResult?.plan ?? local!, command.receipt)\n return Response.json({\n plan: command.authorityResult?.plan ?? local,\n receipt: command.receipt,\n replayed: true,\n ...(!effectComplete ? { effectPending: true } : {}),\n })\n }\n }\n return errorResponse(error, 'DURABLE_CHAT_UNAVAILABLE', 503, 'plan authority unavailable')\n }\n if (!authoritative.plan) {\n return Response.json({ code: 'DURABLE_CHAT_GONE', error: 'plan is no longer available' }, { status: 410 })\n }\n const authorityPlan = authoritative.plan\n const localWins = Boolean(local && (\n local.revision > authorityPlan.revision ||\n (local.revision === authorityPlan.revision && !canTransitionPlanStatus(local.status, authorityPlan.status))\n ))\n const plan = localWins ? local! : authorityPlan\n let projectionPending = false\n if (!localWins) {\n try {\n await options.store.putPlanProjection(scope, plan)\n } catch (error) {\n projectionPending = true\n logger.warn('[durable-chat] failed to heal plan projection:', error)\n }\n }\n const decision = terminalDecision(plan)\n let receipt = localWins ? undefined : authoritative.receipt\n if (!receipt && decision) {\n const command = await options.store.getPlanCommand(scope, planCommandKey(plan.planId, plan.revision, decision))\n receipt = command?.receipt\n if (!receipt) receipt = recoverReceipt(scope, plan, decision)\n }\n const effectComplete = decision && receipt ? await runEffect(scope, plan, receipt) : true\n return Response.json({\n plan,\n ...(receipt ? { receipt } : {}),\n ...(projectionPending ? { projectionPending: true } : {}),\n ...(!effectComplete ? { effectPending: true } : {}),\n })\n }\n\n async function decide(request: Request): Promise<Response> {\n const body = await request.json().catch(() => null)\n const parsed = parseDecisionBody(body)\n if ('error' in parsed) return Response.json({ code: 'DURABLE_CHAT_BAD_REQUEST', error: parsed.error }, { status: 400 })\n const scope = await authorize(request, 'decide', parsed.planId)\n if (scope instanceof Response) return scope\n const local = await options.store.getPlanProjection(scope, parsed.planId, parsed.revision)\n const current = await options.store.getPlanProjection(scope, parsed.planId)\n if (!local) {\n return Response.json({ code: 'DURABLE_CHAT_UNAVAILABLE', error: 'submitted plan projection is required before deciding' }, { status: 503 })\n }\n if (current && parsed.revision < current.revision) {\n return Response.json({ code: 'DURABLE_CHAT_CONFLICT', error: 'stale plan revision' }, { status: 409 })\n }\n if (local.status !== 'pending') {\n const existingTerminal = terminalDecision(local)\n if (existingTerminal === parsed.decision) {\n const existing = await options.store.getPlanCommand(scope, planCommandKey(parsed.planId, parsed.revision, parsed.decision))\n const receipt = existing?.receipt ?? recoverReceipt(scope, local, parsed.decision)\n const effectComplete = await runEffect(scope, existing?.authorityResult?.plan ?? local, receipt)\n return Response.json({\n plan: existing?.authorityResult?.plan ?? local,\n receipt,\n replayed: true,\n ...(!effectComplete ? { effectPending: true } : {}),\n })\n }\n return Response.json({\n code: 'DURABLE_CHAT_CONFLICT',\n error: 'plan is not pending',\n plan: local,\n }, { status: 409 })\n }\n const opposite = parsed.decision === 'approved' ? 'rejected' : 'approved'\n const oppositeCommand = await options.store.getPlanCommand(scope, planCommandKey(parsed.planId, parsed.revision, opposite))\n if (oppositeCommand) return Response.json({ code: 'DURABLE_CHAT_CONFLICT', error: 'competing decision for plan revision' }, { status: 409 })\n const commandKey = planCommandKey(parsed.planId, parsed.revision, parsed.decision)\n const authorityIdempotencyKey = planAuthorityIdempotencyKey(scope, parsed.planId, parsed.revision, parsed.decision)\n const command: DurablePlanCommandRecord = {\n scope, planId: parsed.planId, revision: parsed.revision, decision: parsed.decision,\n commandKey, authorityIdempotencyKey, state: 'claimed', claimedAt: now(),\n }\n const claimed = await options.store.claimPlanCommand(scope, command)\n if (claimed.status === 'conflict') return Response.json({ code: 'DURABLE_CHAT_CONFLICT', error: claimed.reason }, { status: 409 })\n if (claimed.status === 'existing' && claimed.record.receipt) {\n // Effect errors are intentionally retried on a known committed command.\n const effectComplete = await runEffect(scope, claimed.record.authorityResult?.plan ?? local, claimed.record.receipt)\n return Response.json({\n plan: claimed.record.authorityResult?.plan ?? local,\n receipt: claimed.record.receipt,\n replayed: true,\n ...(!effectComplete ? { effectPending: true } : {}),\n })\n }\n let authorityResult: DurablePlanAuthorityResult & { receipt: DurableFollowUpReceipt }\n try {\n authorityResult = withReceipt(await options.authority.decide({\n scope, planId: parsed.planId, revision: parsed.revision, decision: parsed.decision,\n ...(parsed.feedback ? { feedback: parsed.feedback } : {}), idempotencyKey: authorityIdempotencyKey,\n }), scope, parsed)\n } catch (error) {\n try {\n const reconciled = asCurrent(await options.authority.current({\n scope,\n planId: parsed.planId,\n revision: parsed.revision,\n }))\n const terminal = terminalDecision(reconciled.plan)\n if (reconciled.plan && terminal === parsed.decision) {\n const receipt = reconciled.receipt ?? recoverReceipt(scope, reconciled.plan, terminal)\n authorityResult = {\n plan: reconciled.plan,\n followUp: { turnId: receipt.turnId, state: receipt.state },\n idempotent: true,\n receipt,\n }\n } else if (reconciled.plan && reconciled.plan.status !== 'pending') {\n // The authority has already moved this revision out of its pending\n // state, but not to the decision this request asked for. Surface the\n // authoritative snapshot so the client can converge instead of\n // retrying an already-lost decision as if the authority were down.\n let projectionPending = false\n try {\n await options.store.putPlanProjection(scope, reconciled.plan)\n } catch (projectionError) {\n projectionPending = true\n logger.warn('[durable-chat] failed to reconcile conflicting plan projection:', projectionError)\n }\n return Response.json({\n code: 'DURABLE_CHAT_CONFLICT',\n error: 'plan is not pending',\n plan: reconciled.plan,\n ...(reconciled.receipt ? { receipt: reconciled.receipt } : {}),\n ...(projectionPending ? { projectionPending: true } : {}),\n }, { status: 409 })\n } else {\n return errorResponse(error, 'DURABLE_CHAT_UNAVAILABLE', 503, 'plan authority unavailable')\n }\n } catch {\n return errorResponse(error, 'DURABLE_CHAT_UNAVAILABLE', 503, 'plan authority unavailable')\n }\n }\n let projectionPending = false\n try {\n await options.store.recordPlanAuthorityResult(scope, commandKey, authorityResult, authorityResult.receipt)\n } catch (error) {\n projectionPending = true\n // The authority has committed; the receipt remains safe to return and\n // GET can reconcile from the authority on the next request.\n logger.warn('[durable-chat] failed to record authority result:', error)\n }\n try {\n await options.store.putPlanProjection(scope, authorityResult.plan)\n await options.store.finalizePlanCommand(scope, commandKey)\n } catch (error) {\n projectionPending = true\n logger.warn('[durable-chat] failed to finalize plan projection:', error)\n }\n const effectComplete = await runEffect(scope, authorityResult.plan, authorityResult.receipt)\n return Response.json({\n plan: authorityResult.plan,\n receipt: authorityResult.receipt,\n ...(projectionPending ? { projectionPending: true } : {}),\n ...(!effectComplete ? { effectPending: true } : {}),\n })\n }\n\n return { current, decide }\n}\n"],"mappings":"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","names":["intentKey","intent","decision","effectComplete","current","projectionPending"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/knowledge/index.ts"],"sourcesContent":["/**\n * Declarative knowledge-requirement gate.\n *\n * Every agent product hand-rolls the same pair: a `buildXKnowledgeRequirements`\n * declaring the requirements that gate its control loop, and a\n * `deriveXRuntimeKnowledge` that scores each one from workspace state. Across\n * the fleet those derives are uniformly \"is a config field set\" / \"are there\n * >= N rows in table T (optionally with a status filter)\" / \"any/all of the\n * above\" — data, not logic. This module makes both DATA: a spec list with\n * declarative `satisfiedBy` rules, plus a per-spec `derive` escape hatch for\n * the rare rule a declarative form can't express (e.g. an aggregate over a\n * JSON column).\n *\n * Substrate-free: the only seam is `KnowledgeStateAccessor` (a config lookup +\n * a row count), which the consumer's backend — or `agent-app/preset-cloudflare`\n * — implements. Emits `@tangle-network/agent-eval`'s `KnowledgeRequirement[]`,\n * exactly what the agent-runtime control loop consumes.\n */\n\nimport type {\n KnowledgeAcquisitionMode,\n KnowledgeFreshness,\n KnowledgeImportance,\n KnowledgeRequirement,\n KnowledgeRequirementCategory,\n KnowledgeSensitivity,\n} from '@tangle-network/agent-eval'\n\n/** A declarative rule for satisfying a requirement from workspace state. */\nexport type SatisfiedByRule =\n /** A workspace-config field (dot-path) is set. `nonEmpty` requires a\n * non-empty array/string rather than mere presence. */\n | { config: string; nonEmpty?: boolean }\n /** At least `minRows` (default 1) rows exist in `table` for the workspace,\n * optionally filtered to `statusIn`. `where` names the workspace fk column\n * the accessor scopes on (default: the accessor's convention). */\n | { table: string; where?: string; statusIn?: string[]; minRows?: number }\n | { anyOf: SatisfiedByRule[] }\n | { allOf: SatisfiedByRule[] }\n\nexport interface KnowledgeRequirementSpec {\n id: string\n description: string\n category: KnowledgeRequirementCategory\n acquisitionMode: KnowledgeAcquisitionMode\n importance?: KnowledgeImportance\n freshness?: KnowledgeFreshness\n sensitivity?: KnowledgeSensitivity\n confidenceNeeded?: number\n requiredFor?: string[]\n /** The data path — evaluated against the `KnowledgeStateAccessor`. */\n satisfiedBy?: SatisfiedByRule\n /** The escape hatch — a code derive for what a rule can't express. Wins\n * over `satisfiedBy` when both are present. Returns confidence in [0, 1]. */\n derive?: (ctx: KnowledgeStateAccessor) => number | Promise<number>\n /** Evidence id attached when satisfied (default: a description of the rule). */\n evidence?: string\n}\n\n/** The single seam a backend implements. `preset-cloudflare` provides a D1\n * implementation; a custom stack supplies its own. */\nexport interface KnowledgeStateAccessor {\n /** Resolve a workspace-config field value (dot-path), or undefined. */\n config: (path: string) => unknown\n /** Count rows in `table` for the active workspace, optionally status-filtered. */\n count: (query: { table: string; where?: string; statusIn?: string[] }) => number | Promise<number>\n}\n\nexport interface KnowledgeSignal {\n confidence: number\n evidence?: string\n}\n\nfunction clamp(value: number): number {\n if (!Number.isFinite(value)) return 0\n if (value < 0) return 0\n if (value > 1) return 1\n return value\n}\n\n/**\n * Map specs -> the runtime's `KnowledgeRequirement[]`, folding in per-spec\n * confidence from `signals` (default 0). Pure + sync: an eval harness can pass\n * hand-authored signals; production passes the output of {@link deriveSignals}.\n */\nexport function buildKnowledgeRequirements(\n specs: KnowledgeRequirementSpec[],\n signals: Record<string, KnowledgeSignal> = {},\n): KnowledgeRequirement[] {\n return specs.map((spec) => {\n const signal = signals[spec.id]\n return {\n id: spec.id,\n description: spec.description,\n requiredFor: spec.requiredFor ?? [],\n category: spec.category,\n acquisitionMode: spec.acquisitionMode,\n importance: spec.importance ?? 'blocking',\n freshness: spec.freshness ?? 'static',\n sensitivity: spec.sensitivity ?? 'private',\n confidenceNeeded: spec.confidenceNeeded ?? 1,\n currentConfidence: clamp(signal?.confidence ?? 0),\n evidenceIds: signal?.evidence ? [signal.evidence] : [],\n fallbackPolicy: spec.acquisitionMode === 'ask_user' ? 'ask' : 'block',\n }\n })\n}\n\n/**\n * Score every spec from workspace state. `derive` (code) wins; otherwise the\n * declarative `satisfiedBy` rule is evaluated through the accessor; a spec with\n * neither scores 0 (an acquisition gate, e.g. `search_web`).\n */\nexport async function deriveSignals(\n specs: KnowledgeRequirementSpec[],\n ctx: KnowledgeStateAccessor,\n): Promise<Record<string, KnowledgeSignal>> {\n const out: Record<string, KnowledgeSignal> = {}\n for (const spec of specs) {\n if (spec.derive) {\n out[spec.id] = { confidence: clamp(await spec.derive(ctx)), evidence: spec.evidence }\n } else if (spec.satisfiedBy) {\n const ok = await evalRule(spec.satisfiedBy, ctx)\n out[spec.id] = ok\n ? { confidence: 1, evidence: spec.evidence ?? describeRule(spec.satisfiedBy) }\n : { confidence: 0 }\n } else {\n out[spec.id] = { confidence: 0 }\n }\n }\n return out\n}\n\nasync function evalRule(rule: SatisfiedByRule, ctx: KnowledgeStateAccessor): Promise<boolean> {\n if ('anyOf' in rule) {\n for (const sub of rule.anyOf) if (await evalRule(sub, ctx)) return true\n return false\n }\n if ('allOf' in rule) {\n for (const sub of rule.allOf) if (!(await evalRule(sub, ctx))) return false\n return true\n }\n if ('config' in rule) {\n const value = ctx.config(rule.config)\n if (rule.nonEmpty) return Array.isArray(value) ? value.length > 0 : value != null && value !== ''\n return value != null && value !== '' && value !== false\n }\n const rows = await ctx.count({ table: rule.table, where: rule.where, statusIn: rule.statusIn })\n return rows >= (rule.minRows ?? 1)\n}\n\nfunction describeRule(rule: SatisfiedByRule): string {\n if ('anyOf' in rule) return `anyOf(${rule.anyOf.map(describeRule).join(',')})`\n if ('allOf' in rule) return `allOf(${rule.allOf.map(describeRule).join(',')})`\n if ('config' in rule) return `config:${rule.config}`\n return `${rule.table}${rule.statusIn ? `[${rule.statusIn.join('|')}]` : ''}>=${rule.minRows ?? 1}`\n}\n"],"mappings":";AAyEA,SAAS,MAAM,OAAuB;AACpC,MAAI,CAAC,OAAO,SAAS,KAAK,EAAG,QAAO;AACpC,MAAI,QAAQ,EAAG,QAAO;AACtB,MAAI,QAAQ,EAAG,QAAO;AACtB,SAAO;AACT;AAOO,SAAS,2BACd,OACA,UAA2C,CAAC,GACpB;AACxB,SAAO,MAAM,IAAI,CAAC,SAAS;AACzB,UAAM,SAAS,QAAQ,KAAK,EAAE;AAC9B,WAAO;AAAA,MACL,IAAI,KAAK;AAAA,MACT,aAAa,KAAK;AAAA,MAClB,aAAa,KAAK,eAAe,CAAC;AAAA,MAClC,UAAU,KAAK;AAAA,MACf,iBAAiB,KAAK;AAAA,MACtB,YAAY,KAAK,cAAc;AAAA,MAC/B,WAAW,KAAK,aAAa;AAAA,MAC7B,aAAa,KAAK,eAAe;AAAA,MACjC,kBAAkB,KAAK,oBAAoB;AAAA,MAC3C,mBAAmB,MAAM,QAAQ,cAAc,CAAC;AAAA,MAChD,aAAa,QAAQ,WAAW,CAAC,OAAO,QAAQ,IAAI,CAAC;AAAA,MACrD,gBAAgB,KAAK,oBAAoB,aAAa,QAAQ;AAAA,IAChE;AAAA,EACF,CAAC;AACH;AAOA,eAAsB,cACpB,OACA,KAC0C;AAC1C,QAAM,MAAuC,CAAC;AAC9C,aAAW,QAAQ,OAAO;AACxB,QAAI,KAAK,QAAQ;AACf,UAAI,KAAK,EAAE,IAAI,EAAE,YAAY,MAAM,MAAM,KAAK,OAAO,GAAG,CAAC,GAAG,UAAU,KAAK,SAAS;AAAA,IACtF,WAAW,KAAK,aAAa;AAC3B,YAAM,KAAK,MAAM,SAAS,KAAK,aAAa,GAAG;AAC/C,UAAI,KAAK,EAAE,IAAI,KACX,EAAE,YAAY,GAAG,UAAU,KAAK,YAAY,aAAa,KAAK,WAAW,EAAE,IAC3E,EAAE,YAAY,EAAE;AAAA,IACtB,OAAO;AACL,UAAI,KAAK,EAAE,IAAI,EAAE,YAAY,EAAE;AAAA,IACjC;AAAA,EACF;AACA,SAAO;AACT;AAEA,eAAe,SAAS,MAAuB,KAA+C;AAC5F,MAAI,WAAW,MAAM;AACnB,eAAW,OAAO,KAAK,MAAO,KAAI,MAAM,SAAS,KAAK,GAAG,EAAG,QAAO;AACnE,WAAO;AAAA,EACT;AACA,MAAI,WAAW,MAAM;AACnB,eAAW,OAAO,KAAK,MAAO,KAAI,CAAE,MAAM,SAAS,KAAK,GAAG,EAAI,QAAO;AACtE,WAAO;AAAA,EACT;AACA,MAAI,YAAY,MAAM;AACpB,UAAM,QAAQ,IAAI,OAAO,KAAK,MAAM;AACpC,QAAI,KAAK,SAAU,QAAO,MAAM,QAAQ,KAAK,IAAI,MAAM,SAAS,IAAI,SAAS,QAAQ,UAAU;AAC/F,WAAO,SAAS,QAAQ,UAAU,MAAM,UAAU;AAAA,EACpD;AACA,QAAM,OAAO,MAAM,IAAI,MAAM,EAAE,OAAO,KAAK,OAAO,OAAO,KAAK,OAAO,UAAU,KAAK,SAAS,CAAC;AAC9F,SAAO,SAAS,KAAK,WAAW;AAClC;AAEA,SAAS,aAAa,MAA+B;AACnD,MAAI,WAAW,KAAM,QAAO,SAAS,KAAK,MAAM,IAAI,YAAY,EAAE,KAAK,GAAG,CAAC;AAC3E,MAAI,WAAW,KAAM,QAAO,SAAS,KAAK,MAAM,IAAI,YAAY,EAAE,KAAK,GAAG,CAAC;AAC3E,MAAI,YAAY,KAAM,QAAO,UAAU,KAAK,MAAM;AAClD,SAAO,GAAG,KAAK,KAAK,GAAG,KAAK,WAAW,IAAI,KAAK,SAAS,KAAK,GAAG,CAAC,MAAM,EAAE,KAAK,KAAK,WAAW,CAAC;AAClG;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/sequences/model.ts"],"sourcesContent":["/**\n * Frame-accurate sequence timeline model — the product-agnostic spine of the\n * sequences surface. A sequence is a fixed-fps, fixed-duration timeline of\n * tracks; clips sit on tracks at integer frame positions with non-destructive\n * source in/out points. Products bind this model to their own storage through\n * `SequenceStore` (./store) and surface it to agents through the MCP toolset\n * (./mcp).\n *\n * All positions and durations are integer FRAMES at the sequence's fps.\n * Seconds appear only at the API edge (agent tools speak seconds; the\n * dispatcher converts exactly once). Nothing here touches a database, the DOM,\n * or React.\n */\n\nexport const MIN_SEQUENCE_CLIP_FRAMES = 1\n\n/** Track kinds. `reference` holds non-rendered guide media; `agent` holds the\n * agent-decision lane rendered as markers, never as media. */\nexport type SequenceTrackKind = 'video' | 'audio' | 'caption' | 'reference' | 'agent'\n\nexport type SequenceStatus = 'draft' | 'active' | 'exporting' | 'archived'\n\nexport type SequenceExportFormat = 'mp4' | 'otio' | 'xml' | 'edl' | 'vtt' | 'srt' | 'contact_sheet'\n\nexport type SequenceExportStatus = 'queued' | 'processing' | 'completed' | 'failed' | 'cancelled'\n\nexport type SequenceMediaKind = 'video' | 'image' | 'audio'\n\nexport interface SequenceMeta {\n id: string\n title: string\n fps: number\n width: number\n height: number\n aspectRatio: string\n durationFrames: number\n status: SequenceStatus\n metadata: Record<string, unknown>\n}\n\nexport interface SequenceTrack {\n id: string\n kind: SequenceTrackKind\n name: string\n sortOrder: number\n locked: boolean\n muted: boolean\n metadata: Record<string, unknown>\n}\n\n/** Resolved playable media behind a clip. The store resolves product-specific\n * references (generation rows, asset rows) into this shape; the core model\n * never sees the product's tables. */\nexport interface SequenceClipMedia {\n url: string\n kind: SequenceMediaKind\n /** Natural duration of the source media when known. */\n durationSeconds?: number\n /** Provider job state for media still rendering upstream. */\n providerStatus?: 'queued' | 'processing' | 'completed' | 'failed'\n}\n\nexport interface SequenceClip {\n id: string\n trackId: string\n label: string\n startFrame: number\n durationFrames: number\n /** Source-relative in point (frames into the source media). */\n sourceInFrame: number\n /** Source-relative out point; null = natural end of the source. */\n sourceOutFrame: number | null\n disabled: boolean\n /** Caption/text body for clips on caption tracks. */\n text?: string\n /** BCP-47 language tag for caption clips (e.g. 'en', 'es', 'ja'). */\n language?: string\n /** Opaque product reference to a generation row, when the clip came from one. */\n generationId?: string\n /** Opaque product reference to an asset row, when the clip came from one. */\n assetId?: string\n media?: SequenceClipMedia\n metadata: Record<string, unknown>\n}\n\n/** One entry in the sequence's decision log — human edits, agent proposals,\n * agent edits, exports, and notes all land here so the edit history is a\n * single auditable lane. */\nexport interface SequenceDecision {\n id: string\n clipId: string | null\n kind: 'human_edit' | 'agent_proposal' | 'agent_edit' | 'export' | 'note'\n instruction: string\n reasoningSummary: string | null\n accepted: boolean | null\n metadata: Record<string, unknown>\n createdAt: Date\n}\n\nexport interface SequenceExportRecord {\n id: string\n format: SequenceExportFormat\n status: SequenceExportStatus\n resultUrl: string | null\n metadata: Record<string, unknown>\n createdAt: Date\n}\n\n/** The full timeline aggregate — what `get_timeline_state` returns and what\n * every operation validates against. */\nexport interface SequenceTimeline {\n sequence: SequenceMeta\n tracks: SequenceTrack[]\n clips: SequenceClip[]\n}\n\n/** What is on screen/audible at a single frame — the answer shape for\n * \"what is happening at 0:34\". */\nexport interface SequenceFrameSnapshot {\n frame: number\n seconds: number\n /** Active (enabled, in-range) clips at this frame, with their track. */\n active: Array<{ track: SequenceTrack; clip: SequenceClip }>\n /** Caption text visible at this frame, in track sort order. */\n captions: Array<{ text: string; language?: string; clipId: string }>\n}\n\n// ---------------------------------------------------------------------------\n// Frame math\n// ---------------------------------------------------------------------------\n\nexport function secondsToFrames(seconds: number, fps: number): number {\n if (!Number.isFinite(seconds) || seconds < 0) throw new Error('seconds must be a non-negative finite number')\n assertFps(fps)\n return Math.round(seconds * fps)\n}\n\nexport function framesToSeconds(frames: number, fps: number): number {\n if (!Number.isInteger(frames) || frames < 0) throw new Error('frames must be a non-negative integer')\n assertFps(fps)\n return frames / fps\n}\n\nexport function formatSeconds(seconds: number): string {\n return Number.isInteger(seconds) ? `${seconds}s` : `${seconds.toFixed(2)}s`\n}\n\n/** `m:ss.ff` timecode for UI and agent-readable frame references. */\nexport function formatTimecode(frames: number, fps: number): string {\n assertFps(fps)\n if (!Number.isInteger(frames) || frames < 0) throw new Error('frames must be a non-negative integer')\n const totalSeconds = Math.floor(frames / fps)\n const minutes = Math.floor(totalSeconds / 60)\n const seconds = totalSeconds % 60\n const residualFrames = frames % fps\n return `${minutes}:${String(seconds).padStart(2, '0')}.${String(residualFrames).padStart(2, '0')}`\n}\n\nexport interface TimelineClipBounds {\n startFrame: number\n durationFrames: number\n}\n\nexport interface TimelineInterval {\n startFrame: number\n endFrame: number\n}\n\nexport function clampClipStart(input: {\n startFrame: number\n durationFrames: number\n sequenceDurationFrames: number\n}): number {\n assertSequenceDuration(input.sequenceDurationFrames)\n assertClipDuration(input.durationFrames)\n return Math.max(0, Math.min(input.sequenceDurationFrames - input.durationFrames, input.startFrame))\n}\n\nexport function clampClipDuration(input: {\n startFrame: number\n durationFrames: number\n sequenceDurationFrames: number\n}): number {\n assertSequenceDuration(input.sequenceDurationFrames)\n if (input.startFrame < 0 || input.startFrame >= input.sequenceDurationFrames) {\n throw new Error('startFrame must be inside the sequence')\n }\n return Math.max(MIN_SEQUENCE_CLIP_FRAMES, Math.min(input.durationFrames, input.sequenceDurationFrames - input.startFrame))\n}\n\nexport function assertClipFitsSequence(input: {\n startFrame: number\n durationFrames: number\n sequenceDurationFrames: number\n label: string\n}): void {\n assertSequenceDuration(input.sequenceDurationFrames)\n assertClipDuration(input.durationFrames)\n if (!Number.isInteger(input.startFrame) || input.startFrame < 0) {\n throw new Error(`${input.label} startFrame must be a non-negative integer`)\n }\n if (input.startFrame + input.durationFrames > input.sequenceDurationFrames) {\n throw new Error(`${input.label} extends beyond the sequence duration`)\n }\n}\n\n/** Place a caption near the playhead inside FREE space only — the caption\n * track never double-books. Prefers fps*3 frames, floors at fps. The gap\n * holding (or first after) the playhead wins; with everything ahead occupied\n * the latest earlier gap is used instead. Throws when no gap can hold the\n * minimum — the caller must supply explicit bounds or clear space. */\nexport function chooseCaptionPlacement(input: {\n playheadFrame: number\n fps: number\n sequenceDurationFrames: number\n occupiedIntervals: TimelineInterval[]\n}): TimelineClipBounds {\n const preferredDurationFrames = Math.min(input.fps * 3, input.sequenceDurationFrames)\n const minimumDurationFrames = Math.min(input.fps, preferredDurationFrames)\n const occupied = input.occupiedIntervals\n .map((interval) => ({\n startFrame: Math.max(0, interval.startFrame),\n endFrame: Math.min(input.sequenceDurationFrames, interval.endFrame),\n }))\n .filter((interval) => interval.endFrame > interval.startFrame)\n .sort((a, b) => a.startFrame - b.startFrame)\n\n // Merge overlaps so the gap walk sees each free run exactly once.\n const merged: TimelineInterval[] = []\n for (const interval of occupied) {\n const last = merged[merged.length - 1]\n if (last && interval.startFrame <= last.endFrame) last.endFrame = Math.max(last.endFrame, interval.endFrame)\n else merged.push({ ...interval })\n }\n\n const gaps: TimelineInterval[] = []\n let cursor = 0\n for (const interval of merged) {\n if (interval.startFrame > cursor) gaps.push({ startFrame: cursor, endFrame: interval.startFrame })\n cursor = interval.endFrame\n }\n if (cursor < input.sequenceDurationFrames) gaps.push({ startFrame: cursor, endFrame: input.sequenceDurationFrames })\n\n const usable = gaps.filter((gap) => gap.endFrame - gap.startFrame >= minimumDurationFrames)\n const latestUsable = usable[usable.length - 1]\n if (latestUsable === undefined) {\n throw new Error(\n `no free gap of at least ${minimumDurationFrames} frames on the caption track — pass explicit startFrame/durationFrames or clear space first`,\n )\n }\n const gap = usable.find((candidate) => candidate.endFrame > input.playheadFrame) ?? latestUsable\n const durationFrames = Math.min(preferredDurationFrames, gap.endFrame - gap.startFrame)\n const startFrame = Math.max(gap.startFrame, Math.min(input.playheadFrame, gap.endFrame - durationFrames))\n return { startFrame, durationFrames }\n}\n\n/** Resolve everything active at one frame — the core of `get_frame_at_time`. */\nexport function snapshotFrame(timeline: SequenceTimeline, frame: number): SequenceFrameSnapshot {\n if (!Number.isInteger(frame) || frame < 0) throw new Error('frame must be a non-negative integer')\n if (frame >= timeline.sequence.durationFrames) {\n throw new Error(`frame ${frame} is beyond the sequence (${timeline.sequence.durationFrames} frames)`)\n }\n const trackById = new Map(timeline.tracks.map((track) => [track.id, track]))\n const sortedTracks = [...timeline.tracks].sort((a, b) => a.sortOrder - b.sortOrder)\n const trackOrder = new Map(sortedTracks.map((track, index) => [track.id, index]))\n\n const active = timeline.clips\n .filter((clip) => !clip.disabled && clip.startFrame <= frame && frame < clip.startFrame + clip.durationFrames)\n .map((clip) => {\n const track = trackById.get(clip.trackId)\n if (!track) throw new Error(`clip ${clip.id} references unknown track ${clip.trackId}`)\n return { track, clip }\n })\n .sort((a, b) => (trackOrder.get(a.track.id) ?? 0) - (trackOrder.get(b.track.id) ?? 0))\n\n const captions = active\n .filter(({ track, clip }) => track.kind === 'caption' && typeof clip.text === 'string' && clip.text.length > 0)\n .map(({ clip }) => ({ text: clip.text as string, language: clip.language, clipId: clip.id }))\n\n return {\n frame,\n seconds: framesToSeconds(frame, timeline.sequence.fps),\n active,\n captions,\n }\n}\n\n/** Occupied intervals on one track, for placement collision checks. */\nexport function trackIntervals(timeline: SequenceTimeline, trackId: string): TimelineInterval[] {\n return timeline.clips\n .filter((clip) => clip.trackId === trackId && !clip.disabled)\n .map((clip) => ({ startFrame: clip.startFrame, endFrame: clip.startFrame + clip.durationFrames }))\n .sort((a, b) => a.startFrame - b.startFrame)\n}\n\nfunction assertFps(fps: number): void {\n if (!Number.isInteger(fps) || fps <= 0) throw new Error('fps must be a positive integer')\n}\n\nfunction assertSequenceDuration(sequenceDurationFrames: number): void {\n if (!Number.isInteger(sequenceDurationFrames) || sequenceDurationFrames < MIN_SEQUENCE_CLIP_FRAMES) {\n throw new Error('sequenceDurationFrames must be a positive integer')\n }\n}\n\nfunction assertClipDuration(durationFrames: number): void {\n if (!Number.isInteger(durationFrames) || durationFrames < MIN_SEQUENCE_CLIP_FRAMES) {\n throw new Error('durationFrames must be a positive integer')\n }\n}\n"],"mappings":"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","names":["gap"]}