@rudderhq/server 0.3.6-canary.9 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (325) hide show
  1. package/dist/bootstrap/register-api-routes.d.ts.map +1 -1
  2. package/dist/bootstrap/register-api-routes.js +2 -0
  3. package/dist/bootstrap/register-api-routes.js.map +1 -1
  4. package/dist/bundled-plugins/plugin-linear/dist/worker.js +35 -2
  5. package/dist/bundled-plugins/plugin-linear/dist/worker.js.map +2 -2
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +103 -26
  8. package/dist/index.js.map +1 -1
  9. package/dist/routes/activity.d.ts +1 -1
  10. package/dist/routes/activity.d.ts.map +1 -1
  11. package/dist/routes/activity.js +21 -5
  12. package/dist/routes/activity.js.map +1 -1
  13. package/dist/routes/agents.d.ts.map +1 -1
  14. package/dist/routes/agents.js +254 -4
  15. package/dist/routes/agents.js.map +1 -1
  16. package/dist/routes/agents.management-routes.d.ts.map +1 -1
  17. package/dist/routes/agents.management-routes.js +132 -40
  18. package/dist/routes/agents.management-routes.js.map +1 -1
  19. package/dist/routes/automations.d.ts +8 -0
  20. package/dist/routes/automations.d.ts.map +1 -1
  21. package/dist/routes/automations.js.map +1 -1
  22. package/dist/routes/chats.d.ts.map +1 -1
  23. package/dist/routes/chats.js +228 -115
  24. package/dist/routes/chats.js.map +1 -1
  25. package/dist/routes/chats.stream-routes.d.ts.map +1 -1
  26. package/dist/routes/chats.stream-routes.js +94 -5
  27. package/dist/routes/chats.stream-routes.js.map +1 -1
  28. package/dist/routes/issues.comments-attachments.d.ts.map +1 -1
  29. package/dist/routes/issues.comments-attachments.js +72 -83
  30. package/dist/routes/issues.comments-attachments.js.map +1 -1
  31. package/dist/routes/issues.d.ts +8 -0
  32. package/dist/routes/issues.d.ts.map +1 -1
  33. package/dist/routes/issues.js +0 -4
  34. package/dist/routes/issues.js.map +1 -1
  35. package/dist/routes/issues.mutations.d.ts.map +1 -1
  36. package/dist/routes/issues.mutations.js +2 -26
  37. package/dist/routes/issues.mutations.js.map +1 -1
  38. package/dist/routes/messenger.d.ts.map +1 -1
  39. package/dist/routes/messenger.js +33 -1
  40. package/dist/routes/messenger.js.map +1 -1
  41. package/dist/routes/orgs.d.ts.map +1 -1
  42. package/dist/routes/orgs.js +16 -0
  43. package/dist/routes/orgs.js.map +1 -1
  44. package/dist/routes/plugin-ui-static.d.ts +4 -4
  45. package/dist/routes/plugin-ui-static.js +5 -5
  46. package/dist/routes/plugin-ui-static.js.map +1 -1
  47. package/dist/routes/plugins.d.ts +4 -4
  48. package/dist/routes/plugins.js +10 -10
  49. package/dist/routes/plugins.operations-routes.d.ts +1 -1
  50. package/dist/routes/plugins.operations-routes.js +2 -2
  51. package/dist/routes/plugins.operations-routes.js.map +1 -1
  52. package/dist/routes/run-intelligence.js +5 -5
  53. package/dist/routes/run-intelligence.js.map +1 -1
  54. package/dist/routes/website-metadata.d.ts +8 -0
  55. package/dist/routes/website-metadata.d.ts.map +1 -0
  56. package/dist/routes/website-metadata.js +67 -0
  57. package/dist/routes/website-metadata.js.map +1 -0
  58. package/dist/services/activity.d.ts.map +1 -1
  59. package/dist/services/activity.js +1 -1
  60. package/dist/services/activity.js.map +1 -1
  61. package/dist/services/agent-instructions.d.ts +1 -0
  62. package/dist/services/agent-instructions.d.ts.map +1 -1
  63. package/dist/services/agent-instructions.js +57 -6
  64. package/dist/services/agent-instructions.js.map +1 -1
  65. package/dist/services/agent-run-context.d.ts +3 -1
  66. package/dist/services/agent-run-context.d.ts.map +1 -1
  67. package/dist/services/agent-run-context.js +7 -0
  68. package/dist/services/agent-run-context.js.map +1 -1
  69. package/dist/services/agent-startup-context.d.ts +2 -1
  70. package/dist/services/agent-startup-context.d.ts.map +1 -1
  71. package/dist/services/agent-startup-context.js +6 -26
  72. package/dist/services/agent-startup-context.js.map +1 -1
  73. package/dist/services/agents.d.ts +9 -1
  74. package/dist/services/agents.d.ts.map +1 -1
  75. package/dist/services/agents.js.map +1 -1
  76. package/dist/services/approvals.d.ts +8 -0
  77. package/dist/services/approvals.d.ts.map +1 -1
  78. package/dist/services/approvals.js.map +1 -1
  79. package/dist/services/automation-chat-output.d.ts +1 -1
  80. package/dist/services/automations.d.ts +4 -4
  81. package/dist/services/automations.d.ts.map +1 -1
  82. package/dist/services/automations.js +12 -0
  83. package/dist/services/automations.js.map +1 -1
  84. package/dist/services/budgets.d.ts +8 -0
  85. package/dist/services/budgets.d.ts.map +1 -1
  86. package/dist/services/budgets.js.map +1 -1
  87. package/dist/services/calendar.d.ts +2 -2
  88. package/dist/services/calendar.js +3 -3
  89. package/dist/services/calendar.js.map +1 -1
  90. package/dist/services/chat-agent-runs.d.ts +2 -0
  91. package/dist/services/chat-agent-runs.d.ts.map +1 -1
  92. package/dist/services/chat-agent-runs.js +6 -0
  93. package/dist/services/chat-agent-runs.js.map +1 -1
  94. package/dist/services/chat-assistant.d.ts.map +1 -1
  95. package/dist/services/chat-assistant.helpers.d.ts +1 -0
  96. package/dist/services/chat-assistant.helpers.d.ts.map +1 -1
  97. package/dist/services/chat-assistant.helpers.js +7 -10
  98. package/dist/services/chat-assistant.helpers.js.map +1 -1
  99. package/dist/services/chat-assistant.js +23 -6
  100. package/dist/services/chat-assistant.js.map +1 -1
  101. package/dist/services/chat-generation-locks.d.ts +6 -1
  102. package/dist/services/chat-generation-locks.d.ts.map +1 -1
  103. package/dist/services/chat-generation-locks.js +20 -2
  104. package/dist/services/chat-generation-locks.js.map +1 -1
  105. package/dist/services/chat-title-generation.d.ts +36 -0
  106. package/dist/services/chat-title-generation.d.ts.map +1 -0
  107. package/dist/services/chat-title-generation.js +107 -0
  108. package/dist/services/chat-title-generation.js.map +1 -0
  109. package/dist/services/chats.d.ts +471 -25
  110. package/dist/services/chats.d.ts.map +1 -1
  111. package/dist/services/chats.helpers.d.ts +2 -2
  112. package/dist/services/chats.js +651 -35
  113. package/dist/services/chats.js.map +1 -1
  114. package/dist/services/costs.d.ts +1 -1
  115. package/dist/services/finance.d.ts +2 -2
  116. package/dist/services/finance.js +1 -1
  117. package/dist/services/finance.js.map +1 -1
  118. package/dist/services/goals.d.ts +8 -0
  119. package/dist/services/goals.d.ts.map +1 -1
  120. package/dist/services/goals.js.map +1 -1
  121. package/dist/services/heartbeat-run-reference.d.ts +1 -0
  122. package/dist/services/heartbeat-run-reference.d.ts.map +1 -1
  123. package/dist/services/heartbeat-run-reference.js +3 -2
  124. package/dist/services/heartbeat-run-reference.js.map +1 -1
  125. package/dist/services/integrations/agent-integrations.d.ts +19 -0
  126. package/dist/services/integrations/agent-integrations.d.ts.map +1 -1
  127. package/dist/services/integrations/agent-integrations.js +30 -0
  128. package/dist/services/integrations/agent-integrations.js.map +1 -1
  129. package/dist/services/integrations/feishu/app-registration.d.ts +74 -0
  130. package/dist/services/integrations/feishu/app-registration.d.ts.map +1 -0
  131. package/dist/services/integrations/feishu/app-registration.js +226 -0
  132. package/dist/services/integrations/feishu/app-registration.js.map +1 -0
  133. package/dist/services/integrations/feishu/inbound-dispatcher-db.d.ts +4 -0
  134. package/dist/services/integrations/feishu/inbound-dispatcher-db.d.ts.map +1 -1
  135. package/dist/services/integrations/feishu/inbound-dispatcher-db.js +35 -5
  136. package/dist/services/integrations/feishu/inbound-dispatcher-db.js.map +1 -1
  137. package/dist/services/integrations/feishu/inbound-dispatcher.d.ts +2 -0
  138. package/dist/services/integrations/feishu/inbound-dispatcher.d.ts.map +1 -1
  139. package/dist/services/integrations/feishu/inbound-dispatcher.js.map +1 -1
  140. package/dist/services/integrations/feishu/runtime-registry.d.ts +20 -0
  141. package/dist/services/integrations/feishu/runtime-registry.d.ts.map +1 -0
  142. package/dist/services/integrations/feishu/runtime-registry.js +41 -0
  143. package/dist/services/integrations/feishu/runtime-registry.js.map +1 -0
  144. package/dist/services/integrations/feishu/runtime.d.ts +21 -23
  145. package/dist/services/integrations/feishu/runtime.d.ts.map +1 -1
  146. package/dist/services/integrations/feishu/runtime.js +135 -45
  147. package/dist/services/integrations/feishu/runtime.js.map +1 -1
  148. package/dist/services/integrations/feishu/user-bindings.d.ts +22 -0
  149. package/dist/services/integrations/feishu/user-bindings.d.ts.map +1 -0
  150. package/dist/services/integrations/feishu/user-bindings.js +57 -0
  151. package/dist/services/integrations/feishu/user-bindings.js.map +1 -0
  152. package/dist/services/issue-approvals.d.ts +2 -2
  153. package/dist/services/issue-review-wakeup.js +1 -1
  154. package/dist/services/issue-review-wakeup.js.map +1 -1
  155. package/dist/services/issues.comments-attachments.d.ts +3 -3
  156. package/dist/services/issues.d.ts +14 -6
  157. package/dist/services/issues.d.ts.map +1 -1
  158. package/dist/services/issues.helpers.d.ts +9 -1
  159. package/dist/services/issues.helpers.d.ts.map +1 -1
  160. package/dist/services/issues.helpers.js +8 -0
  161. package/dist/services/issues.helpers.js.map +1 -1
  162. package/dist/services/issues.js +1 -1
  163. package/dist/services/issues.js.map +1 -1
  164. package/dist/services/knowledge-portability/organization-skills.catalog.d.ts +1 -1
  165. package/dist/services/library-entries.d.ts +8 -0
  166. package/dist/services/library-entries.d.ts.map +1 -1
  167. package/dist/services/library-entries.js +8 -0
  168. package/dist/services/library-entries.js.map +1 -1
  169. package/dist/services/messenger.d.ts +19 -4
  170. package/dist/services/messenger.d.ts.map +1 -1
  171. package/dist/services/messenger.js +136 -18
  172. package/dist/services/messenger.js.map +1 -1
  173. package/dist/services/plugin-capability-validator.d.ts +1 -1
  174. package/dist/services/plugin-capability-validator.js +2 -2
  175. package/dist/services/plugin-dev-watcher.js +1 -1
  176. package/dist/services/plugin-event-bus.d.ts +3 -3
  177. package/dist/services/plugin-event-bus.js +3 -3
  178. package/dist/services/plugin-job-coordinator.d.ts +1 -1
  179. package/dist/services/plugin-job-coordinator.js +1 -1
  180. package/dist/services/plugin-job-scheduler.d.ts +1 -1
  181. package/dist/services/plugin-job-scheduler.js +1 -1
  182. package/dist/services/plugin-job-store.d.ts +2 -2
  183. package/dist/services/plugin-job-store.js +2 -2
  184. package/dist/services/plugin-lifecycle.d.ts +4 -4
  185. package/dist/services/plugin-lifecycle.js +3 -3
  186. package/dist/services/plugin-loader.core.d.ts +4 -4
  187. package/dist/services/plugin-loader.core.js +2 -2
  188. package/dist/services/plugin-loader.core.js.map +1 -1
  189. package/dist/services/plugin-loader.d.ts +3 -3
  190. package/dist/services/plugin-loader.helpers.d.ts +26 -25
  191. package/dist/services/plugin-loader.helpers.d.ts.map +1 -1
  192. package/dist/services/plugin-loader.helpers.js +10 -9
  193. package/dist/services/plugin-loader.helpers.js.map +1 -1
  194. package/dist/services/plugin-loader.js +3 -3
  195. package/dist/services/plugin-loader.worker-paths.d.ts +3 -3
  196. package/dist/services/plugin-manifest-validator.d.ts +2 -2
  197. package/dist/services/plugin-registry.d.ts +9 -9
  198. package/dist/services/plugin-registry.js +1 -1
  199. package/dist/services/plugin-secrets-handler.d.ts +2 -2
  200. package/dist/services/plugin-secrets-handler.js +2 -2
  201. package/dist/services/plugin-state-store.d.ts +3 -3
  202. package/dist/services/plugin-state-store.js +3 -3
  203. package/dist/services/plugin-stream-bus.d.ts +1 -1
  204. package/dist/services/plugin-stream-bus.js +1 -1
  205. package/dist/services/plugin-tool-dispatcher.d.ts +2 -2
  206. package/dist/services/plugin-tool-dispatcher.js +2 -2
  207. package/dist/services/plugin-tool-registry.d.ts +2 -2
  208. package/dist/services/plugin-tool-registry.js +2 -2
  209. package/dist/services/plugin-worker-manager.d.ts +4 -4
  210. package/dist/services/plugin-worker-manager.d.ts.map +1 -1
  211. package/dist/services/plugin-worker-manager.js +8 -7
  212. package/dist/services/plugin-worker-manager.js.map +1 -1
  213. package/dist/services/run-intelligence.js +3 -3
  214. package/dist/services/run-intelligence.js.map +1 -1
  215. package/dist/services/runtime-kernel/heartbeat.core.d.ts +9 -1
  216. package/dist/services/runtime-kernel/heartbeat.core.d.ts.map +1 -1
  217. package/dist/services/runtime-kernel/heartbeat.core.js +16 -5
  218. package/dist/services/runtime-kernel/heartbeat.core.js.map +1 -1
  219. package/dist/services/runtime-kernel/heartbeat.d.ts +3 -3
  220. package/dist/services/runtime-kernel/heartbeat.execute.d.ts.map +1 -1
  221. package/dist/services/runtime-kernel/heartbeat.execute.js +17 -3
  222. package/dist/services/runtime-kernel/heartbeat.execute.js.map +1 -1
  223. package/dist/services/runtime-kernel/heartbeat.misc.js +1 -1
  224. package/dist/services/runtime-kernel/heartbeat.recovery.d.ts +1 -1
  225. package/dist/services/runtime-kernel/heartbeat.recovery.js +7 -7
  226. package/dist/services/runtime-kernel/heartbeat.recovery.js.map +1 -1
  227. package/dist/services/runtime-kernel/heartbeat.release.js +4 -4
  228. package/dist/services/runtime-kernel/heartbeat.release.js.map +1 -1
  229. package/dist/services/runtime-kernel/heartbeat.wakeup.js +1 -1
  230. package/dist/services/sidebar-badges.d.ts.map +1 -1
  231. package/dist/services/sidebar-badges.js +13 -1
  232. package/dist/services/sidebar-badges.js.map +1 -1
  233. package/dist/services/title-generation.d.ts +13 -0
  234. package/dist/services/title-generation.d.ts.map +1 -0
  235. package/dist/services/title-generation.js +77 -0
  236. package/dist/services/title-generation.js.map +1 -0
  237. package/dist/services/website-metadata.d.ts +17 -0
  238. package/dist/services/website-metadata.d.ts.map +1 -0
  239. package/dist/services/website-metadata.js +293 -0
  240. package/dist/services/website-metadata.js.map +1 -0
  241. package/dist/services/workspace-backups.d.ts +22 -0
  242. package/dist/services/workspace-backups.d.ts.map +1 -1
  243. package/dist/services/workspace-backups.js +161 -5
  244. package/dist/services/workspace-backups.js.map +1 -1
  245. package/dist/services/workspace-runtime.services.d.ts +1 -1
  246. package/package.json +14 -13
  247. package/resources/bundled-skills/rudder/SKILL.md +7 -6
  248. package/resources/bundled-skills/rudder/references/cli-reference.md +3 -3
  249. package/resources/bundled-skills/rudder-create-plugin/SKILL.md +2 -2
  250. package/skills/rudder/SKILL.md +7 -6
  251. package/skills/rudder/references/cli-reference.md +3 -3
  252. package/skills/rudder-create-plugin/SKILL.md +2 -2
  253. package/ui-dist/assets/{_basePickBy-CM0XatHw.js → _basePickBy-CyhMvFCw.js} +1 -1
  254. package/ui-dist/assets/{_baseUniq-CdBBmdoP.js → _baseUniq-k3x-egdv.js} +1 -1
  255. package/ui-dist/assets/{arc-DSTtxyJ9.js → arc-DVwZMGLR.js} +1 -1
  256. package/ui-dist/assets/{architectureDiagram-2XIMDMQ5-BkHMYb5I.js → architectureDiagram-2XIMDMQ5-BvKp7DHW.js} +1 -1
  257. package/ui-dist/assets/{blockDiagram-WCTKOSBZ-EYczZxwW.js → blockDiagram-WCTKOSBZ-CQ0KLoGd.js} +1 -1
  258. package/ui-dist/assets/{c4Diagram-IC4MRINW-Xo9qijMi.js → c4Diagram-IC4MRINW-BY44eeD7.js} +1 -1
  259. package/ui-dist/assets/channel-Cb8ybYSt.js +1 -0
  260. package/ui-dist/assets/{chunk-4BX2VUAB-CKzta1FF.js → chunk-4BX2VUAB-D7D6PuZU.js} +1 -1
  261. package/ui-dist/assets/{chunk-55IACEB6-BxxUB0ww.js → chunk-55IACEB6-CraDwywJ.js} +1 -1
  262. package/ui-dist/assets/{chunk-FMBD7UC4-0F4oxVtY.js → chunk-FMBD7UC4-BGYh4kbq.js} +1 -1
  263. package/ui-dist/assets/{chunk-JSJVCQXG-CVMPMlVD.js → chunk-JSJVCQXG-DndeOLp_.js} +1 -1
  264. package/ui-dist/assets/{chunk-KX2RTZJC-lPrAWT94.js → chunk-KX2RTZJC-Cx0eOadl.js} +1 -1
  265. package/ui-dist/assets/{chunk-NQ4KR5QH-Bufcm7Iv.js → chunk-NQ4KR5QH-PNVbQ_36.js} +1 -1
  266. package/ui-dist/assets/{chunk-QZHKN3VN-CucmdWnl.js → chunk-QZHKN3VN-DKsTZ1-C.js} +1 -1
  267. package/ui-dist/assets/{chunk-WL4C6EOR-Dz0oYQrJ.js → chunk-WL4C6EOR-B4aSDSws.js} +1 -1
  268. package/ui-dist/assets/classDiagram-VBA2DB6C-US2diuPm.js +1 -0
  269. package/ui-dist/assets/classDiagram-v2-RAHNMMFH-US2diuPm.js +1 -0
  270. package/ui-dist/assets/clone-CbFoFrhF.js +1 -0
  271. package/ui-dist/assets/{cose-bilkent-S5V4N54A-DzyX-Er8.js → cose-bilkent-S5V4N54A-DwgS7_iT.js} +1 -1
  272. package/ui-dist/assets/{dagre-KLK3FWXG-BbZzCKuN.js → dagre-KLK3FWXG-CGMeHr1L.js} +1 -1
  273. package/ui-dist/assets/{diagram-E7M64L7V-B5Rb7Bm4.js → diagram-E7M64L7V-1AVTOsRS.js} +1 -1
  274. package/ui-dist/assets/{diagram-IFDJBPK2-Cpu1Xc8G.js → diagram-IFDJBPK2-hfbRtuHq.js} +1 -1
  275. package/ui-dist/assets/{diagram-P4PSJMXO-kLfHqRlk.js → diagram-P4PSJMXO-BypSVEur.js} +1 -1
  276. package/ui-dist/assets/{erDiagram-INFDFZHY-rCnUyQUb.js → erDiagram-INFDFZHY-DWMZC8pq.js} +1 -1
  277. package/ui-dist/assets/{flowDiagram-PKNHOUZH-BhOz8cvV.js → flowDiagram-PKNHOUZH-C4ODl03j.js} +1 -1
  278. package/ui-dist/assets/{ganttDiagram-A5KZAMGK-Csg9g3hz.js → ganttDiagram-A5KZAMGK-K0UbcKpB.js} +1 -1
  279. package/ui-dist/assets/{gitGraphDiagram-K3NZZRJ6-DLPnSlRs.js → gitGraphDiagram-K3NZZRJ6-CEtw8QBQ.js} +1 -1
  280. package/ui-dist/assets/{graph-BrxYh68N.js → graph-D-YEM3eO.js} +1 -1
  281. package/ui-dist/assets/{index-DMkClCka.js → index--e_5aFtV.js} +1 -1
  282. package/ui-dist/assets/{index-G_y-oBne.js → index--wGHASfH.js} +1 -1
  283. package/ui-dist/assets/index-2sHXDbqQ.css +1 -0
  284. package/ui-dist/assets/{index-BQW242nS.js → index-8D4SrlCZ.js} +1 -1
  285. package/ui-dist/assets/{index-DpdsNIGb.js → index-B9FglFcf.js} +1 -1
  286. package/ui-dist/assets/{index-D4FFno9t.js → index-BOLNi_9C.js} +1 -1
  287. package/ui-dist/assets/{index-COBwmF-C.js → index-BVSwDm4x.js} +1 -1
  288. package/ui-dist/assets/{index-BkzdTp2b.js → index-C4vZZ0jB.js} +1 -1
  289. package/ui-dist/assets/{index-Br07RRw1.js → index-CMZ9kRE0.js} +1 -1
  290. package/ui-dist/assets/{index-mOyLwPeE.js → index-CZb2W71E.js} +1 -1
  291. package/ui-dist/assets/{index-DiMeST8g.js → index-Cfp9ooqM.js} +1 -1
  292. package/ui-dist/assets/{index-BeZv_Z0b.js → index-CyWWtF_x.js} +1 -1
  293. package/ui-dist/assets/{index-CsraICzz.js → index-CzQXnPD8.js} +1 -1
  294. package/ui-dist/assets/{index-Bw8MgLaM.js → index-D2uGk78l.js} +446 -446
  295. package/ui-dist/assets/{index-Cb2qts0o.js → index-DVl9xsgP.js} +1 -1
  296. package/ui-dist/assets/{index-D0rP99dj.js → index-T4jxRzoA.js} +1 -1
  297. package/ui-dist/assets/{index-B6_gFYNj.js → index-V0iKRmt7.js} +1 -1
  298. package/ui-dist/assets/{index-Dj72ldIc.js → index-byosaKUg.js} +1 -1
  299. package/ui-dist/assets/{index-D_06pAuN.js → index-nUtypsmU.js} +1 -1
  300. package/ui-dist/assets/{infoDiagram-LFFYTUFH-CRBKgiEf.js → infoDiagram-LFFYTUFH-C6TFdmXf.js} +1 -1
  301. package/ui-dist/assets/{ishikawaDiagram-PHBUUO56-DI3jquVE.js → ishikawaDiagram-PHBUUO56-DyiJqs9l.js} +1 -1
  302. package/ui-dist/assets/{journeyDiagram-4ABVD52K-DDFZ2Q6D.js → journeyDiagram-4ABVD52K-A-q8eZgk.js} +1 -1
  303. package/ui-dist/assets/{kanban-definition-K7BYSVSG-yL5u3cXV.js → kanban-definition-K7BYSVSG-B_1rE7-5.js} +1 -1
  304. package/ui-dist/assets/{layout-DDZaRkp7.js → layout-kMGiiRYg.js} +1 -1
  305. package/ui-dist/assets/{linear-CWngWuQa.js → linear-PdbwVMlL.js} +1 -1
  306. package/ui-dist/assets/{mermaid.core-CQuYoq9K.js → mermaid.core-Cev3K7jk.js} +4 -4
  307. package/ui-dist/assets/{mindmap-definition-YRQLILUH-BwSsHtTO.js → mindmap-definition-YRQLILUH-C8xkWcsS.js} +1 -1
  308. package/ui-dist/assets/{pieDiagram-SKSYHLDU-BasUkvwH.js → pieDiagram-SKSYHLDU-D2I_J-RQ.js} +1 -1
  309. package/ui-dist/assets/{quadrantDiagram-337W2JSQ-D0D2xp36.js → quadrantDiagram-337W2JSQ-B6BBePuG.js} +1 -1
  310. package/ui-dist/assets/{requirementDiagram-Z7DCOOCP-CztN9kDh.js → requirementDiagram-Z7DCOOCP-BUl7tTbs.js} +1 -1
  311. package/ui-dist/assets/{sankeyDiagram-WA2Y5GQK-2xDqN9ox.js → sankeyDiagram-WA2Y5GQK-Dus4b_vo.js} +1 -1
  312. package/ui-dist/assets/{sequenceDiagram-2WXFIKYE-CO3OUPIJ.js → sequenceDiagram-2WXFIKYE-DnwzyMvt.js} +1 -1
  313. package/ui-dist/assets/{stateDiagram-RAJIS63D-DuQ1QZcT.js → stateDiagram-RAJIS63D-CWYWUh48.js} +1 -1
  314. package/ui-dist/assets/stateDiagram-v2-FVOUBMTO-yRuBqnCA.js +1 -0
  315. package/ui-dist/assets/{timeline-definition-YZTLITO2-CnOxbQeb.js → timeline-definition-YZTLITO2-D7vwtWgR.js} +1 -1
  316. package/ui-dist/assets/{treemap-KZPCXAKY-DD77IFOl.js → treemap-KZPCXAKY-CGCh4N8D.js} +1 -1
  317. package/ui-dist/assets/{vennDiagram-LZ73GAT5-DOXTGJn1.js → vennDiagram-LZ73GAT5-Bq1y4idx.js} +1 -1
  318. package/ui-dist/assets/{xychartDiagram-JWTSCODW-DWkrcdri.js → xychartDiagram-JWTSCODW-CMFdYJaJ.js} +1 -1
  319. package/ui-dist/index.html +2 -2
  320. package/ui-dist/assets/channel-BRnz7pSY.js +0 -1
  321. package/ui-dist/assets/classDiagram-VBA2DB6C-Q3YuI9dv.js +0 -1
  322. package/ui-dist/assets/classDiagram-v2-RAHNMMFH-Q3YuI9dv.js +0 -1
  323. package/ui-dist/assets/clone-C1exIyAN.js +0 -1
  324. package/ui-dist/assets/index-Dz8xxyqB.css +0 -1
  325. package/ui-dist/assets/stateDiagram-v2-FVOUBMTO-DQbZqbl9.js +0 -1
