@sentry/junior 0.128.1 → 0.129.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 (71) hide show
  1. package/dist/{agent-hooks-IWQNFJMS.js → agent-hooks-ZM4WMZCN.js} +9 -9
  2. package/dist/api/schema/task.d.ts +152 -0
  3. package/dist/api/schema.d.ts +2 -0
  4. package/dist/api/schema.js +13 -3
  5. package/dist/api/tasks/routes.d.ts +4 -0
  6. package/dist/api.js +425 -23
  7. package/dist/app.js +309 -27
  8. package/dist/{catalog-runtime-KLSYFS4A.js → catalog-runtime-TN2GPVEI.js} +5 -5
  9. package/dist/chat/agent-dispatch/context.d.ts +18 -0
  10. package/dist/chat/event-tasks/store.d.ts +7 -1
  11. package/dist/chat/event-tasks/tool-support.d.ts +1 -0
  12. package/dist/chat/resource-events/runtime-catalog.d.ts +3 -0
  13. package/dist/chat/scheduled-tasks/cadence.d.ts +27 -0
  14. package/dist/chat/scheduled-tasks/heartbeat.d.ts +6 -0
  15. package/dist/chat/scheduled-tasks/identity.d.ts +6 -0
  16. package/dist/chat/scheduled-tasks/personal.d.ts +26 -0
  17. package/dist/chat/scheduled-tasks/schedule-intent.d.ts +61 -0
  18. package/dist/chat/scheduled-tasks/store.d.ts +77 -0
  19. package/dist/chat/scheduled-tasks/tool-support.d.ts +203 -0
  20. package/dist/chat/scheduled-tasks/tools/create.d.ts +80 -0
  21. package/dist/chat/scheduled-tasks/tools/delete.d.ts +56 -0
  22. package/dist/chat/scheduled-tasks/tools/list.d.ts +54 -0
  23. package/dist/chat/scheduled-tasks/tools/run-now.d.ts +56 -0
  24. package/dist/chat/scheduled-tasks/tools/update.d.ts +82 -0
  25. package/dist/chat/scheduled-tasks/tools.d.ts +6 -0
  26. package/dist/chat/scheduled-tasks/types.d.ts +81 -0
  27. package/dist/chat/tasks/read.d.ts +9 -0
  28. package/dist/chat/tools/scheduled-tasks.d.ts +4 -0
  29. package/dist/{chunk-5IR7SGRO.js → chunk-2BCQKJTX.js} +2 -2
  30. package/dist/{chunk-MZ6ZOWMJ.js → chunk-3N4MWF3R.js} +1 -1
  31. package/dist/{chunk-ETGUGGO2.js → chunk-6CMYU62S.js} +7 -7
  32. package/dist/{chunk-AAMF2IRC.js → chunk-6QQOLIJJ.js} +1308 -804
  33. package/dist/{chunk-R2LXTX4J.js → chunk-A6QQPVSH.js} +1 -1
  34. package/dist/chunk-BDIEEZK7.js +1470 -0
  35. package/dist/{chunk-AFJF3RGU.js → chunk-BVKP37NE.js} +61 -13
  36. package/dist/{chunk-54KP2EAH.js → chunk-CZJUBH6O.js} +12 -12
  37. package/dist/{chunk-O3YROBH4.js → chunk-CZQ2DSQZ.js} +1 -1
  38. package/dist/{chunk-O3C5BJFD.js → chunk-E4UCPRYL.js} +1 -1
  39. package/dist/{chunk-S2O64VXP.js → chunk-ESYXDXNX.js} +6 -6
  40. package/dist/{chunk-EHEACYJ6.js → chunk-GPWHBHAG.js} +1 -1
  41. package/dist/{chunk-PLAMHS3D.js → chunk-GURSWCI4.js} +2 -2
  42. package/dist/{chunk-Q5TMMCBV.js → chunk-K3U5447S.js} +1 -1
  43. package/dist/{chunk-SHMHHXD5.js → chunk-KB2M27DL.js} +58 -0
  44. package/dist/{chunk-LIJ4APSS.js → chunk-LV2BQ26V.js} +1 -1
  45. package/dist/{chunk-MKM5552O.js → chunk-NNRV4T2C.js} +1 -2
  46. package/dist/{chunk-TONH5NXR.js → chunk-P6I7LFKC.js} +1 -1
  47. package/dist/{chunk-J3YIO3LE.js → chunk-PQFNTLZI.js} +3 -3
  48. package/dist/{chunk-H4HMCSOZ.js → chunk-QAOA4GUG.js} +5 -5
  49. package/dist/{chunk-EOIA25WC.js → chunk-R62DRNKB.js} +2 -2
  50. package/dist/{chunk-OIQCRZ67.js → chunk-U23X4ASF.js} +4 -4
  51. package/dist/{chunk-P7UUNWC4.js → chunk-XENYUOZG.js} +4 -4
  52. package/dist/cli/chat.js +24 -24
  53. package/dist/cli/check.js +6 -6
  54. package/dist/cli/plugins.js +12 -12
  55. package/dist/cli/snapshot-create.js +9 -9
  56. package/dist/cli/upgrade.js +9 -17
  57. package/dist/db/schema/scheduled-tasks.d.ts +257 -0
  58. package/dist/db/schema.d.ts +258 -1
  59. package/dist/{db-VOU5QNLY.js → db-ZI6TBXIS.js} +7 -7
  60. package/dist/instrumentation.js +4 -4
  61. package/dist/nitro.js +2 -2
  62. package/dist/{oauth-callback-server-GS7FBYBC.js → oauth-callback-server-TLA4GARF.js} +17 -17
  63. package/dist/{runner-GSJSBI3J.js → runner-23YFRWQJ.js} +11 -11
  64. package/dist/{skills-DSHG54LM.js → skills-QIUG4D6M.js} +6 -6
  65. package/dist/{validation-25XWT6TA.js → validation-3KGEBATS.js} +6 -6
  66. package/dist/version.js +1 -1
  67. package/migrations/0016_neat_the_anarchist.sql +171 -0
  68. package/migrations/meta/0016_snapshot.json +1617 -0
  69. package/migrations/meta/_journal.json +7 -0
  70. package/package.json +5 -6
  71. package/dist/chunk-REHOYCYS.js +0 -37
