@rudderhq/server 0.2.5-canary.9 → 0.2.5

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 (309) hide show
  1. package/dist/bootstrap/plugin-host-runtime.d.ts +39 -39
  2. package/dist/bundled-plugins/plugin-linear/dist/worker.js +101 -147
  3. package/dist/bundled-plugins/plugin-linear/dist/worker.js.map +2 -2
  4. package/dist/bundled-plugins/plugin-linear/package.json +1 -1
  5. package/dist/routes/access-onboarding.helpers.d.ts +142 -0
  6. package/dist/routes/access-onboarding.helpers.d.ts.map +1 -0
  7. package/dist/routes/access-onboarding.helpers.js +762 -0
  8. package/dist/routes/access-onboarding.helpers.js.map +1 -0
  9. package/dist/routes/access.d.ts +2 -48
  10. package/dist/routes/access.d.ts.map +1 -1
  11. package/dist/routes/access.helpers.d.ts +109 -0
  12. package/dist/routes/access.helpers.d.ts.map +1 -0
  13. package/dist/routes/access.helpers.js +460 -0
  14. package/dist/routes/access.helpers.js.map +1 -0
  15. package/dist/routes/access.js +6 -1218
  16. package/dist/routes/access.js.map +1 -1
  17. package/dist/routes/agents.d.ts.map +1 -1
  18. package/dist/routes/agents.js +55 -1057
  19. package/dist/routes/agents.js.map +1 -1
  20. package/dist/routes/agents.management-routes.d.ts +12 -0
  21. package/dist/routes/agents.management-routes.d.ts.map +1 -0
  22. package/dist/routes/agents.management-routes.js +1067 -0
  23. package/dist/routes/agents.management-routes.js.map +1 -0
  24. package/dist/routes/chats.d.ts.map +1 -1
  25. package/dist/routes/chats.js +42 -652
  26. package/dist/routes/chats.js.map +1 -1
  27. package/dist/routes/chats.stream-routes.d.ts +12 -0
  28. package/dist/routes/chats.stream-routes.d.ts.map +1 -0
  29. package/dist/routes/chats.stream-routes.js +666 -0
  30. package/dist/routes/chats.stream-routes.js.map +1 -0
  31. package/dist/routes/issues.comments-attachments.d.ts +12 -0
  32. package/dist/routes/issues.comments-attachments.d.ts.map +1 -0
  33. package/dist/routes/issues.comments-attachments.js +511 -0
  34. package/dist/routes/issues.comments-attachments.js.map +1 -0
  35. package/dist/routes/issues.d.ts.map +1 -1
  36. package/dist/routes/issues.js +43 -1128
  37. package/dist/routes/issues.js.map +1 -1
  38. package/dist/routes/issues.mutations.d.ts +12 -0
  39. package/dist/routes/issues.mutations.d.ts.map +1 -0
  40. package/dist/routes/issues.mutations.js +635 -0
  41. package/dist/routes/issues.mutations.js.map +1 -0
  42. package/dist/routes/plugins.d.ts.map +1 -1
  43. package/dist/routes/plugins.js +14 -694
  44. package/dist/routes/plugins.js.map +1 -1
  45. package/dist/routes/plugins.operations-routes.d.ts +28 -0
  46. package/dist/routes/plugins.operations-routes.d.ts.map +1 -0
  47. package/dist/routes/plugins.operations-routes.js +720 -0
  48. package/dist/routes/plugins.operations-routes.js.map +1 -0
  49. package/dist/services/access.d.ts +21 -21
  50. package/dist/services/activity.d.ts +19 -19
  51. package/dist/services/agents.d.ts +158 -158
  52. package/dist/services/approvals.d.ts +29 -29
  53. package/dist/services/assets.d.ts +8 -8
  54. package/dist/services/automations.d.ts +41 -27
  55. package/dist/services/automations.d.ts.map +1 -1
  56. package/dist/services/automations.js +287 -110
  57. package/dist/services/automations.js.map +1 -1
  58. package/dist/services/automations.scheduler.d.ts +9 -0
  59. package/dist/services/automations.scheduler.d.ts.map +1 -0
  60. package/dist/services/automations.scheduler.js +101 -0
  61. package/dist/services/automations.scheduler.js.map +1 -0
  62. package/dist/services/board-auth.d.ts +32 -32
  63. package/dist/services/calendar.d.ts +26 -26
  64. package/dist/services/chat-assistant.d.ts +3 -47
  65. package/dist/services/chat-assistant.d.ts.map +1 -1
  66. package/dist/services/chat-assistant.helpers.d.ts +156 -0
  67. package/dist/services/chat-assistant.helpers.d.ts.map +1 -0
  68. package/dist/services/chat-assistant.helpers.js +862 -0
  69. package/dist/services/chat-assistant.helpers.js.map +1 -0
  70. package/dist/services/chat-assistant.js +2 -861
  71. package/dist/services/chat-assistant.js.map +1 -1
  72. package/dist/services/chats.d.ts +149 -247
  73. package/dist/services/chats.d.ts.map +1 -1
  74. package/dist/services/chats.helpers.d.ts +117 -0
  75. package/dist/services/chats.helpers.d.ts.map +1 -0
  76. package/dist/services/chats.helpers.js +285 -0
  77. package/dist/services/chats.helpers.js.map +1 -0
  78. package/dist/services/chats.js +6 -286
  79. package/dist/services/chats.js.map +1 -1
  80. package/dist/services/costs.d.ts +8 -8
  81. package/dist/services/finance.d.ts +18 -18
  82. package/dist/services/goals.d.ts +30 -30
  83. package/dist/services/heartbeat.d.ts +3 -1
  84. package/dist/services/heartbeat.d.ts.map +1 -1
  85. package/dist/services/heartbeat.js +3 -1
  86. package/dist/services/heartbeat.js.map +1 -1
  87. package/dist/services/issue-approvals.d.ts +4 -4
  88. package/dist/services/issue-review-wakeup.d.ts +3 -3
  89. package/dist/services/issues.comments-attachments.d.ts +141 -0
  90. package/dist/services/issues.comments-attachments.d.ts.map +1 -0
  91. package/dist/services/issues.comments-attachments.js +313 -0
  92. package/dist/services/issues.comments-attachments.js.map +1 -0
  93. package/dist/services/issues.d.ts +205 -256
  94. package/dist/services/issues.d.ts.map +1 -1
  95. package/dist/services/issues.helpers.d.ts +87 -0
  96. package/dist/services/issues.helpers.d.ts.map +1 -0
  97. package/dist/services/issues.helpers.js +270 -0
  98. package/dist/services/issues.helpers.js.map +1 -0
  99. package/dist/services/issues.js +5 -569
  100. package/dist/services/issues.js.map +1 -1
  101. package/dist/services/knowledge-portability/organization-portability.core.d.ts +210 -0
  102. package/dist/services/knowledge-portability/organization-portability.core.d.ts.map +1 -0
  103. package/dist/services/knowledge-portability/organization-portability.core.js +997 -0
  104. package/dist/services/knowledge-portability/organization-portability.core.js.map +1 -0
  105. package/dist/services/knowledge-portability/organization-portability.d.ts +6 -28
  106. package/dist/services/knowledge-portability/organization-portability.d.ts.map +1 -1
  107. package/dist/services/knowledge-portability/organization-portability.export.d.ts +24 -0
  108. package/dist/services/knowledge-portability/organization-portability.export.d.ts.map +1 -0
  109. package/dist/services/knowledge-portability/organization-portability.export.js +607 -0
  110. package/dist/services/knowledge-portability/organization-portability.export.js.map +1 -0
  111. package/dist/services/knowledge-portability/organization-portability.files.d.ts +69 -0
  112. package/dist/services/knowledge-portability/organization-portability.files.d.ts.map +1 -0
  113. package/dist/services/knowledge-portability/organization-portability.files.js +597 -0
  114. package/dist/services/knowledge-portability/organization-portability.files.js.map +1 -0
  115. package/dist/services/knowledge-portability/organization-portability.import.d.ts +31 -0
  116. package/dist/services/knowledge-portability/organization-portability.import.d.ts.map +1 -0
  117. package/dist/services/knowledge-portability/organization-portability.import.js +575 -0
  118. package/dist/services/knowledge-portability/organization-portability.import.js.map +1 -0
  119. package/dist/services/knowledge-portability/organization-portability.js +37 -3848
  120. package/dist/services/knowledge-portability/organization-portability.js.map +1 -1
  121. package/dist/services/knowledge-portability/organization-portability.package.d.ts +72 -0
  122. package/dist/services/knowledge-portability/organization-portability.package.d.ts.map +1 -0
  123. package/dist/services/knowledge-portability/organization-portability.package.js +749 -0
  124. package/dist/services/knowledge-portability/organization-portability.package.js.map +1 -0
  125. package/dist/services/knowledge-portability/organization-portability.preview.d.ts +18 -0
  126. package/dist/services/knowledge-portability/organization-portability.preview.d.ts.map +1 -0
  127. package/dist/services/knowledge-portability/organization-portability.preview.js +333 -0
  128. package/dist/services/knowledge-portability/organization-portability.preview.js.map +1 -0
  129. package/dist/services/knowledge-portability/organization-portability.resolve-source.d.ts +4 -0
  130. package/dist/services/knowledge-portability/organization-portability.resolve-source.d.ts.map +1 -0
  131. package/dist/services/knowledge-portability/organization-portability.resolve-source.js +86 -0
  132. package/dist/services/knowledge-portability/organization-portability.resolve-source.js.map +1 -0
  133. package/dist/services/knowledge-portability/organization-skills.catalog.d.ts +221 -0
  134. package/dist/services/knowledge-portability/organization-skills.catalog.d.ts.map +1 -0
  135. package/dist/services/knowledge-portability/organization-skills.catalog.js +999 -0
  136. package/dist/services/knowledge-portability/organization-skills.catalog.js.map +1 -0
  137. package/dist/services/knowledge-portability/organization-skills.d.ts +4 -75
  138. package/dist/services/knowledge-portability/organization-skills.d.ts.map +1 -1
  139. package/dist/services/knowledge-portability/organization-skills.js +11 -2008
  140. package/dist/services/knowledge-portability/organization-skills.js.map +1 -1
  141. package/dist/services/knowledge-portability/organization-skills.scans.d.ts +16 -0
  142. package/dist/services/knowledge-portability/organization-skills.scans.d.ts.map +1 -0
  143. package/dist/services/knowledge-portability/organization-skills.scans.js +300 -0
  144. package/dist/services/knowledge-portability/organization-skills.scans.js.map +1 -0
  145. package/dist/services/knowledge-portability/organization-skills.sources.d.ts +68 -0
  146. package/dist/services/knowledge-portability/organization-skills.sources.d.ts.map +1 -0
  147. package/dist/services/knowledge-portability/organization-skills.sources.js +728 -0
  148. package/dist/services/knowledge-portability/organization-skills.sources.js.map +1 -0
  149. package/dist/services/messenger.d.ts +2 -2
  150. package/dist/services/messenger.js +2 -2
  151. package/dist/services/messenger.js.map +1 -1
  152. package/dist/services/organization-skills.d.ts +3 -1
  153. package/dist/services/organization-skills.d.ts.map +1 -1
  154. package/dist/services/organization-skills.js +3 -1
  155. package/dist/services/organization-skills.js.map +1 -1
  156. package/dist/services/orgs.d.ts +9 -9
  157. package/dist/services/plugin-loader.core.d.ts +14 -0
  158. package/dist/services/plugin-loader.core.d.ts.map +1 -0
  159. package/dist/services/plugin-loader.core.js +905 -0
  160. package/dist/services/plugin-loader.core.js.map +1 -0
  161. package/dist/services/plugin-loader.d.ts +3 -440
  162. package/dist/services/plugin-loader.d.ts.map +1 -1
  163. package/dist/services/plugin-loader.helpers.d.ts +468 -0
  164. package/dist/services/plugin-loader.helpers.d.ts.map +1 -0
  165. package/dist/services/plugin-loader.helpers.js +263 -0
  166. package/dist/services/plugin-loader.helpers.js.map +1 -0
  167. package/dist/services/plugin-loader.js +3 -1191
  168. package/dist/services/plugin-loader.js.map +1 -1
  169. package/dist/services/plugin-loader.worker-paths.d.ts +7 -0
  170. package/dist/services/plugin-loader.worker-paths.d.ts.map +1 -0
  171. package/dist/services/plugin-loader.worker-paths.js +85 -0
  172. package/dist/services/plugin-loader.worker-paths.js.map +1 -0
  173. package/dist/services/plugin-registry.d.ts +123 -123
  174. package/dist/services/projects.d.ts +8 -8
  175. package/dist/services/runtime-kernel/heartbeat.core.d.ts +725 -0
  176. package/dist/services/runtime-kernel/heartbeat.core.d.ts.map +1 -0
  177. package/dist/services/runtime-kernel/heartbeat.core.js +525 -0
  178. package/dist/services/runtime-kernel/heartbeat.core.js.map +1 -0
  179. package/dist/services/runtime-kernel/heartbeat.d.ts +38 -259
  180. package/dist/services/runtime-kernel/heartbeat.d.ts.map +1 -1
  181. package/dist/services/runtime-kernel/heartbeat.execute.d.ts +5 -0
  182. package/dist/services/runtime-kernel/heartbeat.execute.d.ts.map +1 -0
  183. package/dist/services/runtime-kernel/heartbeat.execute.js +1052 -0
  184. package/dist/services/runtime-kernel/heartbeat.execute.js.map +1 -0
  185. package/dist/services/runtime-kernel/heartbeat.js +50 -4142
  186. package/dist/services/runtime-kernel/heartbeat.js.map +1 -1
  187. package/dist/services/runtime-kernel/heartbeat.misc.d.ts +30 -0
  188. package/dist/services/runtime-kernel/heartbeat.misc.d.ts.map +1 -0
  189. package/dist/services/runtime-kernel/heartbeat.misc.js +483 -0
  190. package/dist/services/runtime-kernel/heartbeat.misc.js.map +1 -0
  191. package/dist/services/runtime-kernel/heartbeat.recovery.d.ts +38 -0
  192. package/dist/services/runtime-kernel/heartbeat.recovery.d.ts.map +1 -0
  193. package/dist/services/runtime-kernel/heartbeat.recovery.js +605 -0
  194. package/dist/services/runtime-kernel/heartbeat.recovery.js.map +1 -0
  195. package/dist/services/runtime-kernel/heartbeat.release.d.ts +6 -0
  196. package/dist/services/runtime-kernel/heartbeat.release.d.ts.map +1 -0
  197. package/dist/services/runtime-kernel/heartbeat.release.js +398 -0
  198. package/dist/services/runtime-kernel/heartbeat.release.js.map +1 -0
  199. package/dist/services/runtime-kernel/heartbeat.sessions.d.ts +229 -0
  200. package/dist/services/runtime-kernel/heartbeat.sessions.d.ts.map +1 -0
  201. package/dist/services/runtime-kernel/heartbeat.sessions.js +708 -0
  202. package/dist/services/runtime-kernel/heartbeat.sessions.js.map +1 -0
  203. package/dist/services/runtime-kernel/heartbeat.wakeup.d.ts +5 -0
  204. package/dist/services/runtime-kernel/heartbeat.wakeup.d.ts.map +1 -0
  205. package/dist/services/runtime-kernel/heartbeat.wakeup.js +552 -0
  206. package/dist/services/runtime-kernel/heartbeat.wakeup.js.map +1 -0
  207. package/dist/services/secrets.d.ts +25 -25
  208. package/dist/services/sidebar-badges.js +1 -1
  209. package/dist/services/sidebar-badges.js.map +1 -1
  210. package/dist/services/workspace-runtime.comments.d.ts +6 -0
  211. package/dist/services/workspace-runtime.comments.d.ts.map +1 -0
  212. package/dist/services/workspace-runtime.comments.js +17 -0
  213. package/dist/services/workspace-runtime.comments.js.map +1 -0
  214. package/dist/services/workspace-runtime.d.ts +4 -163
  215. package/dist/services/workspace-runtime.d.ts.map +1 -1
  216. package/dist/services/workspace-runtime.helpers.d.ts +163 -0
  217. package/dist/services/workspace-runtime.helpers.d.ts.map +1 -0
  218. package/dist/services/workspace-runtime.helpers.js +360 -0
  219. package/dist/services/workspace-runtime.helpers.js.map +1 -0
  220. package/dist/services/workspace-runtime.js +4 -1236
  221. package/dist/services/workspace-runtime.js.map +1 -1
  222. package/dist/services/workspace-runtime.lifecycle.d.ts +35 -0
  223. package/dist/services/workspace-runtime.lifecycle.d.ts.map +1 -0
  224. package/dist/services/workspace-runtime.lifecycle.js +266 -0
  225. package/dist/services/workspace-runtime.lifecycle.js.map +1 -0
  226. package/dist/services/workspace-runtime.services.d.ts +140 -0
  227. package/dist/services/workspace-runtime.services.d.ts.map +1 -0
  228. package/dist/services/workspace-runtime.services.js +606 -0
  229. package/dist/services/workspace-runtime.services.js.map +1 -0
  230. package/package.json +21 -15
  231. package/ui-dist/assets/{_basePickBy-B5mJzzqZ.js → _basePickBy-N8I9ml5Y.js} +1 -1
  232. package/ui-dist/assets/{_baseUniq-B10Ec09o.js → _baseUniq-BuSlpRSQ.js} +1 -1
  233. package/ui-dist/assets/{arc-Bw7wimOa.js → arc-qX-dPyA1.js} +1 -1
  234. package/ui-dist/assets/{architectureDiagram-2XIMDMQ5-DZr0XEvv.js → architectureDiagram-2XIMDMQ5-DhjkbXsp.js} +1 -1
  235. package/ui-dist/assets/{blockDiagram-WCTKOSBZ-D0jl0LgB.js → blockDiagram-WCTKOSBZ-JS-tTu3J.js} +1 -1
  236. package/ui-dist/assets/{c4Diagram-IC4MRINW-BEFxBnEm.js → c4Diagram-IC4MRINW-4DqwCWIx.js} +1 -1
  237. package/ui-dist/assets/channel-CccCW5_a.js +1 -0
  238. package/ui-dist/assets/{chunk-4BX2VUAB-Cbul1GoA.js → chunk-4BX2VUAB-T37SqBpp.js} +1 -1
  239. package/ui-dist/assets/{chunk-55IACEB6-DuouC3bT.js → chunk-55IACEB6-BSj9hdqK.js} +1 -1
  240. package/ui-dist/assets/{chunk-FMBD7UC4-bN1jF9xw.js → chunk-FMBD7UC4-Dkrlh0Wk.js} +1 -1
  241. package/ui-dist/assets/{chunk-JSJVCQXG-B0-Ij6ZF.js → chunk-JSJVCQXG-C0ZE3QdB.js} +1 -1
  242. package/ui-dist/assets/{chunk-KX2RTZJC-BjI3IEjI.js → chunk-KX2RTZJC-DOZQM9gW.js} +1 -1
  243. package/ui-dist/assets/{chunk-NQ4KR5QH-MUoGr46n.js → chunk-NQ4KR5QH-5Yr3U2k8.js} +1 -1
  244. package/ui-dist/assets/{chunk-QZHKN3VN-CQoI9Ouy.js → chunk-QZHKN3VN-CvKTufwF.js} +1 -1
  245. package/ui-dist/assets/{chunk-WL4C6EOR-DSJh3iDp.js → chunk-WL4C6EOR-IoEM0jyx.js} +1 -1
  246. package/ui-dist/assets/classDiagram-VBA2DB6C-JKk4tCW2.js +1 -0
  247. package/ui-dist/assets/classDiagram-v2-RAHNMMFH-JKk4tCW2.js +1 -0
  248. package/ui-dist/assets/clone-Onaweg8D.js +1 -0
  249. package/ui-dist/assets/{cose-bilkent-S5V4N54A-BPepglgB.js → cose-bilkent-S5V4N54A-CTvr1OFj.js} +1 -1
  250. package/ui-dist/assets/{dagre-KLK3FWXG-DhnHVZkt.js → dagre-KLK3FWXG-UZ-SNjVK.js} +1 -1
  251. package/ui-dist/assets/{diagram-E7M64L7V-DNvXtoOO.js → diagram-E7M64L7V-D7RAN0Hr.js} +1 -1
  252. package/ui-dist/assets/{diagram-IFDJBPK2-DhGlDTgn.js → diagram-IFDJBPK2-B4LViaFR.js} +1 -1
  253. package/ui-dist/assets/{diagram-P4PSJMXO-BmXEloWS.js → diagram-P4PSJMXO-CY1be7ak.js} +1 -1
  254. package/ui-dist/assets/{erDiagram-INFDFZHY-BTYVzaLM.js → erDiagram-INFDFZHY-Dca0KkvJ.js} +1 -1
  255. package/ui-dist/assets/{flowDiagram-PKNHOUZH-CqMNQUVv.js → flowDiagram-PKNHOUZH-i-qMvfwg.js} +1 -1
  256. package/ui-dist/assets/{ganttDiagram-A5KZAMGK-B2le_64a.js → ganttDiagram-A5KZAMGK-Wxq2lhbh.js} +1 -1
  257. package/ui-dist/assets/{gitGraphDiagram-K3NZZRJ6-BtxOBq5A.js → gitGraphDiagram-K3NZZRJ6-DwzgPlAY.js} +1 -1
  258. package/ui-dist/assets/{graph-C5E6qFfm.js → graph-BAqf89Tz.js} +1 -1
  259. package/ui-dist/assets/{index-Piq-IPXt.js → index-4eCzaLuY.js} +1 -1
  260. package/ui-dist/assets/{index-DT6UN2ec.js → index-8uu-nKqK.js} +1 -1
  261. package/ui-dist/assets/{index-T5NVZ3nR.js → index-B-1NEcI_.js} +1 -1
  262. package/ui-dist/assets/{index-D-MoarxG.js → index-B0b_3Eu5.js} +1 -1
  263. package/ui-dist/assets/{index-CZiP3FBQ.js → index-B8v0eZjP.js} +1 -1
  264. package/ui-dist/assets/{index-C1Ga66FM.js → index-BN7Moj3u.js} +1 -1
  265. package/ui-dist/assets/{index-xBUfBdQn.js → index-BSpxh3cY.js} +1 -1
  266. package/ui-dist/assets/{index-CQcMWp51.js → index-BY44RIi9.js} +1 -1
  267. package/ui-dist/assets/{index-3a93sZNI.js → index-BhyQJhdZ.js} +1 -1
  268. package/ui-dist/assets/{index-BsVDit5y.js → index-BkPL_iGU.js} +1 -1
  269. package/ui-dist/assets/{index-88lBSTsW.js → index-BsPfoHXS.js} +1 -1
  270. package/ui-dist/assets/{index-CyJtcUF0.js → index-BstW7nmv.js} +1 -1
  271. package/ui-dist/assets/{index-BvZ0Ptfl.js → index-BwB67Zyz.js} +1 -1
  272. package/ui-dist/assets/index-C2peSkmT.css +1 -0
  273. package/ui-dist/assets/{index-vkCrQLeX.js → index-C3ktOsS_.js} +1 -1
  274. package/ui-dist/assets/{index-D2hZpQJT.js → index-CMyABlS-.js} +1 -1
  275. package/ui-dist/assets/{index-C4WCPEY4.js → index-CyBJ8ujC.js} +1 -1
  276. package/ui-dist/assets/{index-Bf7NB_lK.js → index-DAxM2W3O.js} +1 -1
  277. package/ui-dist/assets/{index-Dq7H6-Lm.js → index-DVZXPmhk.js} +1 -1
  278. package/ui-dist/assets/{index-CskDu6A3.js → index-Dc19uAyw.js} +1 -1
  279. package/ui-dist/assets/index-DzHrwZu1.js +1511 -0
  280. package/ui-dist/assets/{index-B20JneLK.js → index-LJuf53Ye.js} +1 -1
  281. package/ui-dist/assets/{index-D6McTDMQ.js → index-Ugw5VWWz.js} +1 -1
  282. package/ui-dist/assets/{index-CcVGS6HJ.js → index-YGraEFR7.js} +1 -1
  283. package/ui-dist/assets/{infoDiagram-LFFYTUFH-BiCCZcIW.js → infoDiagram-LFFYTUFH-jLmDtFVR.js} +1 -1
  284. package/ui-dist/assets/{ishikawaDiagram-PHBUUO56-BiwBemM5.js → ishikawaDiagram-PHBUUO56-6OGMyLT8.js} +1 -1
  285. package/ui-dist/assets/{journeyDiagram-4ABVD52K-D8RGr2xl.js → journeyDiagram-4ABVD52K-yQjl6E0t.js} +1 -1
  286. package/ui-dist/assets/{kanban-definition-K7BYSVSG-C733Fj-E.js → kanban-definition-K7BYSVSG-DkdCeQlS.js} +1 -1
  287. package/ui-dist/assets/{layout-CM4c3NA_.js → layout-CqSYvZ_w.js} +1 -1
  288. package/ui-dist/assets/{linear-DzH21Xsf.js → linear-B8xGZaoi.js} +1 -1
  289. package/ui-dist/assets/{mermaid.core-Z2rpoVP2.js → mermaid.core-AKL_cdyk.js} +4 -4
  290. package/ui-dist/assets/{mindmap-definition-YRQLILUH-DylLLj9w.js → mindmap-definition-YRQLILUH-Zr-dXC0x.js} +1 -1
  291. package/ui-dist/assets/{pieDiagram-SKSYHLDU-617wI_rr.js → pieDiagram-SKSYHLDU-BvDAU-Nk.js} +1 -1
  292. package/ui-dist/assets/{quadrantDiagram-337W2JSQ-lxoCPJIL.js → quadrantDiagram-337W2JSQ-Dn9kM62o.js} +1 -1
  293. package/ui-dist/assets/{requirementDiagram-Z7DCOOCP-C5XydQ9-.js → requirementDiagram-Z7DCOOCP-GIsIh7Sd.js} +1 -1
  294. package/ui-dist/assets/{sankeyDiagram-WA2Y5GQK--grmq-Q8.js → sankeyDiagram-WA2Y5GQK-CUCuBkuf.js} +1 -1
  295. package/ui-dist/assets/{sequenceDiagram-2WXFIKYE-BS2PeYH-.js → sequenceDiagram-2WXFIKYE-MDpUY2HM.js} +1 -1
  296. package/ui-dist/assets/{stateDiagram-RAJIS63D-CeuZtj2z.js → stateDiagram-RAJIS63D-BymMpuUU.js} +1 -1
  297. package/ui-dist/assets/stateDiagram-v2-FVOUBMTO-Bi2oCU6d.js +1 -0
  298. package/ui-dist/assets/{timeline-definition-YZTLITO2-DxHdMpRr.js → timeline-definition-YZTLITO2-B6ofPhhy.js} +1 -1
  299. package/ui-dist/assets/{treemap-KZPCXAKY-Bv1ZlC5h.js → treemap-KZPCXAKY-DnLO6w1l.js} +1 -1
  300. package/ui-dist/assets/{vennDiagram-LZ73GAT5-DvpZSXY2.js → vennDiagram-LZ73GAT5-D0MyZIDl.js} +1 -1
  301. package/ui-dist/assets/{xychartDiagram-JWTSCODW-DttOu1GC.js → xychartDiagram-JWTSCODW-rADY1iUG.js} +1 -1
  302. package/ui-dist/index.html +2 -2
  303. package/ui-dist/assets/channel-DGUh6rEi.js +0 -1
  304. package/ui-dist/assets/classDiagram-VBA2DB6C-1ntk2IOV.js +0 -1
  305. package/ui-dist/assets/classDiagram-v2-RAHNMMFH-1ntk2IOV.js +0 -1
  306. package/ui-dist/assets/clone-BpddY88c.js +0 -1
  307. package/ui-dist/assets/index-C8AD6s7S.js +0 -1510
  308. package/ui-dist/assets/index-Ded0dPwB.css +0 -1
  309. package/ui-dist/assets/stateDiagram-v2-FVOUBMTO-DXq0yC5C.js +0 -1