@@ -1,8 +1,8 @@
1
- import { approvals, assets, chatAttachments, chatContextLinks, chatConversations, chatConversationUserStates, chatMessages } from "@rudderhq/db";
2
- import { sanitizeChatStructuredPayload } from "@rudderhq/shared";
3
- import { and, desc, eq, gt, gte, inArray, isNull, sql } from "drizzle-orm";
1
+ import { agentIntegrationChatBindings, agentIntegrations, approvals, assets, chatAttachments, chatContextLinks, chatConversations, chatConversationUserStates, chatGenerations, chatMessages, chatQueuedMessages, messengerCustomGroupEntries, messengerCustomGroups } from "@rudderhq/db";
2
+ import { MESSENGER_FORK_GROUP_DEFAULT_ICON, sanitizeChatStructuredPayload } from "@rudderhq/shared";
3
+ import { and, asc, desc, eq, gt, gte, inArray, isNull, lt, or, sql } from "drizzle-orm";
4
4
  import { randomUUID } from "node:crypto";
5
- import { notFound, unprocessable } from "../errors.js";
5
+ import { conflict, notFound, unprocessable } from "../errors.js";
6
6
  import { logActivity } from "./activity-log.js";
7
7
  import { agentService } from "./agents.js";
8
8
  import { approvalService } from "./approvals.js";