package/dist/app.js CHANGED
@@ -50,7 +50,7 @@ import {
50
50
  startConversationWork,
51
51
  startSlackProcessingReaction,
52
52
  stripLeadingBotMention
53
- } from "./chunk-54KP2EAH.js";
53
+ } from "./chunk-CZJUBH6O.js";
54
54
  import "./chunk-FA6POGLH.js";
55
55
  import {
56
56
  ConversationTurnLifecycleService,
@@ -65,13 +65,13 @@ import {
65
65
  processPluginTask,
66
66
  requireTurnFailureEventId,
67
67
  scheduleSessionCompletedPluginTasks
68
- } from "./chunk-S2O64VXP.js";
68
+ } from "./chunk-ESYXDXNX.js";
69
69
  import {
70
70
  GET,
71
71
  conversationVisibilityFromSlackChannelType,
72
72
  resolveSlackChannelTypeFromMessage,
73
73
  resolveSlackConversationContext
74
- } from "./chunk-PLAMHS3D.js";
74
+ } from "./chunk-GURSWCI4.js";
75
75
  import "./chunk-4CODA7P5.js";
76
76
  import "./chunk-UTL3MHPC.js";
77
77
  import {
@@ -90,7 +90,7 @@ import {
90
90
  import {
91
91
  validatePluginEgressCredentialHooks,
92
92
  validatePluginRegistrations
93
- } from "./chunk-O3C5BJFD.js";
93
+ } from "./chunk-E4UCPRYL.js";
94
94
  import {
95
95
  defineJuniorPlugins,
96
96
  pluginCatalogConfigFromEnv,
@@ -112,7 +112,6 @@ import {
112
112
  deliverResourceEventSubscription,
113
113
  executeAgentRun,
114
114
  executeCredentialedEgressRequest,
115
- findMatchingEventTasks,
116
115
  findMatchingResourceEventSubscriptions,
117
116
  getConfigDefaults,
118
117
  latestReportedProgress,
@@ -122,10 +121,11 @@ import {
122
121
  renderSlackLegacyAttachmentText,
123
122
  resolveDestinationVisibility,
124
123
  resolveSandboxEgressProviderForHost,
124
+ sanitizeScheduledTaskPrincipal,
125
125
  setConfigDefaults,
126
126
  shouldEmitDevAgentTrace,
127
127
  shouldPropagateSandboxEgressTrace
128
- } from "./chunk-AAMF2IRC.js";
128
+ } from "./chunk-6QQOLIJJ.js";
129
129
  import "./chunk-DIB4GX3G.js";
130
130
  import {
131
131
  RetryableDeliveryError,
@@ -136,7 +136,7 @@ import {
136
136
  formatProviderLabel,
137
137
  postSlackMessage,
138
138
  startOAuthFlow
139
- } from "./chunk-ETGUGGO2.js";
139
+ } from "./chunk-6CMYU62S.js";
140
140
  import {
141
141
  ConversationTurnBoundaryError,
142
142
  CooperativeTurnYieldError,
@@ -192,16 +192,19 @@ import {
192
192
  trimTrailingAssistantMessages,
193
193
  updateConversationStats,
194
194
  upsertConversationMessage
195
- } from "./chunk-OIQCRZ67.js";
195
+ } from "./chunk-U23X4ASF.js";
196
196
  import "./chunk-EPBBYMNE.js";
197
197
  import {
198
198
  getSandboxResourceConfig,
199
199
  setSandboxResourceConfig
200
- } from "./chunk-P7UUNWC4.js";
201
- import "./chunk-REHOYCYS.js";
200
+ } from "./chunk-XENYUOZG.js";
201
+ import {
202
+ createSchedulerSqlStore,
203
+ findMatchingEventTasks
204
+ } from "./chunk-BDIEEZK7.js";
202
205
  import {
203
206
  parseSlackThreadId
204
- } from "./chunk-LIJ4APSS.js";
207
+ } from "./chunk-LV2BQ26V.js";
205
208
  import "./chunk-HX236W2U.js";
