@rudderhq/server 0.5.2-canary.2 → 0.6.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 (386) hide show
  1. package/dist/bootstrap/register-api-routes.d.ts.map +1 -1
  2. package/dist/bootstrap/register-api-routes.js +15 -0
  3. package/dist/bootstrap/register-api-routes.js.map +1 -1
  4. package/dist/bootstrap/types.d.ts +11 -0
  5. package/dist/bootstrap/types.d.ts.map +1 -1
  6. package/dist/bundled-plugins/plugin-linear/dist/worker.js +741 -7
  7. package/dist/bundled-plugins/plugin-linear/dist/worker.js.map +3 -3
  8. package/dist/config.d.ts +2 -0
  9. package/dist/config.d.ts.map +1 -1
  10. package/dist/config.js +3 -0
  11. package/dist/config.js.map +1 -1
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +28 -1
  14. package/dist/index.js.map +1 -1
  15. package/dist/local-runtime.d.ts +7 -0
  16. package/dist/local-runtime.d.ts.map +1 -1
  17. package/dist/local-runtime.js +15 -1
  18. package/dist/local-runtime.js.map +1 -1
  19. package/dist/middleware/auth.js +2 -2
  20. package/dist/middleware/auth.js.map +1 -1
  21. package/dist/middleware/error-handler.d.ts.map +1 -1
  22. package/dist/middleware/error-handler.js +3 -2
  23. package/dist/middleware/error-handler.js.map +1 -1
  24. package/dist/middleware/logger.d.ts +4 -0
  25. package/dist/middleware/logger.d.ts.map +1 -1
  26. package/dist/middleware/logger.js +82 -7
  27. package/dist/middleware/logger.js.map +1 -1
  28. package/dist/middleware/logger.test.js +94 -1
  29. package/dist/middleware/logger.test.js.map +1 -1
  30. package/dist/routes/chats.annotation-routes.d.ts +47 -0
  31. package/dist/routes/chats.annotation-routes.d.ts.map +1 -0
  32. package/dist/routes/chats.annotation-routes.js +164 -0
  33. package/dist/routes/chats.annotation-routes.js.map +1 -0
  34. package/dist/routes/chats.d.ts.map +1 -1
  35. package/dist/routes/chats.js +467 -165
  36. package/dist/routes/chats.js.map +1 -1
  37. package/dist/routes/chats.stream-routes.d.ts +1 -10
  38. package/dist/routes/chats.stream-routes.d.ts.map +1 -1
  39. package/dist/routes/chats.stream-routes.js +168 -110
  40. package/dist/routes/chats.stream-routes.js.map +1 -1
  41. package/dist/routes/chats.stream-support.d.ts +79 -0
  42. package/dist/routes/chats.stream-support.d.ts.map +1 -0
  43. package/dist/routes/chats.stream-support.js +121 -0
  44. package/dist/routes/chats.stream-support.js.map +1 -0
  45. package/dist/routes/issues.comments-attachments.d.ts.map +1 -1
  46. package/dist/routes/issues.comments-attachments.js +16 -2
  47. package/dist/routes/issues.comments-attachments.js.map +1 -1
  48. package/dist/routes/issues.d.ts.map +1 -1
  49. package/dist/routes/issues.js +21 -4
  50. package/dist/routes/issues.js.map +1 -1
  51. package/dist/routes/issues.mutations.d.ts.map +1 -1
  52. package/dist/routes/issues.mutations.js +20 -33
  53. package/dist/routes/issues.mutations.js.map +1 -1
  54. package/dist/routes/managed-mcp-agent-bindings.d.ts +15 -0
  55. package/dist/routes/managed-mcp-agent-bindings.d.ts.map +1 -0
  56. package/dist/routes/managed-mcp-agent-bindings.js +66 -0
  57. package/dist/routes/managed-mcp-agent-bindings.js.map +1 -0
  58. package/dist/routes/managed-mcp-connections.d.ts +8 -0
  59. package/dist/routes/managed-mcp-connections.d.ts.map +1 -0
  60. package/dist/routes/managed-mcp-connections.js +189 -0
  61. package/dist/routes/managed-mcp-connections.js.map +1 -0
  62. package/dist/routes/managed-mcp-runtime.d.ts +5 -0
  63. package/dist/routes/managed-mcp-runtime.d.ts.map +1 -0
  64. package/dist/routes/managed-mcp-runtime.js +148 -0
  65. package/dist/routes/managed-mcp-runtime.js.map +1 -0
  66. package/dist/routes/messenger.d.ts.map +1 -1
  67. package/dist/routes/messenger.js +1 -1
  68. package/dist/routes/messenger.js.map +1 -1
  69. package/dist/services/access.d.ts.map +1 -1
  70. package/dist/services/access.js +91 -12
  71. package/dist/services/access.js.map +1 -1
  72. package/dist/services/agent-run-context.d.ts +1 -0
  73. package/dist/services/agent-run-context.d.ts.map +1 -1
  74. package/dist/services/agent-run-context.js +5 -1
  75. package/dist/services/agent-run-context.js.map +1 -1
  76. package/dist/services/board-auth.d.ts +2 -2
  77. package/dist/services/chat-annotation-copy-lineage.d.ts +32 -0
  78. package/dist/services/chat-annotation-copy-lineage.d.ts.map +1 -0
  79. package/dist/services/chat-annotation-copy-lineage.js +110 -0
  80. package/dist/services/chat-annotation-copy-lineage.js.map +1 -0
  81. package/dist/services/chat-assistant.annotations.d.ts +16 -0
  82. package/dist/services/chat-assistant.annotations.d.ts.map +1 -0
  83. package/dist/services/chat-assistant.annotations.js +108 -0
  84. package/dist/services/chat-assistant.annotations.js.map +1 -0
  85. package/dist/services/chat-assistant.annotations.test.d.ts +2 -0
  86. package/dist/services/chat-assistant.annotations.test.d.ts.map +1 -0
  87. package/dist/services/chat-assistant.annotations.test.js +257 -0
  88. package/dist/services/chat-assistant.annotations.test.js.map +1 -0
  89. package/dist/services/chat-assistant.d.ts.map +1 -1
  90. package/dist/services/chat-assistant.helpers.d.ts +2 -3
  91. package/dist/services/chat-assistant.helpers.d.ts.map +1 -1
  92. package/dist/services/chat-assistant.helpers.js +37 -53
  93. package/dist/services/chat-assistant.helpers.js.map +1 -1
  94. package/dist/services/chat-assistant.js +61 -5
  95. package/dist/services/chat-assistant.js.map +1 -1
  96. package/dist/services/chat-generation-protocol.d.ts +8 -15
  97. package/dist/services/chat-generation-protocol.d.ts.map +1 -1
  98. package/dist/services/chat-generation-protocol.js +19 -59
  99. package/dist/services/chat-generation-protocol.js.map +1 -1
  100. package/dist/services/chat-generation-protocol.test.js +168 -6
  101. package/dist/services/chat-generation-protocol.test.js.map +1 -1
  102. package/dist/services/chat-generation-provenance.d.ts +14 -0
  103. package/dist/services/chat-generation-provenance.d.ts.map +1 -0
  104. package/dist/services/chat-generation-provenance.js +71 -0
  105. package/dist/services/chat-generation-provenance.js.map +1 -0
  106. package/dist/services/chat-inline-annotation-rendering.d.ts +2 -0
  107. package/dist/services/chat-inline-annotation-rendering.d.ts.map +1 -0
  108. package/dist/services/chat-inline-annotation-rendering.js +265 -0
  109. package/dist/services/chat-inline-annotation-rendering.js.map +1 -0
  110. package/dist/services/chat-inline-annotation-resolved-labels.d.ts +11 -0
  111. package/dist/services/chat-inline-annotation-resolved-labels.d.ts.map +1 -0
  112. package/dist/services/chat-inline-annotation-resolved-labels.js +490 -0
  113. package/dist/services/chat-inline-annotation-resolved-labels.js.map +1 -0
  114. package/dist/services/chat-inline-annotation-validation.d.ts +15 -0
  115. package/dist/services/chat-inline-annotation-validation.d.ts.map +1 -0
  116. package/dist/services/chat-inline-annotation-validation.js +430 -0
  117. package/dist/services/chat-inline-annotation-validation.js.map +1 -0
  118. package/dist/services/chat-inline-annotations.d.ts +18 -0
  119. package/dist/services/chat-inline-annotations.d.ts.map +1 -0
  120. package/dist/services/chat-inline-annotations.js +52 -0
  121. package/dist/services/chat-inline-annotations.js.map +1 -0
  122. package/dist/services/chat-inline-annotations.test.d.ts +2 -0
  123. package/dist/services/chat-inline-annotations.test.d.ts.map +1 -0
  124. package/dist/services/chat-inline-annotations.test.js +1306 -0
  125. package/dist/services/chat-inline-annotations.test.js.map +1 -0
  126. package/dist/services/chat-queued-message-materialization.d.ts +126 -0
  127. package/dist/services/chat-queued-message-materialization.d.ts.map +1 -0
  128. package/dist/services/chat-queued-message-materialization.js +327 -0
  129. package/dist/services/chat-queued-message-materialization.js.map +1 -0
  130. package/dist/services/chat-steer-messages.d.ts +3 -3
  131. package/dist/services/chat-steer-messages.d.ts.map +1 -1
  132. package/dist/services/chat-steer-messages.js +50 -80
  133. package/dist/services/chat-steer-messages.js.map +1 -1
  134. package/dist/services/chat-work-manifest.d.ts.map +1 -1
  135. package/dist/services/chat-work-manifest.js +5 -1
  136. package/dist/services/chat-work-manifest.js.map +1 -1
  137. package/dist/services/chats.annotation-persistence.d.ts +20 -0
  138. package/dist/services/chats.annotation-persistence.d.ts.map +1 -0
  139. package/dist/services/chats.annotation-persistence.js +206 -0
  140. package/dist/services/chats.annotation-persistence.js.map +1 -0
  141. package/dist/services/chats.d.ts +129 -96
  142. package/dist/services/chats.d.ts.map +1 -1
  143. package/dist/services/chats.helpers.d.ts +2 -2
  144. package/dist/services/chats.inline-visual-persistence.d.ts +3 -3
  145. package/dist/services/chats.inline-visual-persistence.d.ts.map +1 -1
  146. package/dist/services/chats.inline-visual-persistence.js +114 -54
  147. package/dist/services/chats.inline-visual-persistence.js.map +1 -1
  148. package/dist/services/chats.js +743 -252
  149. package/dist/services/chats.js.map +1 -1
  150. package/dist/services/finance.d.ts +1 -1
  151. package/dist/services/index.d.ts +8 -0
  152. package/dist/services/index.d.ts.map +1 -1
  153. package/dist/services/index.js +8 -0
  154. package/dist/services/index.js.map +1 -1
  155. package/dist/services/integrations/agent-integrations.d.ts +2 -2
  156. package/dist/services/integrations/custom-integrations.d.ts +3 -1
  157. package/dist/services/integrations/custom-integrations.d.ts.map +1 -1
  158. package/dist/services/integrations/custom-integrations.js +13 -2
  159. package/dist/services/integrations/custom-integrations.js.map +1 -1
  160. package/dist/services/issues.comments-attachments.d.ts +7 -1
  161. package/dist/services/issues.comments-attachments.d.ts.map +1 -1
  162. package/dist/services/issues.comments-attachments.js +13 -1
  163. package/dist/services/issues.comments-attachments.js.map +1 -1
  164. package/dist/services/issues.d.ts +6 -0
  165. package/dist/services/issues.d.ts.map +1 -1
  166. package/dist/services/issues.js +22 -3
  167. package/dist/services/issues.js.map +1 -1
  168. package/dist/services/knowledge-portability/organization-portability.core.d.ts +4 -1
  169. package/dist/services/knowledge-portability/organization-portability.core.d.ts.map +1 -1
  170. package/dist/services/knowledge-portability/organization-portability.core.js +17 -3
  171. package/dist/services/knowledge-portability/organization-portability.core.js.map +1 -1
  172. package/dist/services/knowledge-portability/organization-portability.export.d.ts.map +1 -1
  173. package/dist/services/knowledge-portability/organization-portability.export.js +2 -2
  174. package/dist/services/knowledge-portability/organization-portability.export.js.map +1 -1
  175. package/dist/services/knowledge-portability/organization-portability.import.d.ts.map +1 -1
  176. package/dist/services/knowledge-portability/organization-portability.import.js +13 -3
  177. package/dist/services/knowledge-portability/organization-portability.import.js.map +1 -1
  178. package/dist/services/knowledge-portability/organization-skills.catalog.d.ts +3 -0
  179. package/dist/services/knowledge-portability/organization-skills.catalog.d.ts.map +1 -1
  180. package/dist/services/knowledge-portability/organization-skills.catalog.js +54 -23
  181. package/dist/services/knowledge-portability/organization-skills.catalog.js.map +1 -1
  182. package/dist/services/knowledge-portability/organization-skills.d.ts.map +1 -1
  183. package/dist/services/knowledge-portability/organization-skills.js +627 -124
  184. package/dist/services/knowledge-portability/organization-skills.js.map +1 -1
  185. package/dist/services/knowledge-portability/organization-skills.sources.d.ts +1 -0
  186. package/dist/services/knowledge-portability/organization-skills.sources.d.ts.map +1 -1
  187. package/dist/services/knowledge-portability/organization-skills.sources.js +53 -10
  188. package/dist/services/knowledge-portability/organization-skills.sources.js.map +1 -1
  189. package/dist/services/mcp/authorizer-lock.d.ts +9 -0
  190. package/dist/services/mcp/authorizer-lock.d.ts.map +1 -0
  191. package/dist/services/mcp/authorizer-lock.js +15 -0
  192. package/dist/services/mcp/authorizer-lock.js.map +1 -0
  193. package/dist/services/mcp/managed-bindings.d.ts +17 -0
  194. package/dist/services/mcp/managed-bindings.d.ts.map +1 -0
  195. package/dist/services/mcp/managed-bindings.js +332 -0
  196. package/dist/services/mcp/managed-bindings.js.map +1 -0
  197. package/dist/services/mcp/managed-client.d.ts +64 -0
  198. package/dist/services/mcp/managed-client.d.ts.map +1 -0
  199. package/dist/services/mcp/managed-client.js +238 -0
  200. package/dist/services/mcp/managed-client.js.map +1 -0
  201. package/dist/services/mcp/managed-client.test.d.ts +2 -0
  202. package/dist/services/mcp/managed-client.test.d.ts.map +1 -0
  203. package/dist/services/mcp/managed-client.test.js +398 -0
  204. package/dist/services/mcp/managed-client.test.js.map +1 -0
  205. package/dist/services/mcp/managed-connections.d.ts +81 -0
  206. package/dist/services/mcp/managed-connections.d.ts.map +1 -0
  207. package/dist/services/mcp/managed-connections.js +920 -0
  208. package/dist/services/mcp/managed-connections.js.map +1 -0
  209. package/dist/services/mcp/managed-runtime.d.ts +134 -0
  210. package/dist/services/mcp/managed-runtime.d.ts.map +1 -0
  211. package/dist/services/mcp/managed-runtime.js +282 -0
  212. package/dist/services/mcp/managed-runtime.js.map +1 -0
  213. package/dist/services/mcp/oauth-provider.d.ts +48 -0
  214. package/dist/services/mcp/oauth-provider.d.ts.map +1 -0
  215. package/dist/services/mcp/oauth-provider.js +120 -0
  216. package/dist/services/mcp/oauth-provider.js.map +1 -0
  217. package/dist/services/mcp/oauth-scope.test.d.ts +2 -0
  218. package/dist/services/mcp/oauth-scope.test.d.ts.map +1 -0
  219. package/dist/services/mcp/oauth-scope.test.js +69 -0
  220. package/dist/services/mcp/oauth-scope.test.js.map +1 -0
  221. package/dist/services/mcp/oauth-session-gc.d.ts +14 -0
  222. package/dist/services/mcp/oauth-session-gc.d.ts.map +1 -0
  223. package/dist/services/mcp/oauth-session-gc.js +34 -0
  224. package/dist/services/mcp/oauth-session-gc.js.map +1 -0
  225. package/dist/services/mcp/oauth-session-gc.test.d.ts +2 -0
  226. package/dist/services/mcp/oauth-session-gc.test.d.ts.map +1 -0
  227. package/dist/services/mcp/oauth-session-gc.test.js +66 -0
  228. package/dist/services/mcp/oauth-session-gc.test.js.map +1 -0
  229. package/dist/services/mcp/oauth.d.ts +61 -0
  230. package/dist/services/mcp/oauth.d.ts.map +1 -0
  231. package/dist/services/mcp/oauth.js +1690 -0
  232. package/dist/services/mcp/oauth.js.map +1 -0
  233. package/dist/services/mcp/pinned-fetch.d.ts +15 -0
  234. package/dist/services/mcp/pinned-fetch.d.ts.map +1 -0
  235. package/dist/services/mcp/pinned-fetch.js +400 -0
  236. package/dist/services/mcp/pinned-fetch.js.map +1 -0
  237. package/dist/services/mcp/pinned-fetch.test.d.ts +2 -0
  238. package/dist/services/mcp/pinned-fetch.test.d.ts.map +1 -0
  239. package/dist/services/mcp/pinned-fetch.test.js +190 -0
  240. package/dist/services/mcp/pinned-fetch.test.js.map +1 -0
  241. package/dist/services/mcp/provider-registry.d.ts +77 -0
  242. package/dist/services/mcp/provider-registry.d.ts.map +1 -0
  243. package/dist/services/mcp/provider-registry.js +80 -0
  244. package/dist/services/mcp/provider-registry.js.map +1 -0
  245. package/dist/services/mcp/provider-registry.test.d.ts +2 -0
  246. package/dist/services/mcp/provider-registry.test.d.ts.map +1 -0
  247. package/dist/services/mcp/provider-registry.test.js +80 -0
  248. package/dist/services/mcp/provider-registry.test.js.map +1 -0
  249. package/dist/services/mcp/security-policy.d.ts +48 -0
  250. package/dist/services/mcp/security-policy.d.ts.map +1 -0
  251. package/dist/services/mcp/security-policy.js +344 -0
  252. package/dist/services/mcp/security-policy.js.map +1 -0
  253. package/dist/services/mcp/security-policy.test.d.ts +2 -0
  254. package/dist/services/mcp/security-policy.test.d.ts.map +1 -0
  255. package/dist/services/mcp/security-policy.test.js +226 -0
  256. package/dist/services/mcp/security-policy.test.js.map +1 -0
  257. package/dist/services/mcp/tool-discovery.d.ts +49 -0
  258. package/dist/services/mcp/tool-discovery.d.ts.map +1 -0
  259. package/dist/services/mcp/tool-discovery.js +198 -0
  260. package/dist/services/mcp/tool-discovery.js.map +1 -0
  261. package/dist/services/mcp/tool-discovery.test.d.ts +2 -0
  262. package/dist/services/mcp/tool-discovery.test.d.ts.map +1 -0
  263. package/dist/services/mcp/tool-discovery.test.js +168 -0
  264. package/dist/services/mcp/tool-discovery.test.js.map +1 -0
  265. package/dist/services/messenger.d.ts +1 -1
  266. package/dist/services/messenger.d.ts.map +1 -1
  267. package/dist/services/messenger.js +21 -3
  268. package/dist/services/messenger.js.map +1 -1
  269. package/dist/services/plugin-registry.d.ts +3 -3
  270. package/dist/services/runtime-kernel/heartbeat.core.d.ts +1 -1
  271. package/dist/services/runtime-kernel/heartbeat.core.d.ts.map +1 -1
  272. package/dist/services/runtime-kernel/heartbeat.core.js +30 -4
  273. package/dist/services/runtime-kernel/heartbeat.core.js.map +1 -1
  274. package/dist/services/runtime-kernel/heartbeat.wakeup.d.ts.map +1 -1
  275. package/dist/services/runtime-kernel/heartbeat.wakeup.js +38 -23
  276. package/dist/services/runtime-kernel/heartbeat.wakeup.js.map +1 -1
  277. package/dist/services/secrets.d.ts +64 -6
  278. package/dist/services/secrets.d.ts.map +1 -1
  279. package/dist/services/secrets.js +38 -15
  280. package/dist/services/secrets.js.map +1 -1
  281. package/dist/services/side-chats.d.ts +1 -1
  282. package/dist/services/side-chats.d.ts.map +1 -1
  283. package/dist/services/side-chats.js +56 -14
  284. package/dist/services/side-chats.js.map +1 -1
  285. package/dist/services/workspace-runtime.services.d.ts +2 -2
  286. package/dist/services/workspace-web-preview.js +1 -1
  287. package/package.json +16 -13
  288. package/resources/bundled-skills/rudder-docs/references/operating-practices.md +12 -5
  289. package/skills/rudder-docs/references/operating-practices.md +12 -5
  290. package/ui-dist/assets/LibraryLiveSurface-CKOthw54.js +1 -0
  291. package/ui-dist/assets/{abnfDiagram-VRR7QNED-KGtP7zoC.js → abnfDiagram-VRR7QNED-C6a_wMho.js} +1 -1
  292. package/ui-dist/assets/{arc-JjnKyiOb.js → arc-CP0En6KA.js} +1 -1
  293. package/ui-dist/assets/architectureDiagram-ZJ3FMSHR-AHfOdcaZ.js +36 -0
  294. package/ui-dist/assets/{blockDiagram-677ZJIJ3-D7YzMYrz.js → blockDiagram-677ZJIJ3-Bkl9lp63.js} +1 -1
  295. package/ui-dist/assets/{c4Diagram-LMCZKHZV-Dpf7UQxl.js → c4Diagram-LMCZKHZV-BpZareVt.js} +1 -1
  296. package/ui-dist/assets/channel-DqxL_Zcy.js +1 -0
  297. package/ui-dist/assets/{chunk-2Q5K7J3B-UJ3gsGlQ.js → chunk-2Q5K7J3B-BQAwVuXc.js} +1 -1
  298. package/ui-dist/assets/{chunk-32BRIVSS-BeHl2j2l.js → chunk-32BRIVSS-YkmfA08F.js} +1 -1
  299. package/ui-dist/assets/{chunk-5VM5RSS4-STE9fJd0.js → chunk-5VM5RSS4-DvvjwTnG.js} +1 -1
  300. package/ui-dist/assets/{chunk-EX3LRPZG-Bj8g8j4B.js → chunk-EX3LRPZG-TXD_Yhg5.js} +1 -1
  301. package/ui-dist/assets/{chunk-JWPE2WC7-0V4Pwta-.js → chunk-JWPE2WC7-CgMlj6XI.js} +1 -1
  302. package/ui-dist/assets/{chunk-MOJQB5TN-DNPn50gm.js → chunk-MOJQB5TN-BnJjU8Rc.js} +1 -1
  303. package/ui-dist/assets/{chunk-RYQCIY6F-Befmnv8r.js → chunk-RYQCIY6F-D9B64JKM.js} +1 -1
  304. package/ui-dist/assets/{chunk-V7JOEXUC-Boh8TjHj.js → chunk-V7JOEXUC-PS09QVpl.js} +1 -1
  305. package/ui-dist/assets/{chunk-VR4S4FIN-B_yEQSo3.js → chunk-VR4S4FIN-DQXb7pRS.js} +1 -1
  306. package/ui-dist/assets/{chunk-XXDRQBXY-Cf1GnUFx.js → chunk-XXDRQBXY-B1UdLiF2.js} +1 -1
  307. package/ui-dist/assets/classDiagram-OUVF2IWQ-DMwjzuJE.js +1 -0
  308. package/ui-dist/assets/classDiagram-v2-EOCWNBFH-DMwjzuJE.js +1 -0
  309. package/ui-dist/assets/cose-bilkent-JH36ORCC-lqqmMB3m.js +1 -0
  310. package/ui-dist/assets/{cynefin-VYW2F7L2-Dv1yELYC.js → cynefin-VYW2F7L2-C9hu0XqZ.js} +1 -1
  311. package/ui-dist/assets/{cynefinDiagram-TSTJHNR4-CeFwjFoZ.js → cynefinDiagram-TSTJHNR4-Uuhz4c-1.js} +1 -1
  312. package/ui-dist/assets/{dagre-VKFMJZFB-8idd_4sX.js → dagre-VKFMJZFB-ChDrQYse.js} +1 -1
  313. package/ui-dist/assets/{diagram-FQU43EPY-BBVua6hM.js → diagram-FQU43EPY-DLtjkVSN.js} +1 -1
  314. package/ui-dist/assets/{diagram-G47NLZAW-D1zLQZlp.js → diagram-G47NLZAW-DCvEm4D0.js} +1 -1
  315. package/ui-dist/assets/{diagram-NH7WQ7WH-DE_FTSNv.js → diagram-NH7WQ7WH-Dz5jlsPG.js} +1 -1
  316. package/ui-dist/assets/{diagram-OA4YK3LP-61VSKBEn.js → diagram-OA4YK3LP-BXmQLO-Q.js} +1 -1
  317. package/ui-dist/assets/{diagram-WEI45ONY-C-tED6BJ.js → diagram-WEI45ONY-JnMU3xrx.js} +1 -1
  318. package/ui-dist/assets/{ebnfDiagram-CCIWWBDH-BEB8WGt0.js → ebnfDiagram-CCIWWBDH-Kryuqj4r.js} +1 -1
  319. package/ui-dist/assets/{erDiagram-Q63AITRT-AN_jm1TZ.js → erDiagram-Q63AITRT-Bn7g2mSW.js} +1 -1
  320. package/ui-dist/assets/{flowDiagram-23GEKE2U-BwLC6tI7.js → flowDiagram-23GEKE2U-DcjqTK91.js} +4 -4
  321. package/ui-dist/assets/ganttDiagram-NO4QXBWP-BRkw9-GE.js +292 -0
  322. package/ui-dist/assets/{gitGraphDiagram-IHSO6WYX-D9HCL9rY.js → gitGraphDiagram-IHSO6WYX-Cexcp6kR.js} +1 -1
  323. package/ui-dist/assets/graph-Cu7OV4Oi.js +1 -0
  324. package/ui-dist/assets/{index-BQXnpwke.js → index-ASZMPBYm.js} +5 -5
  325. package/ui-dist/assets/{index-5OM32_rf.js → index-B17BqCTj.js} +1 -1
  326. package/ui-dist/assets/{index-Cf0gLy6a.js → index-B9MccU1g.js} +1 -1
  327. package/ui-dist/assets/{index-BoSJ4Rx1.js → index-BPqbtW_X.js} +1 -1
  328. package/ui-dist/assets/{index-CpIdZ4-7.js → index-C2HjDa98.js} +1 -1
  329. package/ui-dist/assets/index-CMPtpj0A.js +1 -0
  330. package/ui-dist/assets/{index-p0ZGU1_7.js → index-CZXgOYtk.js} +1 -1
  331. package/ui-dist/assets/{index-74cx3ESJ.js → index-CknAauOP.js} +1 -1
  332. package/ui-dist/assets/index-Cu1GuH0a.js +1 -0
  333. package/ui-dist/assets/index-D8PdEImm.css +1 -0
  334. package/ui-dist/assets/{index-DrVDiJhe.js → index-DB1YX9-p.js} +1 -1
  335. package/ui-dist/assets/{index-vHMnAQSb.js → index-DBxLZXK7.js} +624 -610
  336. package/ui-dist/assets/{index-CpEfk5zb.js → index-DOCVOD97.js} +1 -1
  337. package/ui-dist/assets/{index-DMuWG0YN.js → index-DUbakRAC.js} +1 -1
  338. package/ui-dist/assets/{index-AT8juaIi.js → index-Dbg1ZwO2.js} +1 -1
  339. package/ui-dist/assets/index-DwbLsoLt.js +1 -0
  340. package/ui-dist/assets/index-hCLSotmS.js +1 -0
  341. package/ui-dist/assets/index-jrhEFc-j.js +1 -0
  342. package/ui-dist/assets/index-kz8ueXgo.js +1 -0
  343. package/ui-dist/assets/{infoDiagram-FWYZ7A6U-CyO3CAA1.js → infoDiagram-FWYZ7A6U-DugVGI3K.js} +1 -1
  344. package/ui-dist/assets/{ishikawaDiagram-FXEZZL3T-BLLZuF1_.js → ishikawaDiagram-FXEZZL3T-CHy06Oev.js} +1 -1
  345. package/ui-dist/assets/{journeyDiagram-5HDEW3XC-CWzQocvE.js → journeyDiagram-5HDEW3XC-BI0w53rb.js} +1 -1
  346. package/ui-dist/assets/{kanban-definition-HUTT4EX6-Dz4SFU-I.js → kanban-definition-HUTT4EX6-v_bLwx7B.js} +1 -1
  347. package/ui-dist/assets/layout-Dwp1s_bo.js +1 -0
  348. package/ui-dist/assets/{linear-BlzDlAaL.js → linear-DAV4STLZ.js} +1 -1
  349. package/ui-dist/assets/map-Bs6QpaaQ.js +1 -0
  350. package/ui-dist/assets/{mermaid.core-BtCydd-w.js → mermaid.core-fVmaL47V.js} +4 -4
  351. package/ui-dist/assets/{mindmap-definition-LN4V7U3C-BeIkKRBx.js → mindmap-definition-LN4V7U3C-5dWD4dxm.js} +1 -1
  352. package/ui-dist/assets/{pegDiagram-2B236MQR-BZgpT4TN.js → pegDiagram-2B236MQR-DB2XsDPN.js} +1 -1
  353. package/ui-dist/assets/{pieDiagram-ENE6RG2P-BRFggcg2.js → pieDiagram-ENE6RG2P-Bc5teLCS.js} +1 -1
  354. package/ui-dist/assets/{quadrantDiagram-ABIIQ3AL-CibUo3gT.js → quadrantDiagram-ABIIQ3AL-BlfNdETt.js} +1 -1
  355. package/ui-dist/assets/{railroadDiagram-RFXS5EU6-DINJqhgn.js → railroadDiagram-RFXS5EU6-BbXywkq3.js} +1 -1
  356. package/ui-dist/assets/{requirementDiagram-TGXJPOKE-NegluQIe.js → requirementDiagram-TGXJPOKE-DU-CapdF.js} +1 -1
  357. package/ui-dist/assets/{sankeyDiagram-HTMAVEWB-Kq41yHAd.js → sankeyDiagram-HTMAVEWB-Ftvh3MqA.js} +1 -1
  358. package/ui-dist/assets/{sequenceDiagram-DBY2YBRQ-CngfEI4f.js → sequenceDiagram-DBY2YBRQ-CCtHt9V4.js} +1 -1
  359. package/ui-dist/assets/{sizeCapture-X5ZJPWSS-DUWRNlme.js → sizeCapture-X5ZJPWSS-x0jnIKHx.js} +1 -1
  360. package/ui-dist/assets/{stateDiagram-2N3HPSRC-BwHC_Fz4.js → stateDiagram-2N3HPSRC-2qCrydE7.js} +1 -1
  361. package/ui-dist/assets/stateDiagram-v2-6OUMAXLB-CvGRUgRf.js +1 -0
  362. package/ui-dist/assets/{swimlanes-5IMT3BWC-Cqp2rT1K.js → swimlanes-5IMT3BWC-6kqJq5Xf.js} +2 -2
  363. package/ui-dist/assets/swimlanesDiagram-G3AALYLV-EBa7-qSc.js +8 -0
  364. package/ui-dist/assets/{timeline-definition-FHXFAJF6-BG3GYPCu.js → timeline-definition-FHXFAJF6-BuRz8KHZ.js} +1 -1
  365. package/ui-dist/assets/{vennDiagram-L72KCM5P-D1IFU27N.js → vennDiagram-L72KCM5P-BKzMZ_Rv.js} +1 -1
  366. package/ui-dist/assets/{wardleyDiagram-EHGQE667-DboO2kYu.js → wardleyDiagram-EHGQE667-8uXlFtBV.js} +1 -1
  367. package/ui-dist/assets/{xychartDiagram-FW5EYKEG-vI2BGrXP.js → xychartDiagram-FW5EYKEG-CWKGlpF9.js} +1 -1
  368. package/ui-dist/index.html +2 -2
  369. package/ui-dist/assets/architectureDiagram-ZJ3FMSHR-DHyfFgK0.js +0 -36
  370. package/ui-dist/assets/channel-TMGI9Qt9.js +0 -1
  371. package/ui-dist/assets/classDiagram-OUVF2IWQ-r5zIpY6G.js +0 -1
  372. package/ui-dist/assets/classDiagram-v2-EOCWNBFH-r5zIpY6G.js +0 -1
  373. package/ui-dist/assets/cose-bilkent-JH36ORCC-fSNs9447.js +0 -1
  374. package/ui-dist/assets/ganttDiagram-NO4QXBWP-CJYhBF1C.js +0 -292
  375. package/ui-dist/assets/graph-DdBT9W3J.js +0 -1
  376. package/ui-dist/assets/index-6UIO-gg7.js +0 -1
  377. package/ui-dist/assets/index-BVkQVMFn.js +0 -1
  378. package/ui-dist/assets/index-BaAPxkqh.css +0 -1
  379. package/ui-dist/assets/index-CmCERTmS.js +0 -1
  380. package/ui-dist/assets/index-Cr4lUIaJ.js +0 -1
  381. package/ui-dist/assets/index-D0fAqlES.js +0 -1
  382. package/ui-dist/assets/index-DfNcAyZq.js +0 -1
  383. package/ui-dist/assets/layout-D3tY9q4Q.js +0 -1
  384. package/ui-dist/assets/map-Ctg1-a9N.js +0 -1
  385. package/ui-dist/assets/stateDiagram-v2-6OUMAXLB-C6P3NY96.js +0 -1
  386. package/ui-dist/assets/swimlanesDiagram-G3AALYLV-BNdDt62J.js +0 -8
