@sentry/junior 0.203.0 → 0.204.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 (174) hide show
  1. package/dist/agent-CMyTD4DD.js +17509 -0
  2. package/dist/agent-hooks-Bbcp9e33.js +4040 -0
  3. package/dist/agent-runner-DNb1A49M.js +678 -0
  4. package/dist/api/schema.d.ts +2728 -2639
  5. package/dist/api/schema.js +4 -169
  6. package/dist/api.d.ts +23 -17
  7. package/dist/api.js +3881 -5326
  8. package/dist/app-B1TOqBI8.d.ts +246 -0
  9. package/dist/app.d.ts +4 -520
  10. package/dist/app.js +9522 -11875
  11. package/dist/auth-pause-state-DosEalIG.js +168 -0
  12. package/dist/catalog-runtime-BReos-D4.js +7 -0
  13. package/dist/checkpoint-CNrPyRJN.js +2450 -0
  14. package/dist/cli/chat.d.ts +20 -0
  15. package/dist/cli/chat.js +229 -336
  16. package/dist/cli/check.d.ts +8 -0
  17. package/dist/cli/check.js +513 -806
  18. package/dist/cli/env.d.ts +13 -0
  19. package/dist/cli/env.js +63 -12
  20. package/dist/cli/init.d.ts +3 -0
  21. package/dist/cli/init.js +90 -159
  22. package/dist/cli/main.d.ts +6 -0
  23. package/dist/cli/main.js +43 -64
  24. package/dist/cli/plugins.d.ts +13 -0
  25. package/dist/cli/plugins.js +148 -227
  26. package/dist/cli/run.d.ts +16 -0
  27. package/dist/cli/run.js +52 -8
  28. package/dist/cli/snapshot-create.d.ts +4 -0
  29. package/dist/cli/snapshot-create.js +71 -125
  30. package/dist/cli/upgrade.d.ts +15 -0
  31. package/dist/cli/upgrade.js +232 -335
  32. package/dist/config-nGCW1OTG.js +941 -0
  33. package/dist/context-B45_WqVX.js +298 -0
  34. package/dist/conversation-privacy-B4voFRSm.js +182 -0
  35. package/dist/conversation-work-C6y3T5Hx.js +51 -0
  36. package/dist/credential-sync-kXr1hvO1.js +2660 -0
  37. package/dist/db-BeyXKJOI.d.ts +5 -0
  38. package/dist/destinations-BeBiJeEM.js +29 -0
  39. package/dist/execution-stats-DWfSihcH.js +373 -0
  40. package/dist/executor-DcMSlI5c.js +721 -0
  41. package/dist/experimental-CIXf7Uye.js +46 -0
  42. package/dist/history-ClE4toxe.d.ts +32 -0
  43. package/dist/history-CqTuSAdI.js +441 -0
  44. package/dist/instrumentation.d.ts +3 -3
  45. package/dist/instrumentation.js +130 -176
  46. package/dist/job-queue-Drn_LXlM.js +63 -0
  47. package/dist/logging-BzAk1S_s.js +1210 -0
  48. package/dist/model-profile-CZmVoGdX.js +49 -0
  49. package/dist/nitro.d.ts +31 -36
  50. package/dist/nitro.js +275 -456
  51. package/dist/oauth-callback-server-JiftokvA.js +130 -0
  52. package/dist/oauth-relay-CdCnu80W.js +62 -0
  53. package/dist/package-discovery-7a9YqLhE.js +455 -0
  54. package/dist/pending-auth-CWmSdZVg.js +185 -0
  55. package/dist/plugin-module-BKGOXca2.js +81 -0
  56. package/dist/plugins-BI9A1vKA.d.ts +95 -0
  57. package/dist/plugins-egKWjgue.js +99 -0
  58. package/dist/private-trace-result-BLvBjy_e.js +16 -0
  59. package/dist/profile-CrT5nXbw.js +861 -0
  60. package/dist/registry-DH1xUo2N.js +1419 -0
  61. package/dist/reporting-schema-DosTN__Q.js +105 -0
  62. package/dist/resolve-CZHnfxaS.js +546 -0
  63. package/dist/rolldown-runtime-B4FMCO8f.js +28 -0
  64. package/dist/runner-CZV2sC46.js +392 -0
  65. package/dist/sandbox-egress-signals-bHZfmhbd.js +46 -0
  66. package/dist/schema-Rn8-XEaN.js +1140 -0
  67. package/dist/sentry-CD3SdJl-.js +26 -0
  68. package/dist/skills-CIcDQSgM.js +316 -0
  69. package/dist/spawn-DdkUFRmq.js +29 -0
  70. package/dist/stats-DTUbAb88.js +2634 -0
  71. package/dist/store-C5qxF2ac.d.ts +208 -0
  72. package/dist/task-queue-CJIR4RF7.js +136 -0
  73. package/dist/task-runner-D-O3A-Cp.js +639 -0
  74. package/dist/thread-state-CRc9jTJS.js +431 -0
  75. package/dist/turn-execution-BkinIdJF.js +47 -0
  76. package/dist/turn-lifecycle-DQV19VTW.js +54 -0
  77. package/dist/usage-D3EbSO7w.js +296 -0
  78. package/dist/usage-schema-CKaDkH_j.js +20 -0
  79. package/dist/validation-C2CR-OX3.js +40 -0
  80. package/dist/vercel-queue-DIkVcfK4.js +167 -0
  81. package/dist/vercel.d.ts +6 -6
  82. package/dist/vercel.js +14 -6
  83. package/dist/version-CnXZ614E.d.ts +5 -0
  84. package/dist/version-DxcWdoKa.js +8 -0
  85. package/dist/version.d.ts +2 -4
  86. package/dist/version.js +2 -6
  87. package/dist/web-authorization-RMBxAR_t.js +76 -0
  88. package/dist/web-input-3EPfpf55.js +494 -0
  89. package/dist/work-fGofI7Op.js +509 -0
  90. package/dist/worker-CsOLq-mR.js +625 -0
  91. package/package.json +8 -8
  92. package/dist/agent-hooks-GB2VPTNA.js +0 -51
  93. package/dist/catalog-runtime-UBLIWD3O.js +0 -14
  94. package/dist/chunk-2LYHWMR4.js +0 -46
  95. package/dist/chunk-352OWAZH.js +0 -320
  96. package/dist/chunk-3Y47VMHV.js +0 -6
  97. package/dist/chunk-3YJSKOWF.js +0 -164
  98. package/dist/chunk-4KSSKTQ5.js +0 -229
  99. package/dist/chunk-4UO6FK4G.js +0 -64
  100. package/dist/chunk-5AHX53NQ.js +0 -717
  101. package/dist/chunk-5J3E5FWA.js +0 -700
  102. package/dist/chunk-5PDAG6O3.js +0 -206
  103. package/dist/chunk-6Y5B5PEX.js +0 -80
  104. package/dist/chunk-7GWQ5S5R.js +0 -2086
  105. package/dist/chunk-7H2VZTWN.js +0 -103
  106. package/dist/chunk-7NL7T6VU.js +0 -406
  107. package/dist/chunk-A5L27LE4.js +0 -387
  108. package/dist/chunk-ADIAWL34.js +0 -81
  109. package/dist/chunk-ASTEMBKJ.js +0 -519
  110. package/dist/chunk-CEMMLV5L.js +0 -98
  111. package/dist/chunk-CM5HNV2W.js +0 -131
  112. package/dist/chunk-CRTRBBRI.js +0 -105
  113. package/dist/chunk-DA2KGZZI.js +0 -13
  114. package/dist/chunk-DIB4GX3G.js +0 -8
  115. package/dist/chunk-DKIJ2RP5.js +0 -68
  116. package/dist/chunk-DWPN6SCH.js +0 -172
  117. package/dist/chunk-EDKUFGPL.js +0 -64
  118. package/dist/chunk-ENY6SKIU.js +0 -1228
  119. package/dist/chunk-F4MGCZOX.js +0 -2943
  120. package/dist/chunk-FA6POGLH.js +0 -70
  121. package/dist/chunk-FSUR7GTG.js +0 -20475
  122. package/dist/chunk-GKEOHJL5.js +0 -1856
  123. package/dist/chunk-GMCB6W5Y.js +0 -333
  124. package/dist/chunk-IE2VPG7O.js +0 -1135
  125. package/dist/chunk-IZ5B5IHQ.js +0 -3656
  126. package/dist/chunk-JAL3CH4G.js +0 -27
  127. package/dist/chunk-KIIEUXD3.js +0 -1764
  128. package/dist/chunk-KXU4NIL7.js +0 -692
  129. package/dist/chunk-MTM5IFCY.js +0 -1190
  130. package/dist/chunk-N7TMSOZH.js +0 -235
  131. package/dist/chunk-NCEBK6PH.js +0 -36
  132. package/dist/chunk-NHJU2ZAL.js +0 -56
  133. package/dist/chunk-P6KTRNVC.js +0 -321
  134. package/dist/chunk-PDO5BLNM.js +0 -22
  135. package/dist/chunk-PDRNLLFT.js +0 -3295
  136. package/dist/chunk-PEQXYSSE.js +0 -45
  137. package/dist/chunk-PKEX2AQX.js +0 -198
  138. package/dist/chunk-PSJCGIGO.js +0 -1163
  139. package/dist/chunk-PZUSUL55.js +0 -506
  140. package/dist/chunk-R45M4TUS.js +0 -37
  141. package/dist/chunk-RC745GGZ.js +0 -122
  142. package/dist/chunk-SP3QKZXJ.js +0 -820
  143. package/dist/chunk-TXKUKMVO.js +0 -417
  144. package/dist/chunk-UE4GPWSD.js +0 -931
  145. package/dist/chunk-UIE3R5XU.js +0 -17
  146. package/dist/chunk-UXPG6ZIN.js +0 -84
  147. package/dist/chunk-VFUK3X5B.js +0 -50
  148. package/dist/chunk-VNMJWREK.js +0 -787
  149. package/dist/chunk-VZ7CSOX2.js +0 -706
  150. package/dist/chunk-WCNQJKFT.js +0 -10
  151. package/dist/chunk-WDZR3QQM.js +0 -135
  152. package/dist/chunk-WLYVJCHV.js +0 -1433
  153. package/dist/chunk-WS7NAFTM.js +0 -1520
  154. package/dist/chunk-X6GDDWQH.js +0 -60
  155. package/dist/chunk-XPDEYNQI.js +0 -102
  156. package/dist/chunk-XRW44VGM.js +0 -58
  157. package/dist/chunk-Y2CM7HXH.js +0 -111
  158. package/dist/chunk-Y7X25LFY.js +0 -15
  159. package/dist/conversation-work-VIOQ5ZF3.js +0 -54
  160. package/dist/db-NGDJV7YH.js +0 -26
  161. package/dist/discovery-DWVDM7UK.js +0 -44
  162. package/dist/experimental-P5FZM4CU.js +0 -12
  163. package/dist/oauth-callback-server-YFY7YEGK.js +0 -243
  164. package/dist/oauth-relay-5XPVRPYB.js +0 -8
  165. package/dist/plugins-YTYJMMJF.js +0 -14
  166. package/dist/runner-ORTNSB2Q.js +0 -522
  167. package/dist/sandbox-egress-signals-FIL7EGPR.js +0 -73
  168. package/dist/sentry-MB3NL3IR.js +0 -42
  169. package/dist/skills-H376WQOH.js +0 -27
  170. package/dist/spawn-T43WHLOS.js +0 -42
  171. package/dist/task-runner-LBZWF32V.js +0 -36
  172. package/dist/validation-REJLRKI6.js +0 -17
  173. package/dist/work-3KJ3RE76.js +0 -51
  174. package/dist/worker-QFEBU72E.js +0 -24