206
209
  import "./chunk-PDO5BLNM.js";
207
210
  import "./chunk-UIE3R5XU.js";
@@ -229,7 +232,7 @@ import {
229
232
  verifyEventTaskCredentialSubject,
230
233
  verifyScheduledTaskCredentialSubject,
231
234
  verifySlackDirectCredentialSubject
232
- } from "./chunk-H4HMCSOZ.js";
235
+ } from "./chunk-QAOA4GUG.js";
233
236
  import {
234
237
  contextProvenance,
235
238
  createSlackDestination,
@@ -240,16 +243,16 @@ import {
240
243
  instructionProvenanceFor,
241
244
  requireSlackDestination,
242
245
  withConversationEventLock
243
- } from "./chunk-J3YIO3LE.js";
246
+ } from "./chunk-PQFNTLZI.js";
244
247
  import {
245
248
  juniorConversationEvents,
246
249
  juniorConversations
247
- } from "./chunk-SHMHHXD5.js";
250
+ } from "./chunk-KB2M27DL.js";
248
251
  import {
249
252
  acquireActiveLock,
250
253
  getStateAdapter,
251
254
  sleep
252
- } from "./chunk-MZ6ZOWMJ.js";
255
+ } from "./chunk-3N4MWF3R.js";
253
256
  import {
254
257
  STANDARD_MODEL_PROFILE,
255
258
  botConfig,
@@ -267,16 +270,16 @@ import {
267
270
  modelIdForProfile,
268
271
  setSlackReactionConfig,
269
272
  standardModelId
270
- } from "./chunk-5IR7SGRO.js";
273
+ } from "./chunk-2BCQKJTX.js";
271
274
  import {
272
275
  juniorDestinations
273
276
  } from "./chunk-VFUK3X5B.js";
274
- import "./chunk-EHEACYJ6.js";
275
- import "./chunk-EOIA25WC.js";
277
+ import "./chunk-GPWHBHAG.js";
278
+ import "./chunk-R62DRNKB.js";
276
279
  import {
277
280
  pluginCatalogRuntime
278
- } from "./chunk-R2LXTX4J.js";
279
- import "./chunk-O3YROBH4.js";
281
+ } from "./chunk-A6QQPVSH.js";
282
+ import "./chunk-CZQ2DSQZ.js";
280
283
  import {
281
284
  credentialSubjectSchema
282
285
  } from "./chunk-77VDLF3Z.js";
@@ -308,7 +311,7 @@ import {
308
311
  withContext,
309
312
  withLogContext,
310
313
  withSpan
311
- } from "./chunk-TONH5NXR.js";
314
+ } from "./chunk-P6I7LFKC.js";
312
315
  import {
313
316
  continueTrace
314
317
  } from "./chunk-6GWA276C.js";
@@ -316,10 +319,10 @@ import "./chunk-OOUNJNMR.js";
316
319
  import "./chunk-H4HBKISO.js";
317
320
  import {
318
321
  JUNIOR_PLUGIN_TASK_CALLBACK_ROUTE
319
- } from "./chunk-Q5TMMCBV.js";
322
+ } from "./chunk-K3U5447S.js";
320
323
  import {
321
324
  JUNIOR_VERSION
322
- } from "./chunk-MKM5552O.js";
325
+ } from "./chunk-NNRV4T2C.js";
323
326
  import "./chunk-MLKGABMK.js";
324
327
 
325
328
  // src/app.ts
@@ -1236,6 +1239,11 @@ async function recoverConversationWork(args) {
1236
1239
  return result;
1237
1240
  }
1238
1241
 
1242
+ // src/chat/scheduled-tasks/heartbeat.ts
1243
+ import {
1244
+ createSlackSource
1245
+ } from "@sentry/junior-plugin-api";
1246
+
1239
1247
  // src/chat/agent-dispatch/validation.ts
1240
1248
  import {
1241
1249
  dispatchOptionsSchema,
@@ -1470,6 +1478,264 @@ async function dispatchEventTask(args) {
1470
1478
  plugin
1471
1479
  });
1472
1480
  }
