@rudderhq/server 0.3.5-canary.8 → 0.3.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 (236) hide show
  1. package/dist/agent-runtimes/registry.d.ts.map +1 -1
  2. package/dist/agent-runtimes/registry.js +17 -2
  3. package/dist/agent-runtimes/registry.js.map +1 -1
  4. package/dist/bootstrap/plugin-host-runtime.d.ts +4 -4
  5. package/dist/bundled-plugins/plugin-linear/dist/worker.js +57 -1
  6. package/dist/bundled-plugins/plugin-linear/dist/worker.js.map +3 -3
  7. package/dist/home-paths.d.ts +13 -0
  8. package/dist/home-paths.d.ts.map +1 -1
  9. package/dist/home-paths.js +141 -9
  10. package/dist/home-paths.js.map +1 -1
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +68 -3
  13. package/dist/index.js.map +1 -1
  14. package/dist/routes/activity.d.ts.map +1 -1
  15. package/dist/routes/activity.js +86 -0
  16. package/dist/routes/activity.js.map +1 -1
  17. package/dist/routes/agents.d.ts.map +1 -1
  18. package/dist/routes/agents.js +9 -3
  19. package/dist/routes/agents.js.map +1 -1
  20. package/dist/routes/agents.management-routes.d.ts.map +1 -1
  21. package/dist/routes/agents.management-routes.js +25 -10
  22. package/dist/routes/agents.management-routes.js.map +1 -1
  23. package/dist/routes/authz.d.ts +1 -0
  24. package/dist/routes/authz.d.ts.map +1 -1
  25. package/dist/routes/authz.js +15 -0
  26. package/dist/routes/authz.js.map +1 -1
  27. package/dist/routes/chats.d.ts.map +1 -1
  28. package/dist/routes/chats.js +242 -6
  29. package/dist/routes/chats.js.map +1 -1
  30. package/dist/routes/chats.stream-routes.d.ts.map +1 -1
  31. package/dist/routes/chats.stream-routes.js +38 -7
  32. package/dist/routes/chats.stream-routes.js.map +1 -1
  33. package/dist/routes/issues.comments-attachments.d.ts.map +1 -1
  34. package/dist/routes/issues.comments-attachments.js +24 -8
  35. package/dist/routes/issues.comments-attachments.js.map +1 -1
  36. package/dist/routes/issues.d.ts.map +1 -1
  37. package/dist/routes/issues.js +10 -0
  38. package/dist/routes/issues.js.map +1 -1
  39. package/dist/routes/messenger.d.ts.map +1 -1
  40. package/dist/routes/messenger.js +55 -1
  41. package/dist/routes/messenger.js.map +1 -1
  42. package/dist/routes/orgs.d.ts.map +1 -1
  43. package/dist/routes/orgs.js +68 -4
  44. package/dist/routes/orgs.js.map +1 -1
  45. package/dist/routes/run-intelligence.d.ts.map +1 -1
  46. package/dist/routes/run-intelligence.js +15 -9
  47. package/dist/routes/run-intelligence.js.map +1 -1
  48. package/dist/services/activity.d.ts +37 -0
  49. package/dist/services/activity.d.ts.map +1 -1
  50. package/dist/services/activity.js +635 -27
  51. package/dist/services/activity.js.map +1 -1
  52. package/dist/services/agents.d.ts +48 -13
  53. package/dist/services/agents.d.ts.map +1 -1
  54. package/dist/services/automation-chat-output.d.ts +1 -0
  55. package/dist/services/automation-chat-output.d.ts.map +1 -1
  56. package/dist/services/automations.d.ts.map +1 -1
  57. package/dist/services/automations.js +35 -5
  58. package/dist/services/automations.js.map +1 -1
  59. package/dist/services/board-auth.d.ts +2 -2
  60. package/dist/services/chat-agent-runs.d.ts +65 -0
  61. package/dist/services/chat-agent-runs.d.ts.map +1 -0
  62. package/dist/services/chat-agent-runs.js +267 -0
  63. package/dist/services/chat-agent-runs.js.map +1 -0
  64. package/dist/services/chat-assistant.d.ts.map +1 -1
  65. package/dist/services/chat-assistant.helpers.d.ts +12 -0
  66. package/dist/services/chat-assistant.helpers.d.ts.map +1 -1
  67. package/dist/services/chat-assistant.helpers.js +23 -1
  68. package/dist/services/chat-assistant.helpers.js.map +1 -1
  69. package/dist/services/chat-assistant.js +312 -177
  70. package/dist/services/chat-assistant.js.map +1 -1
  71. package/dist/services/chats.d.ts +90 -1
  72. package/dist/services/chats.d.ts.map +1 -1
  73. package/dist/services/chats.js +36 -19
  74. package/dist/services/chats.js.map +1 -1
  75. package/dist/services/heartbeat-run-reference.d.ts +9 -0
  76. package/dist/services/heartbeat-run-reference.d.ts.map +1 -0
  77. package/dist/services/heartbeat-run-reference.js +51 -0
  78. package/dist/services/heartbeat-run-reference.js.map +1 -0
  79. package/dist/services/index.d.ts +2 -1
  80. package/dist/services/index.d.ts.map +1 -1
  81. package/dist/services/index.js +2 -1
  82. package/dist/services/index.js.map +1 -1
  83. package/dist/services/integrations/agent-integrations.d.ts +661 -0
  84. package/dist/services/integrations/agent-integrations.d.ts.map +1 -0
  85. package/dist/services/integrations/agent-integrations.js +72 -0
  86. package/dist/services/integrations/agent-integrations.js.map +1 -0
  87. package/dist/services/integrations/feishu/inbound-dispatcher.d.ts +85 -0
  88. package/dist/services/integrations/feishu/inbound-dispatcher.d.ts.map +1 -0
  89. package/dist/services/integrations/feishu/inbound-dispatcher.js +79 -0
  90. package/dist/services/integrations/feishu/inbound-dispatcher.js.map +1 -0
  91. package/dist/services/issues.comments-attachments.d.ts +1 -0
  92. package/dist/services/issues.comments-attachments.d.ts.map +1 -1
  93. package/dist/services/issues.comments-attachments.js +25 -1
  94. package/dist/services/issues.comments-attachments.js.map +1 -1
  95. package/dist/services/issues.d.ts +1 -0
  96. package/dist/services/issues.d.ts.map +1 -1
  97. package/dist/services/issues.helpers.d.ts +1 -0
  98. package/dist/services/issues.helpers.d.ts.map +1 -1
  99. package/dist/services/issues.helpers.js.map +1 -1
  100. package/dist/services/issues.js +17 -2
  101. package/dist/services/issues.js.map +1 -1
  102. package/dist/services/knowledge-portability/organization-skills.catalog.d.ts +1 -0
  103. package/dist/services/knowledge-portability/organization-skills.catalog.d.ts.map +1 -1
  104. package/dist/services/knowledge-portability/organization-skills.catalog.js +18 -0
  105. package/dist/services/knowledge-portability/organization-skills.catalog.js.map +1 -1
  106. package/dist/services/knowledge-portability/organization-skills.d.ts.map +1 -1
  107. package/dist/services/knowledge-portability/organization-skills.js +2 -9
  108. package/dist/services/knowledge-portability/organization-skills.js.map +1 -1
  109. package/dist/services/messenger.d.ts +67 -2
  110. package/dist/services/messenger.d.ts.map +1 -1
  111. package/dist/services/messenger.js +220 -3
  112. package/dist/services/messenger.js.map +1 -1
  113. package/dist/services/organization-intelligence-profiles.js +3 -3
  114. package/dist/services/organization-intelligence-profiles.js.map +1 -1
  115. package/dist/services/organization-skills.d.ts +1 -1
  116. package/dist/services/organization-skills.d.ts.map +1 -1
  117. package/dist/services/organization-skills.js +1 -1
  118. package/dist/services/organization-skills.js.map +1 -1
  119. package/dist/services/organization-workspace-browser.d.ts.map +1 -1
  120. package/dist/services/organization-workspace-browser.js +7 -1
  121. package/dist/services/organization-workspace-browser.js.map +1 -1
  122. package/dist/services/plugin-registry.d.ts +11 -11
  123. package/dist/services/run-intelligence.d.ts +8 -4
  124. package/dist/services/run-intelligence.d.ts.map +1 -1
  125. package/dist/services/run-intelligence.js +25 -14
  126. package/dist/services/run-intelligence.js.map +1 -1
  127. package/dist/services/runtime-kernel/heartbeat.core.d.ts +17 -0
  128. package/dist/services/runtime-kernel/heartbeat.core.d.ts.map +1 -1
  129. package/dist/services/runtime-kernel/heartbeat.core.js +1 -0
  130. package/dist/services/runtime-kernel/heartbeat.core.js.map +1 -1
  131. package/dist/services/runtime-kernel/heartbeat.d.ts +8 -1
  132. package/dist/services/runtime-kernel/heartbeat.d.ts.map +1 -1
  133. package/dist/services/runtime-kernel/heartbeat.js +13 -7
  134. package/dist/services/runtime-kernel/heartbeat.js.map +1 -1
  135. package/dist/services/runtime-kernel/heartbeat.recovery.d.ts.map +1 -1
  136. package/dist/services/runtime-kernel/heartbeat.recovery.js +2 -0
  137. package/dist/services/runtime-kernel/heartbeat.recovery.js.map +1 -1
  138. package/dist/services/workspace-backups.d.ts +20 -0
  139. package/dist/services/workspace-backups.d.ts.map +1 -1
  140. package/dist/services/workspace-backups.js +136 -2
  141. package/dist/services/workspace-backups.js.map +1 -1
  142. package/dist/services/workspace-runtime.services.d.ts +1 -1
  143. package/package.json +13 -13
  144. package/resources/bundled-skills/para-memory-files/SKILL.md +80 -4
  145. package/resources/bundled-skills/para-memory-files/evals/conversation-capture.md +128 -0
  146. package/resources/bundled-skills/para-memory-files/references/schemas.md +31 -0
  147. package/resources/bundled-skills/rudder/SKILL.md +33 -6
  148. package/resources/bundled-skills/rudder/references/api-reference.md +12 -2
  149. package/resources/bundled-skills/rudder/references/cli-reference.md +15 -3
  150. package/resources/bundled-skills/rudder/references/organization-skills.md +2 -1
  151. package/skills/para-memory-files/SKILL.md +80 -4
  152. package/skills/para-memory-files/evals/conversation-capture.md +128 -0
  153. package/skills/para-memory-files/references/schemas.md +31 -0
  154. package/skills/rudder/SKILL.md +33 -6
  155. package/skills/rudder/references/api-reference.md +12 -2
  156. package/skills/rudder/references/cli-reference.md +15 -3
  157. package/skills/rudder/references/organization-skills.md +2 -1
  158. package/ui-dist/assets/{_basePickBy-B4BwdoIV.js → _basePickBy-BB7g62ME.js} +1 -1
  159. package/ui-dist/assets/{_baseUniq-C7tc3qhL.js → _baseUniq-CPTrhgCa.js} +1 -1
  160. package/ui-dist/assets/{arc-3Z1LGjGG.js → arc-B7NTQPhP.js} +1 -1
  161. package/ui-dist/assets/{architectureDiagram-2XIMDMQ5-BkkNzie3.js → architectureDiagram-2XIMDMQ5-Bfd7rJIb.js} +1 -1
  162. package/ui-dist/assets/{blockDiagram-WCTKOSBZ-D4_Pubmg.js → blockDiagram-WCTKOSBZ-BTrM4r_V.js} +1 -1
  163. package/ui-dist/assets/{c4Diagram-IC4MRINW-BHJUaTd0.js → c4Diagram-IC4MRINW-BYtI6FYL.js} +1 -1
  164. package/ui-dist/assets/channel-DURfI6uy.js +1 -0
  165. package/ui-dist/assets/{chunk-4BX2VUAB-Dh6FkpvM.js → chunk-4BX2VUAB--Ppxnmb_.js} +1 -1
  166. package/ui-dist/assets/{chunk-55IACEB6-DfWtoOnG.js → chunk-55IACEB6-CQ83eeoT.js} +1 -1
  167. package/ui-dist/assets/{chunk-FMBD7UC4-ArDgD8Vf.js → chunk-FMBD7UC4-OVtz1nfu.js} +1 -1
  168. package/ui-dist/assets/{chunk-JSJVCQXG-BS6Fjnle.js → chunk-JSJVCQXG-CUm2fIgz.js} +1 -1
  169. package/ui-dist/assets/{chunk-KX2RTZJC-2S_FfPO4.js → chunk-KX2RTZJC-SXc6fGM7.js} +1 -1
  170. package/ui-dist/assets/{chunk-NQ4KR5QH-_BjRKX74.js → chunk-NQ4KR5QH-DwGxW1TD.js} +1 -1
  171. package/ui-dist/assets/{chunk-QZHKN3VN-vCTDSb5R.js → chunk-QZHKN3VN-Ck5sO31S.js} +1 -1
  172. package/ui-dist/assets/{chunk-WL4C6EOR-DgMBcyu0.js → chunk-WL4C6EOR-BUSXe5E8.js} +1 -1
  173. package/ui-dist/assets/classDiagram-VBA2DB6C-dFkERNjc.js +1 -0
  174. package/ui-dist/assets/classDiagram-v2-RAHNMMFH-dFkERNjc.js +1 -0
  175. package/ui-dist/assets/clone-CDyUD1RA.js +1 -0
  176. package/ui-dist/assets/{cose-bilkent-S5V4N54A-CR9BA-ZP.js → cose-bilkent-S5V4N54A-CAc4VjLs.js} +1 -1
  177. package/ui-dist/assets/{dagre-KLK3FWXG-BGVGca6W.js → dagre-KLK3FWXG-8nuHebTb.js} +1 -1
  178. package/ui-dist/assets/{diagram-E7M64L7V-CQh-5XLh.js → diagram-E7M64L7V-Cv94_Ur8.js} +1 -1
  179. package/ui-dist/assets/{diagram-IFDJBPK2-Bz6Z3-eI.js → diagram-IFDJBPK2-DU76Wofo.js} +1 -1
  180. package/ui-dist/assets/{diagram-P4PSJMXO-DXo0ya9e.js → diagram-P4PSJMXO-BY2jd1q0.js} +1 -1
  181. package/ui-dist/assets/{erDiagram-INFDFZHY-BizL-7uO.js → erDiagram-INFDFZHY-D8SNSe_N.js} +1 -1
  182. package/ui-dist/assets/{flowDiagram-PKNHOUZH-Cl-eLTEm.js → flowDiagram-PKNHOUZH-CnyHXWrs.js} +1 -1
  183. package/ui-dist/assets/{ganttDiagram-A5KZAMGK-DHrxdhQZ.js → ganttDiagram-A5KZAMGK-BBQy4AfN.js} +1 -1
  184. package/ui-dist/assets/{gitGraphDiagram-K3NZZRJ6-D2mu5Unv.js → gitGraphDiagram-K3NZZRJ6-No-FiKKg.js} +1 -1
  185. package/ui-dist/assets/{graph-8MaRMOSO.js → graph-DLjJiX57.js} +1 -1
  186. package/ui-dist/assets/{index-51u0-Jyx.js → index-Bg1K4IPR.js} +1 -1
  187. package/ui-dist/assets/{index-CfKyImzq.js → index-BidXCe8A.js} +1 -1
  188. package/ui-dist/assets/{index-BaNUC1Xy.js → index-BmKWlvnC.js} +1 -1
  189. package/ui-dist/assets/{index-D6wAnsbb.js → index-BozRwWt9.js} +1 -1
  190. package/ui-dist/assets/{index-CJbv5MDw.js → index-CCmTOQyL.js} +1 -1
  191. package/ui-dist/assets/{index-DMF5tXyH.js → index-CH7djPxu.js} +1 -1
  192. package/ui-dist/assets/index-CQJhrxim.css +1 -0
  193. package/ui-dist/assets/{index-DxySY24Z.js → index-CVCK0DNv.js} +1 -1
  194. package/ui-dist/assets/{index-CeN0bu6Q.js → index-CgrToN7g.js} +1 -1
  195. package/ui-dist/assets/{index-CHa-RUIJ.js → index-CtHNqKUz.js} +1 -1
  196. package/ui-dist/assets/{index-DLdneek7.js → index-D7FFHYtM.js} +1 -1
  197. package/ui-dist/assets/{index-CLdfPdcX.js → index-DAf6hKOQ.js} +1 -1
  198. package/ui-dist/assets/{index-C9tub0fC.js → index-DRZmptwI.js} +1 -1
  199. package/ui-dist/assets/{index-BIv9urkd.js → index-DZ-V8KVP.js} +1 -1
  200. package/ui-dist/assets/{index-DUUgBZtH.js → index-DZb9m95s.js} +1 -1
  201. package/ui-dist/assets/{index-BlyePK0i.js → index-Dqlt5nna.js} +1 -1
  202. package/ui-dist/assets/{index-zLv-sqgD.js → index-Dr13cMjz.js} +1 -1
  203. package/ui-dist/assets/{index-DwhN2-49.js → index-R3qcUUQM.js} +1 -1
  204. package/ui-dist/assets/{index-7mAikEIC.js → index-Ugzuyk1m.js} +1 -1
  205. package/ui-dist/assets/{index-B7C8xUvr.js → index-_DT1YhSt.js} +1 -1
  206. package/ui-dist/assets/{index-BG1rwhc9.js → index-n2PKHeE9.js} +1 -1
  207. package/ui-dist/assets/{index-BC9B4zYT.js → index-p9AqxRLQ.js} +1 -1
  208. package/ui-dist/assets/index-utOKzUnM.js +1749 -0
  209. package/ui-dist/assets/{index-CqgSZG0h.js → index-v7rD-G_w.js} +1 -1
  210. package/ui-dist/assets/{infoDiagram-LFFYTUFH-CRqn4eW3.js → infoDiagram-LFFYTUFH-B6T3mObQ.js} +1 -1
  211. package/ui-dist/assets/{ishikawaDiagram-PHBUUO56-Dy1Y_tsb.js → ishikawaDiagram-PHBUUO56-D6ffyu9A.js} +1 -1
  212. package/ui-dist/assets/{journeyDiagram-4ABVD52K-x17-NUoy.js → journeyDiagram-4ABVD52K-CT1nha3u.js} +1 -1
  213. package/ui-dist/assets/{kanban-definition-K7BYSVSG-D7pBzY35.js → kanban-definition-K7BYSVSG-d8GJ4n_-.js} +1 -1
  214. package/ui-dist/assets/{layout-KzYwZI06.js → layout-gL287YTy.js} +1 -1
  215. package/ui-dist/assets/{linear-C-FBXrsa.js → linear-Bf1ea6Jq.js} +1 -1
  216. package/ui-dist/assets/{mermaid.core-BLYszySg.js → mermaid.core-dHWytwA_.js} +4 -4
  217. package/ui-dist/assets/{mindmap-definition-YRQLILUH-Cj-MYQN5.js → mindmap-definition-YRQLILUH-JrUFNpGy.js} +1 -1
  218. package/ui-dist/assets/{pieDiagram-SKSYHLDU-EKoIxWYC.js → pieDiagram-SKSYHLDU-D4Wfs-K5.js} +1 -1
  219. package/ui-dist/assets/{quadrantDiagram-337W2JSQ-CH4T6qfA.js → quadrantDiagram-337W2JSQ-BVITuQui.js} +1 -1
  220. package/ui-dist/assets/{requirementDiagram-Z7DCOOCP-DVBLVjct.js → requirementDiagram-Z7DCOOCP-CwJeuuXP.js} +1 -1
  221. package/ui-dist/assets/{sankeyDiagram-WA2Y5GQK-BVwJzJkB.js → sankeyDiagram-WA2Y5GQK-Cj7QPF5g.js} +1 -1
  222. package/ui-dist/assets/{sequenceDiagram-2WXFIKYE-r6tmX9Pe.js → sequenceDiagram-2WXFIKYE-CaCPs0da.js} +1 -1
  223. package/ui-dist/assets/{stateDiagram-RAJIS63D-D9Ke-MDr.js → stateDiagram-RAJIS63D-DLlYG-8N.js} +1 -1
  224. package/ui-dist/assets/stateDiagram-v2-FVOUBMTO-I_TDliKE.js +1 -0
  225. package/ui-dist/assets/{timeline-definition-YZTLITO2-BuyA_SOw.js → timeline-definition-YZTLITO2-C_G6klSp.js} +1 -1
  226. package/ui-dist/assets/{treemap-KZPCXAKY-4eE6Nrub.js → treemap-KZPCXAKY-BLd4RSVW.js} +1 -1
  227. package/ui-dist/assets/{vennDiagram-LZ73GAT5-a7mtyRLG.js → vennDiagram-LZ73GAT5-CxeNzJkA.js} +1 -1
  228. package/ui-dist/assets/{xychartDiagram-JWTSCODW-YFwfEfwJ.js → xychartDiagram-JWTSCODW-BJEoZfiu.js} +1 -1
  229. package/ui-dist/index.html +2 -2
  230. package/ui-dist/assets/channel-BpdtXxmS.js +0 -1
  231. package/ui-dist/assets/classDiagram-VBA2DB6C-WRGNaicM.js +0 -1
  232. package/ui-dist/assets/classDiagram-v2-RAHNMMFH-WRGNaicM.js +0 -1
  233. package/ui-dist/assets/clone-DU900k2i.js +0 -1
  234. package/ui-dist/assets/index-C3YeN_zZ.js +0 -1734
  235. package/ui-dist/assets/index-dNVw4Dq5.css +0 -1
  236. package/ui-dist/assets/stateDiagram-v2-FVOUBMTO-B4N5GfLf.js +0 -1