@@ -8,9 +8,11 @@ import { logger } from "../middleware/logger.js";
8
8
  import { validate } from "../middleware/validate.js";
9
9
  import { assertTimeZone } from "../services/automations.scheduler.js";
10
10
  import { chatAgentRunService } from "../services/chat-agent-runs.js";
11
- import { CHAT_ASSISTANT_USER_ERROR_MESSAGE, chatAssistantErrorForLog, chatAssistantService, ChatAssistantStreamError, userVisiblePartialBodyFromError, } from "../services/chat-assistant.js";
11
+ import { buildChatNativeSteerFeedback } from "../services/chat-assistant.annotations.js";
12
+ import { CHAT_ASSISTANT_USER_ERROR_MESSAGE, chatAssistantErrorForLog, chatAssistantService, ChatAssistantStreamError, prepareChatAttachmentReferences, userVisiblePartialBodyFromError, } from "../services/chat-assistant.js";
12
13
  import { cancelAndReleaseActiveChatGeneration, claimChatGeneration, createChatRuntimeControlCoordinator, getActiveChatGeneration, hasActiveChatGeneration, interruptActiveChatGeneration, steerActiveChatGeneration } from "../services/chat-generation-locks.js";
13
14
  import { hashChatGenerationBody } from "../services/chat-generation-protocol.js";