1481
+ async function dispatchScheduledTask(args) {
1482
+ const plugin = "scheduler";
1483
+ const { credentialSubject, ...unboundOptions } = args.options;
1484
+ validateDispatchOptions({ ...unboundOptions });
1485
+ const boundSubject = credentialSubject ? bindScheduledTaskCredentialSubject({
1486
+ plugin,
1487
+ subject: credentialSubject
1488
+ }) : void 0;
1489
+ if (credentialSubject && !boundSubject) {
1490
+ throw new Error("Dispatch credentialSubject is not valid for this action");
1491
+ }
1492
+ const options = {
1493
+ ...unboundOptions,
1494
+ ...boundSubject ? { credentialSubject: boundSubject } : {}
1495
+ };
1496
+ return await dispatch({
1497
+ conversationWorkQueue: args.conversationWorkQueue,
1498
+ nowMs: args.nowMs,
1499
+ options,
1500
+ plugin
1501
+ });
1502
+ }
1503
+ async function getScheduledTaskDispatch(id) {
1504
+ return await getPluginDispatchProjection({ plugin: "scheduler", id });
1505
+ }
1506
+
1507
+ // src/chat/scheduled-tasks/heartbeat.ts
1508
+ var SCHEDULED_TASK_HEARTBEAT_LIMIT = 10;
1509
+ var SCHEDULE_FREQUENCY_COPY = {
1510
+ daily: { label: "Daily", unit: "day" },
1511
+ weekly: { label: "Weekly", unit: "week" },
1512
+ monthly: { label: "Monthly", unit: "month" },
1513
+ yearly: { label: "Yearly", unit: "year" }
1514
+ };
1515
+ function singleLineMetadataValue(value) {
1516
+ return value.replace(/[\r\n]+/g, " ").replace(/\s+/g, " ").trim();
1517
+ }
1518
+ function buildDispatchMetadata(args) {
1519
+ const creator = sanitizeScheduledTaskPrincipal(args.task.createdBy);
1520
+ if (!args.task.task.text?.trim()) {
1521
+ throw new Error("Scheduled task text is required");
1522
+ }
1523
+ return {
1524
+ creatorSlackUserId: creator.slackUserId,
1525
+ runId: args.run.id,
1526
+ schedule: singleLineMetadataValue(args.task.schedule.description),
1527
+ scheduleKind: args.task.schedule.kind,
1528
+ scheduledFor: new Date(args.run.scheduledForMs).toISOString(),
1529
+ runningAt: new Date(args.nowMs).toISOString(),
1530
+ taskId: args.task.id,
1531
+ timezone: args.task.schedule.timezone,
1532
+ ...args.task.schedule.recurrence ? {
1533
+ recurrenceFrequency: args.task.schedule.recurrence.frequency,
1534
+ recurrenceInterval: String(args.task.schedule.recurrence.interval),
1535
+ recurrenceStartDate: args.task.schedule.recurrence.startDate
1536
+ } : {}
1537
+ };
1538
+ }
1539
+ function replyAttribution(task) {
1540
+ if (task.schedule.kind === "one_off") {
1541
+ return { label: "Scheduled task", detail: "One-time" };
1542
+ }
1543
+ const recurrence = task.schedule.recurrence;
1544
+ if (!recurrence) {
1545
+ return { label: "Scheduled task", detail: "Recurring" };
1546
+ }
1547
+ const frequency = SCHEDULE_FREQUENCY_COPY[recurrence.frequency];
1548
+ return recurrence.interval === 1 ? { label: "Scheduled task", detail: frequency.label } : {
1549
+ label: "Scheduled task",
1550
+ detail: `Every ${recurrence.interval} ${frequency.unit}s`
1551
+ };
1552
+ }
1553
+ function dispatchSource(task) {
1554
+ return createSlackSource({
1555
+ teamId: task.destination.teamId,
1556
+ channelId: task.destination.channelId,
1557
+ visibility: task.conversationAccess.visibility
1558
+ });
1559
+ }
1560
+ function shouldSkipRun(task, run) {
1561
+ if (task.status === "deleted") {
1562
+ return `Scheduled task ${task.id} was deleted before the run started.`;
1563
+ }
1564
+ if (task.status !== "active") {
1565
+ return `Scheduled task ${task.id} was ${task.status} before the run started.`;
1566
+ }
1567
+ if (task.nextRunAtMs !== run.scheduledForMs && task.runNowAtMs !== run.scheduledForMs) {
1568
+ return `Scheduled task ${task.id} no longer targets ${new Date(run.scheduledForMs).toISOString()}.`;
1569
+ }
1570
+ return void 0;
1571
+ }
1572
+ async function applyDispatchResult(args) {
1573
+ if (args.dispatch.status === "completed") {
1574
+ const completed = await args.store.markRunCompleted({
1575
+ completedAtMs: args.nowMs,
1576
+ resultMessageTs: args.dispatch.resultMessageTs,
1577
+ runId: args.run.id,
1578
+ startedAtMs: args.run.startedAtMs
1579
+ });
1580
+ if (!completed) return false;
1581
+ await args.store.updateTaskAfterRun({
1582
+ nowMs: args.nowMs,
1583
+ run: args.run,
1584
+ status: "completed"
1585
+ });
1586
+ return true;
1587
+ }
1588
+ if (args.dispatch.status === "blocked") {
1589
+ const blocked = await args.store.markRunBlocked({
1590
+ completedAtMs: args.nowMs,
1591
+ errorMessage: args.dispatch.errorMessage ?? "Dispatch blocked.",
1592
+ runId: args.run.id,
1593
+ startedAtMs: args.run.startedAtMs
1594
+ });
1595
+ if (!blocked) return false;
1596
+ await args.store.updateTaskAfterRun({
1597
+ errorMessage: blocked.errorMessage,
1598
+ nowMs: args.nowMs,
1599
+ run: args.run,
1600
+ status: "blocked"
1601
+ });
1602
+ return true;
1603
+ }
1604
+ if (args.dispatch.status === "failed") {
1605
+ const failed = await args.store.markRunFailed({
1606
+ completedAtMs: args.nowMs,
1607
+ errorMessage: args.dispatch.errorMessage ?? "Dispatch failed.",
1608
+ runId: args.run.id,
1609
+ startedAtMs: args.run.startedAtMs
1610
+ });
1611
+ if (!failed) return false;
1612
+ await args.store.updateTaskAfterRun({
1613
+ errorMessage: failed.errorMessage,
1614
+ nowMs: args.nowMs,
1615
+ run: args.run,
1616
+ status: "failed"
1617
+ });
1618
+ return true;
1619
+ }
1620
+ return false;
1621
+ }
1622
+ async function finishClaimedRun(args) {
1623
+ const finished = args.status === "blocked" ? await args.store.markRunBlocked({
1624
+ completedAtMs: args.nowMs,
1625
+ errorMessage: args.errorMessage,
1626
+ runId: args.run.id
1627
+ }) : await args.store.markRunFailed({
1628
+ completedAtMs: args.nowMs,
1629
+ errorMessage: args.errorMessage,
1630
+ runId: args.run.id,
1631
+ startedAtMs: args.run.startedAtMs
1632
+ });
1633
+ if (!finished) return;
1634
+ await args.store.updateTaskAfterRun({
1635
+ errorMessage: args.errorMessage,
1636
+ nowMs: args.nowMs,
1637
+ run: args.run,
1638
+ status: args.status
1639
+ });
1640
+ }
1641
+ async function runScheduledTaskHeartbeat(args) {
1642
+ const store = createSchedulerSqlStore(getDb());
1643
+ let processedCount = 0;
1644
+ let dispatchCount = 0;
1645
+ for (const run of await store.listIncompleteRuns()) {
1646
+ if (!run.dispatchId) continue;
1647
+ const dispatch2 = await getScheduledTaskDispatch(run.dispatchId);
1648
+ if (!dispatch2) {
1649
+ await finishClaimedRun({
1650
+ errorMessage: "Scheduled task dispatch record is missing.",
1651
+ nowMs: args.nowMs,
1652
+ run,
1653
+ status: "failed",
1654
+ store
1655
+ });
1656
+ continue;
1657
+ }
1658
+ if (await applyDispatchResult({ dispatch: dispatch2, nowMs: args.nowMs, run, store })) {
1659
+ processedCount += 1;
1660
+ }
1661
+ }
1662
+ for (let index = processedCount; index < SCHEDULED_TASK_HEARTBEAT_LIMIT; index += 1) {
1663
+ const run = await store.claimDueRun({ nowMs: args.nowMs });
1664
+ if (!run) break;
1665
+ const task = await store.getTask(run.taskId);
1666
+ if (!task) {
1667
+ await store.markRunFailed({
1668
+ completedAtMs: args.nowMs,
1669
+ errorMessage: `Scheduled task ${run.taskId} was not found`,
1670
+ runId: run.id
1671
+ });
1672
+ continue;
1673
+ }
1674
+ const skippedReason = shouldSkipRun(task, run);
1675
+ if (skippedReason) {
1676
+ await store.markRunSkipped({
1677
+ completedAtMs: args.nowMs,
1678
+ errorMessage: skippedReason,
1679
+ runId: run.id
1680
+ });
1681
+ continue;
1682
+ }
1683
+ let metadata;
1684
+ try {
1685
+ metadata = buildDispatchMetadata({ nowMs: args.nowMs, run, task });
1686
+ } catch (error) {
1687
+ await finishClaimedRun({
1688
+ errorMessage: error instanceof Error ? `Scheduled task dispatch metadata could not be built: ${error.message}` : "Scheduled task dispatch metadata could not be built.",
1689
+ nowMs: args.nowMs,
1690
+ run,
1691
+ status: "blocked",
1692
+ store
1693
+ });
1694
+ continue;
1695
+ }
1696
+ let dispatch2;
1697
+ try {
1698
+ dispatch2 = await dispatchScheduledTask({
1699
+ conversationWorkQueue: args.conversationWorkQueue,
1700
+ nowMs: args.nowMs,
1701
+ options: {
1702
+ idempotencyKey: run.id,
1703
+ ...task.credentialMode === "creator" ? {
1704
+ credentialSubject: {
1705
+ type: "user",
1706
+ userId: task.createdBy.slackUserId,
1707
+ allowedWhen: "scheduled-task",
1708
+ taskId: task.id
1709
+ }
1710
+ } : {},
1711
+ destination: task.destination,
1712
+ destinationVisibility: task.conversationAccess.visibility,
1713
+ input: task.task.text,
1714
+ metadata,
1715
+ replyAttribution: replyAttribution(task),
1716
+ source: dispatchSource(task)
1717
+ }
1718
+ });
1719
+ } catch (error) {
1720
+ await finishClaimedRun({
1721
+ errorMessage: error instanceof Error ? `Scheduled task dispatch could not be created: ${error.message}` : "Scheduled task dispatch could not be created.",
1722
+ nowMs: args.nowMs,
1723
+ run,
1724
+ status: "blocked",
1725
+ store
1726
+ });
1727
+ continue;
1728
+ }
1729
+ await store.markRunDispatched({
1730
+ claimedAtMs: run.claimedAtMs,
1731
+ dispatchId: dispatch2.id,
1732
+ nowMs: args.nowMs,
1733
+ runId: run.id
1734
+ });
1735
+ dispatchCount += 1;
1736
+ }
1737
+ return dispatchCount;
1738
+ }
1473
1739
 