@@ -10,8 +10,18 @@ import { issueApprovalService } from "./issue-approvals.js";
10
10
  import { issueService } from "./issues.js";
11
11
  import { normalizeLocalLibraryPathMarkdown } from "./library-path-markdown.js";
12
12
  import { organizationService } from "./orgs.js";
13
+ function conversationMutability(row, sourceMetadata, sourceMetadataByConversationId) {
14
+ if (sourceMetadata)
15
+ return "external_bound_chat";
16
+ if ((row.forkedFromConversationId && sourceMetadataByConversationId.has(row.forkedFromConversationId)) ||
17
+ (row.forkRootConversationId && sourceMetadataByConversationId.has(row.forkRootConversationId))) {
18
+ return "native_fork_from_external";
19
+ }
20
+ return "native_chat";
21
+ }
13
22
  import { buildSearchSnippet, CHAT_TRANSCRIPT_KEY, chatTranscriptFromPayload, chatTranscriptSummaryFromEntries, contentPath, escapeLikePattern, incomingMessagePreviewSql, issueProposalFromPayload, isVisibleIncomingChatMessage, listContextLinksForConversationIds, listPrimaryIssues, operationProposalDecisionStatusFromPayload, operationProposalFromPayload, resolveContextEntities, safeTrim, stripChatMetadataFromPayload, textContains, truncatePreview, visibleIncomingMessageSql, withOperationProposalDecisionState, withPersistedTranscript, } from "./chats.helpers.js";