15
+ import { chatInlineAnnotationService } from "../services/chat-inline-annotations.js";
14
16
  import { chatSteerMessageService } from "../services/chat-steer-messages.js";
15
17
  import { buildChatTitlePromptFromMessages, chatTitleGenerationService, } from "../services/chat-title-generation.js";
16
18
  import { chatWorkManifestService } from "../services/chat-work-manifest.js";
@@ -20,6 +22,7 @@ import { runtimeResultText, sanitizeGeneratedTitle, } from "../services/title-ge
20
22
  import { assertBoard, assertCompanyAccess, getActorInfo } from "./authz.js";
21
23
  import { createChatBackgroundRuntime, } from "./chat-background-runtime.js";
22
24
  import { wakeIssueAssigneeAfterChatConversion } from "./chat-issue-assignment-wakeup.js";
25
+ import { createChatAnnotationRouteHelpers, turnContextFromUserMessage, } from "./chats.annotation-routes.js";
23
26
  import { attachGeneratedChatFiles } from "./chats.generated-attachments.js";
24
27
  import { isMultipartRequest, paginateChatMessages, positiveIntegerQuery, uploadedMessageFiles, validateUploadedMessageFiles, } from "./chats.helpers.js";
25
28
  import { registerChatStreamRoutes } from "./chats.stream-routes.js";
