@rudderhq/server 0.3.5-canary.2 → 0.3.5-canary.21

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 (189) hide show
  1. package/dist/agent-runtimes/codex-models.d.ts.map +1 -1
  2. package/dist/agent-runtimes/codex-models.js +2 -92
  3. package/dist/agent-runtimes/codex-models.js.map +1 -1
  4. package/dist/agent-runtimes/registry.d.ts.map +1 -1
  5. package/dist/agent-runtimes/registry.js +17 -2
  6. package/dist/agent-runtimes/registry.js.map +1 -1
  7. package/dist/bundled-plugins/plugin-linear/dist/worker.js +20 -1
  8. package/dist/bundled-plugins/plugin-linear/dist/worker.js.map +2 -2
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +26 -0
  11. package/dist/index.js.map +1 -1
  12. package/dist/routes/activity.d.ts.map +1 -1
  13. package/dist/routes/activity.js +86 -0
  14. package/dist/routes/activity.js.map +1 -1
  15. package/dist/routes/agents.d.ts.map +1 -1
  16. package/dist/routes/agents.js +9 -3
  17. package/dist/routes/agents.js.map +1 -1
  18. package/dist/routes/agents.management-routes.d.ts.map +1 -1
  19. package/dist/routes/agents.management-routes.js +17 -3
  20. package/dist/routes/agents.management-routes.js.map +1 -1
  21. package/dist/routes/chats.d.ts.map +1 -1
  22. package/dist/routes/chats.js +238 -5
  23. package/dist/routes/chats.js.map +1 -1
  24. package/dist/routes/chats.stream-routes.d.ts.map +1 -1
  25. package/dist/routes/chats.stream-routes.js +38 -7
  26. package/dist/routes/chats.stream-routes.js.map +1 -1
  27. package/dist/routes/issues.comments-attachments.d.ts.map +1 -1
  28. package/dist/routes/issues.comments-attachments.js +24 -8
  29. package/dist/routes/issues.comments-attachments.js.map +1 -1
  30. package/dist/routes/messenger.d.ts.map +1 -1
  31. package/dist/routes/messenger.js +49 -1
  32. package/dist/routes/messenger.js.map +1 -1
  33. package/dist/routes/orgs.d.ts.map +1 -1
  34. package/dist/routes/orgs.js +65 -2
  35. package/dist/routes/orgs.js.map +1 -1
  36. package/dist/services/activity.d.ts +37 -0
  37. package/dist/services/activity.d.ts.map +1 -1
  38. package/dist/services/activity.js +619 -27
  39. package/dist/services/activity.js.map +1 -1
  40. package/dist/services/agent-run-context.d.ts.map +1 -1
  41. package/dist/services/agent-run-context.js +100 -9
  42. package/dist/services/agent-run-context.js.map +1 -1
  43. package/dist/services/agents.d.ts +48 -13
  44. package/dist/services/agents.d.ts.map +1 -1
  45. package/dist/services/automation-chat-output.d.ts +1 -0
  46. package/dist/services/automation-chat-output.d.ts.map +1 -1
  47. package/dist/services/automations.d.ts.map +1 -1
  48. package/dist/services/automations.js +35 -5
  49. package/dist/services/automations.js.map +1 -1
  50. package/dist/services/board-auth.d.ts +2 -2
  51. package/dist/services/chat-agent-runs.d.ts +65 -0
  52. package/dist/services/chat-agent-runs.d.ts.map +1 -0
  53. package/dist/services/chat-agent-runs.js +267 -0
  54. package/dist/services/chat-agent-runs.js.map +1 -0
  55. package/dist/services/chat-assistant.d.ts.map +1 -1
  56. package/dist/services/chat-assistant.helpers.d.ts +12 -0
  57. package/dist/services/chat-assistant.helpers.d.ts.map +1 -1
  58. package/dist/services/chat-assistant.helpers.js +23 -1
  59. package/dist/services/chat-assistant.helpers.js.map +1 -1
  60. package/dist/services/chat-assistant.js +312 -177
  61. package/dist/services/chat-assistant.js.map +1 -1
  62. package/dist/services/chats.d.ts +89 -1
  63. package/dist/services/chats.d.ts.map +1 -1
  64. package/dist/services/chats.js +29 -17
  65. package/dist/services/chats.js.map +1 -1
  66. package/dist/services/issues.comments-attachments.d.ts +1 -0
  67. package/dist/services/issues.comments-attachments.d.ts.map +1 -1
  68. package/dist/services/issues.comments-attachments.js +25 -1
  69. package/dist/services/issues.comments-attachments.js.map +1 -1
  70. package/dist/services/issues.d.ts +1 -0
  71. package/dist/services/issues.d.ts.map +1 -1
  72. package/dist/services/knowledge-portability/organization-skills.catalog.d.ts +1 -0
  73. package/dist/services/knowledge-portability/organization-skills.catalog.d.ts.map +1 -1
  74. package/dist/services/knowledge-portability/organization-skills.catalog.js +18 -0
  75. package/dist/services/knowledge-portability/organization-skills.catalog.js.map +1 -1
  76. package/dist/services/knowledge-portability/organization-skills.d.ts.map +1 -1
  77. package/dist/services/knowledge-portability/organization-skills.js +2 -9
  78. package/dist/services/knowledge-portability/organization-skills.js.map +1 -1
  79. package/dist/services/messenger.d.ts +56 -2
  80. package/dist/services/messenger.d.ts.map +1 -1
  81. package/dist/services/messenger.js +218 -3
  82. package/dist/services/messenger.js.map +1 -1
  83. package/dist/services/organization-intelligence-profiles.js +3 -3
  84. package/dist/services/organization-intelligence-profiles.js.map +1 -1
  85. package/dist/services/organization-skills.d.ts +1 -1
  86. package/dist/services/organization-skills.d.ts.map +1 -1
  87. package/dist/services/organization-skills.js +1 -1
  88. package/dist/services/organization-skills.js.map +1 -1
  89. package/dist/services/run-intelligence.d.ts.map +1 -1
  90. package/dist/services/run-intelligence.js +6 -2
  91. package/dist/services/run-intelligence.js.map +1 -1
  92. package/dist/services/runtime-kernel/heartbeat.core.d.ts +17 -0
  93. package/dist/services/runtime-kernel/heartbeat.core.d.ts.map +1 -1
  94. package/dist/services/runtime-kernel/heartbeat.core.js +1 -0
  95. package/dist/services/runtime-kernel/heartbeat.core.js.map +1 -1
  96. package/dist/services/runtime-kernel/heartbeat.d.ts +8 -1
  97. package/dist/services/runtime-kernel/heartbeat.d.ts.map +1 -1
  98. package/dist/services/runtime-kernel/heartbeat.js +13 -7
  99. package/dist/services/runtime-kernel/heartbeat.js.map +1 -1
  100. package/dist/services/runtime-kernel/heartbeat.recovery.d.ts.map +1 -1
  101. package/dist/services/runtime-kernel/heartbeat.recovery.js +2 -0
  102. package/dist/services/runtime-kernel/heartbeat.recovery.js.map +1 -1
  103. package/dist/services/workspace-runtime.services.d.ts +1 -1
  104. package/package.json +13 -13
  105. package/resources/bundled-skills/rudder/SKILL.md +29 -32
  106. package/resources/bundled-skills/rudder/references/api-reference.md +12 -2
  107. package/resources/bundled-skills/rudder/references/cli-reference.md +13 -2
  108. package/skills/rudder/SKILL.md +29 -32
  109. package/skills/rudder/references/api-reference.md +12 -2
  110. package/skills/rudder/references/cli-reference.md +13 -2
  111. package/ui-dist/assets/{_basePickBy-pCn65kUg.js → _basePickBy-DQRD3jbd.js} +1 -1
  112. package/ui-dist/assets/{_baseUniq-D_Y907uk.js → _baseUniq-ChSV83Tl.js} +1 -1
  113. package/ui-dist/assets/{arc-D1FOXYxM.js → arc-BCwqsots.js} +1 -1
  114. package/ui-dist/assets/{architectureDiagram-2XIMDMQ5-BqM99oWe.js → architectureDiagram-2XIMDMQ5-D59Qu6r3.js} +1 -1
  115. package/ui-dist/assets/{blockDiagram-WCTKOSBZ-B31x8PiB.js → blockDiagram-WCTKOSBZ-BLJNQhjF.js} +1 -1
  116. package/ui-dist/assets/{c4Diagram-IC4MRINW-BXxWTBO0.js → c4Diagram-IC4MRINW-CK3IxauE.js} +1 -1
  117. package/ui-dist/assets/channel-D0Hj27Hq.js +1 -0
  118. package/ui-dist/assets/{chunk-4BX2VUAB-A-mJSYhX.js → chunk-4BX2VUAB-By54H8_d.js} +1 -1
  119. package/ui-dist/assets/{chunk-55IACEB6-C_xHp5sT.js → chunk-55IACEB6-Ct4oxREP.js} +1 -1
  120. package/ui-dist/assets/{chunk-FMBD7UC4-hzewwsYy.js → chunk-FMBD7UC4-Bo5QQDeN.js} +1 -1
  121. package/ui-dist/assets/{chunk-JSJVCQXG-BOEtQsnL.js → chunk-JSJVCQXG-DRWD6spx.js} +1 -1
  122. package/ui-dist/assets/{chunk-KX2RTZJC-CoIlm_Hx.js → chunk-KX2RTZJC-CduFLzMt.js} +1 -1
  123. package/ui-dist/assets/{chunk-NQ4KR5QH-DmGxBmwd.js → chunk-NQ4KR5QH-Deady69Q.js} +1 -1
  124. package/ui-dist/assets/{chunk-QZHKN3VN-2Yp7ffIW.js → chunk-QZHKN3VN-CBCxkdH6.js} +1 -1
  125. package/ui-dist/assets/{chunk-WL4C6EOR-S0etjqNL.js → chunk-WL4C6EOR-isrINTKV.js} +1 -1
  126. package/ui-dist/assets/classDiagram-VBA2DB6C-Dtwo6gbt.js +1 -0
  127. package/ui-dist/assets/classDiagram-v2-RAHNMMFH-Dtwo6gbt.js +1 -0
  128. package/ui-dist/assets/clone-kRqsxkGl.js +1 -0
  129. package/ui-dist/assets/{cose-bilkent-S5V4N54A-Db8NTY-v.js → cose-bilkent-S5V4N54A-DEqI6MB2.js} +1 -1
  130. package/ui-dist/assets/{dagre-KLK3FWXG-KnB-W-rz.js → dagre-KLK3FWXG-htMxe3b1.js} +1 -1
  131. package/ui-dist/assets/{diagram-E7M64L7V-DZKhPUtZ.js → diagram-E7M64L7V-FoZTYbSd.js} +1 -1
  132. package/ui-dist/assets/{diagram-IFDJBPK2-N9A6mG2G.js → diagram-IFDJBPK2-Ba9PRO_-.js} +1 -1
  133. package/ui-dist/assets/{diagram-P4PSJMXO-O9bD_Xdl.js → diagram-P4PSJMXO-CtCxaSco.js} +1 -1
  134. package/ui-dist/assets/{erDiagram-INFDFZHY-DDZjMLSL.js → erDiagram-INFDFZHY-CSvCOhU9.js} +1 -1
  135. package/ui-dist/assets/{flowDiagram-PKNHOUZH-XvyruncW.js → flowDiagram-PKNHOUZH-BJ8wlqYl.js} +1 -1
  136. package/ui-dist/assets/{ganttDiagram-A5KZAMGK-DyyT5dog.js → ganttDiagram-A5KZAMGK-CZnnlHd9.js} +1 -1
  137. package/ui-dist/assets/{gitGraphDiagram-K3NZZRJ6-gADHznug.js → gitGraphDiagram-K3NZZRJ6-G9KG4OCz.js} +1 -1
  138. package/ui-dist/assets/{graph-7vIigZeu.js → graph-CmEOZML6.js} +1 -1
  139. package/ui-dist/assets/{index-D5KlAqB7.js → index-0jU9C9J5.js} +1 -1
  140. package/ui-dist/assets/{index-ClVYEFCG.js → index-2k06YsZ0.js} +1 -1
  141. package/ui-dist/assets/{index-DTSMhh6G.js → index-B1-HLXQ1.js} +1 -1
  142. package/ui-dist/assets/{index-9UhHaawM.js → index-B4drPtcc.js} +1 -1
  143. package/ui-dist/assets/{index-CyGieKZt.js → index-B76BV2-o.js} +1 -1
  144. package/ui-dist/assets/{index-CwtC_TfQ.js → index-BA75uuCE.js} +1 -1
  145. package/ui-dist/assets/{index-CDS-GPuQ.js → index-BJNHDHzx.js} +1 -1
  146. package/ui-dist/assets/{index-BNrlKXIp.js → index-Bf1iZPV6.js} +1 -1
  147. package/ui-dist/assets/{index-BCFtYRTF.js → index-BuyUdFsk.js} +1 -1
  148. package/ui-dist/assets/{index-CPbseJy6.js → index-Bz9EamK2.js} +1 -1
  149. package/ui-dist/assets/{index-CzAI6rBQ.js → index-CGE3v4f8.js} +1 -1
  150. package/ui-dist/assets/{index-DOFyevXp.js → index-CKQRI15h.js} +1 -1
  151. package/ui-dist/assets/{index-CaT6akeq.js → index-ChZln_1i.js} +1 -1
  152. package/ui-dist/assets/{index-CLcM1MHz.js → index-CmbpxZxI.js} +1 -1
  153. package/ui-dist/assets/{index-lPwz_ooR.js → index-CxjiCZ6r.js} +1 -1
  154. package/ui-dist/assets/{index-Cdg8kabT.js → index-D9SubOrM.js} +1 -1
  155. package/ui-dist/assets/{index-C5eIQEdO.js → index-D9cJ_mwP.js} +1 -1
  156. package/ui-dist/assets/{index-4KVTXANB.js → index-Dfu3lMcj.js} +1 -1
  157. package/ui-dist/assets/{index-D5JBYAir.js → index-DrRZfTTF.js} +1 -1
  158. package/ui-dist/assets/{index-DVz1zQD4.js → index-KkfX5W81.js} +1 -1
  159. package/ui-dist/assets/index-NnBHP7m8.js +1750 -0
  160. package/ui-dist/assets/{index-nWNmZVtb.js → index-OW_vF7mc.js} +1 -1
  161. package/ui-dist/assets/index-XCEW55-_.css +1 -0
  162. package/ui-dist/assets/{index-CATFiL_5.js → index-n3HgsyPM.js} +1 -1
  163. package/ui-dist/assets/{infoDiagram-LFFYTUFH-DZSE3bR9.js → infoDiagram-LFFYTUFH-CULxi1Pi.js} +1 -1
  164. package/ui-dist/assets/{ishikawaDiagram-PHBUUO56-IImK5aQu.js → ishikawaDiagram-PHBUUO56-De4H5SvP.js} +1 -1
  165. package/ui-dist/assets/{journeyDiagram-4ABVD52K-acBx1Vxv.js → journeyDiagram-4ABVD52K-aQURd3zC.js} +1 -1
  166. package/ui-dist/assets/{kanban-definition-K7BYSVSG-CTFjpqbi.js → kanban-definition-K7BYSVSG-sxdgA8Mj.js} +1 -1
  167. package/ui-dist/assets/{layout-C82SEtWJ.js → layout-B0y-hpF1.js} +1 -1
  168. package/ui-dist/assets/{linear-CRoau0uq.js → linear-CcF3oPj1.js} +1 -1
  169. package/ui-dist/assets/{mermaid.core-Ci5CSmlH.js → mermaid.core-Ca_slkna.js} +4 -4
  170. package/ui-dist/assets/{mindmap-definition-YRQLILUH-J7GvZpev.js → mindmap-definition-YRQLILUH-bqiHlPwE.js} +1 -1
  171. package/ui-dist/assets/{pieDiagram-SKSYHLDU-vNggbv_2.js → pieDiagram-SKSYHLDU-1aKBqhWX.js} +1 -1
  172. package/ui-dist/assets/{quadrantDiagram-337W2JSQ-Dw7kvy6Y.js → quadrantDiagram-337W2JSQ-Bca_TP9m.js} +1 -1
  173. package/ui-dist/assets/{requirementDiagram-Z7DCOOCP-BvSEyElm.js → requirementDiagram-Z7DCOOCP-Co_pbTj0.js} +1 -1
  174. package/ui-dist/assets/{sankeyDiagram-WA2Y5GQK-DwCLD2LN.js → sankeyDiagram-WA2Y5GQK-FNWr9Qgb.js} +1 -1
  175. package/ui-dist/assets/{sequenceDiagram-2WXFIKYE-D_s9lUAn.js → sequenceDiagram-2WXFIKYE-Dy0_t8KK.js} +1 -1
  176. package/ui-dist/assets/{stateDiagram-RAJIS63D-B0jPEqLW.js → stateDiagram-RAJIS63D-DJWDtTEw.js} +1 -1
  177. package/ui-dist/assets/stateDiagram-v2-FVOUBMTO-BzIR1BG8.js +1 -0
  178. package/ui-dist/assets/{timeline-definition-YZTLITO2-DdDstc8N.js → timeline-definition-YZTLITO2-BG2EEfkn.js} +1 -1
  179. package/ui-dist/assets/{treemap-KZPCXAKY-CYbOkWQq.js → treemap-KZPCXAKY-memeaR_f.js} +1 -1
  180. package/ui-dist/assets/{vennDiagram-LZ73GAT5-uxQlceJ0.js → vennDiagram-LZ73GAT5-CJ_ThALk.js} +1 -1
  181. package/ui-dist/assets/{xychartDiagram-JWTSCODW-DbYD2ThH.js → xychartDiagram-JWTSCODW-BxKpwCJ-.js} +1 -1
  182. package/ui-dist/index.html +2 -2
  183. package/ui-dist/assets/channel-CEHUUFG8.js +0 -1
  184. package/ui-dist/assets/classDiagram-VBA2DB6C-Bb_snefw.js +0 -1
  185. package/ui-dist/assets/classDiagram-v2-RAHNMMFH-Bb_snefw.js +0 -1
  186. package/ui-dist/assets/clone-B2KToI8w.js +0 -1
  187. package/ui-dist/assets/index-BMr_32cW.css +0 -1
  188. package/ui-dist/assets/index-CgZIlV_x.js +0 -1732
  189. package/ui-dist/assets/stateDiagram-v2-FVOUBMTO-CdSwOHOx.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
  }