@@ -1,6 +1,143 @@
1
- import { activityLog, chatContextLinks, chatConversations, heartbeatRuns, issues } from "@rudderhq/db";
2
- import { isLowSignalIssueContentOnlyUpdate } from "@rudderhq/shared";
3
- import { and, desc, eq, isNotNull, isNull, ne, or, sql } from "drizzle-orm";
1
+ import { activityLog, agents, approvalComments, approvals, chatContextLinks, chatConversations, chatMessages, heartbeatRuns, issueComments, issues, operatorProfiles, } from "@rudderhq/db";
2
+ import { isLowSignalIssueContentOnlyUpdate, } from "@rudderhq/shared";
3
+ import { and, asc, desc, eq, gte, isNotNull, isNull, lt, lte, ne, or, sql } from "drizzle-orm";
4
+ import { badRequest } from "../errors.js";
5
+ import { issueLowSignalContentOnlyActivitySql } from "./issue-activity-filters.js";
6
+ const DEFAULT_ACTIVITY_PAGE_LIMIT = 30;
7
+ const MAX_ACTIVITY_PAGE_LIMIT = 100;
8
+ const ACTIVITY_CURSOR_TIMESTAMP_RE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{6}Z$/;
9
+ const USER_ACTIVITY_CURSOR_TIMESTAMP_RE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/;
10
+ const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
11
+ const DEFAULT_USER_ACTIVITY_PAGE_LIMIT = 30;
12
+ const MAX_USER_ACTIVITY_PAGE_LIMIT = 100;
13
+ const DEFAULT_USER_ACTIVITY_INCLUDES = [
14
+ "chat",
15
+ "comments",
16
+ "approvals",
17
+ "activity",
18
+ ];
19
+ function normalizeActivityPageLimit(limit) {
20
+ if (limit === undefined)
21
+ return DEFAULT_ACTIVITY_PAGE_LIMIT;
22
+ if (!Number.isFinite(limit))
23
+ throw badRequest("invalid 'limit' value");
24
+ const normalized = Math.floor(limit);
25
+ if (normalized < 1 || normalized > MAX_ACTIVITY_PAGE_LIMIT) {
26
+ throw badRequest(`'limit' must be between 1 and ${MAX_ACTIVITY_PAGE_LIMIT}`);
27
+ }
28
+ return normalized;
29
+ }
30
+ function encodeActivityCursor(row) {
31
+ return Buffer.from(JSON.stringify({
32
+ createdAt: row.cursorCreatedAt,
33
+ id: row.activityLog.id,
34
+ })).toString("base64url");
35
+ }
36
+ function decodeActivityCursor(cursor) {
37
+ if (!cursor)
38
+ return null;
39
+ try {
40
+ const decoded = JSON.parse(Buffer.from(cursor, "base64url").toString("utf8"));
41
+ if (typeof decoded.id !== "string"
42
+ || typeof decoded.createdAt !== "string"
43
+ || !UUID_RE.test(decoded.id)
44
+ || !ACTIVITY_CURSOR_TIMESTAMP_RE.test(decoded.createdAt)
45
+ || Number.isNaN(new Date(decoded.createdAt).getTime())) {
46
+ throw new Error("invalid cursor");
47
+ }
48
+ return { id: decoded.id, createdAt: decoded.createdAt };
49
+ }
50
+ catch {
51
+ throw badRequest("Activity cursor is invalid or expired");
52
+ }
53
+ }
54
+ function normalizeUserActivityPageLimit(limit) {
55
+ if (limit === undefined)
56
+ return DEFAULT_USER_ACTIVITY_PAGE_LIMIT;
57
+ if (!Number.isFinite(limit))
58
+ throw badRequest("invalid 'limit' value");
59
+ const normalized = Math.floor(limit);
60
+ if (normalized < 1 || normalized > MAX_USER_ACTIVITY_PAGE_LIMIT) {
61
+ throw badRequest(`'limit' must be between 1 and ${MAX_USER_ACTIVITY_PAGE_LIMIT}`);
62
+ }
63
+ return normalized;
64
+ }
65
+ function normalizeUserActivityIncludes(include) {
66
+ const values = include && include.length > 0 ? include : DEFAULT_USER_ACTIVITY_INCLUDES;
67
+ return new Set(values);
68
+ }
69
+ function encodeUserActivityCursor(item) {
70
+ return Buffer.from(JSON.stringify({
71
+ occurredAt: item.occurredAt,
72
+ kind: item.kind,
73
+ id: item.id,
74
+ })).toString("base64url");
75
+ }
76
+ function decodeUserActivityCursor(cursor) {
77
+ if (!cursor)
78
+ return null;
79
+ try {
80
+ const decoded = JSON.parse(Buffer.from(cursor, "base64url").toString("utf8"));
81
+ if (typeof decoded.id !== "string"
82
+ || typeof decoded.kind !== "string"
83
+ || typeof decoded.occurredAt !== "string"
84
+ || !UUID_RE.test(decoded.id)
85
+ || !USER_ACTIVITY_CURSOR_TIMESTAMP_RE.test(decoded.occurredAt)
86
+ || Number.isNaN(new Date(decoded.occurredAt).getTime())) {
87
+ throw new Error("invalid cursor");
88
+ }
89
+ return {
90
+ id: decoded.id,
91
+ kind: decoded.kind,
92
+ occurredAt: decoded.occurredAt,
93
+ };
94
+ }
95
+ catch {
96
+ throw badRequest("User activity cursor is invalid or expired");
97
+ }
98
+ }
99
+ function compareUserActivityItems(a, b) {
100
+ const timeDiff = Date.parse(b.occurredAt) - Date.parse(a.occurredAt);
101
+ if (timeDiff !== 0)
102
+ return timeDiff;
103
+ const kindDiff = a.kind.localeCompare(b.kind);
104
+ if (kindDiff !== 0)
105
+ return kindDiff;
106
+ return a.id.localeCompare(b.id);
107
+ }
108
+ function itemIsAfterCursor(item, cursor) {
109
+ if (!cursor)
110
+ return true;
111
+ return compareUserActivityItems(item, {
112
+ id: cursor.id,
113
+ kind: cursor.kind,
114
+ occurredAt: cursor.occurredAt,
115
+ userId: item.userId,
116
+ actor: item.actor,
117
+ summary: "",
118
+ excerpt: null,
119
+ source: item.source,
120
+ related: [],
121
+ }) > 0;
122
+ }
123
+ function safeExcerpt(value, maxLength = 240) {
124
+ const collapsed = value?.replace(/\s+/g, " ").trim();
125
+ if (!collapsed)
126
+ return null;
127
+ if (collapsed.length <= maxLength)
128
+ return collapsed;
129
+ return `${collapsed.slice(0, maxLength - 3).trimEnd()}...`;
130
+ }
131
+ function titleFromDetails(details) {
132
+ if (!details)
133
+ return null;
134
+ for (const key of ["title", "issueTitle", "name", "summary"]) {
135
+ const value = details[key];
136
+ if (typeof value === "string" && value.trim())
137
+ return value.trim();
138
+ }
139
+ return null;
140
+ }
4
141
  function isLowSignalContentOnlyIssueUpdate(event) {
5
142
  return isLowSignalIssueContentOnlyUpdate(event.action, event.details);
6
143
  }