@@ -47,6 +50,14 @@ export function chatRoutes(db, storage, backgroundRuntime = createChatBackground
47
50
  const chatTitles = chatTitleGenerationService({ chats: svc, productIntelligence });
48
51
  const steerMessages = chatSteerMessageService(db);
49
52
  const sideChats = sideChatService(db);
53
+ const inlineAnnotations = chatInlineAnnotationService(db);
54
+ const { addAgentAuthoredMessage, addUserMessage, cleanupStoredUserMessageFiles, storeUserMessageFiles, } = createChatAnnotationRouteHelpers({
55
+ db,
56
+ storage,
57
+ chats: svc,
58
+ logActivity,
59
+ assertLocalMutationAllowed: assertChatLocalMutationAllowed,
60
+ });
50
61
  const CHAT_ASSISTANT_RECOVERABLE_FAILURE_FALLBACK_MESSAGE = "The assistant reply could not be completed. Rudder saved this attempt for diagnostics; retry when ready.";
51
62
  const upload = multer({
52
63
  storage: multer.memoryStorage(),
@@ -76,9 +87,112 @@ export function chatRoutes(db, storage, backgroundRuntime = createChatBackground
76
87
  });
77
88
  });
78
89
  }
79
- async function assertConversationAccess(req, conversationId) {
90
+ function parseQueuedMultipartBody(body, mode) {
91
+ const raw = { ...(body ?? {}) };
92
+ let payload = {};
93
+ if (typeof raw.payload === "string") {
94
+ try {
95
+ const parsed = JSON.parse(raw.payload);
96
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
97
+ payload = parsed;
98
+ }
99
+ }
100
+ catch {
101
+ return null;
102
+ }
103
+ }
104
+ else if (raw.payload && typeof raw.payload === "object" && !Array.isArray(raw.payload)) {
105
+ payload = raw.payload;
106
+ }
107
+ else {
108
+ const payloadKeys = [
109
+ "body",
110
+ "inlineAnnotations",
111
+ "projectId",
112
+ "skillRefs",
113
+ "accessMode",
114
+ "model",
115
+ "effort",
116
+ "metadata",
117
+ ];
118
+ for (const key of payloadKeys) {
119
+ if (Object.hasOwn(raw, key))
120
+ payload[key] = raw[key];
121
+ }
122
+ }
123
+ if (typeof payload.inlineAnnotations === "string") {
124
+ try {
125
+ payload.inlineAnnotations = JSON.parse(payload.inlineAnnotations);
126
+ }
127
+ catch {
128
+ return null;
129
+ }
130
+ }
131
+ for (const key of ["skillRefs", "metadata"]) {
132
+ if (typeof payload[key] !== "string")
133
+ continue;
134
+ try {
135
+ payload[key] = JSON.parse(payload[key]);
136
+ }
137
+ catch {
138
+ return null;
139
+ }
140
+ }
141
+ if (mode === "create") {
142
+ return {
143
+ clientMutationId: raw.clientMutationId,
144
+ expectedGenerationId: typeof raw.expectedGenerationId === "string" && raw.expectedGenerationId.trim()
145
+ ? raw.expectedGenerationId
146
+ : null,
147
+ payload,
148
+ };
149
+ }
150
+ const version = typeof raw.version === "string" ? Number(raw.version) : raw.version;
151
+ return { version, payload };
152
+ }
153
+ async function storeQueuedAnnotationFiles(conversation, files) {
154
+ const storedFiles = [];
155
+ try {
156
+ for (const file of files) {
157
+ storedFiles.push(await storage.putFile({
158
+ orgId: conversation.orgId,
159
+ namespace: `chat-queue-annotations/${conversation.id}`,
160
+ originalFilename: file.originalname || null,
161
+ contentType: (file.mimetype || "").toLowerCase(),
162
+ body: file.buffer,
163
+ }));
164
+ }
165
+ return storedFiles;
166
+ }
167
+ catch (error) {
168
+ await Promise.all(storedFiles.map((stored) => storage.deleteObject(conversation.orgId, stored.objectKey).catch(() => undefined)));
169
+ throw error;
170
+ }
171
+ }
172
+ async function cleanupCommittedQueuedAnnotationAssets(orgId, queueItemId, cleanupAttachments) {
173
+ const deletedAssetIds = [];
174
+ for (const attachment of cleanupAttachments) {
175
+ try {
176
+ await storage.deleteObject(orgId, attachment.objectKey);
177
+ deletedAssetIds.push(attachment.assetId);
178
+ }
179
+ catch (error) {
180
+ logger.warn({ err: error, queuedMessageId: queueItemId, assetId: attachment.assetId }, "failed to delete orphaned queued annotation asset");
181
+ }
182
+ }
183
+ if (deletedAssetIds.length > 0) {
184
+ await svc.finalizeQueuedAnnotationAssetCleanup({ orgId, assetIds: deletedAssetIds });
185
+ }
186
+ }
187
+ async function cleanupUncommittedQueuedAnnotationFiles(orgId, queueMutationId, attachments) {
188
+ await Promise.all(attachments.map((attachment) => storage.deleteObject(orgId, attachment.objectKey).catch((error) => {
189
+ logger.warn({ err: error, queueMutationId }, "failed to compensate an uncommitted queued annotation object");
190
+ })));
191
+ }
192
+ async function assertConversationAccess(req, conversationId, expectedOrgId) {
80
193
  const conversation = await svc.getById(conversationId);
81
- if (!conversation)
194
+ if (!conversation
195
+ || (expectedOrgId !== undefined && conversation.orgId !== expectedOrgId))
82
196
  return null;
83
197
  assertCompanyAccess(req, conversation.orgId);
84
198
  await sideChats.assertAccessible(conversation, req.actor.type === "board" ? (req.actor.userId ?? "local-board") : null);
@@ -283,64 +397,6 @@ export function chatRoutes(db, storage, backgroundRuntime = createChatBackground
283
397
  },
284
398
  };
285
399
  }