@@ -1,17 +1,15 @@
1
1
  import { Router } from "express";
2
2
  import multer from "multer";
3
3
  import { buildIssueDocumentsPrompt } from "@rudderhq/agent-runtime-utils/server-utils";
4
- import { addIssueCommentSchema, createIssueAttachmentMetadataSchema, createIssueWorkspaceAttachmentSchema, createIssueWorkProductSchema, createIssueLabelSchema, checkoutIssueSchema, createIssueSchema, linkIssueApprovalSchema, reportIssueCommitSchema, issueDocumentKeySchema, reorderIssueSchema, updateIssueLabelSchema, updateIssueWorkProductSchema, upsertIssueDocumentSchema, updateIssueSchema, isUuidLike, } from "@rudderhq/shared";
4
+ import { createIssueWorkProductSchema, createIssueLabelSchema, linkIssueApprovalSchema, issueDocumentKeySchema, reorderIssueSchema, updateIssueLabelSchema, updateIssueWorkProductSchema, upsertIssueDocumentSchema, isUuidLike, } from "@rudderhq/shared";
5
5
  import { validate } from "../middleware/validate.js";
6
6
  import { accessService, agentService, executionWorkspaceService, goalService, heartbeatService, issueApprovalService, issueService, documentService, logActivity, projectService, automationService, workProductService, } from "../services/index.js";