14
23
  export function chatService(db) {
24
+ const QUEUED_MESSAGE_CLAIM_LEASE_MS = 2 * 60 * 1000;
15
25
  const issuesSvc = issueService(db);
16
26
  const approvalsSvc = approvalService(db);
17
27
  const issueApprovalsSvc = issueApprovalService(db);
@@ -51,7 +61,12 @@ export function chatService(db) {
51
61
  })
52
62
  .from(chatMessages)
53
63
  .innerJoin(chatConversationUserStates, and(eq(chatConversationUserStates.orgId, orgId), eq(chatConversationUserStates.userId, userId), eq(chatConversationUserStates.conversationId, chatMessages.conversationId)))
54
- .where(and(eq(chatMessages.orgId, orgId), inArray(chatMessages.conversationId, conversationIds), isNull(chatMessages.supersededAt), visibleIncomingMessageSql(), gt(chatMessages.createdAt, chatConversationUserStates.lastReadAt)))
64
+ .where(and(eq(chatMessages.orgId, orgId), inArray(chatMessages.conversationId, conversationIds), isNull(chatMessages.supersededAt), visibleIncomingMessageSql(), gt(chatMessages.createdAt, chatConversationUserStates.lastReadAt), sql `not exists (
65
+ select 1
66
+ from ${agentIntegrationChatBindings}
67
+ where ${agentIntegrationChatBindings.orgId} = ${orgId}
68
+ and ${agentIntegrationChatBindings.conversationId} = ${chatMessages.conversationId}
69
+ )`))
55
70
  .groupBy(chatMessages.conversationId);
56
71
  return new Map(rows.map((row) => [row.conversationId, Number(row.count ?? 0)]));
57
72
  }
@@ -68,6 +83,35 @@ export function chatService(db) {
68
83
  .groupBy(chatMessages.conversationId);
69
84
  return new Set(rows.map((row) => row.conversationId));
70
85
  }