286
- function turnContextFromUserMessage(userMessage) {
287
- if (!userMessage.chatTurnId) {
288
- throw new Error("User message missing chat turn id");
289
- }
290
- return { chatTurnId: userMessage.chatTurnId, turnVariant: userMessage.turnVariant };
291
- }
292
- async function addUserMessage(conversation, body, actor, editUserMessageId) {
293
- assertChatLocalMutationAllowed(conversation);
294
- const userMessage = await svc.addUserChatMessage(conversation.id, conversation.orgId, body, editUserMessageId ?? null);
295
- await logActivity(db, {
296
- orgId: conversation.orgId,
297
- actorType: actor.actorType,
298
- actorId: actor.actorId,
299
- agentId: actor.agentId,
300
- runId: actor.runId,
301
- action: "chat.message_added",
302
- entityType: "chat",
303
- entityId: conversation.id,
304
- details: {
305
- messageId: userMessage.id,
306
- role: "user",
307
- kind: "message",
308
- editUserMessageId: editUserMessageId ?? null,
309
- },
310
- });
311
- return userMessage;
312
- }
313
- async function addAgentAuthoredMessage(conversation, body, actor) {
314
- assertChatLocalMutationAllowed(conversation);
315
- if (!actor.agentId) {
316
- throw forbidden("Agent authentication required");
317
- }
318
- const message = await svc.addMessage(conversation.id, {
319
- orgId: conversation.orgId,
320
- role: "assistant",
321
- kind: "message",
322
- body,
323
- replyingAgentId: actor.agentId,
324
- });
325
- await logActivity(db, {
326
- orgId: conversation.orgId,
327
- actorType: "agent",
328
- actorId: actor.agentId,
329
- agentId: actor.agentId,
330
- runId: actor.runId,
331
- action: "chat.message_added",
332
- entityType: "chat",
333
- entityId: conversation.id,
334
- details: {
335
- messageId: message.id,
336
- role: "assistant",
337
- kind: "message",
338
- replyingAgentId: actor.agentId,
339
- source: "agent_direct_message",
340
- },
341
- });
342
- return message;
343
- }
344
400
  async function attachFilesToUserMessage(conversation, messageId, files, actor) {
345
401
  assertChatLocalMutationAllowed(conversation);
346
402
  const attachments = [];
@@ -458,7 +514,7 @@ export function chatRoutes(db, storage, backgroundRuntime = createChatBackground
458
514
  const labels = await issuesSvc.listLabels(orgId);
459
515
  return labels.length >= 5;
460
516
  }
461
- async function persistAssistantReply(req, conversation, actor, assistantReply, turnContext, transcript = [], replyingAgentId = assistantReply.replyingAgentId ?? chatReplyingAgentId(conversation), existingMessageId, runId) {
517
+ async function persistAssistantReply(req, conversation, actor, assistantReply, turnContext, transcript = [], replyingAgentId = assistantReply.replyingAgentId ?? chatReplyingAgentId(conversation), existingMessageId, runId, persistTranscript = true) {
462
518
  const createdMessages = [];
463
519
  const { chatTurnId, turnVariant } = turnContext;
464
520
  const attachGeneratedFiles = (message, generatedAttachments) => attachGeneratedChatFiles({
@@ -477,7 +533,7 @@ export function chatRoutes(db, storage, backgroundRuntime = createChatBackground
477
533
  status: "completed",
478
534
  body: input.body,
479
535
  structuredPayload: input.structuredPayload ?? null,
480
- transcript,
536
+ ...(persistTranscript ? { transcript } : {}),
481
537
  approvalId: input.approvalId ?? null,
482
538
  runId: runId ?? undefined,
483
539
  replyingAgentId,
@@ -491,7 +547,7 @@ export function chatRoutes(db, storage, backgroundRuntime = createChatBackground
491
547
  kind: input.kind,
492
548
  body: input.body,
493
549
  structuredPayload: input.structuredPayload ?? null,
494
- transcript,
550
+ ...(persistTranscript ? { transcript } : {}),
495
551
  approvalId: input.approvalId ?? null,
496
552
  runId: runId ?? null,
497
553
  replyingAgentId,
@@ -783,13 +839,13 @@ export function chatRoutes(db, storage, backgroundRuntime = createChatBackground
783
839
  attachments: [...(message.attachments ?? []), ...attachments],
784
840
  };
785
841
  }
786
- async function persistPartialAssistantMessage(conversation, body, status, turnContext, transcript = [], replyingAgentId = chatReplyingAgentId(conversation), existingMessageId, runId, structuredPayload) {
842
+ async function persistPartialAssistantMessage(conversation, body, status, turnContext, transcript = [], replyingAgentId = chatReplyingAgentId(conversation), existingMessageId, runId, structuredPayload, persistTranscript = true) {
787
843
  const trimmed = body.trim();
788
844
  const fallbackBody = status === "stopped"
789
845
  ? "Chat run stopped before a final reply. Continue the conversation to resume from the preserved context."
790
846
  : CHAT_ASSISTANT_USER_ERROR_MESSAGE;
791
847
  const durableBody = trimmed || (transcript.length > 0 ? fallbackBody : "");
792
- if (!durableBody)
848
+ if (!durableBody && status !== "stopped")
793
849
  return null;
794
850
  const chatTurnId = turnContext?.chatTurnId ?? randomUUID();
795
851
  const turnVariant = turnContext?.turnVariant ?? 0;
@@ -799,7 +855,7 @@ export function chatRoutes(db, storage, backgroundRuntime = createChatBackground
799
855
  status,
800
856
  body: durableBody,
801
857
  structuredPayload: structuredPayload ?? null,
802
- transcript,
858
+ ...(persistTranscript ? { transcript } : {}),
803
859
  runId: runId ?? undefined,
804
860
  replyingAgentId,
805
861
  });
@@ -813,7 +869,7 @@ export function chatRoutes(db, storage, backgroundRuntime = createChatBackground
813
869
  status,
814
870
  body: durableBody,
815
871
  structuredPayload: structuredPayload ?? null,
816
- transcript,
872
+ ...(persistTranscript ? { transcript } : {}),
817
873
  runId: runId ?? null,
818
874
  replyingAgentId,
819
875
  chatTurnId,
@@ -999,8 +1055,10 @@ export function chatRoutes(db, storage, backgroundRuntime = createChatBackground
999
1055
  }
1000
1056
  let leaseRenewing = false;
1001
1057
  let leaseLost = false;
1058
+ let deliveryAcknowledged = false;
1059
+ let deliveryAcknowledging = false;
1002
1060
  const renewLease = async () => {
1003
- if (leaseRenewing || leaseLost)
1061
+ if (leaseRenewing || leaseLost || deliveryAcknowledged || deliveryAcknowledging)
1004
1062
  return;
1005
1063
  leaseRenewing = true;
1006
1064
  try {
@@ -1011,7 +1069,7 @@ export function chatRoutes(db, storage, backgroundRuntime = createChatBackground
1011
1069
  leaseEpoch: claim.leaseEpoch,
1012
1070
  leaseMs: queueLeaseMs,
1013
1071
  });
1014
- if (!renewed) {
1072
+ if (!renewed && !deliveryAcknowledged && !deliveryAcknowledging) {
1015
1073
  leaseLost = true;
1016
1074
  abortController.abort(new Error("Queued chat continuation lost its delivery lease"));
1017
1075
  }
@@ -1022,17 +1080,20 @@ export function chatRoutes(db, storage, backgroundRuntime = createChatBackground
1022
1080
  };
1023
1081
  const leaseTimer = backgroundRuntime.setInterval(() => {
1024
1082
  return renewLease().catch((error) => {
1083
+ if (deliveryAcknowledged || deliveryAcknowledging)
1084
+ return;
1025
1085
  leaseLost = true;
1026
1086
  abortController.abort(error);
1027
1087
  });
1028
1088
  }, Math.floor(queueLeaseMs / 3));
1029
1089
  let terminalStatus = "failed";
1030
1090
  let terminalReason = null;
1031
- let completionRecorded = false;
1032
1091
  let assistantConversation = conversation;
1033
1092
  let activeChatRunId = null;
1034
1093
  const transcript = [];
1035
1094
  let partialBody = "";
1095
+ let assistantProjectionMessageId = null;
1096
+ let activeAttemptEpoch = Math.max(1, getActiveChatGeneration(conversation.id)?.attemptEpoch ?? 1);
1036
1097
  try {
1037
1098
  const userMessage = await svc.getMessage(conversation.id, claim.userMessageId);
1038
1099
  if (!userMessage)
@@ -1040,6 +1101,17 @@ export function chatRoutes(db, storage, backgroundRuntime = createChatBackground
1040
1101
  const turnContext = turnContextFromUserMessage(userMessage);
1041
1102
  const assistantInput = await loadAssistantInput(conversation, actor);
1042
1103
  assistantConversation = assistantInput.conversation;
1104
+ const persistStoppedQueuedMessage = async (replyingAgentId) => {
1105
+ const frozen = await svc.generationProtocol.getFrozenVisibleProjection({
1106
+ orgId: conversation.orgId,
1107
+ conversationId: conversation.id,
1108
+ generationId: claim.generationId,
1109
+ }).catch(() => null);
1110
+ const frozenAtCutoff = frozen && frozen.generation.acceptedThroughSeq !== null
1111
+ ? frozen
1112
+ : null;
1113
+ return persistPartialAssistantMessage(assistantConversation, frozenAtCutoff?.projection.body ?? partialBody, "stopped", turnContext, frozenAtCutoff?.projection.transcript ?? transcript, replyingAgentId, assistantProjectionMessageId, activeChatRunId, undefined, false);
1114
+ };
1043
1115
  const streamed = await assistantSvc.streamChatAssistantReply({
1044
1116
  ...assistantInput,
1045
1117
  userMessageId: userMessage.id,
@@ -1049,6 +1121,7 @@ export function chatRoutes(db, storage, backgroundRuntime = createChatBackground
1049
1121
  abortSignal: abortController.signal,
1050
1122
  controlCoordinator: createChatRuntimeControlCoordinator(conversation.id, claim.generationId, {
1051
1123
  onAttemptStarted: async ({ generationId, attemptEpoch, ownerToken, attempt }) => {
1124
+ activeAttemptEpoch = attemptEpoch;
1052
1125
  await svc.beginGenerationControlAttempt({
1053
1126
  orgId: conversation.orgId,
1054
1127
  conversationId: conversation.id,
@@ -1067,6 +1140,25 @@ export function chatRoutes(db, storage, backgroundRuntime = createChatBackground
1067
1140
  providerThreadId: handle.providerThreadId ?? null,
1068
1141
  providerTurnId: handle.providerTurnId ?? null,
1069
1142
  });
1143
+ if (!deliveryAcknowledged) {
1144
+ deliveryAcknowledging = true;
1145
+ try {
1146
+ const acknowledged = await svc.acknowledgeServerQueuedMessageDelivery({
1147
+ itemId: claim.item.id,
1148
+ generationId: claim.generationId,
1149
+ leaseToken: claim.leaseToken,
1150
+ leaseEpoch: claim.leaseEpoch,
1151
+ });
1152
+ if (!acknowledged) {
1153
+ throw new Error("Queued chat continuation delivery acknowledgement was not recorded");
1154
+ }
1155
+ deliveryAcknowledged = true;
1156
+ backgroundRuntime.clearTimer(leaseTimer);
1157
+ }
1158
+ finally {
1159
+ deliveryAcknowledging = false;
1160
+ }
1161
+ }
1070
1162
  },
1071
1163
  onAttemptLeaseRenewed: async ({ generationId, attemptEpoch, ownerToken }) => {
1072
1164
  await svc.renewGenerationControlLease({ generationId, attemptEpoch, ownerToken });
@@ -1087,15 +1179,38 @@ export function chatRoutes(db, storage, backgroundRuntime = createChatBackground
1087
1179
  partialBody = `${partialBody}${delta}`;
1088
1180
  },
1089
1181
  onTranscriptEntry: async (entry) => {
1090
- if (!abortController.signal.aborted)
1091
- transcript.push(entry);
1182
+ if (abortController.signal.aborted)
1183
+ return;
1184
+ transcript.push(entry);
1185
+ try {
1186
+ const projection = await svc.generationProtocol.appendVisibleEventAndProject({
1187
+ orgId: conversation.orgId,
1188
+ conversationId: conversation.id,
1189
+ generationId: claim.generationId,
1190
+ expectedAttemptEpoch: activeAttemptEpoch,
1191
+ eventKind: "transcript",
1192
+ payload: { entry },
1193
+ messageId: assistantProjectionMessageId,
1194
+ runId: activeChatRunId,
1195
+ bodyHash: hashChatGenerationBody(partialBody),
1196
+ body: partialBody,
1197
+ chatTurnId: turnContext.chatTurnId,
1198
+ turnVariant: turnContext.turnVariant,
1199
+ });
1200
+ assistantProjectionMessageId = projection.message.id;
1201
+ }
1202
+ catch (error) {
1203
+ if (chatVisibleOutputAdmissionClosed(error))
1204
+ return;
1205
+ throw error;
1206
+ }
1092
1207
  },
1093
1208
  });
1094
1209
  partialBody = streamed.partialBody || partialBody;
1095
1210
  if (abortController.signal.aborted || streamed.outcome === "stopped") {
1096
1211
  terminalStatus = "stopped";
1097
1212
  terminalReason = "operator_stop";
1098
- const stoppedMessage = await persistPartialAssistantMessage(assistantConversation, "", "stopped", turnContext, transcript, streamed.replyingAgentId, null, activeChatRunId);
1213
+ const stoppedMessage = await persistStoppedQueuedMessage(streamed.replyingAgentId);
1099
1214
  const stoppedMessages = stoppedMessage ? [stoppedMessage] : [];
1100
1215
  await linkChatRunMessages(assistantConversation, activeChatRunId, stoppedMessages);
1101
1216
  if (stoppedMessages.length > 0) {
@@ -1109,12 +1224,11 @@ export function chatRoutes(db, storage, backgroundRuntime = createChatBackground
1109
1224
  else {
1110
1225
  let completionMessageId = null;
1111
1226
  try {
1112
- const attemptEpoch = Math.max(1, getActiveChatGeneration(conversation.id)?.attemptEpoch ?? 1);
1113
1227
  const completion = await svc.generationProtocol.appendVisibleEventAndProject({
1114
1228
  orgId: conversation.orgId,
1115
1229
  conversationId: conversation.id,
1116
1230
  generationId: claim.generationId,
1117
- expectedAttemptEpoch: attemptEpoch,
1231
+ expectedAttemptEpoch: activeAttemptEpoch,
1118
1232
  eventKind: "runtime_output",
1119
1233
  payload: {
1120
1234
  resultKind: streamed.reply.kind,
@@ -1122,10 +1236,10 @@ export function chatRoutes(db, storage, backgroundRuntime = createChatBackground
1122
1236
  },
1123
1237
  bodyOffset: 0,
1124
1238
  bodyLength: streamed.reply.body.length,
1239
+ messageId: assistantProjectionMessageId,
1125
1240
  runId: activeChatRunId,
1126
1241
  bodyHash: hashChatGenerationBody(streamed.reply.body),
1127
1242
  body: streamed.reply.body,
1128
- transcript,
1129
1243
  replyingAgentId: streamed.replyingAgentId,
1130
1244
  chatTurnId: turnContext.chatTurnId,
1131
1245
  turnVariant: turnContext.turnVariant,
@@ -1137,7 +1251,7 @@ export function chatRoutes(db, storage, backgroundRuntime = createChatBackground
1137
1251
  throw error;
1138
1252
  terminalStatus = "stopped";
1139
1253
  terminalReason = "operator_stop";
1140
- const stoppedMessage = await persistPartialAssistantMessage(assistantConversation, "", "stopped", turnContext, transcript, streamed.replyingAgentId, null, activeChatRunId);
1254
+ const stoppedMessage = await persistStoppedQueuedMessage(streamed.replyingAgentId);
1141
1255
  const stoppedMessages = stoppedMessage ? [stoppedMessage] : [];
1142
1256
  await linkChatRunMessages(assistantConversation, activeChatRunId, stoppedMessages);
1143
1257
  if (stoppedMessages.length > 0) {
@@ -1149,7 +1263,7 @@ export function chatRoutes(db, storage, backgroundRuntime = createChatBackground
1149
1263
  }
1150
1264
  return;
1151
1265
  }
1152
- const createdMessages = await persistAssistantReply(request, assistantConversation, actor, streamed.reply, turnContext, transcript, streamed.replyingAgentId, completionMessageId, activeChatRunId);
1266
+ const createdMessages = await persistAssistantReply(request, assistantConversation, actor, streamed.reply, turnContext, transcript, streamed.replyingAgentId, completionMessageId, activeChatRunId, false);
1153
1267
  await linkChatRunMessages(assistantConversation, activeChatRunId, createdMessages);
1154
1268
  await logChatMessagesAdded(assistantConversation, createdMessages, {
1155
1269
  actorType: "system",
@@ -1172,7 +1286,7 @@ export function chatRoutes(db, storage, backgroundRuntime = createChatBackground
1172
1286
  const failureBody = userVisiblePartialBodyFromError(error)
1173
1287
  || recoverableFailureBody(failurePayload)
1174
1288
  || CHAT_ASSISTANT_USER_ERROR_MESSAGE;
1175
- const failedMessage = await persistPartialAssistantMessage(assistantConversation, failureBody, "failed", null, transcript, chatReplyingAgentId(assistantConversation), null, activeChatRunId, failurePayload).catch(() => null);
1289
+ const failedMessage = await persistPartialAssistantMessage(assistantConversation, failureBody, "failed", null, transcript, chatReplyingAgentId(assistantConversation), assistantProjectionMessageId, activeChatRunId, failurePayload, false).catch(() => null);
1176
1290
  const failedMessages = failedMessage ? [failedMessage] : [];
1177
1291
  await linkChatRunMessages(assistantConversation, activeChatRunId, failedMessages).catch(() => undefined);
1178
1292
  if (failedMessages.length > 0) {
@@ -1202,23 +1316,10 @@ export function chatRoutes(db, storage, backgroundRuntime = createChatBackground
1202
1316
  return null;
1203
1317
  })
1204
1318
  : null;
1205
- if (terminalEvidence) {
1319
+ if (terminalEvidence)
1206
1320
  wakeTerminalProjector();
1207
- const completed = await svc.completeServerQueuedMessageDelivery({
1208
- itemId: claim.item.id,
1209
- generationId: claim.generationId,
1210
- leaseToken: claim.leaseToken,
1211
- leaseEpoch: claim.leaseEpoch,
1212
- status: terminalStatus,
1213
- reason: terminalReason,
1214
- }).catch((error) => {
1215
- logger.warn({ err: error, queuedMessageId: claim.item.id }, "failed to complete queued chat continuation");
1216
- return null;
1217
- });
1218
- completionRecorded = Boolean(completed);
1219
- }
1220
1321
  }
1221
- if (!completionRecorded && !leaseLost) {
1322
+ if (!deliveryAcknowledged && !leaseLost) {
1222
1323
  await svc.releaseServerQueuedMessageClaim({
1223
1324
  itemId: claim.item.id,
1224
1325
  generationId: claim.generationId,
@@ -1654,7 +1755,7 @@ export function chatRoutes(db, storage, backgroundRuntime = createChatBackground
1654
1755
  const active = getActiveChatGeneration(conversation.id);
1655
1756
  res.json(await svc.getQueueSnapshot(conversation.id, active?.generationId ?? null));
1656
1757
  });
1657
- router.post("/chats/:id/queue", validate(createChatQueuedMessageSchema), async (req, res) => {
1758
+ router.post("/chats/:id/queue", async (req, res) => {
1658
1759
  const conversation = await assertConversationAccess(req, req.params.id);
1659
1760
  if (!conversation) {
1660
1761
  res.status(404).json({ error: "Chat conversation not found" });
@@ -1662,15 +1763,71 @@ export function chatRoutes(db, storage, backgroundRuntime = createChatBackground
1662
1763
  }
1663
1764
  assertChatLocalMutationAllowed(conversation);
1664
1765
  await assertSideChatMutationAllowed(req, conversation);
1665
- const requestActor = queueRequestActor(req);
1666
- const item = await svc.createQueuedMessage({
1766
+ const multipart = isMultipartRequest(req);
1767
+ if (multipart)
1768
+ await runMessageFileUpload(req, res);
1769
+ const files = multipart ? uploadedMessageFiles(req) : [];
1770
+ const fileValidationError = validateUploadedMessageFiles(files);
1771
+ if (fileValidationError) {
1772
+ res.status(400).json({ error: fileValidationError });
1773
+ return;
1774
+ }
1775
+ const requestBody = multipart
1776
+ ? parseQueuedMultipartBody(req.body, "create")
1777
+ : req.body;
1778
+ const inlineAnnotationsProvided = Boolean(requestBody
1779
+ && typeof requestBody === "object"
1780
+ && "payload" in requestBody
1781
+ && requestBody.payload
1782
+ && typeof requestBody.payload === "object"
1783
+ && Object.hasOwn(requestBody.payload, "inlineAnnotations"));
1784
+ if ((inlineAnnotationsProvided || files.length > 0) && req.actor.type !== "board") {
1785
+ assertBoard(req);
1786
+ }
1787
+ const parsed = createChatQueuedMessageSchema.safeParse(requestBody);
1788
+ if (!parsed.success) {
1789
+ res.status(400).json({
1790
+ error: "Invalid queued message request",
1791
+ details: parsed.error.issues,
1792
+ });
1793
+ return;
1794
+ }
1795
+ const preparedAnnotations = await inlineAnnotations.prepare({
1667
1796
  orgId: conversation.orgId,
1668
1797
  conversationId: conversation.id,
1669
- clientMutationId: req.body.clientMutationId,
1670
- expectedGenerationId: req.body.expectedGenerationId ?? getActiveChatGeneration(conversation.id)?.generationId ?? null,
1671
- payload: req.body.payload,
1672
- requestActor,
1798
+ annotations: parsed.data.payload.inlineAnnotations,
1799
+ uploadedFileCount: files.length,
1673
1800
  });
1801
+ const storedFiles = await storeQueuedAnnotationFiles(conversation, files);
1802
+ const requestActor = queueRequestActor(req);
1803
+ let result;
1804
+ try {
1805
+ result = await svc.createQueuedMessageWithStagedAttachments({
1806
+ orgId: conversation.orgId,
1807
+ conversationId: conversation.id,
1808
+ clientMutationId: parsed.data.clientMutationId,
1809
+ expectedGenerationId: parsed.data.expectedGenerationId
1810
+ ?? getActiveChatGeneration(conversation.id)?.generationId
1811
+ ?? null,
1812
+ payload: {
1813
+ ...parsed.data.payload,
1814
+ inlineAnnotations: preparedAnnotations.annotations,
1815
+ },
1816
+ requestActor,
1817
+ stagedAttachments: storedFiles.map((attachment) => ({
1818
+ ...attachment,
1819
+ createdByAgentId: req.actor.type === "agent" ? (req.actor.agentId ?? null) : null,
1820
+ createdByUserId: req.actor.type === "board" ? (req.actor.userId ?? null) : null,
1821
+ })),
1822
+ attachmentFileIndexesByAnnotationId: preparedAnnotations.attachmentFileIndexesByAnnotationId,
1823
+ });
1824
+ }
1825
+ catch (error) {
1826
+ await cleanupUncommittedQueuedAnnotationFiles(conversation.orgId, parsed.data.clientMutationId, storedFiles);
1827
+ throw error;
1828
+ }
1829
+ await cleanupUncommittedQueuedAnnotationFiles(conversation.orgId, parsed.data.clientMutationId, result.cleanupAttachments);
1830
+ const item = result.item;
1674
1831
  const actor = getActorInfo(req);
1675
1832
  await logActivity(db, {
1676
1833
  orgId: conversation.orgId,
@@ -1684,6 +1841,10 @@ export function chatRoutes(db, storage, backgroundRuntime = createChatBackground
1684
1841
  details: {
1685
1842
  queuedMessageId: item.id,
1686
1843
  position: item.position,
1844
+ annotationCount: item.annotationCount ?? 0,
1845
+ annotationSourceMessageIds: [
1846
+ ...new Set((item.payload.inlineAnnotations ?? []).map((annotation) => annotation.sourceMessageId)),
1847
+ ],
1687
1848
  },
1688
1849
  });
1689
1850
  wakeServerQueue();
@@ -1701,8 +1862,11 @@ export function chatRoutes(db, storage, backgroundRuntime = createChatBackground
1701
1862
  throw conflict("Cannot dequeue the next message while a reply is in progress");
1702
1863
  }
1703
1864
  const latestGeneration = await svc.getLatestGeneration(conversation.id);
1704
- if (latestGeneration && latestGeneration.status !== "completed") {
1705
- throw conflict("Queue remains parked after a stopped or failed reply");
1865
+ if (latestGeneration
1866
+ && latestGeneration.status !== "completed"
1867
+ && !(latestGeneration.status === "stopped"
1868
+ && latestGeneration.terminalReason === "operator_stop")) {
1869
+ throw conflict("Queue remains parked after a failed or unverified reply");
1706
1870
  }
1707
1871
  const item = await svc.claimNextQueuedMessage(conversation.id);
1708
1872
  if (item) {
@@ -1739,7 +1903,7 @@ export function chatRoutes(db, storage, backgroundRuntime = createChatBackground
1739
1903
  });
1740
1904
  res.json({ item });
1741
1905
  });
1742
- router.patch("/chats/:id/queue/:itemId", validate(updateChatQueuedMessageSchema), async (req, res) => {
1906
+ router.patch("/chats/:id/queue/:itemId", async (req, res) => {
1743
1907
  const conversation = await assertConversationAccess(req, req.params.id);
1744
1908
  if (!conversation) {
1745
1909
  res.status(404).json({ error: "Chat conversation not found" });
@@ -1747,13 +1911,71 @@ export function chatRoutes(db, storage, backgroundRuntime = createChatBackground
1747
1911
  }
1748
1912
  assertChatLocalMutationAllowed(conversation);
1749
1913
  await assertSideChatMutationAllowed(req, conversation);
1750
- const item = await svc.updateQueuedMessage({
1751
- conversationId: conversation.id,
1752
- itemId: req.params.itemId,
1753
- version: req.body.version,
1754
- payload: req.body.payload,
1755
- });
1756
- res.json(item);
1914
+ const multipart = isMultipartRequest(req);
1915
+ if (multipart)
1916
+ await runMessageFileUpload(req, res);
1917
+ const files = multipart ? uploadedMessageFiles(req) : [];
1918
+ const fileValidationError = validateUploadedMessageFiles(files);
1919
+ if (fileValidationError) {
1920
+ res.status(400).json({ error: fileValidationError });
1921
+ return;
1922
+ }
1923
+ const requestBody = multipart
1924
+ ? parseQueuedMultipartBody(req.body, "update")
1925
+ : req.body;
1926
+ const inlineAnnotationsProvided = Boolean(requestBody
1927
+ && typeof requestBody === "object"
1928
+ && "payload" in requestBody
1929
+ && requestBody.payload
1930
+ && typeof requestBody.payload === "object"
1931
+ && Object.hasOwn(requestBody.payload, "inlineAnnotations"));
1932
+ if ((inlineAnnotationsProvided || files.length > 0) && req.actor.type !== "board") {
1933
+ assertBoard(req);
1934
+ }
1935
+ const parsed = updateChatQueuedMessageSchema.safeParse(requestBody);
1936
+ if (!parsed.success) {
1937
+ res.status(400).json({
1938
+ error: "Invalid queued message update",
1939
+ details: parsed.error.issues,
1940
+ });
1941
+ return;
1942
+ }
1943
+ const preparedAnnotations = inlineAnnotationsProvided
1944
+ ? await inlineAnnotations.prepare({
1945
+ orgId: conversation.orgId,
1946
+ conversationId: conversation.id,
1947
+ annotations: parsed.data.payload.inlineAnnotations ?? [],
1948
+ uploadedFileCount: files.length,
1949
+ })
1950
+ : null;
1951
+ const storedFiles = await storeQueuedAnnotationFiles(conversation, files);
1952
+ let result;
1953
+ try {
1954
+ result = await svc.updateQueuedMessageWithStagedAttachments({
1955
+ orgId: conversation.orgId,
1956
+ conversationId: conversation.id,
1957
+ itemId: req.params.itemId,
1958
+ version: parsed.data.version,
1959
+ payload: {
1960
+ ...parsed.data.payload,
1961
+ ...(inlineAnnotationsProvided
1962
+ ? { inlineAnnotations: preparedAnnotations?.annotations ?? [] }
1963
+ : {}),
1964
+ },
1965
+ stagedAttachments: storedFiles.map((attachment) => ({
1966
+ ...attachment,
1967
+ createdByAgentId: req.actor.type === "agent" ? (req.actor.agentId ?? null) : null,
1968
+ createdByUserId: req.actor.type === "board" ? (req.actor.userId ?? null) : null,
1969
+ })),
1970
+ attachmentFileIndexesByAnnotationId: preparedAnnotations?.attachmentFileIndexesByAnnotationId ?? new Map(),
1971
+ });
1972
+ }
1973
+ catch (error) {
1974
+ await cleanupUncommittedQueuedAnnotationFiles(conversation.orgId, req.params.itemId, storedFiles);
1975
+ throw error;
1976
+ }
1977
+ await cleanupCommittedQueuedAnnotationAssets(conversation.orgId, result.item.id, result.cleanupAttachments);
1978
+ res.json(result.item);
1757
1979
  });
1758
1980
  router.delete("/chats/:id/queue/:itemId", async (req, res) => {
1759
1981
  const conversation = await assertConversationAccess(req, req.params.id);
@@ -1768,12 +1990,14 @@ export function chatRoutes(db, storage, backgroundRuntime = createChatBackground
1768
1990
  res.status(400).json({ error: "Invalid queued message cancel request", details: parsed.error.issues });
1769
1991
  return;
1770
1992
  }
1771
- const item = await svc.cancelQueuedMessage({
1993
+ const result = await svc.cancelQueuedMessageWithStagedAttachments({
1994
+ orgId: conversation.orgId,
1772
1995
  conversationId: conversation.id,
1773
1996
  itemId: req.params.itemId,
1774
1997
  version: parsed.data.version ?? null,
1775
1998
  });
1776
- res.json(item);
1999
+ await cleanupCommittedQueuedAnnotationAssets(conversation.orgId, result.item.id, result.cleanupAttachments);
2000
+ res.json(result.item);
1777
2001
  });
1778
2002
  router.post("/chats/:id/queue/:itemId/steer", validate(steerChatQueuedMessageSchema), async (req, res) => {
1779
2003
  const conversation = await assertConversationAccess(req, req.params.id);
@@ -1863,62 +2087,101 @@ export function chatRoutes(db, storage, backgroundRuntime = createChatBackground
1863
2087
  return;
1864
2088
  }
1865
2089
  const providerSendState = { denied: null };
1866
- const runtimeResult = await steerActiveChatGeneration({
1867
- conversationId: conversation.id,
1868
- expectedGenerationId: durableGenerationId,
1869
- expectedAttemptEpoch: durableAttemptEpoch,
1870
- feedback: {
2090
+ const nativeSteerMessageId = started.item.continuationMessageId
2091
+ ?? started.item.sourceMessageId;
2092
+ const nativeSteerMessage = nativeSteerMessageId
2093
+ ? await svc.getMessage(conversation.id, nativeSteerMessageId)
2094
+ : null;
2095
+ if ((started.item.payload.inlineAnnotations?.length ?? 0) > 0
2096
+ && !nativeSteerMessage) {
2097
+ throw conflict("Materialized Steer annotation message could not be loaded");
2098
+ }
2099
+ const nativeSteerRuntimeType = (started.generation?.controlRuntimeType
2100
+ ?? getActiveChatGeneration(conversation.id)?.runtimeType
2101
+ ?? active?.runtimeType
2102
+ ?? null);
2103
+ const preparedSteerAttachments = nativeSteerMessage && nativeSteerRuntimeType
2104
+ ? await prepareChatAttachmentReferences({
2105
+ runtimeType: nativeSteerRuntimeType,
2106
+ messages: [nativeSteerMessage],
2107
+ storage,
2108
+ runId: durableControlActionId,
2109
+ })
2110
+ : {
2111
+ references: new Map(),
2112
+ media: [],
2113
+ cleanup: async () => undefined,
2114
+ };
2115
+ const nativeSteerFeedback = nativeSteerMessage
2116
+ ? buildChatNativeSteerFeedback({
2117
+ message: nativeSteerMessage,
2118
+ clientMessageId: started.action.providerClientMessageId ?? durableControlActionId,
2119
+ attachmentReferences: preparedSteerAttachments.references,
2120
+ media: preparedSteerAttachments.media,
2121
+ })
2122
+ : {
1871
2123
  text: started.item.payload.body,
1872
2124
  clientMessageId: started.action.providerClientMessageId ?? durableControlActionId,
1873
- },
1874
- claimProviderSend: async () => {
1875
- const sendClaim = await svc.claimSteerProviderSend({
1876
- orgId: conversation.orgId,
1877
- controlActionId: durableControlActionId,
1878
- });
1879
- if (!sendClaim)
1880
- return null;
1881
- if ("sendDenied" in sendClaim) {
1882
- providerSendState.denied = sendClaim;
1883
- return {
1884
- sendDenied: true,
1885
- reason: "generation_fence_changed",
1886
- };
1887
- }
1888
- try {
1889
- await svc.appendGenerationEvent({
2125
+ };
2126
+ let runtimeResult;
2127
+ try {
2128
+ runtimeResult = await steerActiveChatGeneration({
2129
+ conversationId: conversation.id,
2130
+ expectedGenerationId: durableGenerationId,
2131
+ expectedAttemptEpoch: durableAttemptEpoch,
2132
+ feedback: nativeSteerFeedback,
2133
+ claimProviderSend: async () => {
2134
+ const sendClaim = await svc.claimSteerProviderSend({
1890
2135
  orgId: conversation.orgId,
1891
- generationId: durableGenerationId,
1892
- attemptEpoch: durableAttemptEpoch,
1893
- eventKind: "steer_requested",
1894
- payload: { controlActionId: durableControlActionId, queueItemId: started.item.id },
1895
2136
  controlActionId: durableControlActionId,
1896
- queueItemId: started.item.id,
1897
2137
  });
1898
- }
1899
- catch (error) {
1900
- await svc.releaseSteerProviderSendClaim({
1901
- orgId: conversation.orgId,
1902
- controlActionId: durableControlActionId,
1903
- reason: "steer_requested_event_failed",
1904
- }).catch(() => null);
1905
- throw error;
1906
- }
1907
- return {
1908
- clientMessageId: sendClaim.providerClientMessageId ?? durableControlActionId,
1909
- release: async () => {
1910
- const released = await svc.releaseSteerProviderSendClaim({
2138
+ if (!sendClaim)
2139
+ return null;
2140
+ if ("sendDenied" in sendClaim) {
2141
+ providerSendState.denied = sendClaim;
2142
+ return {
2143
+ sendDenied: true,
2144
+ reason: "generation_fence_changed",
2145
+ };
2146
+ }
2147
+ try {
2148
+ await svc.appendGenerationEvent({
1911
2149
  orgId: conversation.orgId,
2150
+ generationId: durableGenerationId,
2151
+ attemptEpoch: durableAttemptEpoch,
2152
+ eventKind: "steer_requested",
2153
+ payload: { controlActionId: durableControlActionId, queueItemId: started.item.id },
1912
2154
  controlActionId: durableControlActionId,
1913
- reason: "runtime_owner_changed_before_provider_send",
2155
+ queueItemId: started.item.id,
1914
2156
  });
1915
- if (!released) {
1916
- throw new Error("Steer provider send claim could not be safely released before send");
1917
- }
1918
- },
1919
- };
1920
- },
1921
- });
2157
+ }
2158
+ catch (error) {
2159
+ await svc.releaseSteerProviderSendClaim({
2160
+ orgId: conversation.orgId,
2161
+ controlActionId: durableControlActionId,
2162
+ reason: "steer_requested_event_failed",
2163
+ }).catch(() => null);
2164
+ throw error;
2165
+ }
2166
+ return {
2167
+ clientMessageId: sendClaim.providerClientMessageId ?? durableControlActionId,
2168
+ release: async () => {
2169
+ const released = await svc.releaseSteerProviderSendClaim({
2170
+ orgId: conversation.orgId,
2171
+ controlActionId: durableControlActionId,
2172
+ reason: "runtime_owner_changed_before_provider_send",
2173
+ });
2174
+ if (!released) {
2175
+ throw new Error("Steer provider send claim could not be safely released before send");
2176
+ }
2177
+ },
2178
+ };
2179
+ },
2180
+ });
2181
+ }
2182
+ finally {
2183
+ await preparedSteerAttachments.cleanup().catch(() => undefined);
2184
+ }
1922
2185
  if (runtimeResult.status === "provider_send_in_flight") {
1923
2186
  res.json(responseForDurableDisposition(started.item, started.action.localDisposition));
1924
2187
  return;
@@ -2065,7 +2328,14 @@ export function chatRoutes(db, storage, backgroundRuntime = createChatBackground
2065
2328
  });
2066
2329
  });
2067
2330
  router.get("/chats/:id/messages", async (req, res) => {
2068
- const conversation = await assertConversationAccess(req, req.params.id);
2331
+ const requestedOrgId = typeof req.query.orgId === "string"
2332
+ ? req.query.orgId.trim()
2333
+ : undefined;
2334
+ if (req.query.orgId !== undefined && requestedOrgId === undefined) {
2335
+ res.status(404).json({ error: "Chat conversation not found" });
2336
+ return;
2337
+ }
2338
+ const conversation = await assertConversationAccess(req, req.params.id, requestedOrgId);
2069
2339
  if (!conversation) {
2070
2340
  res.status(404).json({ error: "Chat conversation not found" });
2071
2341
  return;
@@ -2094,7 +2364,10 @@ export function chatRoutes(db, storage, backgroundRuntime = createChatBackground
2094
2364
  }
2095
2365
  res.json(transcript);
2096
2366
  });
2097
- router.post("/chats/:id/messages", validate(addChatMessageSchema), async (req, res) => {
2367
+ router.post("/chats/:id/messages", (req, res, next) => {
2368
+ res.locals.inlineAnnotationsProvided = Object.hasOwn(req.body ?? {}, "inlineAnnotations");
2369
+ next();
2370
+ }, validate(addChatMessageSchema), async (req, res) => {
2098
2371
  const conversation = await assertConversationAccess(req, req.params.id);
2099
2372
  if (!conversation) {
2100
2373
  res.status(404).json({ error: "Chat conversation not found" });
@@ -2104,6 +2377,14 @@ export function chatRoutes(db, storage, backgroundRuntime = createChatBackground
2104
2377
  assertChatLocalMutationAllowed(conversation);
2105
2378
  await assertSideChatMutationAllowed(req, conversation);
2106
2379
  if (actor.actorType === "agent") {
2380
+ if (!req.body.body.trim()) {
2381
+ res.status(422).json({ error: "Agent-authored chat messages require a nonempty body" });
2382
+ return;
2383
+ }
2384
+ if (res.locals.inlineAnnotationsProvided === true) {
2385
+ res.status(422).json({ error: "Agent-authored chat messages cannot include response annotations" });
2386
+ return;
2387
+ }
2107
2388
  if (req.body.editUserMessageId) {
2108
2389
  res.status(422).json({ error: "Agent-authored chat messages cannot edit operator messages" });
2109
2390
  return;
@@ -2112,6 +2393,16 @@ export function chatRoutes(db, storage, backgroundRuntime = createChatBackground
2112
2393
  res.status(201).json({ messages: [message] });
2113
2394
  return;
2114
2395
  }
2396
+ const inlineAnnotationsProvided = res.locals.inlineAnnotationsProvided === true;
2397
+ const preparedAnnotations = inlineAnnotationsProvided
2398
+ ? await inlineAnnotations.prepare({
2399
+ orgId: conversation.orgId,
2400
+ conversationId: conversation.id,
2401
+ annotations: req.body.inlineAnnotations ?? [],
2402
+ uploadedFileCount: 0,
2403
+ editUserMessageId: req.body.editUserMessageId ?? null,
2404
+ })
2405
+ : null;
2115
2406
  const assistantAvailability = await assistantSvc.getChatAssistantAvailability(conversation);
2116
2407
  if (!assistantAvailability.available) {
2117
2408
  res.status(503).json({ error: assistantAvailability.error });
@@ -2132,6 +2423,9 @@ export function chatRoutes(db, storage, backgroundRuntime = createChatBackground
2132
2423
  payload: {
2133
2424
  body: req.body.body,
2134
2425
  attachmentIds: [],
2426
+ ...(inlineAnnotationsProvided
2427
+ ? { inlineAnnotations: preparedAnnotations?.annotations ?? [] }
2428
+ : {}),
2135
2429
  skillRefs: [],
2136
2430
  projectId: null,
2137
2431
  accessMode: null,
@@ -2147,7 +2441,10 @@ export function chatRoutes(db, storage, backgroundRuntime = createChatBackground
2147
2441
  return;
2148
2442
  }
2149
2443
  try {
2150
- const userMessage = await addUserMessage(conversation, req.body.body, actor, req.body.editUserMessageId ?? null);
2444
+ const userMessage = await addUserMessage(conversation, req.body.body, actor, req.body.editUserMessageId ?? null, {
2445
+ provided: inlineAnnotationsProvided,
2446
+ prepared: preparedAnnotations,
2447
+ });
2151
2448
  await touchSideChat(req, conversation);
2152
2449
  if (!req.body.editUserMessageId) {
2153
2450
  startChatTitleGeneration(conversation, userMessage);
@@ -2254,6 +2551,11 @@ export function chatRoutes(db, storage, backgroundRuntime = createChatBackground
2254
2551
  assertContextLinksBelongToCompany,
2255
2552
  turnContextFromUserMessage,
2256
2553
  addUserMessage,
2554
+ inlineAnnotations,
2555
+ storeUserMessageFiles,
2556
+ cleanupStoredUserMessageFiles,
2557
+ storeQueuedAnnotationFiles,
2558
+ cleanupUncommittedQueuedAnnotationFiles,
2257
2559
  startChatTitleGeneration,
2258
2560
  attachFilesToUserMessage,
2259
2561
  loadAssistantInput,