7
7
  import { organizationWorkspaceBrowserService } from "../services/organization-workspace-browser.js";
8
- import { logger } from "../middleware/logger.js";
9
- import { forbidden, HttpError, unauthorized, unprocessable } from "../errors.js";
8
+ import { forbidden, unauthorized } from "../errors.js";
10
9
  import { assertBoard, assertCompanyAccess, getActorInfo } from "./authz.js";
11
- import { shouldWakeAssigneeOnCheckout } from "./issues-checkout-wakeup.js";
12
- import { isAllowedContentType, MAX_ATTACHMENT_BYTES } from "../attachment-types.js";
13
- import { queueIssueAssignmentWakeup } from "../services/issue-assignment-wakeup.js";
14
- import { buildIssueReviewWakeupOptions, queueIssueReviewWakeup } from "../services/issue-review-wakeup.js";
10
+ import { MAX_ATTACHMENT_BYTES } from "../attachment-types.js";
11
+ import { registerIssueCommentAttachmentRoutes } from "./issues.comments-attachments.js";
12
+ import { registerIssueMutationRoutes } from "./issues.mutations.js";
15
13
  const MAX_ISSUE_COMMENT_LIMIT = 500;
16
14
  export function issueRoutes(db, storage) {
17
15
  const router = Router();
@@ -933,1127 +931,44 @@ export function issueRoutes(db, storage) {
933
931
  });