@@ -18,39 +155,510 @@ function shouldShowOrganizationActivity(event) {
18
155
  }
19
156
  export function activityService(db) {
20
157
  const issueIdAsText = sql `${issues.id}::text`;
158
+ const approvalIdAsText = sql `${approvals.id}::text`;
21
159
  const conversationIdAsText = sql `${chatConversations.id}::text`;
160
+ const organizationActivityVisibleCondition = and(ne(activityLog.action, "issue.read_marked"), sql `not (${issueLowSignalContentOnlyActivitySql("activity_log")})`);
161
+ const activityCursorCreatedAt = sql `to_char(${activityLog.createdAt} AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.US"Z"')`;
162
+ function userActivityCursorCondition(createdAtColumn, idColumn, kind, cursor) {
163
+ if (!cursor)
164
+ return undefined;
165
+ const cursorDate = new Date(cursor.occurredAt);
166
+ const cursorTimestamp = cursor.occurredAt;
167
+ const kindOrder = kind.localeCompare(cursor.kind);
168
+ const sameTimestampCondition = kindOrder > 0
169
+ ? sql `${createdAtColumn} = ${cursorTimestamp}::timestamptz`
170
+ : kindOrder === 0
171
+ ? and(sql `${createdAtColumn} = ${cursorTimestamp}::timestamptz`, sql `${idColumn}::text > ${cursor.id}`)
172
+ : undefined;
173
+ return sameTimestampCondition
174
+ ? or(lt(createdAtColumn, cursorDate), sameTimestampCondition)
175
+ : lt(createdAtColumn, cursorDate);
176
+ }
177
+ function timeConditions(createdAtColumn, filters) {
178
+ const conditions = [];
179
+ if (filters.since)
180
+ conditions.push(gte(createdAtColumn, filters.since));
181
+ if (filters.until)
182
+ conditions.push(lte(createdAtColumn, filters.until));
183
+ return conditions;
184
+ }
185
+ function approvalLinkedIssueCondition(issueId) {
186
+ return or(sql `${approvals.payload}->>'issueId' = ${issueId}`, sql `${approvals.payload}->>'primaryIssueId' = ${issueId}`, sql `${approvals.payload}->'issueIds' ? ${issueId}`);
187
+ }
188
+ function approvalProjectCondition(projectId) {
189
+ return or(sql `${approvals.payload}->>'projectId' = ${projectId}`, sql `exists (
190
+ select 1
191
+ from ${issues}
192
+ where ${issues.orgId} = ${approvals.orgId}
193
+ and ${issues.projectId} = ${projectId}
194
+ and ${issues.hiddenAt} is null
195
+ and (
196
+ ${issues.id}::text = ${approvals.payload}->>'issueId'
197
+ or ${issues.id}::text = ${approvals.payload}->>'primaryIssueId'
198
+ or ${approvals.payload}->'issueIds' ? ${issues.id}::text
199
+ )
200
+ )`);
201
+ }
202
+ function withUserActor(userId, displayName) {
203
+ return {
204
+ type: "user",
205
+ id: userId,
206
+ displayName: displayName ?? null,
207
+ };
208
+ }
209
+ function activityRowWithIssueDetails(row) {
210
+ if (row.activityLog.entityType !== "issue")
211
+ return row.activityLog;
212
+ if (!row.issueIdentifier && !row.issueTitle)
213
+ return row.activityLog;
214
+ const details = row.activityLog.details ?? {};
215
+ return {
216
+ ...row.activityLog,
217
+ details: {
218
+ ...details,
219
+ ...(row.issueIdentifier ? { issueIdentifier: row.issueIdentifier } : {}),
220
+ ...(row.issueTitle ? { issueTitle: row.issueTitle } : {}),
221
+ ...(row.issueIdentifier && typeof details.identifier !== "string" ? { identifier: row.issueIdentifier } : {}),
222
+ ...(row.issueTitle && typeof details.title !== "string" ? { title: row.issueTitle } : {}),
223
+ },
224
+ };
225
+ }
226
+ function organizationActivityConditions(filters) {
227
+ const conditions = [eq(activityLog.orgId, filters.orgId)];
228
+ if (organizationActivityVisibleCondition)
229
+ conditions.push(organizationActivityVisibleCondition);
230
+ if (filters.agentId) {
231
+ const agentCondition = or(eq(activityLog.agentId, filters.agentId), and(eq(activityLog.actorType, "agent"), eq(activityLog.actorId, filters.agentId)));
232
+ if (agentCondition)
233
+ conditions.push(agentCondition);
234
+ }
235
+ if (filters.userId) {
236
+ conditions.push(eq(activityLog.actorType, "user"));
237
+ conditions.push(eq(activityLog.actorId, filters.userId));
238
+ }
239
+ if (filters.actorType) {
240
+ conditions.push(eq(activityLog.actorType, filters.actorType));
241
+ }
242
+ if (filters.actorId) {
243
+ conditions.push(eq(activityLog.actorId, filters.actorId));
244
+ }
245
+ if (filters.entityType) {
246
+ conditions.push(eq(activityLog.entityType, filters.entityType));
247
+ }
248
+ if (filters.entityId) {
249
+ conditions.push(eq(activityLog.entityId, filters.entityId));
250
+ }
251
+ return conditions;
252
+ }
22
253
  return {
23
- list: (filters) => {
24
- const conditions = [eq(activityLog.orgId, filters.orgId)];
25
- conditions.push(ne(activityLog.action, "issue.read_marked"));
26
- if (filters.agentId) {
27
- const agentCondition = or(eq(activityLog.agentId, filters.agentId), and(eq(activityLog.actorType, "agent"), eq(activityLog.actorId, filters.agentId)));
28
- if (agentCondition)
29
- conditions.push(agentCondition);
30
- }
31
- if (filters.userId) {
32
- conditions.push(eq(activityLog.actorType, "user"));
33
- conditions.push(eq(activityLog.actorId, filters.userId));
34
- }
35
- if (filters.actorType) {
36
- conditions.push(eq(activityLog.actorType, filters.actorType));
254
+ listUserActivityLedger: async (filters) => {
255
+ const limit = normalizeUserActivityPageLimit(filters.limit);
256
+ const cursor = decodeUserActivityCursor(filters.cursor);
257
+ const includes = normalizeUserActivityIncludes(filters.include);
258
+ const perSourceLimit = limit + 1;
259
+ const [profile] = await db
260
+ .select({ nickname: operatorProfiles.nickname })
261
+ .from(operatorProfiles)
262
+ .where(eq(operatorProfiles.userId, filters.userId))
263
+ .limit(1);
264
+ const actor = withUserActor(filters.userId, profile?.nickname);
265
+ const items = [];
266
+ if (includes.has("chat")) {
267
+ const conditions = [
268
+ eq(chatMessages.orgId, filters.orgId),
269
+ eq(chatMessages.role, "user"),
270
+ eq(chatConversations.orgId, filters.orgId),
271
+ eq(chatConversations.createdByUserId, filters.userId),
272
+ isNull(chatMessages.supersededAt),
273
+ ...timeConditions(chatMessages.createdAt, filters),
274
+ ];
275
+ const cursorSql = userActivityCursorCondition(chatMessages.createdAt, chatMessages.id, "chat_message", cursor);
276
+ if (cursorSql)
277
+ conditions.push(cursorSql);
278
+ if (filters.agentId) {
279
+ conditions.push(or(eq(chatMessages.replyingAgentId, filters.agentId), eq(chatConversations.preferredAgentId, filters.agentId), eq(chatConversations.routedAgentId, filters.agentId)));
280
+ }
281
+ if (filters.projectId) {
282
+ conditions.push(eq(issues.projectId, filters.projectId));
283
+ }
284
+ if (filters.issueId) {
285
+ conditions.push(eq(chatConversations.primaryIssueId, filters.issueId));
286
+ }
287
+ const rows = await db
288
+ .select({
289
+ message: chatMessages,
290
+ conversationTitle: chatConversations.title,
291
+ preferredAgentId: chatConversations.preferredAgentId,
292
+ routedAgentId: chatConversations.routedAgentId,
293
+ primaryIssueId: chatConversations.primaryIssueId,
294
+ issueIdentifier: issues.identifier,
295
+ issueTitle: issues.title,
296
+ issueProjectId: issues.projectId,
297
+ issueGoalId: issues.goalId,
298
+ })
299
+ .from(chatMessages)
300
+ .innerJoin(chatConversations, and(eq(chatMessages.conversationId, chatConversations.id), eq(chatMessages.orgId, chatConversations.orgId)))
301
+ .leftJoin(issues, and(eq(chatConversations.primaryIssueId, issues.id), eq(chatConversations.orgId, issues.orgId), isNull(issues.hiddenAt)))
302
+ .where(and(...conditions))
303
+ .orderBy(desc(chatMessages.createdAt), asc(chatMessages.id))
304
+ .limit(perSourceLimit);
305
+ for (const row of rows) {
306
+ const related = [
307
+ { type: "chat", id: row.message.conversationId, label: row.conversationTitle },
308
+ ];
309
+ if (row.message.replyingAgentId)
310
+ related.push({ type: "agent", id: row.message.replyingAgentId });
311
+ if (row.preferredAgentId && row.preferredAgentId !== row.message.replyingAgentId) {
312
+ related.push({ type: "agent", id: row.preferredAgentId });
313
+ }
314
+ if (row.routedAgentId && row.routedAgentId !== row.message.replyingAgentId && row.routedAgentId !== row.preferredAgentId) {
315
+ related.push({ type: "agent", id: row.routedAgentId });
316
+ }
317
+ if (row.primaryIssueId) {
318
+ related.push({
319
+ type: "issue",
320
+ id: row.primaryIssueId,
321
+ label: row.issueIdentifier ?? row.issueTitle,
322
+ });
323
+ }
324
+ if (row.issueProjectId)
325
+ related.push({ type: "project", id: row.issueProjectId });
326
+ if (row.issueGoalId)
327
+ related.push({ type: "goal", id: row.issueGoalId });
328
+ items.push({
329
+ id: row.message.id,
330
+ kind: "chat_message",
331
+ occurredAt: row.message.createdAt.toISOString(),
332
+ userId: filters.userId,
333
+ actor,
334
+ summary: `User message in chat: ${row.conversationTitle || "Untitled chat"}`,
335
+ excerpt: safeExcerpt(row.message.body),
336
+ source: {
337
+ type: "chat",
338
+ id: row.message.conversationId,
339
+ link: `chat://${row.message.conversationId}`,
340
+ provenance: {
341
+ table: "chat_messages",
342
+ id: row.message.id,
343
+ orgId: row.message.orgId,
344
+ },
345
+ },
346
+ related,
347
+ metadata: {
348
+ messageId: row.message.id,
349
+ messageKind: row.message.kind,
350
+ messageStatus: row.message.status,
351
+ runId: row.message.runId,
352
+ },
353
+ });
354
+ }
37
355
  }
38
- if (filters.actorId) {
39
- conditions.push(eq(activityLog.actorId, filters.actorId));
356
+ if (includes.has("comments") || includes.has("issues")) {
357
+ const conditions = [
358
+ eq(issueComments.orgId, filters.orgId),
359
+ eq(issueComments.authorUserId, filters.userId),
360
+ eq(issues.orgId, filters.orgId),
361
+ isNull(issueComments.deletedAt),
362
+ isNull(issues.hiddenAt),
363
+ ...timeConditions(issueComments.createdAt, filters),
364
+ ];
365
+ const cursorSql = userActivityCursorCondition(issueComments.createdAt, issueComments.id, "issue_comment", cursor);
366
+ if (cursorSql)
367
+ conditions.push(cursorSql);
368
+ if (filters.projectId)
369
+ conditions.push(eq(issues.projectId, filters.projectId));
370
+ if (filters.issueId)
371
+ conditions.push(eq(issueComments.issueId, filters.issueId));
372
+ if (filters.agentId) {
373
+ conditions.push(or(eq(issues.assigneeAgentId, filters.agentId), eq(issues.reviewerAgentId, filters.agentId)));
374
+ }
375
+ const rows = await db
376
+ .select({
377
+ comment: issueComments,
378
+ issueIdentifier: issues.identifier,
379
+ issueTitle: issues.title,
380
+ issueStatus: issues.status,
381
+ projectId: issues.projectId,
382
+ goalId: issues.goalId,
383
+ assigneeAgentId: issues.assigneeAgentId,
384
+ reviewerAgentId: issues.reviewerAgentId,
385
+ })
386
+ .from(issueComments)
387
+ .innerJoin(issues, and(eq(issueComments.issueId, issues.id), eq(issueComments.orgId, issues.orgId)))
388
+ .where(and(...conditions))
389
+ .orderBy(desc(issueComments.createdAt), asc(issueComments.id))
390
+ .limit(perSourceLimit);
391
+ for (const row of rows) {
392
+ const related = [
393
+ {
394
+ type: "issue",
395
+ id: row.comment.issueId,
396
+ label: row.issueIdentifier ?? row.issueTitle,
397
+ },
398
+ ];
399
+ if (row.projectId)
400
+ related.push({ type: "project", id: row.projectId });
401
+ if (row.goalId)
402
+ related.push({ type: "goal", id: row.goalId });
403
+ if (row.assigneeAgentId)
404
+ related.push({ type: "agent", id: row.assigneeAgentId });
405
+ if (row.reviewerAgentId && row.reviewerAgentId !== row.assigneeAgentId) {
406
+ related.push({ type: "agent", id: row.reviewerAgentId });
407
+ }
408
+ items.push({
409
+ id: row.comment.id,
410
+ kind: "issue_comment",
411
+ occurredAt: row.comment.createdAt.toISOString(),
412
+ userId: filters.userId,
413
+ actor,
414
+ summary: `User commented on ${row.issueIdentifier ?? "issue"}: ${row.issueTitle}`,
415
+ excerpt: safeExcerpt(row.comment.body),
416
+ source: {
417
+ type: "comment",
418
+ id: row.comment.id,
419
+ link: `issue://${row.comment.issueId}?c=${row.comment.id}`,
420
+ provenance: {
421
+ table: "issue_comments",
422
+ id: row.comment.id,
423
+ orgId: row.comment.orgId,
424
+ },
425
+ },
426
+ related,
427
+ metadata: {
428
+ issueId: row.comment.issueId,
429
+ issueIdentifier: row.issueIdentifier,
430
+ issueStatus: row.issueStatus,
431
+ },
432
+ });
433
+ }
40
434
  }
41
- if (filters.entityType) {
42
- conditions.push(eq(activityLog.entityType, filters.entityType));
435
+ if (includes.has("approvals")) {
436
+ const conditions = [
437
+ eq(approvalComments.orgId, filters.orgId),
438
+ eq(approvalComments.authorUserId, filters.userId),
439
+ eq(approvals.orgId, filters.orgId),
440
+ ...timeConditions(approvalComments.createdAt, filters),
441
+ ];
442
+ const cursorSql = userActivityCursorCondition(approvalComments.createdAt, approvalComments.id, "approval_comment", cursor);
443
+ if (cursorSql)
444
+ conditions.push(cursorSql);
445
+ if (filters.agentId)
446
+ conditions.push(eq(approvals.requestedByAgentId, filters.agentId));
447
+ if (filters.issueId) {
448
+ conditions.push(approvalLinkedIssueCondition(filters.issueId));
449
+ }
450
+ if (filters.projectId) {
451
+ conditions.push(approvalProjectCondition(filters.projectId));
452
+ }
453
+ const rows = await db
454
+ .select({
455
+ comment: approvalComments,
456
+ approvalType: approvals.type,
457
+ approvalStatus: approvals.status,
458
+ requestedByAgentId: approvals.requestedByAgentId,
459
+ })
460
+ .from(approvalComments)
461
+ .innerJoin(approvals, and(eq(approvalComments.approvalId, approvals.id), eq(approvalComments.orgId, approvals.orgId)))
462
+ .where(and(...conditions))
463
+ .orderBy(desc(approvalComments.createdAt), asc(approvalComments.id))
464
+ .limit(perSourceLimit);
465
+ for (const row of rows) {
466
+ const related = [
467
+ { type: "approval", id: row.comment.approvalId, label: row.approvalType },
468
+ ];
469
+ if (row.requestedByAgentId)
470
+ related.push({ type: "agent", id: row.requestedByAgentId });
471
+ items.push({
472
+ id: row.comment.id,
473
+ kind: "approval_comment",
474
+ occurredAt: row.comment.createdAt.toISOString(),
475
+ userId: filters.userId,
476
+ actor,
477
+ summary: `User commented on ${row.approvalType} approval`,
478
+ excerpt: safeExcerpt(row.comment.body),
479
+ source: {
480
+ type: "approval",
481
+ id: row.comment.approvalId,
482
+ link: `approval://${row.comment.approvalId}?c=${row.comment.id}`,
483
+ provenance: {
484
+ table: "approval_comments",
485
+ id: row.comment.id,
486
+ orgId: row.comment.orgId,
487
+ },
488
+ },
489
+ related,
490
+ metadata: {
491
+ approvalId: row.comment.approvalId,
492
+ approvalType: row.approvalType,
493
+ approvalStatus: row.approvalStatus,
494
+ },
495
+ });
496
+ }
43
497
  }
44
- if (filters.entityId) {
45
- conditions.push(eq(activityLog.entityId, filters.entityId));
498
+ if (includes.has("activity") || includes.has("issues") || includes.has("approvals")) {
499
+ const activityIncludeCondition = includes.has("activity")
500
+ ? undefined
501
+ : or(includes.has("issues") ? eq(activityLog.entityType, "issue") : undefined, includes.has("approvals") ? eq(activityLog.entityType, "approval") : undefined);
502
+ const conditions = [
503
+ eq(activityLog.orgId, filters.orgId),
504
+ eq(activityLog.actorType, "user"),
505
+ eq(activityLog.actorId, filters.userId),
506
+ ne(activityLog.action, "issue.comment_added"),
507
+ ne(activityLog.action, "approval.comment_added"),
508
+ ne(activityLog.action, "issue.read_marked"),
509
+ sql `not (${issueLowSignalContentOnlyActivitySql("activity_log")})`,
510
+ ...timeConditions(activityLog.createdAt, filters),
511
+ ];
512
+ if (activityIncludeCondition)
513
+ conditions.push(activityIncludeCondition);
514
+ const cursorSql = userActivityCursorCondition(activityLog.createdAt, activityLog.id, "activity_event", cursor);
515
+ if (cursorSql)
516
+ conditions.push(cursorSql);
517
+ if (filters.agentId) {
518
+ conditions.push(or(eq(activityLog.agentId, filters.agentId), and(eq(activityLog.actorType, "agent"), eq(activityLog.actorId, filters.agentId)), eq(approvals.requestedByAgentId, filters.agentId)));
519
+ }
520
+ if (filters.issueId) {
521
+ conditions.push(or(and(eq(activityLog.entityType, "issue"), eq(activityLog.entityId, filters.issueId)), and(eq(activityLog.entityType, "approval"), approvalLinkedIssueCondition(filters.issueId))));
522
+ }
523
+ if (filters.projectId) {
524
+ conditions.push(or(and(eq(activityLog.entityType, "project"), eq(activityLog.entityId, filters.projectId)), eq(issues.projectId, filters.projectId), and(eq(activityLog.entityType, "approval"), approvalProjectCondition(filters.projectId))));
525
+ }
526
+ const rows = await db
527
+ .select({
528
+ event: activityLog,
529
+ issueIdentifier: issues.identifier,
530
+ issueTitle: issues.title,
531
+ issueProjectId: issues.projectId,
532
+ issueGoalId: issues.goalId,
533
+ agentName: agents.name,
534
+ approvalType: approvals.type,
535
+ approvalRequestedByAgentId: approvals.requestedByAgentId,
536
+ })
537
+ .from(activityLog)
538
+ .leftJoin(issues, and(eq(activityLog.entityType, sql `'issue'`), eq(issues.orgId, activityLog.orgId), eq(activityLog.entityId, issueIdAsText), isNull(issues.hiddenAt)))
539
+ .leftJoin(approvals, and(eq(activityLog.entityType, sql `'approval'`), eq(approvals.orgId, activityLog.orgId), eq(activityLog.entityId, approvalIdAsText)))
540
+ .leftJoin(agents, and(eq(activityLog.agentId, agents.id), eq(activityLog.orgId, agents.orgId)))
541
+ .where(and(...conditions))
542
+ .orderBy(desc(activityLog.createdAt), asc(activityLog.id))
543
+ .limit(perSourceLimit);
544
+ for (const row of rows) {
545
+ if (row.event.entityType === "issue" && !row.issueIdentifier && !row.issueTitle)
546
+ continue;
547
+ if (row.event.entityType === "approval" && !row.approvalType)
548
+ continue;
549
+ const details = row.event.details ?? null;
550
+ const related = [];
551
+ if (row.event.agentId)
552
+ related.push({ type: "agent", id: row.event.agentId, label: row.agentName });
553
+ if (row.approvalRequestedByAgentId && row.approvalRequestedByAgentId !== row.event.agentId) {
554
+ related.push({ type: "agent", id: row.approvalRequestedByAgentId });
555
+ }
556
+ if (row.event.runId)
557
+ related.push({ type: "run", id: row.event.runId });
558
+ if (row.event.entityType === "issue") {
559
+ related.push({
560
+ type: "issue",
561
+ id: row.event.entityId,
562
+ label: row.issueIdentifier ?? row.issueTitle,
563
+ });
564
+ }
565
+ if (row.event.entityType === "chat")
566
+ related.push({ type: "chat", id: row.event.entityId });
567
+ if (row.event.entityType === "approval") {
568
+ related.push({ type: "approval", id: row.event.entityId, label: row.approvalType });
569
+ }
570
+ if (row.event.entityType === "project")
571
+ related.push({ type: "project", id: row.event.entityId });
572
+ if (row.issueProjectId)
573
+ related.push({ type: "project", id: row.issueProjectId });
574
+ if (row.issueGoalId)
575
+ related.push({ type: "goal", id: row.issueGoalId });
576
+ const detailTitle = titleFromDetails(details);
577
+ items.push({
578
+ id: row.event.id,
579
+ kind: "activity_event",
580
+ occurredAt: row.event.createdAt.toISOString(),
581
+ userId: filters.userId,
582
+ actor,
583
+ summary: detailTitle
584
+ ? `User ${row.event.action} on ${row.event.entityType}: ${detailTitle}`
585
+ : `User ${row.event.action} on ${row.event.entityType}`,
586
+ excerpt: safeExcerpt(typeof details?.note === "string"
587
+ ? details.note
588
+ : typeof details?.comment === "string"
589
+ ? details.comment
590
+ : null),
591
+ source: {
592
+ type: "activity",
593
+ id: row.event.id,
594
+ link: row.event.entityType === "issue" ? `issue://${row.event.entityId}` : null,
595
+ provenance: {
596
+ table: "activity_log",
597
+ id: row.event.id,
598
+ orgId: row.event.orgId,
599
+ },
600
+ },
601
+ related,
602
+ metadata: {
603
+ action: row.event.action,
604
+ entityType: row.event.entityType,
605
+ entityId: row.event.entityId,
606
+ details,
607
+ },
608
+ });
609
+ }
46
610
  }
611
+ const sortedItems = items
612
+ .filter((item) => itemIsAfterCursor(item, cursor))
613
+ .sort(compareUserActivityItems);
614
+ const pageItems = sortedItems.slice(0, limit);
615
+ return {
616
+ items: pageItems,
617
+ nextCursor: sortedItems.length > limit && pageItems.length > 0
618
+ ? encodeUserActivityCursor(pageItems[pageItems.length - 1])
619
+ : null,
620
+ };
621
+ },
622
+ list: (filters) => {
623
+ const conditions = organizationActivityConditions(filters);
47
624
  return db
48
- .select({ activityLog })
625
+ .select({
626
+ activityLog,
627
+ issueIdentifier: issues.identifier,
628
+ issueTitle: issues.title,
629
+ })
630
+ .from(activityLog)
631
+ .leftJoin(issues, and(eq(activityLog.entityType, sql `'issue'`), eq(issues.orgId, activityLog.orgId), eq(activityLog.entityId, issueIdAsText)))
632
+ .where(and(...conditions, or(sql `${activityLog.entityType} != 'issue'`, isNull(issues.hiddenAt))))
633
+ .orderBy(desc(activityLog.createdAt), desc(activityLog.id))
634
+ .then((rows) => rows.map(activityRowWithIssueDetails).filter(shouldShowOrganizationActivity));
635
+ },
636
+ listPage: async (filters) => {
637
+ const limit = normalizeActivityPageLimit(filters.limit);
638
+ const decodedCursor = decodeActivityCursor(filters.cursor);
639
+ const conditions = organizationActivityConditions(filters);
640
+ if (decodedCursor) {
641
+ conditions.push(or(sql `${activityLog.createdAt} < ${decodedCursor.createdAt}::timestamptz`, and(sql `${activityLog.createdAt} = ${decodedCursor.createdAt}::timestamptz`, sql `${activityLog.id} < ${decodedCursor.id}::uuid`)));
642
+ }
643
+ const rows = await db
644
+ .select({
645
+ activityLog,
646
+ issueIdentifier: issues.identifier,
647
+ issueTitle: issues.title,
648
+ cursorCreatedAt: activityCursorCreatedAt,
649
+ })
49
650
  .from(activityLog)
50
- .leftJoin(issues, and(eq(activityLog.entityType, sql `'issue'`), eq(activityLog.entityId, issueIdAsText)))
651
+ .leftJoin(issues, and(eq(activityLog.entityType, sql `'issue'`), eq(issues.orgId, activityLog.orgId), eq(activityLog.entityId, issueIdAsText)))
51
652
  .where(and(...conditions, or(sql `${activityLog.entityType} != 'issue'`, isNull(issues.hiddenAt))))
52
- .orderBy(desc(activityLog.createdAt))
53
- .then((rows) => rows.map((r) => r.activityLog).filter(shouldShowOrganizationActivity));
653
+ .orderBy(desc(activityLog.createdAt), desc(activityLog.id))
654
+ .limit(limit + 1);
655
+ const pageRows = rows.slice(0, limit);
656
+ return {
657
+ items: pageRows.map(activityRowWithIssueDetails).filter(shouldShowOrganizationActivity),
658
+ nextCursor: rows.length > limit && pageRows.length > 0
659
+ ? encodeActivityCursor(pageRows[pageRows.length - 1])
660
+ : null,
661
+ };
54
662
  },
55
663
  forIssue: async (issueId) => {
56
664
  const [issueEvents, relatedChatEvents] = await Promise.all([