1474
1740
  // src/chat/agent-dispatch/heartbeat.ts
1475
1741
  var DEFAULT_PLUGIN_LIMIT = 25;
@@ -1571,6 +1837,19 @@ async function runHeartbeat(args) {
1571
1837
  conversationWorkQueue: queue,
1572
1838
  nowMs: args.nowMs
1573
1839
  });
1840
+ try {
1841
+ const dispatchCount = await runScheduledTaskHeartbeat({
1842
+ conversationWorkQueue: queue,
1843
+ nowMs: args.nowMs
1844
+ });
1845
+ if (dispatchCount > 0) {
1846
+ logInfo("scheduled_tasks.heartbeat.dispatched", {
1847
+ "app.dispatch.count": dispatchCount
1848
+ });
1849
+ }
1850
+ } catch (error) {
1851
+ logException(error, "scheduled_tasks.heartbeat.failed");
1852
+ }
1574
1853
  await runPluginHeartbeats({
1575
1854
  conversationWorkQueue: queue,
1576
1855
  nowMs: args.nowMs
@@ -6259,7 +6538,7 @@ function createJuniorRuntimeServices(overrides = {}) {
6259
6538
  }
6260
6539
 
6261
6540
  // src/chat/runtime/reply-executor.ts
6262
- import { createSlackSource } from "@sentry/junior-plugin-api";
6541
+ import { createSlackSource as createSlackSource2 } from "@sentry/junior-plugin-api";
6263
6542
 
6264
6543
  // src/chat/slack/message.ts
6265
6544
  function getSlackMessageTs(message) {
@@ -6549,7 +6828,7 @@ function createReplyToThread(deps) {
6549
6828
  const assistantThreadContext = getAssistantThreadContext(message);
6550
6829
  const messageTs = getMessageTs(message);
6551
6830
  const teamId = destination.teamId;
6552
- const source = options.execution?.source ?? createSlackSource({
6831
+ const source = options.execution?.source ?? createSlackSource2({
6553
6832
  channelId: channelId ?? destination.channelId,
6554
6833
  messageTs,
6555
6834
  teamId,
@@ -8958,7 +9237,7 @@ async function ingestResourceEvent(input, options) {
8958
9237
  // src/chat/event-tasks/ingest.ts
8959
9238
  import { createHash as createHash2 } from "crypto";
8960
9239
  import {
8961
- createSlackSource as createSlackSource2,
9240
+ createSlackSource as createSlackSource3,
8962
9241
  RESOURCE_EVENT_SUMMARY_MAX_LENGTH,
8963
9242
  RESOURCE_EVENT_TEXT_MAX_LENGTH,
8964
9243
  resourceEventSchema as resourceEventSchema2
@@ -9021,7 +9300,7 @@ async function ingestEventTasks(input, options) {
9021
9300
  destination: task.destination,
9022
9301
  destinationVisibility: task.destinationVisibility,
9023
9302
  input: eventInput(task, event),
9024
- source: createSlackSource2({
9303
+ source: createSlackSource3({
9025
9304
  teamId: task.destination.teamId,
9026
9305
  channelId: task.destination.channelId,
9027
9306
  visibility: task.destinationVisibility
@@ -9196,6 +9475,7 @@ function dashboardHostRoutePaths(dashboard) {
9196
9475
  `${pagePath("locations")}/*`,
9197
9476
  peoplePath,
9198
9477
  `${peoplePath}/*`,
9478
+ pagePath("tasks"),
9199
9479
  pagePath("system"),
9200
9480
  `${pagePath("system")}/*`,
9201
9481
  pagePath("plugins"),
@@ -9219,6 +9499,8 @@ function dashboardHostRoutePaths(dashboard) {
9219
9499
  "/api/plugin-reports",
9220
9500
  "/api/user-pages",
9221
9501
  "/api/user-pages/*",
9502
+ "/api/tasks",
9503
+ "/api/tasks/*",
9222
9504
  "/api/skills",
9223
9505
  "/api/stats",
9224
9506
  "/api/conversations",
@@ -1,16 +1,16 @@
1
1
  import {
2
2
  pluginCatalogRuntime
3
- } from "./chunk-R2LXTX4J.js";
4
- import "./chunk-O3YROBH4.js";
3
+ } from "./chunk-A6QQPVSH.js";
4
+ import "./chunk-CZQ2DSQZ.js";
5
5
  import "./chunk-77VDLF3Z.js";
6
6
  import "./chunk-HZF5P5UN.js";
7
7
  import "./chunk-F3B2YJ76.js";
8
- import "./chunk-TONH5NXR.js";
8
+ import "./chunk-P6I7LFKC.js";
9
9
  import "./chunk-6GWA276C.js";
10
10
  import "./chunk-OOUNJNMR.js";
11
11
  import "./chunk-H4HBKISO.js";
12
- import "./chunk-Q5TMMCBV.js";
13
- import "./chunk-MKM5552O.js";
12
+ import "./chunk-K3U5447S.js";
13
+ import "./chunk-NNRV4T2C.js";
14
14
  import "./chunk-MLKGABMK.js";
15
15
  export {
16
16
  pluginCatalogRuntime
@@ -23,3 +23,21 @@ export declare function dispatchEventTask(args: {
23
23
  id: string;
24
24
  status: "already_exists" | "created";
25
25
  }>;
26
+ /** Create and enqueue one core-owned scheduled task dispatch. */
27
+ export declare function dispatchScheduledTask(args: {
28
+ conversationWorkQueue: ConversationWorkQueue;
29
+ nowMs: number;
30
+ options: Omit<SlackDispatchOptions, "credentialSubject"> & {
31
+ credentialSubject?: {
32
+ allowedWhen: "scheduled-task";
33
+ taskId: string;
34
+ type: "user";
35
+ userId: string;
36
+ };
37
+ };
38
+ }): Promise<{
39
+ id: string;
40
+ status: "already_exists" | "created";
41
+ }>;
42
+ /** Read the bounded outcome for one core-owned scheduled task dispatch. */
43
+ export declare function getScheduledTaskDispatch(id: string): Promise<import("./types").DispatchProjection | undefined>;
@@ -1,4 +1,4 @@
1
- import { type ResourceEvent } from "@sentry/junior-plugin-api";
1
+ import { type ResourceEvent, type User } from "@sentry/junior-plugin-api";
2
2
  import type { JuniorDatabase } from "@/db/db";
3
3
  import { type EventTask } from "./types";
4
4
  /** Read one event task by id. */
@@ -11,5 +11,11 @@ export declare function saveEventTask(db: JuniorDatabase, task: EventTask): Prom
11
11
  export declare function deleteEventTask(db: JuniorDatabase, id: string): Promise<EventTask | undefined>;
12
12
  /** List event tasks in one Slack workspace. */
13
13
  export declare function listEventTasksForTeam(db: JuniorDatabase, teamId: string): Promise<EventTask[]>;
14
+ /** Return whether one event task was created by a viewer-linked Slack identity. */
15
+ export declare function eventTaskBelongsToUser(task: EventTask, user: User): boolean;
16
+ /** List a bounded newest-first page of event tasks created by one user. */
17
+ export declare function listEventTasksCreatedBy(db: JuniorDatabase, user: User, limit: number): Promise<EventTask[]>;
18
+ /** List event tasks whose current Slack destination is public. */
19
+ export declare function listPublicEventTasksForTeams(db: JuniorDatabase, teamIds: string[], limit: number): Promise<EventTask[]>;
14
20
  /** Find every task matching one normalized resource event. */
15
21
  export declare function findMatchingEventTasks(db: JuniorDatabase, event: ResourceEvent, teamId: string): Promise<EventTask[]>;
@@ -102,6 +102,7 @@ export declare function eventTaskMatchesDestination(task: EventTask, destination
102
102
  }): boolean;
103
103
  /** Load one task only when it belongs to this Slack channel or DM. */
104
104
  export declare function writableEventTask(context: ToolRuntimeContext, id: string): Promise<EventTask>;
105
+ export declare function eventTaskTriggerAvailable(task: EventTask, catalog: ResourceEventCatalog): boolean;
105
106
  /** Project an event task into the bounded tool-result shape. */
106
107
  export declare function compactEventTask(task: EventTask, catalog: ResourceEventCatalog): {
107
108
  id: string;
@@ -0,0 +1,3 @@
1
+ import type { ResourceEventCatalog } from "@/chat/resource-events/catalog";
2
+ /** Read enabled plugin resource-event registrations as one core catalog. */
3
+ export declare function getResourceEventCatalog(): ResourceEventCatalog;
@@ -0,0 +1,27 @@
1
+ /** Calendar arithmetic for core-owned scheduled tasks. */
2
+ import type { ScheduledLocalTime, ScheduledTask, ScheduledTaskRecurrence } from "./types";
3
+ export interface ZonedDateTimeParts {
4
+ day: number;
5
+ hour: number;
6
+ minute: number;
7
+ month: number;
8
+ second: number;
9
+ weekday: number;
10
+ year: number;
11
+ }
12
+ /** Resolve a UTC timestamp into calendar parts for a named time zone. */
13
+ export declare function getZonedDateTimeParts(timestampMs: number, timezone: string): ZonedDateTimeParts;
14
+ /** Resolve local time, rejecting DST gaps and choosing the earlier instant in a fold. */
15
+ export declare function resolveLocalScheduleAtMs(args: {
16
+ date: string;
17
+ time: ScheduledLocalTime;
18
+ timezone: string;
19
+ }): number | undefined;
20
+ /** Compute the first recurring calendar occurrence strictly after a timestamp. */
21
+ export declare function getFirstRunAtMs(args: {
22
+ afterMs: number;
23
+ recurrence: ScheduledTaskRecurrence;
24
+ timezone: string;
25
+ }): number | undefined;
26
+ /** Return the next fire time after a completed run, when the task recurs. */
27
+ export declare function getNextRunAtMs(task: ScheduledTask, scheduledForMs: number, afterMs?: number): number | undefined;
@@ -0,0 +1,6 @@
1
+ import type { ConversationWorkQueue } from "@/chat/task-execution/queue";
2
+ /** Reconcile completed dispatches and enqueue a bounded batch of due tasks. */
3
+ export declare function runScheduledTaskHeartbeat(args: {
4
+ conversationWorkQueue: ConversationWorkQueue;
5
+ nowMs: number;
6
+ }): Promise<number>;
@@ -0,0 +1,6 @@
1
+ /** Presentation helpers for persisted scheduled-task creators. */
2
+ import type { ScheduledTaskPrincipal } from "./types";
3
+ /** Keep scheduler creator metadata from promoting Slack IDs into display names. */
4
+ export declare function sanitizeScheduledTaskPrincipal(principal: ScheduledTaskPrincipal): ScheduledTaskPrincipal;
5
+ /** Render scheduler creator metadata without inventing human profile fields. */
6
+ export declare function scheduledTaskPrincipalLabel(principal: ScheduledTaskPrincipal): string;
@@ -0,0 +1,26 @@
1
+ /** Viewer-scoped scheduled-task queries and mutations. */
2
+ import type { User } from "@sentry/junior-plugin-api";
3
+ import type { SchedulerStore } from "./store";
4
+ import type { ScheduledTask } from "./types";
5
+ type PersonalScheduledTaskStore = Pick<SchedulerStore, "getTask" | "listTasksCreatedBy" | "saveTask">;
6
+ export interface ViewerScheduledTaskPage {
7
+ nextCursor?: string;
8
+ tasks: ScheduledTask[];
9
+ }
10
+ export interface ViewerScheduledTaskPageInput {
11
+ cursor?: string;
12
+ limit: number;
13
+ query?: string;
14
+ }
15
+ export declare class InvalidScheduledTaskCursorError extends Error {
16
+ constructor();
17
+ }
18
+ export declare class PersonalScheduledTaskNotFoundError extends Error {
19
+ constructor();
20
+ }
21
+ /** Build scheduled-task operations limited to tasks created by one user. */
22
+ export declare function createViewerScheduledTasks(store: PersonalScheduledTaskStore, user: User): {
23
+ delete(id: string, nowMs?: number): Promise<void>;
24
+ list(input: ViewerScheduledTaskPageInput): Promise<ViewerScheduledTaskPage>;
25
+ };
26
+ export {};