934
932
  res.json({ ok: true });
935
933
  });
936
- router.post("/orgs/:orgId/issues", validate(createIssueSchema), async (req, res) => {
937
- const orgId = req.params.orgId;
938
- assertCompanyAccess(req, orgId);
939
- if (req.body.assigneeAgentId || req.body.assigneeUserId || req.body.reviewerAgentId || req.body.reviewerUserId) {
940
- await assertCanAssignTasks(req, orgId);
941
- }
942
- const actor = getActorInfo(req);
943
- const createInput = {
944
- ...req.body,
945
- createdByAgentId: actor.agentId,
946
- createdByUserId: actor.actorType === "user" ? actor.actorId : null,
947
- };
948
- const hasExplicitAssignee = Object.prototype.hasOwnProperty.call(req.body, "assigneeAgentId") ||
949
- Object.prototype.hasOwnProperty.call(req.body, "assigneeUserId");
950
- if (actor.actorType === "agent" && actor.agentId && !hasExplicitAssignee) {
951
- createInput.assigneeAgentId = actor.agentId;
952
- }
953
- const issue = await svc.create(orgId, {
954
- ...createInput,
955
- });
956
- await logActivity(db, {
957
- orgId,
958
- actorType: actor.actorType,
959
- actorId: actor.actorId,
960
- agentId: actor.agentId,
961
- runId: actor.runId,
962
- action: "issue.created",
963
- entityType: "issue",
964
- entityId: issue.id,
965
- details: { title: issue.title, identifier: issue.identifier },
966
- });
967
- void queueIssueAssignmentWakeup({
968
- heartbeat,
969
- issue,
970
- reason: "issue_assigned",
971
- mutation: "create",
972
- contextSource: "issue.create",
973
- requestedByActorType: actor.actorType,
974
- requestedByActorId: actor.actorId,
975
- });
976
- void queueIssueReviewWakeup({
977
- heartbeat,
978
- issue,
979
- mutation: "create_in_review",
980
- contextSource: "issue.create",
981
- requestedByActorType: actor.actorType,
982
- requestedByActorId: actor.actorId,
983
- actorAgentId: actor.agentId,
984
- });
985
- res.status(201).json(issue);
986
- });
987
- router.patch("/issues/:id", validate(updateIssueSchema), async (req, res) => {
988
- const id = req.params.id;
989
- const existing = await svc.getById(id);
990
- if (!existing) {
991
- res.status(404).json({ error: "Issue not found" });
992
- return;
993
- }
994
- assertCompanyAccess(req, existing.orgId);
995
- const assigneeWillChange = (req.body.assigneeAgentId !== undefined && req.body.assigneeAgentId !== existing.assigneeAgentId) ||
996
- (req.body.assigneeUserId !== undefined && req.body.assigneeUserId !== existing.assigneeUserId);
997
- const reviewerWillChange = (req.body.reviewerAgentId !== undefined && req.body.reviewerAgentId !== existing.reviewerAgentId) ||
998
- (req.body.reviewerUserId !== undefined && req.body.reviewerUserId !== existing.reviewerUserId);
999
- const isAgentReturningIssueToCreator = req.actor.type === "agent" &&
1000
- !!req.actor.agentId &&
1001
- existing.assigneeAgentId === req.actor.agentId &&
1002
- req.body.assigneeAgentId === null &&
1003
- typeof req.body.assigneeUserId === "string" &&
1004
- !!existing.createdByUserId &&
1005
- req.body.assigneeUserId === existing.createdByUserId;
1006
- if (assigneeWillChange) {
1007
- if (!isAgentReturningIssueToCreator) {
1008
- await assertCanAssignTasks(req, existing.orgId);
1009
- }
1010
- }
1011
- if (reviewerWillChange) {
1012
- await assertCanAssignTasks(req, existing.orgId);
1013
- }
1014
- if (!(await assertAgentRunCheckoutOwnership(req, res, existing)))
1015
- return;
1016
- const actor = getActorInfo(req);
1017
- const isClosed = existing.status === "done" || existing.status === "cancelled";
1018
- const { comment: commentBody, reopen: reopenRequested, hiddenAt: hiddenAtRaw, reviewDecision, ...updateFields } = req.body;
1019
- if (hiddenAtRaw !== undefined) {
1020
- updateFields.hiddenAt = hiddenAtRaw ? new Date(hiddenAtRaw) : null;
1021
- }
1022
- if (commentBody && reopenRequested === true && isClosed && updateFields.status === undefined) {
1023
- updateFields.status = "todo";
1024
- }
1025
- if (reviewDecision !== undefined) {
1026
- if (!commentBody) {
1027
- throw unprocessable("Reviewer decisions require a comment");
1028
- }
1029
- if (!statusAcceptsReviewerDecision(existing.status)) {
1030
- throw unprocessable("Reviewer decisions can only be recorded while the issue is in_review or blocked");
1031
- }
1032
- if (actor.actorType === "agent" && !isReviewerAgentForIssue(actor, existing)) {
1033
- throw forbidden("Only the reviewer agent can record a reviewer decision");
1034
- }
1035
- const decisionStatus = statusForReviewDecision(reviewDecision);
1036
- if (decisionStatus) {
1037
- updateFields.status = decisionStatus;
1038
- }
1039
- else {
1040
- delete updateFields.status;
1041
- }
1042
- }
1043
- let reviewedCompletionNormalized = false;
1044
- if (updateFields.status === "done" &&
1045
- !canAgentCompleteIssue(actor, existing)) {
1046
- res.status(403).json({ error: "Only the checked-out assignee or reviewer can complete issue" });
1047
- return;
1048
- }
1049
- if (updateFields.status === "done" &&
1050
- issueHasReviewer(existing) &&
1051
- actor.actorType === "agent" &&
1052
- !(statusAcceptsReviewerDecision(existing.status) && isReviewerAgentForIssue(actor, existing))) {
1053
- updateFields.status = "in_review";
1054
- reviewedCompletionNormalized = true;
1055
- }
1056
- let issue;
1057
- try {
1058
- issue = await svc.update(id, updateFields);
1059
- }
1060
- catch (err) {
1061
- if (err instanceof HttpError && err.status === 422) {
1062
- logger.warn({
1063
- issueId: id,
1064
- orgId: existing.orgId,
1065
- assigneePatch: {
1066
- assigneeAgentId: req.body.assigneeAgentId === undefined ? "__omitted__" : req.body.assigneeAgentId,
1067
- assigneeUserId: req.body.assigneeUserId === undefined ? "__omitted__" : req.body.assigneeUserId,
1068
- reviewerAgentId: req.body.reviewerAgentId === undefined ? "__omitted__" : req.body.reviewerAgentId,
1069
- reviewerUserId: req.body.reviewerUserId === undefined ? "__omitted__" : req.body.reviewerUserId,
1070
- },
1071
- currentAssignee: {
1072
- assigneeAgentId: existing.assigneeAgentId,
1073
- assigneeUserId: existing.assigneeUserId,
1074
- reviewerAgentId: existing.reviewerAgentId,
1075
- reviewerUserId: existing.reviewerUserId,
1076
- },
1077
- error: err.message,
1078
- details: err.details,
1079
- }, "issue update rejected with 422");
1080
- }
1081
- throw err;
1082
- }
1083
- if (!issue) {
1084
- res.status(404).json({ error: "Issue not found" });
1085
- return;
1086
- }
1087
- await automationsSvc.syncRunStatusForIssue(issue.id);
1088
- if (actor.runId) {
1089
- await heartbeat.reportRunActivity(actor.runId).catch((err) => logger.warn({ err, runId: actor.runId }, "failed to clear detached run warning after issue activity"));
1090
- }
1091
- // Build activity details with previous values for changed fields
1092
- const previous = {};
1093
- for (const key of Object.keys(updateFields)) {
1094
- if (key in existing && existing[key] !== updateFields[key]) {
1095
- previous[key] = existing[key];
1096
- }
1097
- }
1098
- const hasFieldChanges = Object.keys(previous).length > 0;
1099
- const reopened = commentBody &&
1100
- reopenRequested === true &&
1101
- isClosed &&
1102
- previous.status !== undefined &&
1103
- issue.status === "todo";
1104
- const reopenFromStatus = reopened ? existing.status : null;
1105
- await logActivity(db, {
1106
- orgId: issue.orgId,
1107
- actorType: actor.actorType,
1108
- actorId: actor.actorId,
1109
- agentId: actor.agentId,
1110
- runId: actor.runId,
1111
- action: "issue.updated",
1112
- entityType: "issue",
1113
- entityId: issue.id,
1114
- details: {
1115
- ...updateFields,
1116
- identifier: issue.identifier,
1117
- ...(commentBody ? { source: "comment" } : {}),
1118
- ...(reopened ? { reopened: true, reopenedFrom: reopenFromStatus } : {}),
1119
- ...(reviewedCompletionNormalized
1120
- ? {
1121
- normalizedFromStatus: "done",
1122
- normalizedReason: "reviewed_issue_assignee_completion",
1123
- }
1124
- : {}),
1125
- _previous: hasFieldChanges ? previous : undefined,
1126
- },
1127
- });
1128
- let comment = null;
1129
- if (commentBody) {
1130
- comment = await svc.addComment(id, commentBody, {
1131
- agentId: actor.agentId ?? undefined,
1132
- userId: actor.actorType === "user" ? actor.actorId : undefined,
1133
- });
1134
- await logActivity(db, {
1135
- orgId: issue.orgId,
1136
- actorType: actor.actorType,
1137
- actorId: actor.actorId,
1138
- agentId: actor.agentId,
1139
- runId: actor.runId,
1140
- action: "issue.comment_added",
1141
- entityType: "issue",
1142
- entityId: issue.id,
1143
- details: {
1144
- commentId: comment.id,
1145
- bodySnippet: comment.body.slice(0, 120),
1146
- identifier: issue.identifier,
1147
- issueTitle: issue.title,
1148
- ...(reopened ? { reopened: true, reopenedFrom: reopenFromStatus, source: "comment" } : {}),
1149
- ...(hasFieldChanges ? { updated: true } : {}),
1150
- },
1151
- });
1152
- }
1153
- if (reviewDecision !== undefined) {
1154
- const reviewOutcome = reviewDecision === "blocked" ? "human_handoff" : "review_closed";
1155
- await logActivity(db, {
1156
- orgId: issue.orgId,
1157
- actorType: actor.actorType,
1158
- actorId: actor.actorId,
1159
- agentId: actor.agentId,
1160
- runId: actor.runId,
1161
- action: "issue.review_decision_recorded",
1162
- entityType: "issue",
1163
- entityId: issue.id,
1164
- details: {
1165
- decision: reviewDecision,
1166
- outcome: reviewOutcome,
1167
- operatorActionRequired: reviewOutcome === "human_handoff",
1168
- status: issue.status,
1169
- identifier: issue.identifier,
1170
- commentId: comment?.id ?? null,
1171
- },
1172
- });
1173
- if (reviewerDecisionRequiresHumanHandoff(reviewDecision)) {
1174
- await logActivity(db, {
1175
- orgId: issue.orgId,
1176
- actorType: actor.actorType,
1177
- actorId: actor.actorId,
1178
- agentId: actor.agentId,
1179
- runId: actor.runId,
1180
- action: "issue.human_intervention_required",
1181
- entityType: "issue",
1182
- entityId: issue.id,
1183
- details: {
1184
- decision: reviewDecision,
1185
- status: issue.status,
1186
- identifier: issue.identifier,
1187
- issueTitle: issue.title,
1188
- commentId: comment?.id ?? null,
1189
- previousReviewerAgentId: existing.reviewerAgentId,
1190
- previousReviewerUserId: existing.reviewerUserId,
1191
- nextAction: "Human/operator intervention is required before agent review can continue.",
1192
- },
1193
- });
1194
- }
1195
- }
1196
- const assigneeChanged = assigneeWillChange;
1197
- const reviewerChanged = reviewerWillChange;
1198
- const statusChangedFromBacklog = existing.status === "backlog" &&
1199
- issue.status !== "backlog" &&
1200
- updateFields.status !== undefined;
1201
- const statusChangedToInReview = existing.status !== "in_review" &&
1202
- issue.status === "in_review" &&
1203
- updateFields.status !== undefined;
1204
- const statusChangedToBlocked = existing.status !== "blocked" &&
1205
- issue.status === "blocked" &&
1206
- updateFields.status !== undefined;
1207
- const statusReturnedFromReviewToAssignee = statusAcceptsReviewerDecision(existing.status) &&
1208
- (issue.status === "in_progress" || issue.status === "todo") &&
1209
- updateFields.status !== undefined;
1210
- const reviewerChangedInReviewableStatus = reviewerChanged &&
1211
- statusAcceptsReviewerDecision(existing.status) &&
1212
- statusAcceptsReviewerDecision(issue.status);
1213
- // Merge all wakeups from this update into one enqueue per agent to avoid duplicate runs.
1214
- void (async () => {
1215
- const wakeups = new Map();
1216
- if (assigneeChanged && issue.assigneeAgentId && issue.status !== "backlog") {
1217
- wakeups.set(issue.assigneeAgentId, {
1218
- source: "assignment",
1219
- triggerDetail: "system",
1220
- reason: "issue_assigned",
1221
- payload: { issueId: issue.id, mutation: "update" },
1222
- requestedByActorType: actor.actorType,
1223
- requestedByActorId: actor.actorId,
1224
- contextSnapshot: {
1225
- issueId: issue.id,
1226
- source: "issue.update",
1227
- wakeSource: "assignment",
1228
- wakeReason: "issue_assigned",
1229
- issue: {
1230
- id: issue.id,
1231
- title: issue.title,
1232
- description: issue.description,
1233
- status: issue.status,
1234
- priority: issue.priority,
1235
- },
1236
- },
1237
- });
1238
- }
1239
- if (!assigneeChanged && statusChangedFromBacklog && issue.assigneeAgentId) {
1240
- wakeups.set(issue.assigneeAgentId, {
1241
- source: "automation",
1242
- triggerDetail: "system",
1243
- reason: "issue_status_changed",
1244
- payload: { issueId: issue.id, mutation: "update" },
1245
- requestedByActorType: actor.actorType,
1246
- requestedByActorId: actor.actorId,
1247
- contextSnapshot: {
1248
- issueId: issue.id,
1249
- source: "issue.status_change",
1250
- wakeSource: "automation",
1251
- wakeReason: "issue_status_changed",
1252
- issue: {
1253
- id: issue.id,
1254
- title: issue.title,
1255
- description: issue.description,
1256
- status: issue.status,
1257
- priority: issue.priority,
1258
- },
1259
- },
1260
- });
1261
- }
1262
- if (!assigneeChanged && statusReturnedFromReviewToAssignee && issue.assigneeAgentId) {
1263
- const commentContext = comment
1264
- ? {
1265
- commentId: comment.id,
1266
- wakeCommentId: comment.id,
1267
- comment: {
1268
- id: comment.id,
1269
- body: comment.body,
1270
- authorAgentId: comment.authorAgentId,
1271
- authorUserId: comment.authorUserId,
1272
- },
1273
- }
1274
- : {};
1275
- wakeups.set(issue.assigneeAgentId, {
1276
- source: "assignment",
1277
- triggerDetail: "system",
1278
- reason: "issue_changes_requested",
1279
- payload: {
1280
- issueId: issue.id,
1281
- mutation: "review_changes_requested",
1282
- ...(comment ? { commentId: comment.id } : {}),
1283
- },
1284
- requestedByActorType: actor.actorType,
1285
- requestedByActorId: actor.actorId,
1286
- contextSnapshot: {
1287
- issueId: issue.id,
1288
- taskId: issue.id,
1289
- source: "issue.review_changes_requested",
1290
- wakeSource: "assignment",
1291
- wakeReason: "issue_changes_requested",
1292
- issue: {
1293
- id: issue.id,
1294
- title: issue.title,
1295
- description: issue.description,
1296
- status: issue.status,
1297
- priority: issue.priority,
1298
- },
1299
- ...commentContext,
1300
- },
1301
- });
1302
- }
1303
- if ((statusChangedToInReview || statusChangedToBlocked || reviewerChangedInReviewableStatus) && issue.reviewerAgentId) {
1304
- const mutation = statusChangedToInReview
1305
- ? "status_to_in_review"
1306
- : statusChangedToBlocked
1307
- ? "status_to_blocked"
1308
- : issue.status === "blocked"
1309
- ? "reviewer_changed_blocked"
1310
- : "reviewer_changed_in_review";
1311
- const actorIsReviewerAgent = actor.actorType === "agent" && actor.actorId === issue.reviewerAgentId;
1312
- const actorIsAssigneeAgent = actor.actorType === "agent" && actor.actorId === issue.assigneeAgentId;
1313
- const assigneeHandoffToReview = (statusChangedToInReview || statusChangedToBlocked) && actorIsAssigneeAgent;
1314
- if (!actorIsReviewerAgent || assigneeHandoffToReview) {
1315
- wakeups.set(issue.reviewerAgentId, buildIssueReviewWakeupOptions({
1316
- issue,
1317
- mutation,
1318
- contextSource: statusChangedToInReview || statusChangedToBlocked ? "issue.status_change" : "issue.reviewer_change",
1319
- requestedByActorType: actor.actorType,
1320
- requestedByActorId: actor.actorId,
1321
- }));
1322
- }
1323
- }
1324
- if (commentBody && comment) {
1325
- let mentionedIds = [];
1326
- try {
1327
- mentionedIds = await svc.findMentionedAgents(issue.orgId, commentBody);
1328
- }
1329
- catch (err) {
1330
- logger.warn({ err, issueId: id }, "failed to resolve @-mentions");
1331
- }
1332
- for (const mentionedId of mentionedIds) {
1333
- if (wakeups.has(mentionedId))
1334
- continue;
1335
- if (actor.actorType === "agent" && actor.actorId === mentionedId)
1336
- continue;
1337
- wakeups.set(mentionedId, {
1338
- source: "automation",
1339
- triggerDetail: "system",
1340
- reason: "issue_comment_mentioned",
1341
- payload: { issueId: id, commentId: comment.id },
1342
- requestedByActorType: actor.actorType,
1343
- requestedByActorId: actor.actorId,
1344
- contextSnapshot: {
1345
- issueId: id,
1346
- taskId: id,
1347
- commentId: comment.id,
1348
- wakeCommentId: comment.id,
1349
- wakeReason: "issue_comment_mentioned",
1350
- wakeSource: "comment.mention",
1351
- source: "comment.mention",
1352
- issue: {
1353
- id: issue.id,
1354
- title: issue.title,
1355
- description: issue.description,
1356
- status: issue.status,
1357
- priority: issue.priority,
1358
- },
1359
- comment: {
1360
- id: comment.id,
1361
- body: comment.body,
1362
- authorAgentId: comment.authorAgentId,
1363
- authorUserId: comment.authorUserId,
1364
- },
1365
- },
1366
- });
1367
- }
1368
- }
1369
- for (const [agentId, wakeup] of wakeups.entries()) {
1370
- heartbeat
1371
- .wakeup(agentId, wakeup)
1372
- .catch((err) => logger.warn({ err, issueId: issue.id, agentId }, "failed to wake agent on issue update"));
1373
- }
1374
- })();
1375
- res.json({ ...issue, comment });
1376
- });
1377
- router.delete("/issues/:id", async (req, res) => {
1378
- const id = req.params.id;
1379
- const existing = await svc.getById(id);
1380
- if (!existing) {
1381
- res.status(404).json({ error: "Issue not found" });
1382
- return;
1383
- }
1384
- assertCompanyAccess(req, existing.orgId);
1385
- const attachments = await svc.listAttachments(id);
1386
- const issue = await svc.remove(id);
1387
- if (!issue) {
1388
- res.status(404).json({ error: "Issue not found" });
1389
- return;
1390
- }
1391
- for (const attachment of attachments) {
1392
- try {
1393
- await storage.deleteObject(attachment.orgId, attachment.objectKey);
1394
- }
1395
- catch (err) {
1396
- logger.warn({ err, issueId: id, attachmentId: attachment.id }, "failed to delete attachment object during issue delete");
1397
- }
1398
- }
1399
- const actor = getActorInfo(req);
1400
- await logActivity(db, {
1401
- orgId: issue.orgId,
1402
- actorType: actor.actorType,
1403
- actorId: actor.actorId,
1404
- agentId: actor.agentId,
1405
- runId: actor.runId,
1406
- action: "issue.deleted",
1407
- entityType: "issue",
1408
- entityId: issue.id,
1409
- });
1410
- res.json(issue);
1411
- });
1412
- router.post("/issues/:id/checkout", validate(checkoutIssueSchema), async (req, res) => {
1413
- const id = req.params.id;
1414
- const issue = await svc.getById(id);
1415
- if (!issue) {
1416
- res.status(404).json({ error: "Issue not found" });
1417
- return;
1418
- }
1419
- assertCompanyAccess(req, issue.orgId);
1420
- if (issue.projectId) {
1421
- const project = await projectsSvc.getById(issue.projectId);
1422
- if (project?.pausedAt) {
1423
- res.status(409).json({
1424
- error: project.pauseReason === "budget"
1425
- ? "Project is paused because its budget hard-stop was reached"
1426
- : "Project is paused",
1427
- });
1428
- return;
1429
- }
1430
- }
1431
- if (req.actor.type === "agent" && req.actor.agentId !== req.body.agentId) {
1432
- res.status(403).json({ error: "Agent can only checkout as itself" });
1433
- return;
1434
- }
1435
- const checkoutRunId = requireAgentRunId(req, res);
1436
- if (req.actor.type === "agent" && !checkoutRunId)
1437
- return;
1438
- const updated = await svc.checkout(id, req.body.agentId, req.body.expectedStatuses, checkoutRunId);
1439
- const actor = getActorInfo(req);
1440
- await logActivity(db, {
1441
- orgId: issue.orgId,
1442
- actorType: actor.actorType,
1443
- actorId: actor.actorId,
1444
- agentId: actor.agentId,
1445
- runId: actor.runId,
1446
- action: "issue.checked_out",
1447
- entityType: "issue",
1448
- entityId: issue.id,
1449
- details: { agentId: req.body.agentId },
1450
- });
1451
- if (shouldWakeAssigneeOnCheckout({
1452
- actorType: req.actor.type,
1453
- actorAgentId: req.actor.type === "agent" ? req.actor.agentId ?? null : null,
1454
- checkoutAgentId: req.body.agentId,
1455
- checkoutRunId,
1456
- })) {
1457
- void heartbeat
1458
- .wakeup(req.body.agentId, {
1459
- source: "assignment",
1460
- triggerDetail: "system",
1461
- reason: "issue_checked_out",
1462
- payload: { issueId: issue.id, mutation: "checkout" },
1463
- requestedByActorType: actor.actorType,
1464
- requestedByActorId: actor.actorId,
1465
- contextSnapshot: { issueId: issue.id, source: "issue.checkout" },
1466
- })
1467
- .catch((err) => logger.warn({ err, issueId: issue.id }, "failed to wake assignee on issue checkout"));
1468
- }
1469
- res.json(updated);
1470
- });
1471
- router.post("/issues/:id/release", async (req, res) => {
1472
- const id = req.params.id;
1473
- const existing = await svc.getById(id);
1474
- if (!existing) {
1475
- res.status(404).json({ error: "Issue not found" });
1476
- return;
1477
- }
1478
- assertCompanyAccess(req, existing.orgId);
1479
- if (!(await assertAgentRunCheckoutOwnership(req, res, existing)))
1480
- return;
1481
- const actorRunId = requireAgentRunId(req, res);
1482
- if (req.actor.type === "agent" && !actorRunId)
1483
- return;
1484
- const released = await svc.release(id, req.actor.type === "agent" ? req.actor.agentId : undefined, actorRunId);
1485
- if (!released) {
1486
- res.status(404).json({ error: "Issue not found" });
1487
- return;
1488
- }
1489
- const actor = getActorInfo(req);
1490
- await logActivity(db, {
1491
- orgId: released.orgId,
1492
- actorType: actor.actorType,
1493
- actorId: actor.actorId,
1494
- agentId: actor.agentId,
1495
- runId: actor.runId,
1496
- action: "issue.released",
1497
- entityType: "issue",
1498
- entityId: released.id,
1499
- });
1500
- res.json(released);
1501
- });
1502
- router.post("/issues/:id/commit", validate(reportIssueCommitSchema), async (req, res) => {
1503
- const id = req.params.id;
1504
- const issue = await svc.getById(id);
1505
- if (!issue) {
1506
- res.status(404).json({ error: "Issue not found" });
1507
- return;
1508
- }
1509
- assertCompanyAccess(req, issue.orgId);
1510
- if (req.actor.type !== "agent") {
1511
- res.status(403).json({ error: "Agent authentication required" });
1512
- return;
1513
- }
1514
- if (!(await assertAgentRunCheckoutOwnership(req, res, issue)))
1515
- return;
1516
- const actor = getActorInfo(req);
1517
- const commitRun = await resolveAgentIssueRunId(req, res, issue);
1518
- if (!commitRun.ok)
1519
- return;
1520
- const sha = req.body.sha.trim().toLowerCase();
1521
- const subject = commitSubject(req.body.message);
1522
- const shortSha = sha.slice(0, 7);
1523
- if (commitRun.runId) {
1524
- await heartbeat.reportRunActivity(commitRun.runId).catch((err) => logger.warn({ err, runId: commitRun.runId }, "failed to clear detached run warning after issue commit activity"));
1525
- }
1526
- await logActivity(db, {
1527
- orgId: issue.orgId,
1528
- actorType: actor.actorType,
1529
- actorId: actor.actorId,
1530
- agentId: actor.agentId,
1531
- runId: commitRun.runId,
1532
- action: "issue.code_committed",
1533
- entityType: "issue",
1534
- entityId: issue.id,
1535
- details: {
1536
- sha,
1537
- shortSha,
1538
- message: req.body.message,
1539
- subject,
1540
- identifier: issue.identifier,
1541
- issueTitle: issue.title,
1542
- branch: req.body.branch ?? null,
1543
- repoPath: req.body.repoPath ?? null,
1544
- workspacePath: req.body.workspacePath ?? null,
1545
- commitCount: req.body.commitCount ?? 1,
1546
- },
1547
- });
1548
- res.status(201).json({
1549
- ok: true,
1550
- issueId: issue.id,
1551
- sha,
1552
- shortSha,
1553
- message: req.body.message,
1554
- subject,
1555
- runId: commitRun.runId,
1556
- });
1557
- });
1558
- router.get("/issues/:id/comments", async (req, res) => {
1559
- const id = req.params.id;
1560
- const issue = await svc.getById(id);
1561
- if (!issue) {
1562
- res.status(404).json({ error: "Issue not found" });
1563
- return;
1564
- }
1565
- assertCompanyAccess(req, issue.orgId);
1566
- const afterCommentId = typeof req.query.after === "string" && req.query.after.trim().length > 0
1567
- ? req.query.after.trim()
1568
- : typeof req.query.afterCommentId === "string" && req.query.afterCommentId.trim().length > 0
1569
- ? req.query.afterCommentId.trim()
1570
- : null;
1571
- const order = typeof req.query.order === "string" && req.query.order.trim().toLowerCase() === "asc"
1572
- ? "asc"
1573
- : "desc";
1574
- const limitRaw = typeof req.query.limit === "string" && req.query.limit.trim().length > 0
1575
- ? Number(req.query.limit)
1576
- : null;
1577
- const limit = limitRaw && Number.isFinite(limitRaw) && limitRaw > 0
1578
- ? Math.min(Math.floor(limitRaw), MAX_ISSUE_COMMENT_LIMIT)
1579
- : null;
1580
- const comments = await svc.listComments(id, {
1581
- afterCommentId,
1582
- order,
1583
- limit,
1584
- });
1585
- res.json(comments);
1586
- });
1587
- router.get("/issues/:id/comments/:commentId", async (req, res) => {
1588
- const id = req.params.id;
1589
- const commentId = req.params.commentId;
1590
- const issue = await svc.getById(id);
1591
- if (!issue) {
1592
- res.status(404).json({ error: "Issue not found" });
1593
- return;
1594
- }
1595
- assertCompanyAccess(req, issue.orgId);
1596
- const comment = await svc.getComment(commentId);
1597
- if (!comment || comment.issueId !== id) {
1598
- res.status(404).json({ error: "Comment not found" });
1599
- return;
1600
- }
1601
- res.json(comment);
1602
- });
1603
- router.post("/issues/:id/comments", validate(addIssueCommentSchema), async (req, res) => {
1604
- const id = req.params.id;
1605
- const issue = await svc.getById(id);
1606
- if (!issue) {
1607
- res.status(404).json({ error: "Issue not found" });
1608
- return;
1609
- }
1610
- assertCompanyAccess(req, issue.orgId);
1611
- if (!(await assertAgentRunCheckoutOwnership(req, res, issue)))
1612
- return;
1613
- const commentRun = await resolveAgentIssueRunId(req, res, issue);
1614
- if (!commentRun.ok)
1615
- return;
1616
- const actor = getActorInfo(req);
1617
- const reopenRequested = req.body.reopen === true;
1618
- const interruptRequested = req.body.interrupt === true;
1619
- const isClosed = issue.status === "done" || issue.status === "cancelled";
1620
- let reopened = false;
1621
- let reopenFromStatus = null;
1622
- let interruptedRunId = null;
1623
- let currentIssue = issue;
1624
- if (reopenRequested && isClosed) {
1625
- const reopenedIssue = await svc.update(id, { status: "todo" });
1626
- if (!reopenedIssue) {
1627
- res.status(404).json({ error: "Issue not found" });
1628
- return;
1629
- }
1630
- reopened = true;
1631
- reopenFromStatus = issue.status;
1632
- currentIssue = reopenedIssue;
1633
- await logActivity(db, {
1634
- orgId: currentIssue.orgId,
1635
- actorType: actor.actorType,
1636
- actorId: actor.actorId,
1637
- agentId: actor.agentId,
1638
- runId: actor.runId,
1639
- action: "issue.updated",
1640
- entityType: "issue",
1641
- entityId: currentIssue.id,
1642
- details: {
1643
- status: "todo",
1644
- reopened: true,
1645
- reopenedFrom: reopenFromStatus,
1646
- source: "comment",
1647
- identifier: currentIssue.identifier,
1648
- },
1649
- });
1650
- }
1651
- if (interruptRequested) {
1652
- if (req.actor.type !== "board") {
1653
- res.status(403).json({ error: "Only board users can interrupt active runs from issue comments" });
1654
- return;
1655
- }
1656
- let runToInterrupt = currentIssue.executionRunId
1657
- ? await heartbeat.getRun(currentIssue.executionRunId)
1658
- : null;
1659
- if ((!runToInterrupt || runToInterrupt.status !== "running") &&
1660
- currentIssue.assigneeAgentId) {
1661
- const activeRun = await heartbeat.getActiveRunForAgent(currentIssue.assigneeAgentId);
1662
- const activeIssueId = activeRun &&
1663
- activeRun.contextSnapshot &&
1664
- typeof activeRun.contextSnapshot === "object" &&
1665
- typeof activeRun.contextSnapshot.issueId === "string"
1666
- ? activeRun.contextSnapshot.issueId
1667
- : null;
1668
- if (activeRun && activeRun.status === "running" && activeIssueId === currentIssue.id) {
1669
- runToInterrupt = activeRun;
1670
- }
1671
- }
1672
- if (runToInterrupt && runToInterrupt.status === "running") {
1673
- const cancelled = await heartbeat.cancelRun(runToInterrupt.id);
1674
- if (cancelled) {
1675
- interruptedRunId = cancelled.id;
1676
- await logActivity(db, {
1677
- orgId: cancelled.orgId,
1678
- actorType: actor.actorType,
1679
- actorId: actor.actorId,
1680
- agentId: actor.agentId,
1681
- runId: actor.runId,
1682
- action: "heartbeat.cancelled",
1683
- entityType: "heartbeat_run",
1684
- entityId: cancelled.id,
1685
- details: { agentId: cancelled.agentId, source: "issue_comment_interrupt", issueId: currentIssue.id },
1686
- });
1687
- }
1688
- }
1689
- }
1690
- const comment = await svc.addComment(id, req.body.body, {
1691
- agentId: actor.agentId ?? undefined,
1692
- userId: actor.actorType === "user" ? actor.actorId : undefined,
1693
- });
1694
- if (commentRun.runId) {
1695
- await heartbeat.reportRunActivity(commentRun.runId).catch((err) => logger.warn({ err, runId: commentRun.runId }, "failed to clear detached run warning after issue comment"));
1696
- }
1697
- await logActivity(db, {
1698
- orgId: currentIssue.orgId,
1699
- actorType: actor.actorType,
1700
- actorId: actor.actorId,
1701
- agentId: actor.agentId,
1702
- runId: commentRun.runId,
1703
- action: "issue.comment_added",
1704
- entityType: "issue",
1705
- entityId: currentIssue.id,
1706
- details: {
1707
- commentId: comment.id,
1708
- bodySnippet: comment.body.slice(0, 120),
1709
- identifier: currentIssue.identifier,
1710
- issueTitle: currentIssue.title,
1711
- ...(reopened ? { reopened: true, reopenedFrom: reopenFromStatus, source: "comment" } : {}),
1712
- ...(interruptedRunId ? { interruptedRunId } : {}),
1713
- },
1714
- });
1715
- // Merge all wakeups from this comment into one enqueue per agent to avoid duplicate runs.
1716
- void (async () => {
1717
- const wakeups = new Map();
1718
- const assigneeId = currentIssue.assigneeAgentId;
1719
- const actorIsAgent = actor.actorType === "agent";
1720
- const selfComment = actorIsAgent && actor.actorId === assigneeId;
1721
- const backlogComment = currentIssue.status === "backlog";
1722
- const skipWake = selfComment || isClosed || backlogComment;
1723
- if (assigneeId && (reopened || !skipWake)) {
1724
- if (reopened) {
1725
- wakeups.set(assigneeId, {
1726
- source: "automation",
1727
- triggerDetail: "system",
1728
- reason: "issue_reopened_via_comment",
1729
- payload: {
1730
- issueId: currentIssue.id,
1731
- commentId: comment.id,
1732
- reopenedFrom: reopenFromStatus,
1733
- mutation: "comment",
1734
- ...(interruptedRunId ? { interruptedRunId } : {}),
1735
- },
1736
- requestedByActorType: actor.actorType,
1737
- requestedByActorId: actor.actorId,
1738
- contextSnapshot: {
1739
- issueId: currentIssue.id,
1740
- taskId: currentIssue.id,
1741
- commentId: comment.id,
1742
- source: "issue.comment.reopen",
1743
- wakeReason: "issue_reopened_via_comment",
1744
- reopenedFrom: reopenFromStatus,
1745
- ...(interruptedRunId ? { interruptedRunId } : {}),
1746
- },
1747
- });
1748
- }
1749
- else {
1750
- wakeups.set(assigneeId, {
1751
- source: "automation",
1752
- triggerDetail: "system",
1753
- reason: "issue_commented",
1754
- payload: {
1755
- issueId: currentIssue.id,
1756
- commentId: comment.id,
1757
- mutation: "comment",
1758
- ...(interruptedRunId ? { interruptedRunId } : {}),
1759
- },
1760
- requestedByActorType: actor.actorType,
1761
- requestedByActorId: actor.actorId,
1762
- contextSnapshot: {
1763
- issueId: currentIssue.id,
1764
- taskId: currentIssue.id,
1765
- commentId: comment.id,
1766
- wakeCommentId: comment.id,
1767
- source: "issue.comment",
1768
- wakeReason: "issue_commented",
1769
- issue: {
1770
- id: currentIssue.id,
1771
- title: currentIssue.title,
1772
- description: currentIssue.description,
1773
- status: currentIssue.status,
1774
- priority: currentIssue.priority,
1775
- },
1776
- comment: {
1777
- id: comment.id,
1778
- body: comment.body,
1779
- authorAgentId: comment.authorAgentId,
1780
- authorUserId: comment.authorUserId,
1781
- },
1782
- ...(interruptedRunId ? { interruptedRunId } : {}),
1783
- },
1784
- });
1785
- }
1786
- }
1787
- let mentionedIds = [];
1788
- try {
1789
- mentionedIds = await svc.findMentionedAgents(issue.orgId, req.body.body);
1790
- }
1791
- catch (err) {
1792
- logger.warn({ err, issueId: id }, "failed to resolve @-mentions");
1793
- }
1794
- for (const mentionedId of mentionedIds) {
1795
- if (wakeups.has(mentionedId))
1796
- continue;
1797
- if (actorIsAgent && actor.actorId === mentionedId)
1798
- continue;
1799
- wakeups.set(mentionedId, {
1800
- source: "automation",
1801
- triggerDetail: "system",
1802
- reason: "issue_comment_mentioned",
1803
- payload: { issueId: id, commentId: comment.id },
1804
- requestedByActorType: actor.actorType,
1805
- requestedByActorId: actor.actorId,
1806
- contextSnapshot: {
1807
- issueId: id,
1808
- taskId: id,
1809
- commentId: comment.id,
1810
- wakeCommentId: comment.id,
1811
- wakeReason: "issue_comment_mentioned",
1812
- wakeSource: "comment.mention",
1813
- source: "comment.mention",
1814
- issue: {
1815
- id: currentIssue.id,
1816
- title: currentIssue.title,
1817
- description: currentIssue.description,
1818
- status: currentIssue.status,
1819
- priority: currentIssue.priority,
1820
- },
1821
- comment: {
1822
- id: comment.id,
1823
- body: comment.body,
1824
- authorAgentId: comment.authorAgentId,
1825
- authorUserId: comment.authorUserId,
1826
- },
1827
- },
1828
- });
1829
- }
1830
- for (const [agentId, wakeup] of wakeups.entries()) {
1831
- heartbeat
1832
- .wakeup(agentId, wakeup)
1833
- .catch((err) => logger.warn({ err, issueId: currentIssue.id, agentId }, "failed to wake agent on issue comment"));
1834
- }
1835
- })();
1836
- res.status(201).json(comment);
1837
- });
1838
- router.get("/issues/:id/attachments", async (req, res) => {
1839
- const issueId = req.params.id;
1840
- const issue = await svc.getById(issueId);
1841
- if (!issue) {
1842
- res.status(404).json({ error: "Issue not found" });
1843
- return;
1844
- }
1845
- assertCompanyAccess(req, issue.orgId);
1846
- const attachments = await svc.listAttachments(issueId);
1847
- res.json(attachments.map(withContentPath));
1848
- });
1849
- router.post("/orgs/:orgId/issues/:issueId/attachments", async (req, res) => {
1850
- const orgId = req.params.orgId;
1851
- const issueId = req.params.issueId;
1852
- assertCompanyAccess(req, orgId);
1853
- const issue = await svc.getById(issueId);
1854
- if (!issue) {
1855
- res.status(404).json({ error: "Issue not found" });
1856
- return;
1857
- }
1858
- if (issue.orgId !== orgId) {
1859
- res.status(422).json({ error: "Issue does not belong to organization" });
1860
- return;
1861
- }
1862
- try {
1863
- await runSingleFileUpload(req, res);
1864
- }
1865
- catch (err) {
1866
- if (err instanceof multer.MulterError) {
1867
- if (err.code === "LIMIT_FILE_SIZE") {
1868
- res.status(422).json({ error: `Attachment exceeds ${MAX_ATTACHMENT_BYTES} bytes` });
1869
- return;
1870
- }
1871
- res.status(400).json({ error: err.message });
1872
- return;
1873
- }
1874
- throw err;
1875
- }
1876
- const file = req.file;
1877
- if (!file) {
1878
- res.status(400).json({ error: "Missing file field 'file'" });
1879
- return;
1880
- }
1881
- const contentType = (file.mimetype || "").toLowerCase();
1882
- if (!isAllowedContentType(contentType)) {
1883
- res.status(422).json({ error: `Unsupported attachment type: ${contentType || "unknown"}` });
1884
- return;
1885
- }
1886
- if (file.buffer.length <= 0) {
1887
- res.status(422).json({ error: "Attachment is empty" });
1888
- return;
1889
- }
1890
- const parsedMeta = createIssueAttachmentMetadataSchema.safeParse(req.body ?? {});
1891
- if (!parsedMeta.success) {
1892
- res.status(400).json({ error: "Invalid attachment metadata", details: parsedMeta.error.issues });
1893
- return;
1894
- }
1895
- const actor = getActorInfo(req);
1896
- const stored = await storage.putFile({
1897
- orgId,
1898
- namespace: `issues/${issueId}`,
1899
- originalFilename: file.originalname || null,
1900
- contentType,
1901
- body: file.buffer,
1902
- });
1903
- const attachment = await svc.createAttachment({
1904
- issueId,
1905
- issueCommentId: parsedMeta.data.issueCommentId ?? null,
1906
- usage: parsedMeta.data.usage ?? "issue",
1907
- provider: stored.provider,
1908
- objectKey: stored.objectKey,
1909
- contentType: stored.contentType,
1910
- byteSize: stored.byteSize,
1911
- sha256: stored.sha256,
1912
- originalFilename: stored.originalFilename,
1913
- createdByAgentId: actor.agentId,
1914
- createdByUserId: actor.actorType === "user" ? actor.actorId : null,
1915
- });
1916
- if (attachment.usage === "issue") {
1917
- await logActivity(db, {
1918
- orgId,
1919
- actorType: actor.actorType,
1920
- actorId: actor.actorId,
1921
- agentId: actor.agentId,
1922
- runId: actor.runId,
1923
- action: "issue.attachment_added",
1924
- entityType: "issue",
1925
- entityId: issueId,
1926
- details: {
1927
- attachmentId: attachment.id,
1928
- usage: attachment.usage,
1929
- originalFilename: attachment.originalFilename,
1930
- contentType: attachment.contentType,
1931
- byteSize: attachment.byteSize,
1932
- },
1933
- });
1934
- }
1935
- res.status(201).json(withContentPath(attachment));
1936
- });
1937
- router.post("/orgs/:orgId/issues/:issueId/attachments/workspace-file", validate(createIssueWorkspaceAttachmentSchema), async (req, res) => {
1938
- const orgId = req.params.orgId;
1939
- const issueId = req.params.issueId;
1940
- assertCompanyAccess(req, orgId);
1941
- const issue = await svc.getById(issueId);
1942
- if (!issue) {
1943
- res.status(404).json({ error: "Issue not found" });
1944
- return;
1945
- }
1946
- if (issue.orgId !== orgId) {
1947
- res.status(422).json({ error: "Issue does not belong to organization" });
1948
- return;
1949
- }
1950
- const workspaceFile = await workspaceBrowser.readAttachmentFile(orgId, req.body.path);
1951
- if (workspaceFile.buffer.length <= 0) {
1952
- res.status(422).json({ error: "Attachment is empty" });
1953
- return;
1954
- }
1955
- if (workspaceFile.buffer.length > MAX_ATTACHMENT_BYTES) {
1956
- res.status(422).json({ error: `Attachment exceeds ${MAX_ATTACHMENT_BYTES} bytes` });
1957
- return;
1958
- }
1959
- if (!isAllowedContentType(workspaceFile.contentType)) {
1960
- res.status(422).json({ error: `Unsupported attachment type: ${workspaceFile.contentType || "unknown"}` });
1961
- return;
1962
- }
1963
- const actor = getActorInfo(req);
1964
- const stored = await storage.putFile({
1965
- orgId,
1966
- namespace: `issues/${issueId}`,
1967
- originalFilename: workspaceFile.originalFilename,
1968
- contentType: workspaceFile.contentType,
1969
- body: workspaceFile.buffer,
1970
- });
1971
- const attachment = await svc.createAttachment({
1972
- issueId,
1973
- issueCommentId: null,
1974
- usage: "issue",
1975
- provider: stored.provider,
1976
- objectKey: stored.objectKey,
1977
- contentType: stored.contentType,
1978
- byteSize: stored.byteSize,
1979
- sha256: stored.sha256,
1980
- originalFilename: stored.originalFilename,
1981
- createdByAgentId: actor.agentId,
1982
- createdByUserId: actor.actorType === "user" ? actor.actorId : null,
1983
- });
1984
- await logActivity(db, {
1985
- orgId,
1986
- actorType: actor.actorType,
1987
- actorId: actor.actorId,
1988
- agentId: actor.agentId,
1989
- runId: actor.runId,
1990
- action: "issue.attachment_added",
1991
- entityType: "issue",
1992
- entityId: issueId,
1993
- details: {
1994
- attachmentId: attachment.id,
1995
- usage: attachment.usage,
1996
- originalFilename: attachment.originalFilename,
1997
- contentType: attachment.contentType,
1998
- byteSize: attachment.byteSize,
1999
- workspacePath: workspaceFile.normalizedPath,
2000
- },
2001
- });
2002
- res.status(201).json(withContentPath(attachment));
2003
- });
2004
- router.get("/attachments/:attachmentId/content", async (req, res, next) => {
2005
- const attachmentId = req.params.attachmentId;
2006
- const attachment = await svc.getAttachmentById(attachmentId);
2007
- if (!attachment) {
2008
- res.status(404).json({ error: "Attachment not found" });
2009
- return;
2010
- }
2011
- assertCompanyAccess(req, attachment.orgId);
2012
- const object = await storage.getObject(attachment.orgId, attachment.objectKey);
2013
- res.setHeader("Content-Type", attachment.contentType || object.contentType || "application/octet-stream");
2014
- res.setHeader("Content-Length", String(attachment.byteSize || object.contentLength || 0));
2015
- res.setHeader("Cache-Control", "private, max-age=60");
2016
- const filename = attachment.originalFilename ?? "attachment";
2017
- res.setHeader("Content-Disposition", `inline; filename=\"${filename.replaceAll("\"", "")}\"`);
2018
- object.stream.on("error", (err) => {
2019
- next(err);
2020
- });
2021
- object.stream.pipe(res);
2022
- });
2023
- router.delete("/attachments/:attachmentId", async (req, res) => {
2024
- const attachmentId = req.params.attachmentId;
2025
- const attachment = await svc.getAttachmentById(attachmentId);
2026
- if (!attachment) {
2027
- res.status(404).json({ error: "Attachment not found" });
2028
- return;
2029
- }
2030
- assertCompanyAccess(req, attachment.orgId);
2031
- try {
2032
- await storage.deleteObject(attachment.orgId, attachment.objectKey);
2033
- }
2034
- catch (err) {
2035
- logger.warn({ err, attachmentId }, "storage delete failed while removing attachment");
2036
- }
2037
- const removed = await svc.removeAttachment(attachmentId);
2038
- if (!removed) {
2039
- res.status(404).json({ error: "Attachment not found" });
2040
- return;
2041
- }
2042
- const actor = getActorInfo(req);
2043
- await logActivity(db, {
2044
- orgId: removed.orgId,
2045
- actorType: actor.actorType,
2046
- actorId: actor.actorId,
2047
- agentId: actor.agentId,
2048
- runId: actor.runId,
2049
- action: "issue.attachment_removed",
2050
- entityType: "issue",
2051
- entityId: removed.issueId,
2052
- details: {
2053
- attachmentId: removed.id,
2054
- },
2055
- });
2056
- res.json({ ok: true });
934
+ registerIssueMutationRoutes({
935
+ router,
936
+ db,
937
+ storage,
938
+ svc,
939
+ access,
940
+ agentsSvc,
941
+ projectsSvc,
942
+ goalsSvc,
943
+ heartbeat,
944
+ issueApprovalsSvc,
945
+ automationsSvc,
946
+ executionWorkspacesSvc,
947
+ assertCanAssignTasks,
948
+ boardUserId,
949
+ assertAgentRunCheckoutOwnership,
950
+ resolveAgentIssueRunId,
951
+ requireAgentRunId,
952
+ issueHasReviewer,
953
+ isReviewerAgentForIssue,
954
+ canAgentCompleteIssue,
955
+ statusForReviewDecision,
956
+ statusAcceptsReviewerDecision,
957
+ reviewerDecisionRequiresHumanHandoff,
958
+ commitSubject,
959
+ });
960
+ registerIssueCommentAttachmentRoutes({
961
+ router,
962
+ db,
963
+ storage,
964
+ svc,
965
+ runSingleFileUpload,
966
+ withContentPath,
967
+ MAX_ISSUE_COMMENT_LIMIT,
968
+ heartbeat,
969
+ workspaceBrowser,
970
+ assertAgentRunCheckoutOwnership,
971
+ resolveAgentIssueRunId,
2057
972
  });
2058
973
  return router;
2059
974
  }