86
+ async function listConversationSourceMetadata(orgId, conversationIds) {
87
+ if (conversationIds.length === 0)
88
+ return new Map();
89
+ const rows = await db
90
+ .select({
91
+ conversationId: agentIntegrationChatBindings.conversationId,
92
+ integrationId: agentIntegrationChatBindings.integrationId,
93
+ provider: agentIntegrations.provider,
94
+ externalChatId: agentIntegrationChatBindings.externalChatId,
95
+ externalChatType: agentIntegrationChatBindings.externalChatType,
96
+ })
97
+ .from(agentIntegrationChatBindings)
98
+ .innerJoin(agentIntegrations, eq(agentIntegrations.id, agentIntegrationChatBindings.integrationId))
99
+ .where(and(eq(agentIntegrationChatBindings.orgId, orgId), inArray(agentIntegrationChatBindings.conversationId, conversationIds)))
100
+ .orderBy(agentIntegrationChatBindings.createdAt);
101
+ const map = new Map();
102
+ for (const row of rows) {
103
+ if (map.has(row.conversationId))
104
+ continue;
105
+ map.set(row.conversationId, {
106
+ source: "agent_integration",
107
+ provider: row.provider,
108
+ integrationId: row.integrationId,
109
+ externalChatId: row.externalChatId,
110
+ externalChatType: row.externalChatType,
111
+ });
112
+ }
113
+ return map;
114
+ }
71
115
  async function listLatestReplyPreviews(orgId, conversationIds) {
72
116
  if (conversationIds.length === 0)
73
117
  return new Map();
@@ -175,8 +219,14 @@ export function chatService(db) {
175
219
  await ensureConversationUserStates(rows, userId);
176
220
  }
177
221
  const conversationIds = rows.map((row) => row.id);
222
+ const sourceLookupConversationIds = [
223
+ ...new Set([
224
+ ...conversationIds,
225
+ ...rows.flatMap((row) => [row.forkedFromConversationId, row.forkRootConversationId].filter((id) => Boolean(id))),
226
+ ]),
227
+ ];
178
228
  const orgId = rows[0]?.orgId ?? null;
179
- const [contextLinksByConversationId, primaryIssuesById, userStatesByConversationId, unreadCountsByConversationId, pendingProposalConversationIds, latestReplyPreviewsByConversationId, userMessageSummariesByConversationId,] = await Promise.all([
229
+ const [contextLinksByConversationId, primaryIssuesById, userStatesByConversationId, unreadCountsByConversationId, pendingProposalConversationIds, latestReplyPreviewsByConversationId, userMessageSummariesByConversationId, sourceMetadataByConversationId,] = await Promise.all([
180
230
  listContextLinksForConversationIds(db, rows.map((row) => row.id)),
181
231
  listPrimaryIssues(db, rows),
182
232
  userId && orgId
@@ -194,29 +244,45 @@ export function chatService(db) {
194
244
  orgId
195
245
  ? listUserMessageSummaries(orgId, conversationIds)
196
246
  : Promise.resolve(new Map()),
247
+ orgId
248
+ ? listConversationSourceMetadata(orgId, sourceLookupConversationIds)
249
+ : Promise.resolve(new Map()),
197
250
  ]);
198
- return rows.map((row) => ({
199
- ...row,
200
- primaryIssue: row.primaryIssueId ? (primaryIssuesById.get(row.primaryIssueId) ?? null) : null,
201
- latestReplyPreview: latestReplyPreviewsByConversationId.get(row.id) ?? null,
202
- latestUserMessagePreview: userMessageSummariesByConversationId.get(row.id)?.latestPreview ?? null,
203
- userMessageCount: userMessageSummariesByConversationId.get(row.id)?.count ?? 0,
204
- contextLinks: contextLinksByConversationId.get(row.id) ?? [],
205
- lastReadAt: userStatesByConversationId.get(row.id)?.lastReadAt ?? null,
206
- isPinned: Boolean(userStatesByConversationId.get(row.id)?.pinnedAt),
207
- unreadCount: unreadCountsByConversationId.get(row.id) ?? 0,
208
- isUnread: (unreadCountsByConversationId.get(row.id) ?? 0) > 0,
209
- needsAttention: (unreadCountsByConversationId.get(row.id) ?? 0) > 0 ||
210
- pendingProposalConversationIds.has(row.id),
211
- }));
251
+ return rows.map((row) => {
252
+ const sourceMetadata = sourceMetadataByConversationId.get(row.id) ?? null;
253
+ const isExternalBound = Boolean(sourceMetadata);
254
+ const unreadCount = isExternalBound ? 0 : (unreadCountsByConversationId.get(row.id) ?? 0);
255
+ return {
256
+ ...row,
257
+ primaryIssue: row.primaryIssueId ? (primaryIssuesById.get(row.primaryIssueId) ?? null) : null,
258
+ latestReplyPreview: latestReplyPreviewsByConversationId.get(row.id) ?? null,
259
+ latestUserMessagePreview: userMessageSummariesByConversationId.get(row.id)?.latestPreview ?? null,
260
+ userMessageCount: userMessageSummariesByConversationId.get(row.id)?.count ?? 0,
261
+ contextLinks: contextLinksByConversationId.get(row.id) ?? [],
262
+ sourceMetadata,
263
+ mutability: conversationMutability(row, sourceMetadata, sourceMetadataByConversationId),
264
+ lastReadAt: userStatesByConversationId.get(row.id)?.lastReadAt ?? null,
265
+ isPinned: Boolean(userStatesByConversationId.get(row.id)?.pinnedAt),
266
+ unreadCount,
267
+ isUnread: unreadCount > 0,
268
+ needsAttention: !isExternalBound && (unreadCount > 0 ||
269
+ pendingProposalConversationIds.has(row.id)),
270
+ };
271
+ });
212
272
  }
213
273
  async function hydrateConversationSummaries(rows, userId) {
214
274
  if (userId) {
215
275
  await ensureConversationUserStates(rows, userId);
216
276
  }
217
277
  const conversationIds = rows.map((row) => row.id);
278
+ const sourceLookupConversationIds = [
279
+ ...new Set([
280
+ ...conversationIds,
281
+ ...rows.flatMap((row) => [row.forkedFromConversationId, row.forkRootConversationId].filter((id) => Boolean(id))),
282
+ ]),
283
+ ];
218
284
  const orgId = rows[0]?.orgId ?? null;
219
- const [userStatesByConversationId, unreadCountsByConversationId, pendingProposalConversationIds, latestReplyPreviewsByConversationId, userMessageSummariesByConversationId,] = await Promise.all([
285
+ const [userStatesByConversationId, unreadCountsByConversationId, pendingProposalConversationIds, latestReplyPreviewsByConversationId, userMessageSummariesByConversationId, sourceMetadataByConversationId,] = await Promise.all([
220
286
  userId && orgId
221
287
  ? listConversationUserStates(orgId, userId, conversationIds)
222
288
  : Promise.resolve(new Map()),
@@ -232,19 +298,29 @@ export function chatService(db) {
232
298
  orgId
233
299
  ? listUserMessageSummaries(orgId, conversationIds)
234
300
  : Promise.resolve(new Map()),
301
+ orgId
302
+ ? listConversationSourceMetadata(orgId, sourceLookupConversationIds)
303
+ : Promise.resolve(new Map()),
235
304
  ]);
236
- return rows.map((row) => ({
237
- ...row,
238
- latestReplyPreview: latestReplyPreviewsByConversationId.get(row.id) ?? null,
239
- latestUserMessagePreview: userMessageSummariesByConversationId.get(row.id)?.latestPreview ?? null,
240
- userMessageCount: userMessageSummariesByConversationId.get(row.id)?.count ?? 0,
241
- lastReadAt: userStatesByConversationId.get(row.id)?.lastReadAt ?? null,
242
- isPinned: Boolean(userStatesByConversationId.get(row.id)?.pinnedAt),
243
- unreadCount: unreadCountsByConversationId.get(row.id) ?? 0,
244
- isUnread: (unreadCountsByConversationId.get(row.id) ?? 0) > 0,
245
- needsAttention: (unreadCountsByConversationId.get(row.id) ?? 0) > 0 ||
246
- pendingProposalConversationIds.has(row.id),
247
- }));
305
+ return rows.map((row) => {
306
+ const sourceMetadata = sourceMetadataByConversationId.get(row.id) ?? null;
307
+ const isExternalBound = Boolean(sourceMetadata);
308
+ const unreadCount = isExternalBound ? 0 : (unreadCountsByConversationId.get(row.id) ?? 0);
309
+ return {
310
+ ...row,
311
+ latestReplyPreview: latestReplyPreviewsByConversationId.get(row.id) ?? null,
312
+ latestUserMessagePreview: userMessageSummariesByConversationId.get(row.id)?.latestPreview ?? null,
313
+ userMessageCount: userMessageSummariesByConversationId.get(row.id)?.count ?? 0,
314
+ sourceMetadata,
315
+ mutability: conversationMutability(row, sourceMetadata, sourceMetadataByConversationId),
316
+ lastReadAt: userStatesByConversationId.get(row.id)?.lastReadAt ?? null,
317
+ isPinned: Boolean(userStatesByConversationId.get(row.id)?.pinnedAt),
318
+ unreadCount,
319
+ isUnread: unreadCount > 0,
320
+ needsAttention: !isExternalBound && (unreadCount > 0 ||
321
+ pendingProposalConversationIds.has(row.id)),
322
+ };
323
+ });
248
324
  }
249
325
  async function getConversationOrThrow(id) {
250
326
  const row = await db
@@ -305,6 +381,328 @@ export function chatService(db) {
305
381
  .where(inArray(approvals.id, approvalIds));
306
382
  return new Map(approvalRows.map((row) => [row.id, row]));
307
383
  }
384
+ function isQueuePositionConflict(error) {
385
+ return Boolean(error
386
+ && typeof error === "object"
387
+ && "code" in error
388
+ && error.code === "23505");
389
+ }
390
+ function normalizeQueuedPayload(payload) {
391
+ return {
392
+ body: String(payload.body ?? ""),
393
+ attachmentIds: Array.isArray(payload.attachmentIds)
394
+ ? payload.attachmentIds.filter((id) => typeof id === "string")
395
+ : [],
396
+ projectId: typeof payload.projectId === "string" ? payload.projectId : null,
397
+ skillRefs: Array.isArray(payload.skillRefs)
398
+ ? payload.skillRefs.filter((ref) => typeof ref === "string")
399
+ : [],
400
+ accessMode: typeof payload.accessMode === "string" ? payload.accessMode : null,
401
+ model: typeof payload.model === "string" ? payload.model : null,
402
+ effort: typeof payload.effort === "string" ? payload.effort : null,
403
+ metadata: payload.metadata && typeof payload.metadata === "object" && !Array.isArray(payload.metadata)
404
+ ? payload.metadata
405
+ : null,
406
+ };
407
+ }
408
+ function hydrateQueuedMessage(row) {
409
+ return {
410
+ ...row,
411
+ payload: normalizeQueuedPayload(row.payload),
412
+ };
413
+ }
414
+ async function createGeneration(orgId, conversationId) {
415
+ const [row] = await db
416
+ .insert(chatGenerations)
417
+ .values({
418
+ orgId,
419
+ conversationId,
420
+ status: "active",
421
+ })
422
+ .returning();
423
+ if (!row)
424
+ throw new Error("Failed to create chat generation");
425
+ return row;
426
+ }
427
+ async function markGenerationTerminal(generationId, status) {
428
+ if (!generationId)
429
+ return null;
430
+ const now = new Date();
431
+ const [row] = await db
432
+ .update(chatGenerations)
433
+ .set({
434
+ status,
435
+ terminalReason: status,
436
+ completedAt: now,
437
+ updatedAt: now,
438
+ })
439
+ .where(eq(chatGenerations.id, generationId))
440
+ .returning();
441
+ return row ?? null;
442
+ }
443
+ async function getLatestActiveGeneration(conversationId) {
444
+ return db
445
+ .select()
446
+ .from(chatGenerations)
447
+ .where(and(eq(chatGenerations.conversationId, conversationId), inArray(chatGenerations.status, ["active", "tool_busy", "closing"])))
448
+ .orderBy(desc(chatGenerations.startedAt), desc(chatGenerations.createdAt))
449
+ .limit(1)
450
+ .then((rows) => rows[0] ?? null);
451
+ }
452
+ async function getLatestGeneration(conversationId) {
453
+ return db
454
+ .select()
455
+ .from(chatGenerations)
456
+ .where(eq(chatGenerations.conversationId, conversationId))
457
+ .orderBy(desc(chatGenerations.startedAt), desc(chatGenerations.createdAt))
458
+ .limit(1)
459
+ .then((rows) => rows[0] ?? null);
460
+ }
461
+ async function listQueuedMessages(conversationId) {
462
+ await reclaimStaleQueuedMessageClaims(conversationId);
463
+ const rows = await db
464
+ .select()
465
+ .from(chatQueuedMessages)
466
+ .where(and(eq(chatQueuedMessages.conversationId, conversationId), inArray(chatQueuedMessages.status, ["queued", "steer_pending", "dequeue_claimed", "running"])))
467
+ .orderBy(asc(chatQueuedMessages.position), asc(chatQueuedMessages.createdAt));
468
+ return rows.map(hydrateQueuedMessage);
469
+ }
470
+ async function getQueueSnapshot(conversationId, activeGenerationId) {
471
+ const activeGeneration = activeGenerationId
472
+ ? { id: activeGenerationId }
473
+ : await getLatestActiveGeneration(conversationId);
474
+ return {
475
+ activeGenerationId: activeGeneration?.id ?? null,
476
+ items: await listQueuedMessages(conversationId),
477
+ };
478
+ }
479
+ async function createQueuedMessage(input) {
480
+ const payload = {
481
+ ...input.payload,
482
+ body: input.payload.body.trim(),
483
+ attachmentIds: input.payload.attachmentIds ?? [],
484
+ skillRefs: input.payload.skillRefs ?? [],
485
+ projectId: input.payload.projectId ?? null,
486
+ accessMode: input.payload.accessMode ?? null,
487
+ model: input.payload.model ?? null,
488
+ effort: input.payload.effort ?? null,
489
+ metadata: input.payload.metadata ?? null,
490
+ };
491
+ for (let attempt = 0; attempt < 3; attempt += 1) {
492
+ try {
493
+ return await db.transaction(async (tx) => {
494
+ const existing = await tx
495
+ .select()
496
+ .from(chatQueuedMessages)
497
+ .where(and(eq(chatQueuedMessages.conversationId, input.conversationId), eq(chatQueuedMessages.clientMutationId, input.clientMutationId)))
498
+ .limit(1)
499
+ .then((rows) => rows[0] ?? null);
500
+ if (existing) {
501
+ if (JSON.stringify(existing.payload) !== JSON.stringify(payload)) {
502
+ throw conflict("Queued message idempotency key reused with a different payload");
503
+ }
504
+ return hydrateQueuedMessage(existing);
505
+ }
506
+ const [positionRow] = await tx
507
+ .select({
508
+ nextPosition: sql `coalesce(max(${chatQueuedMessages.position}), 0) + 1`,
509
+ })
510
+ .from(chatQueuedMessages)
511
+ .where(eq(chatQueuedMessages.conversationId, input.conversationId));
512
+ const [row] = await tx
513
+ .insert(chatQueuedMessages)
514
+ .values({
515
+ orgId: input.orgId,
516
+ conversationId: input.conversationId,
517
+ clientMutationId: input.clientMutationId,
518
+ position: Number(positionRow?.nextPosition ?? 1),
519
+ payload,
520
+ expectedGenerationId: input.expectedGenerationId ?? null,
521
+ })
522
+ .returning();
523
+ if (!row)
524
+ throw new Error("Failed to create queued chat message");
525
+ return hydrateQueuedMessage(row);
526
+ });
527
+ }
528
+ catch (error) {
529
+ if (attempt < 2 && isQueuePositionConflict(error))
530
+ continue;
531
+ throw error;
532
+ }
533
+ }
534
+ throw new Error("Failed to create queued chat message");
535
+ }
536
+ async function updateQueuedMessage(input) {
537
+ const now = new Date();
538
+ const [row] = await db
539
+ .update(chatQueuedMessages)
540
+ .set({
541
+ payload: {
542
+ ...input.payload,
543
+ body: input.payload.body.trim(),
544
+ attachmentIds: input.payload.attachmentIds ?? [],
545
+ skillRefs: input.payload.skillRefs ?? [],
546
+ projectId: input.payload.projectId ?? null,
547
+ accessMode: input.payload.accessMode ?? null,
548
+ model: input.payload.model ?? null,
549
+ effort: input.payload.effort ?? null,
550
+ metadata: input.payload.metadata ?? null,
551
+ },
552
+ version: input.version + 1,
553
+ lastDeliveryReason: null,
554
+ updatedAt: now,
555
+ })
556
+ .where(and(eq(chatQueuedMessages.conversationId, input.conversationId), eq(chatQueuedMessages.id, input.itemId), eq(chatQueuedMessages.version, input.version), eq(chatQueuedMessages.status, "queued")))
557
+ .returning();
558
+ if (!row)
559
+ throw conflict("Queued message was changed or is no longer editable");
560
+ return hydrateQueuedMessage(row);
561
+ }
562
+ async function cancelQueuedMessage(input) {
563
+ const now = new Date();
564
+ const conditions = [
565
+ eq(chatQueuedMessages.conversationId, input.conversationId),
566
+ eq(chatQueuedMessages.id, input.itemId),
567
+ eq(chatQueuedMessages.status, "queued"),
568
+ ];
569
+ if (input.version)
570
+ conditions.push(eq(chatQueuedMessages.version, input.version));
571
+ const [row] = await db
572
+ .update(chatQueuedMessages)
573
+ .set({
574
+ status: "cancelled",
575
+ version: sql `${chatQueuedMessages.version} + 1`,
576
+ cancelledAt: now,
577
+ updatedAt: now,
578
+ })
579
+ .where(and(...conditions))
580
+ .returning();
581
+ if (!row)
582
+ throw conflict("Queued message was changed or is no longer cancellable");
583
+ return hydrateQueuedMessage(row);
584
+ }
585
+ async function markQueuedMessageSteerFallback(input) {
586
+ const now = new Date();
587
+ const [row] = await db
588
+ .update(chatQueuedMessages)
589
+ .set({
590
+ status: "queued",
591
+ activeGenerationId: input.activeGenerationId ?? null,
592
+ deliveryAttempts: sql `${chatQueuedMessages.deliveryAttempts} + 1`,
593
+ lastAttemptAt: now,
594
+ lastDeliveryReason: input.reason,
595
+ version: sql `${chatQueuedMessages.version} + 1`,
596
+ updatedAt: now,
597
+ })
598
+ .where(and(eq(chatQueuedMessages.conversationId, input.conversationId), eq(chatQueuedMessages.id, input.itemId), eq(chatQueuedMessages.status, "queued")))
599
+ .returning();
600
+ if (!row)
601
+ throw conflict("Queued message was changed or is no longer steerable");
602
+ return hydrateQueuedMessage(row);
603
+ }
604
+ async function claimNextQueuedMessage(conversationId) {
605
+ await reclaimStaleQueuedMessageClaims(conversationId);
606
+ const now = new Date();
607
+ return db.transaction(async (tx) => {
608
+ const [candidate] = await tx
609
+ .select()
610
+ .from(chatQueuedMessages)
611
+ .where(and(eq(chatQueuedMessages.conversationId, conversationId), eq(chatQueuedMessages.status, "queued")))
612
+ .orderBy(asc(chatQueuedMessages.position), asc(chatQueuedMessages.createdAt))
613
+ .limit(1);
614
+ if (!candidate)
615
+ return null;
616
+ const [row] = await tx
617
+ .update(chatQueuedMessages)
618
+ .set({
619
+ status: "dequeue_claimed",
620
+ dequeuedAt: now,
621
+ deliveryAttempts: sql `${chatQueuedMessages.deliveryAttempts} + 1`,
622
+ lastAttemptAt: now,
623
+ lastDeliveryReason: null,
624
+ version: sql `${chatQueuedMessages.version} + 1`,
625
+ updatedAt: now,
626
+ })
627
+ .where(and(eq(chatQueuedMessages.id, candidate.id), eq(chatQueuedMessages.conversationId, conversationId), eq(chatQueuedMessages.status, "queued"), eq(chatQueuedMessages.version, candidate.version)))
628
+ .returning();
629
+ return row ? hydrateQueuedMessage(row) : null;
630
+ });
631
+ }
632
+ async function releaseQueuedMessageClaim(input) {
633
+ const now = new Date();
634
+ const [row] = await db
635
+ .update(chatQueuedMessages)
636
+ .set({
637
+ status: "queued",
638
+ lastDeliveryReason: input.reason,
639
+ version: sql `${chatQueuedMessages.version} + 1`,
640
+ updatedAt: now,
641
+ })
642
+ .where(and(eq(chatQueuedMessages.conversationId, input.conversationId), eq(chatQueuedMessages.id, input.itemId), eq(chatQueuedMessages.status, "dequeue_claimed")))
643
+ .returning();
644
+ return row ? hydrateQueuedMessage(row) : null;
645
+ }
646
+ async function reclaimStaleQueuedMessageClaims(conversationId) {
647
+ const cutoff = new Date(Date.now() - QUEUED_MESSAGE_CLAIM_LEASE_MS);
648
+ await db
649
+ .update(chatQueuedMessages)
650
+ .set({
651
+ status: "queued",
652
+ lastDeliveryReason: "claim_expired",
653
+ version: sql `${chatQueuedMessages.version} + 1`,
654
+ updatedAt: new Date(),
655
+ })
656
+ .where(and(eq(chatQueuedMessages.conversationId, conversationId), eq(chatQueuedMessages.status, "dequeue_claimed"), lt(chatQueuedMessages.updatedAt, cutoff)));
657
+ }
658
+ async function assertQueuedMessageClaimedForDelivery(input) {
659
+ const row = await db
660
+ .select()
661
+ .from(chatQueuedMessages)
662
+ .where(and(eq(chatQueuedMessages.conversationId, input.conversationId), eq(chatQueuedMessages.id, input.itemId)))
663
+ .limit(1)
664
+ .then((rows) => rows[0] ?? null);
665
+ if (!row || row.status !== "dequeue_claimed") {
666
+ throw conflict("Queued message is not claimed for delivery");
667
+ }
668
+ const payload = normalizeQueuedPayload(row.payload);
669
+ if (payload.body.trim() !== input.body.trim()) {
670
+ throw conflict("Queued message body no longer matches claimed payload");
671
+ }
672
+ return hydrateQueuedMessage(row);
673
+ }
674
+ async function markQueuedMessageRunning(input) {
675
+ const now = new Date();
676
+ const [row] = await db
677
+ .update(chatQueuedMessages)
678
+ .set({
679
+ status: "running",
680
+ sourceMessageId: input.sourceMessageId,
681
+ deliveredMessageId: input.sourceMessageId,
682
+ version: sql `${chatQueuedMessages.version} + 1`,
683
+ updatedAt: now,
684
+ })
685
+ .where(and(eq(chatQueuedMessages.conversationId, input.conversationId), eq(chatQueuedMessages.id, input.itemId), eq(chatQueuedMessages.status, "dequeue_claimed")))
686
+ .returning();
687
+ if (!row)
688
+ throw conflict("Queued message is no longer deliverable");
689
+ return hydrateQueuedMessage(row);
690
+ }
691
+ async function markQueuedMessageDeliveryTerminal(input) {
692
+ const now = new Date();
693
+ const nextStatus = input.status === "completed" ? "completed" : "failed";
694
+ const [row] = await db
695
+ .update(chatQueuedMessages)
696
+ .set({
697
+ status: nextStatus,
698
+ lastDeliveryReason: input.status === "completed" ? null : input.status,
699
+ version: sql `${chatQueuedMessages.version} + 1`,
700
+ updatedAt: now,
701
+ })
702
+ .where(and(eq(chatQueuedMessages.conversationId, input.conversationId), eq(chatQueuedMessages.id, input.itemId), inArray(chatQueuedMessages.status, ["dequeue_claimed", "running"])))
703
+ .returning();
704
+ return row ? hydrateQueuedMessage(row) : null;
705
+ }
308
706
  async function hydrateMessages(rows, options = {}) {
309
707
  const includeTranscript = options.includeTranscript !== false;
310
708
  const [attachmentsByMessageId, approvalsById] = await Promise.all([
@@ -493,6 +891,208 @@ export function chatService(db) {
493
891
  });
494
892
  return getById(created.id);
495
893
  }
894
+ function forkSystemEventBody(sourceConversation, sourceMessageId) {
895
+ const messageSuffix = sourceMessageId ? ` at message ${sourceMessageId}` : "";
896
+ return `Forked from [${sourceConversation.title}](chat://${sourceConversation.id})${messageSuffix}.`;
897
+ }
898
+ async function findForkFamilyGroup(client, orgId, userId, rootConversationId) {
899
+ const rootThreadKey = `chat:${rootConversationId}`;
900
+ return client
901
+ .select({ id: messengerCustomGroups.id })
902
+ .from(messengerCustomGroups)
903
+ .innerJoin(messengerCustomGroupEntries, eq(messengerCustomGroupEntries.groupId, messengerCustomGroups.id))
904
+ .where(and(eq(messengerCustomGroups.orgId, orgId), eq(messengerCustomGroups.userId, userId), eq(messengerCustomGroupEntries.orgId, orgId), eq(messengerCustomGroupEntries.userId, userId), eq(messengerCustomGroupEntries.threadKey, rootThreadKey)))
905
+ .orderBy(asc(messengerCustomGroups.sortOrder), asc(messengerCustomGroups.createdAt))
906
+ .limit(1)
907
+ .then((rows) => rows[0] ?? null);
908
+ }
909
+ async function createForkFamilyGroup(client, orgId, userId, name) {
910
+ const [lastGroup] = await client
911
+ .select({ sortOrder: messengerCustomGroups.sortOrder })
912
+ .from(messengerCustomGroups)
913
+ .where(and(eq(messengerCustomGroups.orgId, orgId), eq(messengerCustomGroups.userId, userId)))
914
+ .orderBy(desc(messengerCustomGroups.sortOrder))
915
+ .limit(1);
916
+ const now = new Date();
917
+ const [group] = await client
918
+ .insert(messengerCustomGroups)
919
+ .values({
920
+ orgId,
921
+ userId,
922
+ name: name.trim() || "Forked conversation",
923
+ icon: MESSENGER_FORK_GROUP_DEFAULT_ICON,
924
+ sortOrder: (lastGroup?.sortOrder ?? -1) + 1,
925
+ updatedAt: now,
926
+ })
927
+ .returning();
928
+ if (!group)
929
+ throw new Error("Failed to create Messenger fork group");
930
+ return group;
931
+ }
932
+ async function assignForkThreadToGroup(client, orgId, userId, groupId, threadKey) {
933
+ const [lastEntry] = await client
934
+ .select({ sortOrder: messengerCustomGroupEntries.sortOrder })
935
+ .from(messengerCustomGroupEntries)
936
+ .where(and(eq(messengerCustomGroupEntries.orgId, orgId), eq(messengerCustomGroupEntries.userId, userId), eq(messengerCustomGroupEntries.groupId, groupId)))
937
+ .orderBy(desc(messengerCustomGroupEntries.sortOrder))
938
+ .limit(1);
939
+ const now = new Date();
940
+ await client
941
+ .insert(messengerCustomGroupEntries)
942
+ .values({
943
+ orgId,
944
+ userId,
945
+ groupId,
946
+ threadKey,
947
+ sortOrder: (lastEntry?.sortOrder ?? -1) + 1,
948
+ updatedAt: now,
949
+ })
950
+ .onConflictDoNothing();
951
+ }
952
+ async function ensureForkFamilyGroup(client, orgId, userId, rootConversationId, sourceConversationId, childConversationId, groupName) {
953
+ const existing = await findForkFamilyGroup(client, orgId, userId, rootConversationId);
954
+ const group = existing ?? await createForkFamilyGroup(client, orgId, userId, groupName);
955
+ for (const threadKey of [
956
+ `chat:${rootConversationId}`,
957
+ `chat:${sourceConversationId}`,
958
+ `chat:${childConversationId}`,
959
+ ]) {
960
+ await assignForkThreadToGroup(client, orgId, userId, group.id, threadKey);
961
+ }
962
+ }
963
+ async function forkConversation(input) {
964
+ const created = await db.transaction(async (tx) => {
965
+ const source = await tx
966
+ .select()
967
+ .from(chatConversations)
968
+ .where(and(eq(chatConversations.id, input.sourceConversationId), eq(chatConversations.orgId, input.orgId)))
969
+ .then((rows) => rows[0] ?? null);
970
+ if (!source)
971
+ throw notFound("Chat conversation not found");
972
+ const rootConversationId = source.forkRootConversationId ?? source.id;
973
+ const messageConditions = [
974
+ eq(chatMessages.conversationId, source.id),
975
+ eq(chatMessages.orgId, input.orgId),
976
+ isNull(chatMessages.supersededAt),
977
+ ];
978
+ let forkSourceCreatedAt = null;
979
+ if (input.sourceMessageId) {
980
+ const sourceMessage = await tx
981
+ .select({
982
+ id: chatMessages.id,
983
+ kind: chatMessages.kind,
984
+ role: chatMessages.role,
985
+ createdAt: chatMessages.createdAt,
986
+ })
987
+ .from(chatMessages)
988
+ .where(and(...messageConditions, eq(chatMessages.id, input.sourceMessageId)))
989
+ .then((rows) => rows[0] ?? null);
990
+ if (!sourceMessage)
991
+ throw unprocessable("Fork source message must belong to the source conversation");
992
+ if (sourceMessage.role !== "assistant" || sourceMessage.kind !== "message") {
993
+ throw unprocessable("Fork source message must be an assistant response");
994
+ }
995
+ forkSourceCreatedAt = sourceMessage.createdAt;
996
+ }
997
+ const now = new Date();
998
+ const [child] = await tx
999
+ .insert(chatConversations)
1000
+ .values({
1001
+ orgId: input.orgId,
1002
+ status: "active",
1003
+ title: input.title?.trim() || source.title,
1004
+ summary: source.summary,
1005
+ preferredAgentId: source.preferredAgentId,
1006
+ routedAgentId: source.routedAgentId,
1007
+ primaryIssueId: source.primaryIssueId,
1008
+ forkedFromConversationId: source.id,
1009
+ forkedFromMessageId: input.sourceMessageId ?? null,
1010
+ forkRootConversationId: rootConversationId,
1011
+ issueCreationMode: source.issueCreationMode,
1012
+ planMode: source.planMode,
1013
+ createdByUserId: input.createdByUserId,
1014
+ createdAt: now,
1015
+ updatedAt: now,
1016
+ })
1017
+ .returning();
1018
+ if (!child)
1019
+ throw new Error("Failed to create forked chat conversation");
1020
+ const contextLinks = await tx
1021
+ .select()
1022
+ .from(chatContextLinks)
1023
+ .where(eq(chatContextLinks.conversationId, source.id))
1024
+ .orderBy(chatContextLinks.createdAt);
1025
+ if (contextLinks.length > 0) {
1026
+ await tx
1027
+ .insert(chatContextLinks)
1028
+ .values(contextLinks.map((link) => ({
1029
+ orgId: input.orgId,
1030
+ conversationId: child.id,
1031
+ entityType: link.entityType,
1032
+ entityId: link.entityId,
1033
+ metadata: link.metadata,
1034
+ })))
1035
+ .onConflictDoNothing();
1036
+ }
1037
+ const messagesToCopy = await tx
1038
+ .select()
1039
+ .from(chatMessages)
1040
+ .where(and(...messageConditions, ...(forkSourceCreatedAt && input.sourceMessageId
1041
+ ? [or(lt(chatMessages.createdAt, forkSourceCreatedAt), eq(chatMessages.id, input.sourceMessageId))]
1042
+ : [])))
1043
+ .orderBy(chatMessages.createdAt, chatMessages.id);
1044
+ const forkMessages = input.sourceMessageId
1045
+ ? messagesToCopy.slice(0, messagesToCopy.findIndex((message) => message.id === input.sourceMessageId) + 1)
1046
+ : messagesToCopy;
1047
+ if (forkMessages.length > 0) {
1048
+ await tx.insert(chatMessages).values(forkMessages.map((message) => ({
1049
+ orgId: input.orgId,
1050
+ conversationId: child.id,
1051
+ role: message.role,
1052
+ kind: message.kind,
1053
+ status: message.status === "streaming" ? "interrupted" : message.status,
1054
+ body: message.body,
1055
+ structuredPayload: null,
1056
+ approvalId: null,
1057
+ runId: null,
1058
+ replyingAgentId: message.replyingAgentId,
1059
+ chatTurnId: null,
1060
+ turnVariant: 0,
1061
+ createdAt: message.createdAt,
1062
+ updatedAt: message.updatedAt,
1063
+ })));
1064
+ }
1065
+ const [systemEvent] = await tx
1066
+ .insert(chatMessages)
1067
+ .values({
1068
+ orgId: input.orgId,
1069
+ conversationId: child.id,
1070
+ role: "system",
1071
+ kind: "system_event",
1072
+ status: "completed",
1073
+ body: forkSystemEventBody(source, input.sourceMessageId ?? null),
1074
+ structuredPayload: {
1075
+ type: "chat_fork",
1076
+ sourceConversationId: source.id,
1077
+ sourceMessageId: input.sourceMessageId ?? null,
1078
+ forkRootConversationId: rootConversationId,
1079
+ },
1080
+ createdAt: now,
1081
+ updatedAt: now,
1082
+ })
1083
+ .returning();
1084
+ await tx
1085
+ .update(chatConversations)
1086
+ .set({
1087
+ lastMessageAt: systemEvent?.createdAt ?? now,
1088
+ updatedAt: now,
1089
+ })
1090
+ .where(eq(chatConversations.id, child.id));
1091
+ await ensureForkFamilyGroup(tx, input.orgId, input.userId, rootConversationId, source.id, child.id, source.title);
1092
+ return child;
1093
+ });
1094
+ return getById(created.id, input.userId);
1095
+ }
496
1096
  async function update(id, patch) {
497
1097
  const [updated] = await db
498
1098
  .update(chatConversations)
@@ -506,14 +1106,14 @@ export function chatService(db) {
506
1106
  return null;
507
1107
  return getById(id);
508
1108
  }
509
- async function updateDefaultTitle(id, title) {
1109
+ async function updateDefaultTitle(id, title, expectedCurrentTitle = "New chat") {
510
1110
  const [updated] = await db
511
1111
  .update(chatConversations)
512
1112
  .set({
513
1113
  title,
514
1114
  updatedAt: new Date(),
515
1115
  })
516
- .where(and(eq(chatConversations.id, id), eq(chatConversations.title, "New chat")))
1116
+ .where(and(eq(chatConversations.id, id), eq(chatConversations.title, expectedCurrentTitle)))
517
1117
  .returning();
518
1118
  if (!updated)
519
1119
  return null;
@@ -1462,10 +2062,26 @@ export function chatService(db) {
1462
2062
  replaceSystemGeneratedTitle,
1463
2063
  listAttachmentsForConversation,
1464
2064
  remove,
2065
+ forkConversation,
1465
2066
  resolve,
1466
2067
  markRead,
1467
2068
  markUnread,
1468
2069
  setPinned,
2070
+ createGeneration,
2071
+ markGenerationTerminal,
2072
+ getLatestActiveGeneration,
2073
+ getLatestGeneration,
2074
+ getQueueSnapshot,
2075
+ listQueuedMessages,
2076
+ createQueuedMessage,
2077
+ updateQueuedMessage,
2078
+ cancelQueuedMessage,
2079
+ markQueuedMessageSteerFallback,
2080
+ claimNextQueuedMessage,
2081
+ releaseQueuedMessageClaim,
2082
+ assertQueuedMessageClaimedForDelivery,
2083
+ markQueuedMessageRunning,
2084
+ markQueuedMessageDeliveryTerminal,
1469
2085
  listMessages,
1470
2086
  getMessageTranscript,
1471
2087
  addMessage,