@@ -1,1433 +0,0 @@
1
- import {
2
- ToolInputError
3
- } from "./chunk-GMCB6W5Y.js";
4
- import {
5
- getPlugins
6
- } from "./chunk-IZ5B5IHQ.js";
7
- import {
8
- getDb
9
- } from "./chunk-GKEOHJL5.js";
10
- import {
11
- juniorAttachments,
12
- juniorConversations,
13
- juniorEventTasks,
14
- juniorSchedulerRuns,
15
- juniorSchedulerTasks,
16
- juniorStats
17
- } from "./chunk-IE2VPG7O.js";
18
- import {
19
- juniorDestinations
20
- } from "./chunk-VFUK3X5B.js";
21
-
22
- // src/chat/event-tasks/store.ts
23
- import {
24
- resourceEventMatches
25
- } from "@sentry/junior-plugin-api";
26
- import { and, asc, desc, eq, inArray, ne, or, sql } from "drizzle-orm";
27
-
28
- // src/chat/event-tasks/types.ts
29
- import {
30
- actorUserIdSchema,
31
- destinationVisibilitySchema,
32
- resourceEventMatchSchema,
33
- resourceEventTypeSchema,
34
- slackDestinationSchema
35
- } from "@sentry/junior-plugin-api";
36
- import { z } from "zod";
37
- var EVENT_TASK_IDENTIFIER_MAX_LENGTH = 300;
38
- var eventTaskPrincipalSchema = z.object({
39
- slackUserId: actorUserIdSchema,
40
- fullName: z.string().optional(),
41
- userName: z.string().optional()
42
- }).strict();
43
- var eventTaskTriggerSchema = z.object({
44
- events: z.array(resourceEventTypeSchema).min(1),
45
- label: z.string().min(1),
46
- match: resourceEventMatchSchema.optional(),
47
- namespace: z.string().min(1),
48
- identifier: z.string().min(1).max(EVENT_TASK_IDENTIFIER_MAX_LENGTH),
49
- resourceType: z.string().min(1)
50
- }).strict();
51
- var eventTaskSchema = z.object({
52
- id: z.string().min(1),
53
- createdAtMs: z.number().finite(),
54
- createdBy: eventTaskPrincipalSchema,
55
- credentialMode: z.enum(["system", "creator"]),
56
- destination: slackDestinationSchema,
57
- destinationVisibility: destinationVisibilitySchema,
58
- task: z.object({ text: z.string().min(1) }).strict(),
59
- trigger: eventTaskTriggerSchema
60
- }).strict();
61
-
62
- // src/chat/event-tasks/store.ts
63
- function eventTaskJsonPayload(task) {
64
- const {
65
- status: _status,
66
- title: _title,
67
- ...payload
68
- } = task;
69
- return payload;
70
- }
71
- function parseTask(row) {
72
- const raw = row.task && typeof row.task === "object" ? { ...row.task } : row.task;
73
- if (raw && typeof raw === "object" && "title" in raw) {
74
- delete raw.title;
75
- }
76
- const payload = eventTaskSchema.parse(raw);
77
- const title = row.title?.trim();
78
- return {
79
- ...payload,
80
- status: row.status === "deleted" ? "deleted" : "active",
81
- ...title ? { title } : void 0
82
- };
83
- }
84
- function activeEventTaskWhere() {
85
- return ne(juniorEventTasks.status, "deleted");
86
- }
87
- async function getEventTask(db, id) {
88
- const rows = await db.select({
89
- status: juniorEventTasks.status,
90
- task: juniorEventTasks.task,
91
- title: juniorEventTasks.title
92
- }).from(juniorEventTasks).where(eq(juniorEventTasks.id, id)).limit(1);
93
- return rows[0] ? parseTask(rows[0]) : void 0;
94
- }
95
- async function createEventTask(db, task) {
96
- const parsed = eventTaskSchema.parse(eventTaskJsonPayload(task));
97
- const title = task.title?.trim() || null;
98
- await db.insert(juniorEventTasks).values({
99
- id: parsed.id,
100
- teamId: parsed.destination.teamId,
101
- namespace: parsed.trigger.namespace,
102
- identifier: parsed.trigger.identifier,
103
- createdAtMs: parsed.createdAtMs,
104
- status: "active",
105
- title,
106
- task: parsed
107
- }).onConflictDoNothing();
108
- const existing = await getEventTask(db, parsed.id);
109
- if (!existing) {
110
- return {
111
- ...parsed,
112
- status: "active",
113
- ...title ? { title } : void 0
114
- };
115
- }
116
- if (existing.status !== "deleted") {
117
- return existing;
118
- }
119
- const rows = await db.update(juniorEventTasks).set({
120
- teamId: parsed.destination.teamId,
121
- namespace: parsed.trigger.namespace,
122
- identifier: parsed.trigger.identifier,
123
- status: "active",
124
- title,
125
- task: parsed
126
- }).where(
127
- and(
128
- eq(juniorEventTasks.id, parsed.id),
129
- eq(juniorEventTasks.status, "deleted")
130
- )
131
- ).returning({
132
- status: juniorEventTasks.status,
133
- task: juniorEventTasks.task,
134
- title: juniorEventTasks.title
135
- });
136
- return rows[0] ? parseTask(rows[0]) : await getEventTask(db, parsed.id) ?? {
137
- ...parsed,
138
- status: "active",
139
- ...title ? { title } : void 0
140
- };
141
- }
142
- async function saveEventTask(db, task) {
143
- const parsed = eventTaskSchema.parse(eventTaskJsonPayload(task));
144
- const title = task.title?.trim() || null;
145
- const rows = await db.update(juniorEventTasks).set({
146
- namespace: parsed.trigger.namespace,
147
- identifier: parsed.trigger.identifier,
148
- title,
149
- task: parsed
150
- }).where(and(eq(juniorEventTasks.id, parsed.id), activeEventTaskWhere())).returning({
151
- status: juniorEventTasks.status,
152
- task: juniorEventTasks.task,
153
- title: juniorEventTasks.title
154
- });
155
- return rows[0] ? parseTask(rows[0]) : void 0;
156
- }
157
- async function deleteEventTask(db, id) {
158
- const rows = await db.update(juniorEventTasks).set({ status: "deleted" }).where(and(eq(juniorEventTasks.id, id), activeEventTaskWhere())).returning({
159
- status: juniorEventTasks.status,
160
- task: juniorEventTasks.task,
161
- title: juniorEventTasks.title
162
- });
163
- return rows[0] ? parseTask(rows[0]) : void 0;
164
- }
165
- async function listEventTasksForTeam(db, teamId) {
166
- const rows = await db.select({
167
- status: juniorEventTasks.status,
168
- task: juniorEventTasks.task,
169
- title: juniorEventTasks.title
170
- }).from(juniorEventTasks).where(and(eq(juniorEventTasks.teamId, teamId), activeEventTaskWhere())).orderBy(asc(juniorEventTasks.createdAtMs), asc(juniorEventTasks.id));
171
- return rows.map(parseTask);
172
- }
173
- function viewerSlackIdentities(user) {
174
- return user.identities.filter(
175
- (identity) => identity.provider === "slack" && Boolean(identity.providerTenantId) && Boolean(identity.providerSubjectId)
176
- );
177
- }
178
- function eventTaskBelongsToUser(task, user) {
179
- return viewerSlackIdentities(user).some(
180
- (identity) => identity.providerTenantId === task.destination.teamId && identity.providerSubjectId === task.createdBy.slackUserId
181
- );
182
- }
183
- async function listEventTasksCreatedBy(db, user, limit) {
184
- const identities = viewerSlackIdentities(user);
185
- const ownership = or(
186
- ...identities.map(
187
- (identity) => and(
188
- eq(juniorEventTasks.teamId, identity.providerTenantId),
189
- sql`${juniorEventTasks.task}->'createdBy'->>'slackUserId' = ${identity.providerSubjectId}`
190
- )
191
- )
192
- );
193
- if (!ownership) return [];
194
- const rows = await db.select({
195
- status: juniorEventTasks.status,
196
- task: juniorEventTasks.task,
197
- title: juniorEventTasks.title
198
- }).from(juniorEventTasks).where(and(ownership, activeEventTaskWhere())).orderBy(desc(juniorEventTasks.createdAtMs), desc(juniorEventTasks.id)).limit(limit);
199
- return rows.map(parseTask);
200
- }
201
- async function listDeletedEventTasksCreatedBy(db, user, limit) {
202
- const identities = viewerSlackIdentities(user);
203
- const ownership = or(
204
- ...identities.map(
205
- (identity) => and(
206
- eq(juniorEventTasks.teamId, identity.providerTenantId),
207
- sql`${juniorEventTasks.task}->'createdBy'->>'slackUserId' = ${identity.providerSubjectId}`
208
- )
209
- )
210
- );
211
- if (!ownership) return [];
212
- const rows = await db.select({
213
- status: juniorEventTasks.status,
214
- task: juniorEventTasks.task,
215
- title: juniorEventTasks.title
216
- }).from(juniorEventTasks).where(and(ownership, eq(juniorEventTasks.status, "deleted"))).orderBy(desc(juniorEventTasks.createdAtMs), desc(juniorEventTasks.id)).limit(limit);
217
- return rows.map(parseTask);
218
- }
219
- async function listPublicEventTasksForTeams(db, teamIds, limit) {
220
- if (teamIds.length === 0) return [];
221
- const rows = await db.select({
222
- status: juniorEventTasks.status,
223
- task: juniorEventTasks.task,
224
- title: juniorEventTasks.title
225
- }).from(juniorEventTasks).innerJoin(
226
- juniorDestinations,
227
- and(
228
- eq(juniorDestinations.provider, "slack"),
229
- eq(juniorDestinations.providerTenantId, juniorEventTasks.teamId),
230
- sql`${juniorDestinations.providerDestinationId} = ${juniorEventTasks.task}->'destination'->>'channelId'`
231
- )
232
- ).where(
233
- and(
234
- inArray(juniorEventTasks.teamId, teamIds),
235
- activeEventTaskWhere(),
236
- eq(juniorDestinations.visibility, "public")
237
- )
238
- ).orderBy(desc(juniorEventTasks.createdAtMs), desc(juniorEventTasks.id)).limit(limit);
239
- return rows.map(parseTask);
240
- }
241
- async function collectEventTaskMatchKeys(db, input) {
242
- const eventTypes = new Set(
243
- input.eventTypes.map((eventType) => eventType.trim()).filter(Boolean)
244
- );
245
- const identifiers = [
246
- ...new Set(input.identifiers.map((value) => value.trim()).filter(Boolean))
247
- ];
248
- if (eventTypes.size === 0 || identifiers.length === 0) return [];
249
- const rows = await db.select({
250
- status: juniorEventTasks.status,
251
- task: juniorEventTasks.task,
252
- title: juniorEventTasks.title
253
- }).from(juniorEventTasks).where(
254
- and(
255
- eq(juniorEventTasks.teamId, input.teamId),
256
- eq(juniorEventTasks.namespace, input.namespace),
257
- inArray(juniorEventTasks.identifier, identifiers),
258
- activeEventTaskWhere()
259
- )
260
- ).orderBy(asc(juniorEventTasks.createdAtMs), asc(juniorEventTasks.id));
261
- const keys = /* @__PURE__ */ new Set();
262
- for (const row of rows) {
263
- const task = parseTask(row);
264
- if (!task.trigger.events.some((eventType) => eventTypes.has(eventType))) {
265
- continue;
266
- }
267
- for (const key of Object.keys(task.trigger.match ?? {})) {
268
- keys.add(key);
269
- }
270
- }
271
- return [...keys].sort();
272
- }
273
- async function findMatchingEventTasks(db, event, teamId) {
274
- const rows = await db.select({
275
- status: juniorEventTasks.status,
276
- task: juniorEventTasks.task,
277
- title: juniorEventTasks.title
278
- }).from(juniorEventTasks).where(
279
- and(
280
- eq(juniorEventTasks.teamId, teamId),
281
- eq(juniorEventTasks.namespace, event.namespace),
282
- eq(juniorEventTasks.identifier, event.identifier),
283
- activeEventTaskWhere()
284
- )
285
- ).orderBy(asc(juniorEventTasks.createdAtMs), asc(juniorEventTasks.id));
286
- return rows.map(parseTask).filter(
287
- (task) => task.trigger.events.includes(event.eventType) && resourceEventMatches(task.trigger.match, event.data)
288
- );
289
- }
290
-
291
- // src/stats.ts
292
- import { and as and2, asc as asc2, gte, lte, sql as sql2 } from "drizzle-orm";
293
- function utcDate(nowMs) {
294
- return new Date(nowMs).toISOString().slice(0, 10);
295
- }
296
- async function incrementStat(key, options = {}) {
297
- const date = utcDate(options.nowMs ?? Date.now());
298
- await getDb().insert(juniorStats).values({ ...key, date, count: 1 }).onConflictDoUpdate({
299
- target: [
300
- juniorStats.date,
301
- juniorStats.namespace,
302
- juniorStats.metric,
303
- juniorStats.name
304
- ],
305
- set: { count: sql2`${juniorStats.count} + 1` }
306
- });
307
- }
308
- async function readStats(start, end) {
309
- return await getDb().select().from(juniorStats).where(and2(gte(juniorStats.date, start), lte(juniorStats.date, end))).orderBy(
310
- asc2(juniorStats.date),
311
- asc2(juniorStats.namespace),
312
- asc2(juniorStats.metric),
313
- asc2(juniorStats.name)
314
- );
315
- }
316
-
317
- // src/chat/tool-support/structured-result.ts
318
- import { z as z2 } from "zod";
319
- var juniorToolContinuationSchema = z2.object({
320
- arguments: z2.record(z2.string(), z2.unknown()),
321
- reason: z2.string().min(1).optional()
322
- }).strict();
323
- var juniorToolBoundContinuationSchema = juniorToolContinuationSchema.extend({
324
- tool_name: z2.string().min(1)
325
- });
326
- var juniorToolOutputSchema = z2.object({
327
- target: z2.string().min(1).optional(),
328
- truncated: z2.boolean().optional(),
329
- /** True when this attempt did not finish before its time budget. */
330
- timed_out: z2.boolean().optional(),
331
- continuation: juniorToolContinuationSchema.optional()
332
- }).passthrough();
333
- function sortJsonValue(value) {
334
- if (Array.isArray(value)) {
335
- return value.map(sortJsonValue);
336
- }
337
- if (!value || typeof value !== "object") {
338
- return value;
339
- }
340
- return Object.fromEntries(
341
- Object.entries(value).filter(([, item]) => item !== void 0).sort(([left], [right]) => left.localeCompare(right)).map(([key, item]) => [key, sortJsonValue(item)])
342
- );
343
- }
344
- function isRecord(value) {
345
- return Boolean(value && typeof value === "object" && !Array.isArray(value));
346
- }
347
- function injectContinuationToolName(details, toolName) {
348
- if (!isRecord(details) || !isRecord(details.continuation)) {
349
- return details;
350
- }
351
- return {
352
- ...details,
353
- continuation: juniorToolBoundContinuationSchema.parse({
354
- ...details.continuation,
355
- tool_name: toolName
356
- })
357
- };
358
- }
359
- function makeStructuredToolOutput(details, options = {}) {
360
- return {
361
- content: [
362
- {
363
- type: "text",
364
- text: JSON.stringify(sortJsonValue(details))
365
- },
366
- ...options.content ?? []
367
- ],
368
- details
369
- };
370
- }
371
-
372
- // src/chat/resource-events/catalog.ts
373
- import {
374
- normalizeResourceEventIdentifier,
375
- resourceEventMatchSchema as resourceEventMatchSchema2
376
- } from "@sentry/junior-plugin-api";
377
- import { z as z3 } from "zod";
378
- var CORE_RESOURCE_EVENT_NAMESPACE = "junior";
379
- function hasResourceEventCatalogEntries(catalog) {
380
- return Object.keys(catalog).length > 0;
381
- }
382
- function hasPluginResourceEventCatalogEntries(catalog) {
383
- return Object.keys(pluginResourceEventCatalog(catalog)).length > 0;
384
- }
385
- function pluginResourceEventCatalog(catalog) {
386
- return Object.fromEntries(
387
- Object.entries(catalog).filter(
388
- ([namespace]) => namespace !== CORE_RESOURCE_EVENT_NAMESPACE
389
- )
390
- );
391
- }
392
- function enumSchema(values, unavailableMessage) {
393
- if (values.length === 0) {
394
- return z3.string().refine(() => false, unavailableMessage);
395
- }
396
- return z3.enum(values);
397
- }
398
- function eventNamespaceSchema(catalog) {
399
- return enumSchema(
400
- Object.keys(catalog).sort(),
401
- "No resource event namespaces are enabled."
402
- );
403
- }
404
- function registeredEventTypeSchema(catalog) {
405
- return enumSchema(
406
- [
407
- ...new Set(
408
- Object.values(catalog).flatMap(
409
- (registration) => registration.resourceTypes.flatMap(
410
- (resourceType) => resourceType.supportedEvents
411
- )
412
- )
413
- )
414
- ].sort(),
415
- "No resource event types are enabled."
416
- );
417
- }
418
- function registeredResourceTypeSchema(catalog) {
419
- return enumSchema(
420
- [
421
- ...new Set(
422
- Object.values(catalog).flatMap(
423
- (registration) => registration.resourceTypes.map((resourceType) => resourceType.type)
424
- )
425
- )
426
- ].sort(),
427
- "No resource types are enabled."
428
- );
429
- }
430
- function pluginSupportsEvent(catalog, namespace, resourceType, eventType) {
431
- return catalog[namespace]?.resourceTypes.find((candidate) => candidate.type === resourceType)?.supportedEvents.includes(eventType) ?? false;
432
- }
433
- function resourceEventGuidance(catalog, namespace, resourceType, eventType) {
434
- return catalog[namespace]?.resourceTypes.find(
435
- (candidate) => candidate.type === resourceType
436
- )?.guidance?.[eventType];
437
- }
438
- function resourceEventMatchFields(catalog, namespace, resourceType) {
439
- return catalog[namespace]?.resourceTypes.find(
440
- (candidate) => candidate.type === resourceType
441
- )?.matchFields;
442
- }
443
- function registeredResourceEventMatchSchema() {
444
- return resourceEventMatchSchema2.optional().describe(
445
- "Optional exact values from the resource type matchFields. A single value must equal the event data. A list means the event value may be any one entry. Omit a key to ignore it. Unmatched events are dropped with no agent turn."
446
- );
447
- }
448
- function requireSupportedResourceEventMatch(catalog, input) {
449
- const match = input.match;
450
- if (!match || Object.keys(match).length === 0) return void 0;
451
- const fields = resourceEventMatchFields(
452
- catalog,
453
- input.namespace,
454
- input.resourceType
455
- );
456
- if (!fields || Object.keys(fields).length === 0) {
457
- throw new ToolInputError(
458
- `Resource type "${input.namespace}:${input.resourceType}" does not support match.`
459
- );
460
- }
461
- const normalized = {};
462
- for (const [key, value] of Object.entries(match)) {
463
- const field = fields[key];
464
- if (!field) {
465
- throw new ToolInputError(
466
- `Resource type "${input.namespace}:${input.resourceType}" does not support match key "${key}".`
467
- );
468
- }
469
- if (Array.isArray(value)) {
470
- if (field.kind === "boolean") {
471
- throw new ToolInputError(
472
- `Match key "${key}" is boolean and cannot use a list.`
473
- );
474
- }
475
- for (const entry of value) {
476
- if (typeof entry !== field.kind) {
477
- throw new ToolInputError(
478
- `Match key "${key}" expects ${field.kind} values.`
479
- );
480
- }
481
- if (field.enum && typeof entry === "string" && !field.enum.includes(entry)) {
482
- throw new ToolInputError(
483
- `Match key "${key}" does not allow value "${entry}".`
484
- );
485
- }
486
- }
487
- normalized[key] = [...new Set(value)];
488
- continue;
489
- }
490
- if (typeof value !== field.kind) {
491
- throw new ToolInputError(
492
- `Match key "${key}" expects a ${field.kind} value.`
493
- );
494
- }
495
- if (field.enum && typeof value === "string" && !field.enum.includes(value)) {
496
- throw new ToolInputError(
497
- `Match key "${key}" does not allow value "${value}".`
498
- );
499
- }
500
- normalized[key] = value;
501
- }
502
- return Object.keys(normalized).length > 0 ? normalized : void 0;
503
- }
504
- function normalizeEventIdentifier(catalog, namespace, identifier) {
505
- return normalizeResourceEventIdentifier(catalog[namespace], identifier);
506
- }
507
-
508
- // src/chat/sandbox/snapshot/events.ts
509
- var WORKSPACE_SNAPSHOT_NAMESPACE = CORE_RESOURCE_EVENT_NAMESPACE;
510
- var WORKSPACE_SNAPSHOT_RESOURCE_TYPE = "workspace_snapshot";
511
- var WORKSPACE_SNAPSHOT_READY_EVENT = "workspace_snapshot.ready";
512
- var WORKSPACE_SNAPSHOT_FAILED_EVENT = "workspace_snapshot.failed";
513
- function workspaceSnapshotResourceEvents() {
514
- return {
515
- resourceTypes: [
516
- {
517
- type: WORKSPACE_SNAPSHOT_RESOURCE_TYPE,
518
- supportedEvents: [
519
- WORKSPACE_SNAPSHOT_READY_EVENT,
520
- WORKSPACE_SNAPSHOT_FAILED_EVENT
521
- ],
522
- // switchWorkspace already creates the temporary subscription.
523
- suggestedEvents: [],
524
- guidance: {
525
- [WORKSPACE_SNAPSHOT_READY_EVENT]: "Call switchWorkspace again with the same Workspace name. Do not create another watch when switchWorkspace already returned a subscription.",
526
- [WORKSPACE_SNAPSHOT_FAILED_EVENT]: "Report the snapshot failure. Do not keep waiting for this Workspace unless the user asks to retry."
527
- }
528
- }
529
- ]
530
- };
531
- }
532
- function workspaceSnapshotWatch(input) {
533
- return {
534
- identifier: input.workspaceId,
535
- label: `Workspace ${input.workspaceName} snapshot`,
536
- namespace: WORKSPACE_SNAPSHOT_NAMESPACE,
537
- supportedEvents: [
538
- WORKSPACE_SNAPSHOT_READY_EVENT,
539
- WORKSPACE_SNAPSHOT_FAILED_EVENT
540
- ],
541
- suggestedEvents: [],
542
- type: WORKSPACE_SNAPSHOT_RESOURCE_TYPE
543
- };
544
- }
545
- function workspaceSnapshotFinishedEvent(input) {
546
- const occurredAtMs = input.occurredAtMs ?? Date.now();
547
- const eventType = input.status === "ready" ? WORKSPACE_SNAPSHOT_READY_EVENT : WORKSPACE_SNAPSHOT_FAILED_EVENT;
548
- const trustedSummary = input.status === "ready" ? "Workspace snapshot is ready." : "Workspace snapshot build failed.";
549
- const event = {
550
- eventKey: `${WORKSPACE_SNAPSHOT_NAMESPACE}:${input.workspaceId}:${input.resultId}:${input.status}`,
551
- eventType,
552
- identifier: input.workspaceId,
553
- namespace: WORKSPACE_SNAPSHOT_NAMESPACE,
554
- occurredAtMs,
555
- terminal: true,
556
- trustedSummary,
557
- data: {
558
- workspaceId: input.workspaceId,
559
- status: input.status
560
- }
561
- };
562
- if (input.status === "failed" && input.error) {
563
- event.untrustedText = input.error;
564
- }
565
- return event;
566
- }
567
-
568
- // src/chat/resource-events/runtime-catalog.ts
569
- function getResourceEventCatalog() {
570
- const catalog = {
571
- [CORE_RESOURCE_EVENT_NAMESPACE]: workspaceSnapshotResourceEvents()
572
- };
573
- for (const plugin of getPlugins()) {
574
- const registration = plugin.resourceEvents;
575
- if (!registration || registration.isEnabled?.() === false) {
576
- continue;
577
- }
578
- if (plugin.manifest.name === CORE_RESOURCE_EVENT_NAMESPACE) {
579
- throw new Error(
580
- `Plugin "${CORE_RESOURCE_EVENT_NAMESPACE}" cannot register resource events; that namespace is reserved for core Workspace snapshots`
581
- );
582
- }
583
- catalog[plugin.manifest.name] = registration;
584
- }
585
- return catalog;
586
- }
587
-
588
- // src/chat/event-tasks/tool-support.ts
589
- import { z as z4 } from "zod";
590
- var compactEventTaskResultSchema = z4.object({
591
- id: z4.string().min(1),
592
- task: z4.string().min(1),
593
- namespace: z4.string().min(1),
594
- identifier: z4.string().min(1),
595
- resourceType: z4.string().min(1),
596
- label: z4.string().min(1),
597
- events: z4.array(z4.string().min(1)).min(1),
598
- match: z4.record(z4.string(), z4.unknown()).optional(),
599
- credentialMode: z4.enum(["system", "creator"]),
600
- createdBy: eventTaskPrincipalSchema,
601
- triggerAvailable: z4.boolean()
602
- }).strict();
603
- var eventTaskToolResultSchema = juniorToolOutputSchema.extend({
604
- task: compactEventTaskResultSchema
605
- }).strict();
606
- var eventTaskListToolResultSchema = juniorToolOutputSchema.extend({
607
- tasks: z4.array(compactEventTaskResultSchema),
608
- truncated: z4.boolean()
609
- }).strict();
610
- function registeredEventTaskTriggerSchema(catalog) {
611
- const plugins = pluginResourceEventCatalog(catalog);
612
- return z4.object({
613
- namespace: eventNamespaceSchema(plugins),
614
- identifier: z4.string().trim().min(1).max(EVENT_TASK_IDENTIFIER_MAX_LENGTH),
615
- resourceType: registeredResourceTypeSchema(plugins),
616
- label: z4.string().trim().min(1).max(500),
617
- events: z4.array(registeredEventTypeSchema(plugins)).min(1),
618
- match: registeredResourceEventMatchSchema()
619
- }).strict();
620
- }
621
- function requireSupportedEventTaskTrigger(catalog, trigger) {
622
- const plugins = pluginResourceEventCatalog(catalog);
623
- for (const eventType of trigger.events) {
624
- if (!pluginSupportsEvent(
625
- plugins,
626
- trigger.namespace,
627
- trigger.resourceType,
628
- eventType
629
- )) {
630
- throw new ToolInputError(
631
- `Resource type "${trigger.namespace}:${trigger.resourceType}" does not support event "${eventType}".`
632
- );
633
- }
634
- }
635
- return requireSupportedResourceEventMatch(plugins, {
636
- match: trigger.match,
637
- namespace: trigger.namespace,
638
- resourceType: trigger.resourceType
639
- });
640
- }
641
- function requireEventTaskSlackContext(context) {
642
- if (context.source.kind !== "slack" || context.destination.platform !== "slack" || context.actor?.platform !== "slack" || context.actor.teamId !== context.source.teamId) {
643
- throw new ToolInputError(
644
- "Event tasks require an active Slack channel or DM and actor."
645
- );
646
- }
647
- return {
648
- actor: context.actor,
649
- destination: {
650
- platform: "slack",
651
- teamId: context.source.teamId,
652
- channelId: context.source.channelId
653
- },
654
- source: context.source
655
- };
656
- }
657
- function eventTaskMatchesDestination(task, destination) {
658
- return task.destination.teamId === destination.teamId && task.destination.channelId === destination.channelId;
659
- }
660
- function eventTaskIsWritableFrom(task, destination) {
661
- if (task.destination.teamId !== destination.teamId) {
662
- return false;
663
- }
664
- return task.destination.channelId === destination.channelId || task.destinationVisibility === "public";
665
- }
666
- async function writableEventTask(context, id) {
667
- const { destination } = requireEventTaskSlackContext(context);
668
- const task = await getEventTask(getDb(), id);
669
- if (!task || task.status === "deleted" || !eventTaskIsWritableFrom(task, destination)) {
670
- throw new ToolInputError("Event task was not found.");
671
- }
672
- return task;
673
- }
674
- function eventTaskTriggerAvailable(task, catalog) {
675
- const registration = pluginResourceEventCatalog(catalog)[task.trigger.namespace];
676
- return Boolean(
677
- registration && task.trigger.events.every(
678
- (eventType) => registration.resourceTypes.some(
679
- (resourceType) => resourceType.supportedEvents.includes(eventType)
680
- )
681
- )
682
- );
683
- }
684
- function compactEventTask(task, catalog) {
685
- return compactEventTaskResultSchema.parse({
686
- id: task.id,
687
- task: task.task.text,
688
- namespace: task.trigger.namespace,
689
- identifier: task.trigger.identifier,
690
- resourceType: task.trigger.resourceType,
691
- label: task.trigger.label,
692
- events: task.trigger.events,
693
- ...task.trigger.match ? { match: task.trigger.match } : void 0,
694
- credentialMode: task.credentialMode,
695
- createdBy: task.createdBy,
696
- triggerAvailable: eventTaskTriggerAvailable(task, catalog)
697
- });
698
- }
699
- function eventTaskSuccess(task, catalog) {
700
- return {
701
- task: compactEventTask(task, catalog)
702
- };
703
- }
704
-
705
- // src/chat/scheduled-tasks/tasks.ts
706
- import { and as and3, asc as asc3, desc as desc2, eq as eq2, inArray as inArray2, notInArray, sql as sql3 } from "drizzle-orm";
707
- import { z as z6 } from "zod";
708
-
709
- // src/chat/scheduled-tasks/types.ts
710
- import {
711
- actorUserIdSchema as actorUserIdSchema2,
712
- slackDestinationSchema as slackDestinationSchema2
713
- } from "@sentry/junior-plugin-api";
714
- import { z as z5 } from "zod";
715
- var scheduledTaskStatusSchema = z5.enum([
716
- "active",
717
- "blocked",
718
- "completed",
719
- "deleted"
720
- ]);
721
- var scheduledTaskCredentialModeSchema = z5.enum(["system", "creator"]);
722
- var scheduledRunStatusSchema = z5.enum([
723
- "pending",
724
- "running",
725
- "completed",
726
- "failed",
727
- "blocked",
728
- "skipped"
729
- ]);
730
- var scheduledTaskPrincipalSchema = z5.object({
731
- slackUserId: actorUserIdSchema2,
732
- fullName: z5.string().optional(),
733
- userName: z5.string().optional()
734
- }).strict();
735
- var scheduledTaskRecurrenceSchema = z5.object({
736
- dayOfMonth: z5.number().optional(),
737
- frequency: z5.enum(["daily", "weekly", "monthly", "yearly"]),
738
- interval: z5.number(),
739
- month: z5.number().optional(),
740
- startDate: z5.string(),
741
- time: z5.object({
742
- hour: z5.number(),
743
- minute: z5.number()
744
- }).strict(),
745
- weekdays: z5.array(z5.number()).optional()
746
- }).strict();
747
- var scheduledTaskScheduleSchema = z5.object({
748
- description: z5.string(),
749
- kind: z5.enum(["one_off", "recurring"]),
750
- recurrence: scheduledTaskRecurrenceSchema.optional(),
751
- timezone: z5.string()
752
- }).strict();
753
- var scheduledTaskExecutionActorSchema = z5.object({
754
- platform: z5.literal("system"),
755
- name: z5.string()
756
- }).strict();
757
- var scheduledTaskSchema = z5.object({
758
- id: z5.string(),
759
- conversationAccess: z5.object({
760
- audience: z5.enum(["direct", "group", "channel"]),
761
- visibility: z5.enum(["private", "public"])
762
- }).strict(),
763
- createdAtMs: z5.number(),
764
- createdBy: scheduledTaskPrincipalSchema,
765
- /** Authoritative provider identity that created the task. */
766
- creatorIdentityId: z5.string(),
767
- /** Selects system credentials or task-bound creator credential delegation. */
768
- credentialMode: scheduledTaskCredentialModeSchema,
769
- destination: slackDestinationSchema2,
770
- executionActor: scheduledTaskExecutionActorSchema.optional(),
771
- lastRunAtMs: z5.number().optional(),
772
- nextRunAtMs: z5.number().optional(),
773
- originalRequest: z5.string().optional(),
774
- runNowAtMs: z5.number().optional(),
775
- schedule: scheduledTaskScheduleSchema,
776
- status: scheduledTaskStatusSchema,
777
- statusReason: z5.string().optional(),
778
- task: z5.object({ text: z5.string() }).strict(),
779
- /** SQL-backed short display title generated from the task instruction. */
780
- title: z5.string().optional(),
781
- updatedAtMs: z5.number()
782
- }).strict();
783
- var scheduledRunSchema = z5.object({
784
- id: z5.string(),
785
- attempt: z5.number(),
786
- claimedAtMs: z5.number(),
787
- completedAtMs: z5.number().optional(),
788
- dispatchId: z5.string().optional(),
789
- errorMessage: z5.string().optional(),
790
- resultMessageTs: z5.string().optional(),
791
- scheduledForMs: z5.number(),
792
- startedAtMs: z5.number().optional(),
793
- status: scheduledRunStatusSchema,
794
- taskId: z5.string()
795
- }).strict();
796
- var SCHEDULED_TASK_SYSTEM_ACTOR = Object.freeze({
797
- platform: "system",
798
- name: "scheduled-task"
799
- });
800
-
801
- // src/chat/scheduled-tasks/tasks.ts
802
- var SCHEDULER_KEY_PREFIX = "junior:scheduler";
803
- var retainedScheduledTaskSchema = scheduledTaskSchema.omit({ creatorIdentityId: true, title: true }).extend({
804
- // TODO(dcramer): Remove paused decoding and SQL list filtering after
805
- // v0.129.x workers are unsupported and cannot overlap an upgrade.
806
- status: z6.enum(["active", "blocked", "completed", "deleted", "paused"]),
807
- version: z6.number().optional()
808
- }).strict();
809
- function parseScheduledTaskRow(row) {
810
- const record = z6.record(z6.string(), z6.unknown()).safeParse(row.record);
811
- if (!record.success) return void 0;
812
- const {
813
- creatorIdentityId: legacyCreatorIdentityId,
814
- title: legacyTitle,
815
- ...retained
816
- } = record.data;
817
- const parsed = retainedScheduledTaskSchema.safeParse(retained);
818
- if (!parsed.success) return void 0;
819
- const { status, version: _version, ...task } = parsed.data;
820
- const fallbackIdentity = row.creatorIdentityId === null ? z6.string().safeParse(legacyCreatorIdentityId) : void 0;
821
- const creatorIdentityId = row.creatorIdentityId ?? (fallbackIdentity?.success ? fallbackIdentity.data : void 0);
822
- if (creatorIdentityId === void 0) return void 0;
823
- const fallbackTitle = row.title === null ? z6.string().safeParse(legacyTitle) : void 0;
824
- const titleSource = row.title !== null ? row.title : fallbackTitle?.success ? fallbackTitle.data : void 0;
825
- const title = titleSource?.trim() || void 0;
826
- if (status === "paused") {
827
- const {
828
- nextRunAtMs: _nextRunAtMs,
829
- runNowAtMs: _runNowAtMs,
830
- ...retained2
831
- } = task;
832
- return {
833
- ...retained2,
834
- creatorIdentityId,
835
- status: "deleted",
836
- ...title ? { title } : void 0
837
- };
838
- }
839
- return {
840
- ...task,
841
- creatorIdentityId,
842
- status,
843
- ...title ? { title } : void 0
844
- };
845
- }
846
- function isListedScheduledTask(task) {
847
- return task !== void 0 && task.status !== "deleted";
848
- }
849
- function scheduledRunId(taskId, scheduledForMs) {
850
- return `${taskId}:${scheduledForMs}`;
851
- }
852
- function withScheduledTaskLock(db, taskId, callback) {
853
- return db.transaction(async (tx) => {
854
- await tx.execute(
855
- sql3`SELECT pg_advisory_xact_lock(hashtext(${`${SCHEDULER_KEY_PREFIX}:task:${taskId}:lock`}))`
856
- );
857
- return await callback(tx);
858
- });
859
- }
860
- function requireStoredTask(task) {
861
- const parsed = scheduledTaskSchema.safeParse(task);
862
- if (!parsed.success) {
863
- throw new Error("Scheduled task routing context is invalid.");
864
- }
865
- const { title, ...current } = parsed.data;
866
- const normalizedTitle = title?.trim() || void 0;
867
- return {
868
- ...current,
869
- ...normalizedTitle ? { title: normalizedTitle } : void 0
870
- };
871
- }
872
- function scheduledTaskJsonRecord(task) {
873
- const { title: _title, ...record } = task;
874
- return record;
875
- }
876
- async function upsertScheduledTask(db, task) {
877
- const title = task.title?.trim() || null;
878
- await db.insert(juniorSchedulerTasks).values({
879
- createdAtMs: task.createdAtMs,
880
- creatorIdentityId: task.creatorIdentityId,
881
- // TODO(dcramer): Stop writing this column after v0.127.x runtimes are
882
- // unsupported and no supported runtime reads it.
883
- creatorSlackUserId: task.createdBy.slackUserId,
884
- id: task.id,
885
- nextRunAtMs: task.nextRunAtMs,
886
- record: scheduledTaskJsonRecord(task),
887
- runNowAtMs: task.runNowAtMs,
888
- status: task.status,
889
- teamId: task.destination.teamId,
890
- title
891
- }).onConflictDoUpdate({
892
- target: juniorSchedulerTasks.id,
893
- set: {
894
- createdAtMs: sql3`excluded.created_at_ms`,
895
- creatorIdentityId: sql3`excluded.creator_identity_id`,
896
- creatorSlackUserId: sql3`excluded.creator_slack_user_id`,
897
- nextRunAtMs: sql3`excluded.next_run_at_ms`,
898
- record: sql3`excluded.record`,
899
- runNowAtMs: sql3`excluded.run_now_at_ms`,
900
- status: sql3`excluded.status`,
901
- teamId: sql3`excluded.team_id`,
902
- title: sql3`excluded.title`
903
- }
904
- });
905
- }
906
- async function readScheduledTask(db, taskId) {
907
- const rows = await db.select({
908
- creatorIdentityId: juniorSchedulerTasks.creatorIdentityId,
909
- record: juniorSchedulerTasks.record,
910
- title: juniorSchedulerTasks.title
911
- }).from(juniorSchedulerTasks).where(eq2(juniorSchedulerTasks.id, taskId)).limit(1);
912
- return rows[0] ? parseScheduledTaskRow(rows[0]) : void 0;
913
- }
914
- async function readListedScheduledTasks(db, teamId) {
915
- const rows = await db.select({
916
- creatorIdentityId: juniorSchedulerTasks.creatorIdentityId,
917
- record: juniorSchedulerTasks.record,
918
- title: juniorSchedulerTasks.title
919
- }).from(juniorSchedulerTasks).where(
920
- and3(
921
- notInArray(juniorSchedulerTasks.status, ["deleted", "paused"]),
922
- teamId === void 0 ? void 0 : eq2(juniorSchedulerTasks.teamId, teamId)
923
- )
924
- ).orderBy(
925
- asc3(juniorSchedulerTasks.createdAtMs),
926
- asc3(juniorSchedulerTasks.id)
927
- );
928
- return rows.map(parseScheduledTaskRow).filter(isListedScheduledTask);
929
- }
930
- async function listScheduledTasks(db) {
931
- return await readListedScheduledTasks(db);
932
- }
933
- async function listScheduledTasksForTeam(db, teamId) {
934
- return await readListedScheduledTasks(db, teamId);
935
- }
936
- async function writeScheduledTask(db, task, current) {
937
- if (current?.status === "blocked" && task.status === "active" && typeof task.nextRunAtMs === "number" && Number.isFinite(task.nextRunAtMs)) {
938
- await db.delete(juniorSchedulerRuns).where(
939
- and3(
940
- eq2(juniorSchedulerRuns.id, scheduledRunId(task.id, task.nextRunAtMs)),
941
- eq2(juniorSchedulerRuns.status, "blocked")
942
- )
943
- );
944
- }
945
- if (task.status === "deleted" && current?.status !== "deleted") {
946
- await skipPendingRunsForDeletedTask(db, task);
947
- }
948
- await upsertScheduledTask(db, task);
949
- }
950
- async function skipPendingRunsForDeletedTask(db, task) {
951
- const errorMessage = `Scheduled task ${task.id} was deleted before the run started.`;
952
- const rows = await db.select({ record: juniorSchedulerRuns.record }).from(juniorSchedulerRuns).where(
953
- and3(
954
- eq2(juniorSchedulerRuns.taskId, task.id),
955
- eq2(juniorSchedulerRuns.status, "pending")
956
- )
957
- );
958
- for (const row of rows) {
959
- const parsed = scheduledRunSchema.safeParse(row.record);
960
- if (!parsed.success || parsed.data.status !== "pending") continue;
961
- const next = scheduledRunSchema.parse({
962
- ...parsed.data,
963
- completedAtMs: task.updatedAtMs,
964
- errorMessage,
965
- status: "skipped"
966
- });
967
- await db.update(juniorSchedulerRuns).set({
968
- record: next,
969
- status: next.status
970
- }).where(
971
- and3(
972
- eq2(juniorSchedulerRuns.id, next.id),
973
- eq2(juniorSchedulerRuns.status, "pending")
974
- )
975
- );
976
- }
977
- }
978
- async function createScheduledTask(db, task) {
979
- const next = requireStoredTask(task);
980
- return await withScheduledTaskLock(db, task.id, async (tx) => {
981
- const current = await readScheduledTask(tx, task.id);
982
- if (current) return current;
983
- await writeScheduledTask(tx, next, void 0);
984
- return next;
985
- });
986
- }
987
- async function saveScheduledTask(db, task) {
988
- const next = requireStoredTask(task);
989
- await withScheduledTaskLock(db, task.id, async (tx) => {
990
- const current = await readScheduledTask(tx, task.id);
991
- await writeScheduledTask(tx, next, current);
992
- });
993
- return next;
994
- }
995
- async function saveScheduledTaskInLock(db, task, current) {
996
- await writeScheduledTask(db, requireStoredTask(task), current);
997
- }
998
- async function listPublicScheduledTasksForTeams(db, teamIds, limit) {
999
- if (teamIds.length === 0) return [];
1000
- const rows = await db.select({
1001
- creatorIdentityId: juniorSchedulerTasks.creatorIdentityId,
1002
- record: juniorSchedulerTasks.record,
1003
- title: juniorSchedulerTasks.title
1004
- }).from(juniorSchedulerTasks).innerJoin(
1005
- juniorDestinations,
1006
- and3(
1007
- eq2(juniorDestinations.provider, "slack"),
1008
- eq2(juniorDestinations.providerTenantId, juniorSchedulerTasks.teamId),
1009
- sql3`${juniorDestinations.providerDestinationId} = ${juniorSchedulerTasks.record}->'destination'->>'channelId'`
1010
- )
1011
- ).where(
1012
- and3(
1013
- inArray2(juniorSchedulerTasks.teamId, teamIds),
1014
- notInArray(juniorSchedulerTasks.status, [
1015
- "completed",
1016
- "deleted",
1017
- "paused"
1018
- ]),
1019
- eq2(juniorDestinations.visibility, "public")
1020
- )
1021
- ).orderBy(
1022
- desc2(juniorSchedulerTasks.createdAtMs),
1023
- desc2(juniorSchedulerTasks.id)
1024
- ).limit(limit);
1025
- return rows.map(parseScheduledTaskRow).filter(isListedScheduledTask);
1026
- }
1027
-
1028
- // src/chat/attachments/store.ts
1029
- import { createHash, randomUUID } from "crypto";
1030
- import { and as and4, asc as asc4, eq as eq3, inArray as inArray3, isNotNull, isNull, lte as lte2, or as or2 } from "drizzle-orm";
1031
- var DAY_MS = 24 * 60 * 60 * 1e3;
1032
- var ATTACHMENT_GC_BATCH_LIMIT = 200;
1033
- var ATTACHMENT_RETENTION_MS = 30 * DAY_MS;
1034
- function attachmentDigest(file) {
1035
- return createHash("sha256").update(file.data).digest("hex");
1036
- }
1037
- function attachmentId(args) {
1038
- const identity = [
1039
- args.conversationId,
1040
- args.storageProvider,
1041
- args.sha256,
1042
- args.filename,
1043
- args.contentType
1044
- ];
1045
- if (args.source) {
1046
- identity.push(args.source.provider, args.source.id);
1047
- }
1048
- return createHash("sha256").update(identity.join("\0")).digest("hex");
1049
- }
1050
- function attachmentKey(args) {
1051
- return `conversations/${args.conversationId}/attachments/${args.attachmentId}/${args.writeId}/${args.filename}`;
1052
- }
1053
- async function bestEffortDelete(storage, key) {
1054
- try {
1055
- await storage.delete([key]);
1056
- } catch {
1057
- }
1058
- }
1059
- async function isConversationPurged(db, conversationId) {
1060
- const [row] = await db.db().select({
1061
- transcriptPurgedAt: juniorConversations.transcriptPurgedAt
1062
- }).from(juniorConversations).where(eq3(juniorConversations.conversationId, conversationId));
1063
- return row?.transcriptPurgedAt != null;
1064
- }
1065
- function purgedConversationError(conversationId) {
1066
- return new Error(
1067
- `Cannot store attachment for purged conversation ${conversationId}`
1068
- );
1069
- }
1070
- async function assertConversationNotPurged(db, conversationId) {
1071
- if (await isConversationPurged(db, conversationId)) {
1072
- throw purgedConversationError(conversationId);
1073
- }
1074
- }
1075
- async function rejectPurgedAttachmentWrite(args) {
1076
- await args.db.db().update(juniorAttachments).set({ deleteRequestedAt: args.now }).where(eq3(juniorAttachments.id, args.id));
1077
- await bestEffortDelete(args.storage, args.storageKey);
1078
- throw purgedConversationError(args.conversationId);
1079
- }
1080
- async function reuseLiveAttachment(args) {
1081
- await bestEffortDelete(args.storage, args.storageKey);
1082
- await assertConversationNotPurged(args.db, args.conversationId);
1083
- return { id: args.id };
1084
- }
1085
- async function storeAttachment(args) {
1086
- const now = new Date(args.nowMs ?? Date.now());
1087
- const sha256 = attachmentDigest(args.file);
1088
- const id = attachmentId({
1089
- conversationId: args.conversationId,
1090
- contentType: args.file.mimeType,
1091
- filename: args.file.filename,
1092
- source: args.source,
1093
- storageProvider: args.storage.provider,
1094
- sha256
1095
- });
1096
- const [existing] = await args.db.db().select({
1097
- contentType: juniorAttachments.contentType,
1098
- conversationId: juniorAttachments.conversationId,
1099
- deleteRequestedAt: juniorAttachments.deleteRequestedAt,
1100
- filename: juniorAttachments.filename,
1101
- id: juniorAttachments.id,
1102
- provider: juniorAttachments.provider,
1103
- providerId: juniorAttachments.providerId,
1104
- storageProvider: juniorAttachments.storageProvider,
1105
- sha256: juniorAttachments.sha256,
1106
- storageKey: juniorAttachments.storageKey
1107
- }).from(juniorAttachments).where(eq3(juniorAttachments.id, id));
1108
- if (existing) {
1109
- if (existing.contentType !== args.file.mimeType || existing.conversationId !== args.conversationId || existing.filename !== args.file.filename || existing.provider !== (args.source?.provider ?? null) || existing.providerId !== (args.source?.id ?? null) || existing.storageProvider !== args.storage.provider || existing.sha256 !== sha256) {
1110
- throw new Error(`Attachment write conflicts with ${existing.id}`);
1111
- }
1112
- if (existing.deleteRequestedAt === null) {
1113
- await assertConversationNotPurged(args.db, args.conversationId);
1114
- return { id: existing.id };
1115
- }
1116
- }
1117
- await assertConversationNotPurged(args.db, args.conversationId);
1118
- const storageKey = attachmentKey({
1119
- attachmentId: id,
1120
- conversationId: args.conversationId,
1121
- filename: args.file.filename,
1122
- writeId: randomUUID()
1123
- });
1124
- await args.storage.put({
1125
- body: args.file.data,
1126
- contentType: args.file.mimeType,
1127
- key: storageKey
1128
- });
1129
- if (existing?.deleteRequestedAt != null) {
1130
- if (await isConversationPurged(args.db, args.conversationId)) {
1131
- await bestEffortDelete(args.storage, storageKey);
1132
- throw purgedConversationError(args.conversationId);
1133
- }
1134
- const oldStorageKey = existing.storageKey;
1135
- const revived2 = await args.db.db().update(juniorAttachments).set({
1136
- deleteRequestedAt: null,
1137
- storageKey
1138
- }).where(
1139
- and4(
1140
- eq3(juniorAttachments.id, id),
1141
- isNotNull(juniorAttachments.deleteRequestedAt)
1142
- )
1143
- ).returning({ id: juniorAttachments.id });
1144
- if (revived2.length > 0) {
1145
- if (await isConversationPurged(args.db, args.conversationId)) {
1146
- await rejectPurgedAttachmentWrite({
1147
- conversationId: args.conversationId,
1148
- db: args.db,
1149
- id,
1150
- now,
1151
- storage: args.storage,
1152
- storageKey
1153
- });
1154
- }
1155
- if (oldStorageKey !== storageKey) {
1156
- await bestEffortDelete(args.storage, oldStorageKey);
1157
- }
1158
- return { id };
1159
- }
1160
- const [afterRace2] = await args.db.db().select({
1161
- deleteRequestedAt: juniorAttachments.deleteRequestedAt,
1162
- id: juniorAttachments.id,
1163
- storageKey: juniorAttachments.storageKey
1164
- }).from(juniorAttachments).where(eq3(juniorAttachments.id, id));
1165
- if (afterRace2?.deleteRequestedAt === null) {
1166
- return reuseLiveAttachment({
1167
- conversationId: args.conversationId,
1168
- db: args.db,
1169
- id: afterRace2.id,
1170
- storage: args.storage,
1171
- storageKey
1172
- });
1173
- }
1174
- }
1175
- let inserted = [];
1176
- try {
1177
- inserted = await args.db.db().insert(juniorAttachments).values({
1178
- id,
1179
- conversationId: args.conversationId,
1180
- storageProvider: args.storage.provider,
1181
- storageKey,
1182
- provider: args.source?.provider,
1183
- providerId: args.source?.id,
1184
- filename: args.file.filename,
1185
- contentType: args.file.mimeType,
1186
- bytes: args.file.bytes,
1187
- sha256,
1188
- createdAt: now
1189
- }).onConflictDoNothing({ target: juniorAttachments.id }).returning({ id: juniorAttachments.id });
1190
- } catch (error) {
1191
- await bestEffortDelete(args.storage, storageKey);
1192
- throw error;
1193
- }
1194
- if (inserted.length > 0) {
1195
- if (await isConversationPurged(args.db, args.conversationId)) {
1196
- await rejectPurgedAttachmentWrite({
1197
- conversationId: args.conversationId,
1198
- db: args.db,
1199
- id,
1200
- now,
1201
- storage: args.storage,
1202
- storageKey
1203
- });
1204
- }
1205
- return { id };
1206
- }
1207
- const [row] = await args.db.db().select({
1208
- deleteRequestedAt: juniorAttachments.deleteRequestedAt,
1209
- id: juniorAttachments.id,
1210
- storageKey: juniorAttachments.storageKey
1211
- }).from(juniorAttachments).where(eq3(juniorAttachments.id, id));
1212
- if (!row) {
1213
- await bestEffortDelete(args.storage, storageKey);
1214
- throw new Error(`Attachment row missing after write for ${id}`);
1215
- }
1216
- if (row.deleteRequestedAt === null) {
1217
- return reuseLiveAttachment({
1218
- conversationId: args.conversationId,
1219
- db: args.db,
1220
- id: row.id,
1221
- storage: args.storage,
1222
- storageKey
1223
- });
1224
- }
1225
- if (await isConversationPurged(args.db, args.conversationId)) {
1226
- await bestEffortDelete(args.storage, storageKey);
1227
- throw purgedConversationError(args.conversationId);
1228
- }
1229
- const revived = await args.db.db().update(juniorAttachments).set({
1230
- deleteRequestedAt: null,
1231
- storageKey
1232
- }).where(
1233
- and4(
1234
- eq3(juniorAttachments.id, id),
1235
- isNotNull(juniorAttachments.deleteRequestedAt)
1236
- )
1237
- ).returning({ id: juniorAttachments.id });
1238
- if (revived.length > 0) {
1239
- if (await isConversationPurged(args.db, args.conversationId)) {
1240
- await rejectPurgedAttachmentWrite({
1241
- conversationId: args.conversationId,
1242
- db: args.db,
1243
- id,
1244
- now,
1245
- storage: args.storage,
1246
- storageKey
1247
- });
1248
- }
1249
- if (row.storageKey !== storageKey) {
1250
- await bestEffortDelete(args.storage, row.storageKey);
1251
- }
1252
- return { id };
1253
- }
1254
- const [afterRace] = await args.db.db().select({
1255
- deleteRequestedAt: juniorAttachments.deleteRequestedAt,
1256
- id: juniorAttachments.id
1257
- }).from(juniorAttachments).where(eq3(juniorAttachments.id, id));
1258
- if (afterRace?.deleteRequestedAt === null) {
1259
- return reuseLiveAttachment({
1260
- conversationId: args.conversationId,
1261
- db: args.db,
1262
- id: afterRace.id,
1263
- storage: args.storage,
1264
- storageKey
1265
- });
1266
- }
1267
- await bestEffortDelete(args.storage, storageKey);
1268
- throw new Error(`Attachment ${id} disappeared while reviving`);
1269
- }
1270
- async function readLiveAttachment(args) {
1271
- const [row] = await args.db.db().select({
1272
- bytes: juniorAttachments.bytes,
1273
- contentType: juniorAttachments.contentType,
1274
- conversationId: juniorAttachments.conversationId,
1275
- deleteRequestedAt: juniorAttachments.deleteRequestedAt,
1276
- filename: juniorAttachments.filename,
1277
- id: juniorAttachments.id,
1278
- storageProvider: juniorAttachments.storageProvider,
1279
- storageKey: juniorAttachments.storageKey
1280
- }).from(juniorAttachments).where(
1281
- and4(
1282
- eq3(juniorAttachments.id, args.attachmentId),
1283
- eq3(juniorAttachments.conversationId, args.conversationId),
1284
- isNull(juniorAttachments.deleteRequestedAt)
1285
- )
1286
- );
1287
- if (!row) return null;
1288
- if (await isConversationPurged(args.db, args.conversationId)) {
1289
- return null;
1290
- }
1291
- return {
1292
- bytes: row.bytes,
1293
- contentType: row.contentType,
1294
- conversationId: row.conversationId,
1295
- filename: row.filename,
1296
- id: row.id,
1297
- storageKey: row.storageKey,
1298
- storageProvider: row.storageProvider
1299
- };
1300
- }
1301
- async function storeAttachments(args) {
1302
- const stored = [];
1303
- for (const file of args.files) {
1304
- stored.push(
1305
- await storeAttachment({
1306
- conversationId: args.conversationId,
1307
- db: args.db,
1308
- file,
1309
- ...args.nowMs !== void 0 ? { nowMs: args.nowMs } : void 0,
1310
- storage: args.storage
1311
- })
1312
- );
1313
- }
1314
- return stored;
1315
- }
1316
- async function requestAttachmentDeletion(db, conversationIds, nowMs) {
1317
- if (conversationIds.length === 0) return;
1318
- await db.db().update(juniorAttachments).set({ deleteRequestedAt: new Date(nowMs) }).where(
1319
- and4(
1320
- inArray3(juniorAttachments.conversationId, conversationIds),
1321
- isNull(juniorAttachments.deleteRequestedAt)
1322
- )
1323
- );
1324
- }
1325
- async function collectAttachmentGarbage(args) {
1326
- const expiresBefore = new Date(args.nowMs - ATTACHMENT_RETENTION_MS);
1327
- const rows = await args.db.db().select({
1328
- id: juniorAttachments.id,
1329
- storageKey: juniorAttachments.storageKey
1330
- }).from(juniorAttachments).innerJoin(
1331
- juniorConversations,
1332
- eq3(juniorAttachments.conversationId, juniorConversations.conversationId)
1333
- ).where(
1334
- and4(
1335
- eq3(juniorAttachments.storageProvider, args.storage.provider),
1336
- or2(
1337
- lte2(juniorAttachments.createdAt, expiresBefore),
1338
- isNotNull(juniorAttachments.deleteRequestedAt),
1339
- isNotNull(juniorConversations.transcriptPurgedAt)
1340
- )
1341
- )
1342
- ).orderBy(asc4(juniorAttachments.createdAt), asc4(juniorAttachments.id)).limit(args.limit ?? ATTACHMENT_GC_BATCH_LIMIT);
1343
- if (rows.length === 0) return { deleted: 0 };
1344
- await args.storage.delete(rows.map((row) => row.storageKey));
1345
- const stillEligible = await args.db.db().select({ id: juniorAttachments.id }).from(juniorAttachments).innerJoin(
1346
- juniorConversations,
1347
- eq3(juniorAttachments.conversationId, juniorConversations.conversationId)
1348
- ).where(
1349
- and4(
1350
- inArray3(
1351
- juniorAttachments.id,
1352
- rows.map((row) => row.id)
1353
- ),
1354
- or2(
1355
- lte2(juniorAttachments.createdAt, expiresBefore),
1356
- isNotNull(juniorAttachments.deleteRequestedAt),
1357
- isNotNull(juniorConversations.transcriptPurgedAt)
1358
- )
1359
- )
1360
- );
1361
- if (stillEligible.length === 0) return { deleted: 0 };
1362
- const removed = await args.db.db().delete(juniorAttachments).where(
1363
- inArray3(
1364
- juniorAttachments.id,
1365
- stillEligible.map((row) => row.id)
1366
- )
1367
- ).returning({
1368
- id: juniorAttachments.id
1369
- });
1370
- return { deleted: removed.length };
1371
- }
1372
-
1373
- export {
1374
- juniorToolOutputSchema,
1375
- injectContinuationToolName,
1376
- makeStructuredToolOutput,
1377
- hasResourceEventCatalogEntries,
1378
- hasPluginResourceEventCatalogEntries,
1379
- eventNamespaceSchema,
1380
- registeredEventTypeSchema,
1381
- registeredResourceTypeSchema,
1382
- pluginSupportsEvent,
1383
- resourceEventGuidance,
1384
- registeredResourceEventMatchSchema,
1385
- requireSupportedResourceEventMatch,
1386
- normalizeEventIdentifier,
1387
- WORKSPACE_SNAPSHOT_READY_EVENT,
1388
- WORKSPACE_SNAPSHOT_FAILED_EVENT,
1389
- workspaceSnapshotWatch,
1390
- workspaceSnapshotFinishedEvent,
1391
- getResourceEventCatalog,
1392
- getEventTask,
1393
- createEventTask,
1394
- saveEventTask,
1395
- deleteEventTask,
1396
- listEventTasksForTeam,
1397
- eventTaskBelongsToUser,
1398
- listEventTasksCreatedBy,
1399
- listDeletedEventTasksCreatedBy,
1400
- listPublicEventTasksForTeams,
1401
- collectEventTaskMatchKeys,
1402
- findMatchingEventTasks,
1403
- eventTaskToolResultSchema,
1404
- eventTaskListToolResultSchema,
1405
- registeredEventTaskTriggerSchema,
1406
- requireSupportedEventTaskTrigger,
1407
- requireEventTaskSlackContext,
1408
- eventTaskMatchesDestination,
1409
- writableEventTask,
1410
- eventTaskTriggerAvailable,
1411
- compactEventTask,
1412
- eventTaskSuccess,
1413
- scheduledRunSchema,
1414
- SCHEDULED_TASK_SYSTEM_ACTOR,
1415
- parseScheduledTaskRow,
1416
- isListedScheduledTask,
1417
- scheduledRunId,
1418
- withScheduledTaskLock,
1419
- readScheduledTask,
1420
- listScheduledTasks,
1421
- listScheduledTasksForTeam,
1422
- createScheduledTask,
1423
- saveScheduledTask,
1424
- saveScheduledTaskInLock,
1425
- listPublicScheduledTasksForTeams,
1426
- storeAttachment,
1427
- readLiveAttachment,
1428
- storeAttachments,
1429
- requestAttachmentDeletion,
1430
- collectAttachmentGarbage,
1431
- incrementStat,
1432
- readStats
1433
- };