@@ -19,38 +156,493 @@ function shouldShowOrganizationActivity(event) {
19
156
  export function activityService(db) {
20
157
  const issueIdAsText = sql `${issues.id}::text`;
21
158
  const conversationIdAsText = sql `${chatConversations.id}::text`;
159
+ const organizationActivityVisibleCondition = and(ne(activityLog.action, "issue.read_marked"), sql `not (${issueLowSignalContentOnlyActivitySql("activity_log")})`);
160
+ const activityCursorCreatedAt = sql `to_char(${activityLog.createdAt} AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.US"Z"')`;
161
+ function userActivityCursorCondition(createdAtColumn, idColumn, kind, cursor) {
162
+ if (!cursor)
163
+ return undefined;
164
+ const cursorDate = new Date(cursor.occurredAt);
165
+ const cursorTimestamp = cursor.occurredAt;
166
+ const kindOrder = kind.localeCompare(cursor.kind);
167
+ const sameTimestampCondition = kindOrder > 0
168
+ ? sql `${createdAtColumn} = ${cursorTimestamp}::timestamptz`
169
+ : kindOrder === 0
170
+ ? and(sql `${createdAtColumn} = ${cursorTimestamp}::timestamptz`, sql `${idColumn}::text > ${cursor.id}`)
171
+ : undefined;
172
+ return sameTimestampCondition
173
+ ? or(lt(createdAtColumn, cursorDate), sameTimestampCondition)
174
+ : lt(createdAtColumn, cursorDate);
175
+ }
176
+ function timeConditions(createdAtColumn, filters) {
177
+ const conditions = [];
178
+ if (filters.since)
179
+ conditions.push(gte(createdAtColumn, filters.since));
180
+ if (filters.until)
181
+ conditions.push(lte(createdAtColumn, filters.until));
182
+ return conditions;
183
+ }
184
+ function withUserActor(userId, displayName) {
185
+ return {
186
+ type: "user",
187
+ id: userId,
188
+ displayName: displayName ?? null,
189
+ };
190
+ }
191
+ function activityRowWithIssueDetails(row) {
192
+ if (row.activityLog.entityType !== "issue")
193
+ return row.activityLog;
194
+ if (!row.issueIdentifier && !row.issueTitle)
195
+ return row.activityLog;
196
+ const details = row.activityLog.details ?? {};
197
+ return {
198
+ ...row.activityLog,
199
+ details: {
200
+ ...details,
201
+ ...(row.issueIdentifier ? { issueIdentifier: row.issueIdentifier } : {}),
202
+ ...(row.issueTitle ? { issueTitle: row.issueTitle } : {}),
203
+ ...(row.issueIdentifier && typeof details.identifier !== "string" ? { identifier: row.issueIdentifier } : {}),
204
+ ...(row.issueTitle && typeof details.title !== "string" ? { title: row.issueTitle } : {}),
205
+ },
206
+ };
207
+ }
208
+ function organizationActivityConditions(filters) {
209
+ const conditions = [eq(activityLog.orgId, filters.orgId)];
210
+ if (organizationActivityVisibleCondition)
211
+ conditions.push(organizationActivityVisibleCondition);
212
+ if (filters.agentId) {
213
+ const agentCondition = or(eq(activityLog.agentId, filters.agentId), and(eq(activityLog.actorType, "agent"), eq(activityLog.actorId, filters.agentId)));
214
+ if (agentCondition)
215
+ conditions.push(agentCondition);
216
+ }
217
+ if (filters.userId) {
218
+ conditions.push(eq(activityLog.actorType, "user"));
219
+ conditions.push(eq(activityLog.actorId, filters.userId));
220
+ }
221
+ if (filters.actorType) {
222
+ conditions.push(eq(activityLog.actorType, filters.actorType));
223
+ }
224
+ if (filters.actorId) {
225
+ conditions.push(eq(activityLog.actorId, filters.actorId));
226
+ }
227
+ if (filters.entityType) {
228
+ conditions.push(eq(activityLog.entityType, filters.entityType));
229
+ }
230
+ if (filters.entityId) {
231
+ conditions.push(eq(activityLog.entityId, filters.entityId));
232
+ }
233
+ return conditions;
234
+ }
22
235
  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));
236
+ listUserActivityLedger: async (filters) => {
237
+ const limit = normalizeUserActivityPageLimit(filters.limit);
238
+ const cursor = decodeUserActivityCursor(filters.cursor);
239
+ const includes = normalizeUserActivityIncludes(filters.include);
240
+ const perSourceLimit = limit + 1;
241
+ const [profile] = await db
242
+ .select({ nickname: operatorProfiles.nickname })
243
+ .from(operatorProfiles)
244
+ .where(eq(operatorProfiles.userId, filters.userId))
245
+ .limit(1);
246
+ const actor = withUserActor(filters.userId, profile?.nickname);
247
+ const items = [];
248
+ if (includes.has("chat")) {
249
+ const conditions = [
250
+ eq(chatMessages.orgId, filters.orgId),
251
+ eq(chatMessages.role, "user"),
252
+ eq(chatConversations.orgId, filters.orgId),
253
+ eq(chatConversations.createdByUserId, filters.userId),
254
+ isNull(chatMessages.supersededAt),
255
+ ...timeConditions(chatMessages.createdAt, filters),
256
+ ];
257
+ const cursorSql = userActivityCursorCondition(chatMessages.createdAt, chatMessages.id, "chat_message", cursor);
258
+ if (cursorSql)
259
+ conditions.push(cursorSql);
260
+ if (filters.agentId) {
261
+ conditions.push(or(eq(chatMessages.replyingAgentId, filters.agentId), eq(chatConversations.preferredAgentId, filters.agentId), eq(chatConversations.routedAgentId, filters.agentId)));
262
+ }
263
+ if (filters.projectId) {
264
+ conditions.push(eq(issues.projectId, filters.projectId));
265
+ }
266
+ if (filters.issueId) {
267
+ conditions.push(eq(chatConversations.primaryIssueId, filters.issueId));
268
+ }
269
+ const rows = await db
270
+ .select({
271
+ message: chatMessages,
272
+ conversationTitle: chatConversations.title,
273
+ preferredAgentId: chatConversations.preferredAgentId,
274
+ routedAgentId: chatConversations.routedAgentId,
275
+ primaryIssueId: chatConversations.primaryIssueId,
276
+ issueIdentifier: issues.identifier,
277
+ issueTitle: issues.title,
278
+ issueProjectId: issues.projectId,
279
+ issueGoalId: issues.goalId,
280
+ })
281
+ .from(chatMessages)
282
+ .innerJoin(chatConversations, and(eq(chatMessages.conversationId, chatConversations.id), eq(chatMessages.orgId, chatConversations.orgId)))
283
+ .leftJoin(issues, and(eq(chatConversations.primaryIssueId, issues.id), eq(chatConversations.orgId, issues.orgId), isNull(issues.hiddenAt)))
284
+ .where(and(...conditions))
285
+ .orderBy(desc(chatMessages.createdAt), asc(chatMessages.id))
286
+ .limit(perSourceLimit);
287
+ for (const row of rows) {
288
+ const related = [
289
+ { type: "chat", id: row.message.conversationId, label: row.conversationTitle },
290
+ ];
291
+ if (row.message.replyingAgentId)
292
+ related.push({ type: "agent", id: row.message.replyingAgentId });
293
+ if (row.preferredAgentId && row.preferredAgentId !== row.message.replyingAgentId) {
294
+ related.push({ type: "agent", id: row.preferredAgentId });
295
+ }
296
+ if (row.routedAgentId && row.routedAgentId !== row.message.replyingAgentId && row.routedAgentId !== row.preferredAgentId) {
297
+ related.push({ type: "agent", id: row.routedAgentId });
298
+ }
299
+ if (row.primaryIssueId) {
300
+ related.push({
301
+ type: "issue",
302
+ id: row.primaryIssueId,
303
+ label: row.issueIdentifier ?? row.issueTitle,
304
+ });
305
+ }
306
+ if (row.issueProjectId)
307
+ related.push({ type: "project", id: row.issueProjectId });
308
+ if (row.issueGoalId)
309
+ related.push({ type: "goal", id: row.issueGoalId });
310
+ items.push({
311
+ id: row.message.id,
312
+ kind: "chat_message",
313
+ occurredAt: row.message.createdAt.toISOString(),
314
+ userId: filters.userId,
315
+ actor,
316
+ summary: `User message in chat: ${row.conversationTitle || "Untitled chat"}`,
317
+ excerpt: safeExcerpt(row.message.body),
318
+ source: {
319
+ type: "chat",
320
+ id: row.message.conversationId,
321
+ link: `chat://${row.message.conversationId}`,
322
+ provenance: {
323
+ table: "chat_messages",
324
+ id: row.message.id,
325
+ orgId: row.message.orgId,
326
+ },
327
+ },
328
+ related,
329
+ metadata: {
330
+ messageId: row.message.id,
331
+ messageKind: row.message.kind,
332
+ messageStatus: row.message.status,
333
+ runId: row.message.runId,
334
+ },
335
+ });
336
+ }
37
337
  }
38
- if (filters.actorId) {
39
- conditions.push(eq(activityLog.actorId, filters.actorId));
338
+ if (includes.has("comments") || includes.has("issues")) {
339
+ const conditions = [
340
+ eq(issueComments.orgId, filters.orgId),
341
+ eq(issueComments.authorUserId, filters.userId),
342
+ eq(issues.orgId, filters.orgId),
343
+ isNull(issueComments.deletedAt),
344
+ isNull(issues.hiddenAt),
345
+ ...timeConditions(issueComments.createdAt, filters),
346
+ ];
347
+ const cursorSql = userActivityCursorCondition(issueComments.createdAt, issueComments.id, "issue_comment", cursor);
348
+ if (cursorSql)
349
+ conditions.push(cursorSql);
350
+ if (filters.projectId)
351
+ conditions.push(eq(issues.projectId, filters.projectId));
352
+ if (filters.issueId)
353
+ conditions.push(eq(issueComments.issueId, filters.issueId));
354
+ if (filters.agentId) {
355
+ conditions.push(or(eq(issues.assigneeAgentId, filters.agentId), eq(issues.reviewerAgentId, filters.agentId)));
356
+ }
357
+ const rows = await db
358
+ .select({
359
+ comment: issueComments,
360
+ issueIdentifier: issues.identifier,
361
+ issueTitle: issues.title,
362
+ issueStatus: issues.status,
363
+ projectId: issues.projectId,
364
+ goalId: issues.goalId,
365
+ assigneeAgentId: issues.assigneeAgentId,
366
+ reviewerAgentId: issues.reviewerAgentId,
367
+ })
368
+ .from(issueComments)
369
+ .innerJoin(issues, and(eq(issueComments.issueId, issues.id), eq(issueComments.orgId, issues.orgId)))
370
+ .where(and(...conditions))
371
+ .orderBy(desc(issueComments.createdAt), asc(issueComments.id))
372
+ .limit(perSourceLimit);
373
+ for (const row of rows) {
374
+ const related = [
375
+ {
376
+ type: "issue",
377
+ id: row.comment.issueId,
378
+ label: row.issueIdentifier ?? row.issueTitle,
379
+ },
380
+ ];
381
+ if (row.projectId)
382
+ related.push({ type: "project", id: row.projectId });
383
+ if (row.goalId)
384
+ related.push({ type: "goal", id: row.goalId });
385
+ if (row.assigneeAgentId)
386
+ related.push({ type: "agent", id: row.assigneeAgentId });
387
+ if (row.reviewerAgentId && row.reviewerAgentId !== row.assigneeAgentId) {
388
+ related.push({ type: "agent", id: row.reviewerAgentId });
389
+ }
390
+ items.push({
391
+ id: row.comment.id,
392
+ kind: "issue_comment",
393
+ occurredAt: row.comment.createdAt.toISOString(),
394
+ userId: filters.userId,
395
+ actor,
396
+ summary: `User commented on ${row.issueIdentifier ?? "issue"}: ${row.issueTitle}`,
397
+ excerpt: safeExcerpt(row.comment.body),
398
+ source: {
399
+ type: "comment",
400
+ id: row.comment.id,
401
+ link: `issue://${row.comment.issueId}?c=${row.comment.id}`,
402
+ provenance: {
403
+ table: "issue_comments",
404
+ id: row.comment.id,
405
+ orgId: row.comment.orgId,
406
+ },
407
+ },
408
+ related,
409
+ metadata: {
410
+ issueId: row.comment.issueId,
411
+ issueIdentifier: row.issueIdentifier,
412
+ issueStatus: row.issueStatus,
413
+ },
414
+ });
415
+ }
40
416
  }
41
- if (filters.entityType) {
42
- conditions.push(eq(activityLog.entityType, filters.entityType));
417
+ if (includes.has("approvals")) {
418
+ const conditions = [
419
+ eq(approvalComments.orgId, filters.orgId),
420
+ eq(approvalComments.authorUserId, filters.userId),
421
+ eq(approvals.orgId, filters.orgId),
422
+ ...timeConditions(approvalComments.createdAt, filters),
423
+ ];
424
+ const cursorSql = userActivityCursorCondition(approvalComments.createdAt, approvalComments.id, "approval_comment", cursor);
425
+ if (cursorSql)
426
+ conditions.push(cursorSql);
427
+ if (filters.agentId)
428
+ conditions.push(eq(approvals.requestedByAgentId, filters.agentId));
429
+ if (filters.issueId) {
430
+ conditions.push(or(sql `${approvals.payload}->>'issueId' = ${filters.issueId}`, sql `${approvals.payload}->>'primaryIssueId' = ${filters.issueId}`, sql `${approvals.payload}->'issueIds' ? ${filters.issueId}`));
431
+ }
432
+ if (filters.projectId) {
433
+ conditions.push(or(sql `${approvals.payload}->>'projectId' = ${filters.projectId}`, sql `exists (
434
+ select 1
435
+ from ${issues}
436
+ where ${issues.orgId} = ${approvals.orgId}
437
+ and ${issues.projectId} = ${filters.projectId}
438
+ and ${issues.hiddenAt} is null
439
+ and (
440
+ ${issues.id}::text = ${approvals.payload}->>'issueId'
441
+ or ${issues.id}::text = ${approvals.payload}->>'primaryIssueId'
442
+ or ${approvals.payload}->'issueIds' ? ${issues.id}::text
443
+ )
444
+ )`));
445
+ }
446
+ const rows = await db
447
+ .select({
448
+ comment: approvalComments,
449
+ approvalType: approvals.type,
450
+ approvalStatus: approvals.status,
451
+ requestedByAgentId: approvals.requestedByAgentId,
452
+ })
453
+ .from(approvalComments)
454
+ .innerJoin(approvals, and(eq(approvalComments.approvalId, approvals.id), eq(approvalComments.orgId, approvals.orgId)))
455
+ .where(and(...conditions))
456
+ .orderBy(desc(approvalComments.createdAt), asc(approvalComments.id))
457
+ .limit(perSourceLimit);
458
+ for (const row of rows) {
459
+ const related = [
460
+ { type: "approval", id: row.comment.approvalId, label: row.approvalType },
461
+ ];
462
+ if (row.requestedByAgentId)
463
+ related.push({ type: "agent", id: row.requestedByAgentId });
464
+ items.push({
465
+ id: row.comment.id,
466
+ kind: "approval_comment",
467
+ occurredAt: row.comment.createdAt.toISOString(),
468
+ userId: filters.userId,
469
+ actor,
470
+ summary: `User commented on ${row.approvalType} approval`,
471
+ excerpt: safeExcerpt(row.comment.body),
472
+ source: {
473
+ type: "approval",
474
+ id: row.comment.approvalId,
475
+ link: `approval://${row.comment.approvalId}?c=${row.comment.id}`,
476
+ provenance: {
477
+ table: "approval_comments",
478
+ id: row.comment.id,
479
+ orgId: row.comment.orgId,
480
+ },
481
+ },
482
+ related,
483
+ metadata: {
484
+ approvalId: row.comment.approvalId,
485
+ approvalType: row.approvalType,
486
+ approvalStatus: row.approvalStatus,
487
+ },
488
+ });
489
+ }
43
490
  }
44
- if (filters.entityId) {
45
- conditions.push(eq(activityLog.entityId, filters.entityId));
491
+ if (includes.has("activity") || includes.has("issues") || includes.has("approvals")) {
492
+ const activityIncludeCondition = includes.has("activity")
493
+ ? undefined
494
+ : or(includes.has("issues") ? eq(activityLog.entityType, "issue") : undefined, includes.has("approvals") ? eq(activityLog.entityType, "approval") : undefined);
495
+ const conditions = [
496
+ eq(activityLog.orgId, filters.orgId),
497
+ eq(activityLog.actorType, "user"),
498
+ eq(activityLog.actorId, filters.userId),
499
+ ne(activityLog.action, "issue.comment_added"),
500
+ ne(activityLog.action, "approval.comment_added"),
501
+ ne(activityLog.action, "issue.read_marked"),
502
+ sql `not (${issueLowSignalContentOnlyActivitySql("activity_log")})`,
503
+ ...timeConditions(activityLog.createdAt, filters),
504
+ ];
505
+ if (activityIncludeCondition)
506
+ conditions.push(activityIncludeCondition);
507
+ const cursorSql = userActivityCursorCondition(activityLog.createdAt, activityLog.id, "activity_event", cursor);
508
+ if (cursorSql)
509
+ conditions.push(cursorSql);
510
+ if (filters.agentId) {
511
+ conditions.push(or(eq(activityLog.agentId, filters.agentId), and(eq(activityLog.actorType, "agent"), eq(activityLog.actorId, filters.agentId))));
512
+ }
513
+ if (filters.issueId) {
514
+ conditions.push(and(eq(activityLog.entityType, "issue"), eq(activityLog.entityId, filters.issueId)));
515
+ }
516
+ if (filters.projectId) {
517
+ conditions.push(or(and(eq(activityLog.entityType, "project"), eq(activityLog.entityId, filters.projectId)), eq(issues.projectId, filters.projectId)));
518
+ }
519
+ const rows = await db
520
+ .select({
521
+ event: activityLog,
522
+ issueIdentifier: issues.identifier,
523
+ issueTitle: issues.title,
524
+ issueProjectId: issues.projectId,
525
+ issueGoalId: issues.goalId,
526
+ agentName: agents.name,
527
+ })
528
+ .from(activityLog)
529
+ .leftJoin(issues, and(eq(activityLog.entityType, sql `'issue'`), eq(issues.orgId, activityLog.orgId), eq(activityLog.entityId, issueIdAsText), isNull(issues.hiddenAt)))
530
+ .leftJoin(agents, and(eq(activityLog.agentId, agents.id), eq(activityLog.orgId, agents.orgId)))
531
+ .where(and(...conditions))
532
+ .orderBy(desc(activityLog.createdAt), asc(activityLog.id))
533
+ .limit(perSourceLimit);
534
+ for (const row of rows) {
535
+ if (row.event.entityType === "issue" && !row.issueIdentifier && !row.issueTitle)
536
+ continue;
537
+ const details = row.event.details ?? null;
538
+ const related = [];
539
+ if (row.event.agentId)
540
+ related.push({ type: "agent", id: row.event.agentId, label: row.agentName });
541
+ if (row.event.runId)
542
+ related.push({ type: "run", id: row.event.runId });
543
+ if (row.event.entityType === "issue") {
544
+ related.push({
545
+ type: "issue",
546
+ id: row.event.entityId,
547
+ label: row.issueIdentifier ?? row.issueTitle,
548
+ });
549
+ }
550
+ if (row.event.entityType === "chat")
551
+ related.push({ type: "chat", id: row.event.entityId });
552
+ if (row.event.entityType === "approval")
553
+ related.push({ type: "approval", id: row.event.entityId });
554
+ if (row.event.entityType === "project")
555
+ related.push({ type: "project", id: row.event.entityId });
556
+ if (row.issueProjectId)
557
+ related.push({ type: "project", id: row.issueProjectId });
558
+ if (row.issueGoalId)
559
+ related.push({ type: "goal", id: row.issueGoalId });
560
+ const detailTitle = titleFromDetails(details);
561
+ items.push({
562
+ id: row.event.id,
563
+ kind: "activity_event",
564
+ occurredAt: row.event.createdAt.toISOString(),
565
+ userId: filters.userId,
566
+ actor,
567
+ summary: detailTitle
568
+ ? `User ${row.event.action} on ${row.event.entityType}: ${detailTitle}`
569
+ : `User ${row.event.action} on ${row.event.entityType}`,
570
+ excerpt: safeExcerpt(typeof details?.note === "string"
571
+ ? details.note
572
+ : typeof details?.comment === "string"
573
+ ? details.comment
574
+ : null),
575
+ source: {
576
+ type: "activity",
577
+ id: row.event.id,
578
+ link: row.event.entityType === "issue" ? `issue://${row.event.entityId}` : null,
579
+ provenance: {
580
+ table: "activity_log",
581
+ id: row.event.id,
582
+ orgId: row.event.orgId,
583
+ },
584
+ },
585
+ related,
586
+ metadata: {
587
+ action: row.event.action,
588
+ entityType: row.event.entityType,
589
+ entityId: row.event.entityId,
590
+ details,
591
+ },
592
+ });
593
+ }
46
594
  }
595
+ const sortedItems = items
596
+ .filter((item) => itemIsAfterCursor(item, cursor))
597
+ .sort(compareUserActivityItems);
598
+ const pageItems = sortedItems.slice(0, limit);
599
+ return {
600
+ items: pageItems,
601
+ nextCursor: sortedItems.length > limit && pageItems.length > 0
602
+ ? encodeUserActivityCursor(pageItems[pageItems.length - 1])
603
+ : null,
604
+ };
605
+ },
606
+ list: (filters) => {
607
+ const conditions = organizationActivityConditions(filters);
47
608
  return db
48
- .select({ activityLog })
609
+ .select({
610
+ activityLog,
611
+ issueIdentifier: issues.identifier,
612
+ issueTitle: issues.title,
613
+ })
614
+ .from(activityLog)
615
+ .leftJoin(issues, and(eq(activityLog.entityType, sql `'issue'`), eq(issues.orgId, activityLog.orgId), eq(activityLog.entityId, issueIdAsText)))
616
+ .where(and(...conditions, or(sql `${activityLog.entityType} != 'issue'`, isNull(issues.hiddenAt))))
617
+ .orderBy(desc(activityLog.createdAt), desc(activityLog.id))
618
+ .then((rows) => rows.map(activityRowWithIssueDetails).filter(shouldShowOrganizationActivity));
619
+ },
620
+ listPage: async (filters) => {
621
+ const limit = normalizeActivityPageLimit(filters.limit);
622
+ const decodedCursor = decodeActivityCursor(filters.cursor);
623
+ const conditions = organizationActivityConditions(filters);
624
+ if (decodedCursor) {
625
+ conditions.push(or(sql `${activityLog.createdAt} < ${decodedCursor.createdAt}::timestamptz`, and(sql `${activityLog.createdAt} = ${decodedCursor.createdAt}::timestamptz`, sql `${activityLog.id} < ${decodedCursor.id}::uuid`)));
626
+ }
627
+ const rows = await db
628
+ .select({
629
+ activityLog,
630
+ issueIdentifier: issues.identifier,
631
+ issueTitle: issues.title,
632
+ cursorCreatedAt: activityCursorCreatedAt,
633
+ })
49
634
  .from(activityLog)
50
- .leftJoin(issues, and(eq(activityLog.entityType, sql `'issue'`), eq(activityLog.entityId, issueIdAsText)))
635
+ .leftJoin(issues, and(eq(activityLog.entityType, sql `'issue'`), eq(issues.orgId, activityLog.orgId), eq(activityLog.entityId, issueIdAsText)))
51
636
  .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));
637
+ .orderBy(desc(activityLog.createdAt), desc(activityLog.id))
638
+ .limit(limit + 1);
639
+ const pageRows = rows.slice(0, limit);
640
+ return {
641
+ items: pageRows.map(activityRowWithIssueDetails).filter(shouldShowOrganizationActivity),
642
+ nextCursor: rows.length > limit && pageRows.length > 0
643
+ ? encodeActivityCursor(pageRows[pageRows.length - 1])
644
+ : null,
645
+ };
54
646
  },
55
647
  forIssue: async (issueId) => {
56
648
  const [issueEvents, relatedChatEvents] = await Promise.all([