@sentry/junior-scheduler 0.64.0 → 0.65.1

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.
package/dist/index.js CHANGED
@@ -1012,10 +1012,13 @@ function getCredentialSubject(args) {
1012
1012
  if (args.access.audience !== "direct" || args.access.visibility !== "private") {
1013
1013
  return void 0;
1014
1014
  }
1015
+ if (!args.subject) {
1016
+ return void 0;
1017
+ }
1015
1018
  return {
1016
- type: "user",
1017
- userId: args.requester.slackUserId,
1018
- allowedWhen: "private-direct-conversation"
1019
+ type: args.subject.type,
1020
+ userId: args.subject.userId,
1021
+ allowedWhen: args.subject.allowedWhen
1019
1022
  };
1020
1023
  }
1021
1024
  function sameDestination(task, destination) {
@@ -1139,22 +1142,36 @@ function parseNextRunAtMs(nextRunAtIso) {
1139
1142
  function createSlackScheduleCreateTaskTool(context) {
1140
1143
  return tool({
1141
1144
  description: "Create a future or recurring Junior task in the active Slack conversation. Use only when the user explicitly asks Junior to do work later or on a recurring cadence. Only manage tasks for the active Slack DM or channel; never target threads, other channels, or another user's DM. When the task, schedule, and destination are clear, create it without asking for confirmation; ask only when one of those is ambiguous.",
1145
+ executionMode: "sequential",
1142
1146
  inputSchema: Type.Object({
1143
1147
  task: Type.String({ minLength: 1, maxLength: 4e3 }),
1144
1148
  schedule: Type.String({ minLength: 1, maxLength: 300 }),
1145
- timezone: Type.Optional(Type.String({ minLength: 1, maxLength: 80, description: "IANA timezone, e.g. 'America/Los_Angeles'. Defaults to the channel's configured timezone." })),
1149
+ timezone: Type.Optional(
1150
+ Type.String({
1151
+ minLength: 1,
1152
+ maxLength: 80,
1153
+ description: "IANA timezone, e.g. 'America/Los_Angeles'. Defaults to the channel's configured timezone."
1154
+ })
1155
+ ),
1146
1156
  next_run_at: Type.Optional(
1147
1157
  Type.String({
1148
1158
  minLength: 1,
1149
1159
  description: "Exact next run time as an ISO timestamp, computed from the user's requested schedule."
1150
1160
  })
1151
1161
  ),
1152
- recurrence: Type.Optional(Type.Union([
1153
- Type.Literal("daily"),
1154
- Type.Literal("weekly"),
1155
- Type.Literal("monthly"),
1156
- Type.Literal("yearly")
1157
- ], { description: "Provide only for explicitly repeating schedules; omit for one-time requests like 'in 1 minute', 'tomorrow', or a specific date. Recurring tasks run at most once per day: use daily, weekly, monthly, or yearly only." }))
1162
+ recurrence: Type.Optional(
1163
+ Type.Union(
1164
+ [
1165
+ Type.Literal("daily"),
1166
+ Type.Literal("weekly"),
1167
+ Type.Literal("monthly"),
1168
+ Type.Literal("yearly")
1169
+ ],
1170
+ {
1171
+ description: "Provide only for explicitly repeating schedules; omit for one-time requests like 'in 1 minute', 'tomorrow', or a specific date. Recurring tasks run at most once per day: use daily, weekly, monthly, or yearly only."
1172
+ }
1173
+ )
1174
+ )
1158
1175
  }),
1159
1176
  execute: async (input) => {
1160
1177
  const destination = requireActiveDestination(context);
@@ -1177,7 +1194,7 @@ function createSlackScheduleCreateTaskTool(context) {
1177
1194
  const conversationAccess = getConversationAccess(destination);
1178
1195
  const credentialSubject = getCredentialSubject({
1179
1196
  access: conversationAccess,
1180
- requester
1197
+ subject: context.credentialSubject
1181
1198
  });
1182
1199
  const task = {
1183
1200
  id: buildTaskId(),
@@ -1235,21 +1252,46 @@ function createSlackScheduleListTasksTool(context) {
1235
1252
  function createSlackScheduleUpdateTaskTool(context) {
1236
1253
  return tool({
1237
1254
  description: "Edit, pause, resume, or reschedule an existing Junior scheduled task in the active Slack conversation. Use only task IDs returned for this destination. Do not move scheduled tasks across conversations.",
1255
+ executionMode: "sequential",
1238
1256
  inputSchema: Type.Object({
1239
- task_id: Type.String({ minLength: 1, description: "ID of the task to update. Must be from this active Slack destination." }),
1257
+ task_id: Type.String({
1258
+ minLength: 1,
1259
+ description: "ID of the task to update. Must be from this active Slack destination."
1260
+ }),
1240
1261
  task: Type.Optional(Type.String({ minLength: 1, maxLength: 4e3 })),
1241
1262
  schedule: Type.Optional(Type.String({ minLength: 1, maxLength: 300 })),
1242
1263
  timezone: Type.Optional(Type.String({ minLength: 1, maxLength: 80 })),
1243
- next_run_at: Type.Optional(Type.String({ minLength: 1, description: "Exact ISO timestamp when changing the next run time." })),
1264
+ next_run_at: Type.Optional(
1265
+ Type.String({
1266
+ minLength: 1,
1267
+ description: "Exact ISO timestamp when changing the next run time."
1268
+ })
1269
+ ),
1244
1270
  recurrence: Type.Optional(
1245
- Type.Union([Type.Literal("daily"), Type.Literal("weekly"), Type.Literal("monthly"), Type.Literal("yearly"), Type.Null()], { description: "Provide only for repeating schedules. Omit for one-time requests. Set to null to convert a recurring task to one-time." })
1271
+ Type.Union(
1272
+ [
1273
+ Type.Literal("daily"),
1274
+ Type.Literal("weekly"),
1275
+ Type.Literal("monthly"),
1276
+ Type.Literal("yearly"),
1277
+ Type.Null()
1278
+ ],
1279
+ {
1280
+ description: "Provide only for repeating schedules. Omit for one-time requests. Set to null to convert a recurring task to one-time."
1281
+ }
1282
+ )
1246
1283
  ),
1247
1284
  status: Type.Optional(
1248
- Type.Union([
1249
- Type.Literal("active"),
1250
- Type.Literal("paused"),
1251
- Type.Literal("blocked")
1252
- ], { description: "Set to active, paused, or blocked to resume, pause, or block the task." })
1285
+ Type.Union(
1286
+ [
1287
+ Type.Literal("active"),
1288
+ Type.Literal("paused"),
1289
+ Type.Literal("blocked")
1290
+ ],
1291
+ {
1292
+ description: "Set to active, paused, or blocked to resume, pause, or block the task."
1293
+ }
1294
+ )
1253
1295
  )
1254
1296
  }),
1255
1297
  execute: async (input) => {
@@ -1311,8 +1353,12 @@ function createSlackScheduleUpdateTaskTool(context) {
1311
1353
  function createSlackScheduleDeleteTaskTool(context) {
1312
1354
  return tool({
1313
1355
  description: "Delete one scheduled Junior task from the active Slack conversation. Use only task IDs returned for this destination. Do not delete schedules from threads, other channels, or another user's DM.",
1356
+ executionMode: "sequential",
1314
1357
  inputSchema: Type.Object({
1315
- task_id: Type.String({ minLength: 1, description: "ID of the task to delete. Must be from this active Slack destination." })
1358
+ task_id: Type.String({
1359
+ minLength: 1,
1360
+ description: "ID of the task to delete. Must be from this active Slack destination."
1361
+ })
1316
1362
  }),
1317
1363
  execute: async ({ task_id }) => {
1318
1364
  const lookup = await getWritableTask({ context, taskId: task_id });
@@ -1335,8 +1381,12 @@ function createSlackScheduleDeleteTaskTool(context) {
1335
1381
  function createSlackScheduleRunTaskNowTool(context) {
1336
1382
  return tool({
1337
1383
  description: "Queue an existing active scheduled Junior task to run as soon as possible, without changing its cadence. Use when the user asks to run an existing scheduled task now. Use only task IDs returned for this destination.",
1384
+ executionMode: "sequential",
1338
1385
  inputSchema: Type.Object({
1339
- task_id: Type.String({ minLength: 1, description: "ID of the active task to run now. Must be from this active Slack destination." })
1386
+ task_id: Type.String({
1387
+ minLength: 1,
1388
+ description: "ID of the active task to run now. Must be from this active Slack destination."
1389
+ })
1340
1390
  }),
1341
1391
  execute: async ({ task_id }) => {
1342
1392
  const lookup = await getWritableTask({ context, taskId: task_id });
@@ -1383,6 +1433,7 @@ function createSchedulerToolContext(ctx) {
1383
1433
  canPostToChannel: false
1384
1434
  },
1385
1435
  channelId: ctx.channelId,
1436
+ credentialSubject: ctx.credentialSubject,
1386
1437
  messageTs: ctx.messageTs,
1387
1438
  requester: ctx.requester,
1388
1439
  state: ctx.state,
@@ -1,4 +1,5 @@
1
1
  import { type AgentPluginRequester, type AgentPluginState, type AgentPluginToolDefinition } from "@sentry/junior-plugin-api";
2
+ import type { ScheduledTaskCredentialSubject } from "./types";
2
3
  export interface SchedulerToolContext {
3
4
  channelCapabilities: {
4
5
  canAddReactions: boolean;
@@ -6,6 +7,7 @@ export interface SchedulerToolContext {
6
7
  canPostToChannel: boolean;
7
8
  };
8
9
  channelId?: string;
10
+ credentialSubject?: ScheduledTaskCredentialSubject;
9
11
  messageTs?: string;
10
12
  requester?: AgentPluginRequester;
11
13
  state: AgentPluginState;
package/dist/types.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { AgentPluginCredentialSubject } from "@sentry/junior-plugin-api";
1
2
  export type ScheduledTaskStatus = "active" | "paused" | "blocked" | "deleted";
2
3
  export type ScheduledRunStatus = "pending" | "running" | "completed" | "failed" | "blocked" | "skipped";
3
4
  export interface ScheduledTaskPrincipal {
@@ -22,11 +23,7 @@ export interface ScheduledTaskConversationAccess {
22
23
  audience: "direct" | "group" | "channel";
23
24
  visibility: "private" | "public" | "unknown";
24
25
  }
25
- export interface ScheduledTaskCredentialSubject {
26
- type: "user";
27
- userId: string;
28
- allowedWhen: "private-direct-conversation";
29
- }
26
+ export type ScheduledTaskCredentialSubject = AgentPluginCredentialSubject;
30
27
  export type ScheduledCalendarFrequency = "daily" | "weekly" | "monthly" | "yearly";
31
28
  export interface ScheduledLocalTime {
32
29
  hour: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sentry/junior-scheduler",
3
- "version": "0.64.0",
3
+ "version": "0.65.1",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -23,7 +23,7 @@
23
23
  ],
24
24
  "dependencies": {
25
25
  "@sinclair/typebox": "^0.34.49",
26
- "@sentry/junior-plugin-api": "0.64.0"
26
+ "@sentry/junior-plugin-api": "0.65.1"
27
27
  },
28
28
  "devDependencies": {
29
29
  "@types/node": "^25.9.1",
package/src/plugin.ts CHANGED
@@ -47,6 +47,7 @@ function createSchedulerToolContext(
47
47
  canPostToChannel: false,
48
48
  },
49
49
  channelId: ctx.channelId,
50
+ credentialSubject: ctx.credentialSubject,
50
51
  messageTs: ctx.messageTs,
51
52
  requester: ctx.requester,
52
53
  state: ctx.state,
@@ -27,6 +27,7 @@ export interface SchedulerToolContext {
27
27
  canPostToChannel: boolean;
28
28
  };
29
29
  channelId?: string;
30
+ credentialSubject?: ScheduledTaskCredentialSubject;
30
31
  messageTs?: string;
31
32
  requester?: AgentPluginRequester;
32
33
  state: AgentPluginState;
@@ -126,7 +127,7 @@ function getConversationAccess(
126
127
 
127
128
  function getCredentialSubject(args: {
128
129
  access: ScheduledTaskConversationAccess;
129
- requester: ScheduledTaskPrincipal;
130
+ subject: ScheduledTaskCredentialSubject | undefined;
130
131
  }): ScheduledTaskCredentialSubject | undefined {
131
132
  if (
132
133
  args.access.audience !== "direct" ||
@@ -134,10 +135,13 @@ function getCredentialSubject(args: {
134
135
  ) {
135
136
  return undefined;
136
137
  }
138
+ if (!args.subject) {
139
+ return undefined;
140
+ }
137
141
  return {
138
- type: "user",
139
- userId: args.requester.slackUserId,
140
- allowedWhen: "private-direct-conversation",
142
+ type: args.subject.type,
143
+ userId: args.subject.userId,
144
+ allowedWhen: args.subject.allowedWhen,
141
145
  };
142
146
  }
143
147
 
@@ -337,10 +341,18 @@ export function createSlackScheduleCreateTaskTool(
337
341
  return tool({
338
342
  description:
339
343
  "Create a future or recurring Junior task in the active Slack conversation. Use only when the user explicitly asks Junior to do work later or on a recurring cadence. Only manage tasks for the active Slack DM or channel; never target threads, other channels, or another user's DM. When the task, schedule, and destination are clear, create it without asking for confirmation; ask only when one of those is ambiguous.",
344
+ executionMode: "sequential",
340
345
  inputSchema: Type.Object({
341
346
  task: Type.String({ minLength: 1, maxLength: 4000 }),
342
347
  schedule: Type.String({ minLength: 1, maxLength: 300 }),
343
- timezone: Type.Optional(Type.String({ minLength: 1, maxLength: 80, description: "IANA timezone, e.g. 'America/Los_Angeles'. Defaults to the channel's configured timezone." })),
348
+ timezone: Type.Optional(
349
+ Type.String({
350
+ minLength: 1,
351
+ maxLength: 80,
352
+ description:
353
+ "IANA timezone, e.g. 'America/Los_Angeles'. Defaults to the channel's configured timezone.",
354
+ }),
355
+ ),
344
356
  next_run_at: Type.Optional(
345
357
  Type.String({
346
358
  minLength: 1,
@@ -348,12 +360,20 @@ export function createSlackScheduleCreateTaskTool(
348
360
  "Exact next run time as an ISO timestamp, computed from the user's requested schedule.",
349
361
  }),
350
362
  ),
351
- recurrence: Type.Optional(Type.Union([
352
- Type.Literal("daily"),
353
- Type.Literal("weekly"),
354
- Type.Literal("monthly"),
355
- Type.Literal("yearly"),
356
- ], { description: "Provide only for explicitly repeating schedules; omit for one-time requests like 'in 1 minute', 'tomorrow', or a specific date. Recurring tasks run at most once per day: use daily, weekly, monthly, or yearly only." })),
363
+ recurrence: Type.Optional(
364
+ Type.Union(
365
+ [
366
+ Type.Literal("daily"),
367
+ Type.Literal("weekly"),
368
+ Type.Literal("monthly"),
369
+ Type.Literal("yearly"),
370
+ ],
371
+ {
372
+ description:
373
+ "Provide only for explicitly repeating schedules; omit for one-time requests like 'in 1 minute', 'tomorrow', or a specific date. Recurring tasks run at most once per day: use daily, weekly, monthly, or yearly only.",
374
+ },
375
+ ),
376
+ ),
357
377
  }),
358
378
  execute: async (input) => {
359
379
  const destination = requireActiveDestination(context);
@@ -377,7 +397,7 @@ export function createSlackScheduleCreateTaskTool(
377
397
  const conversationAccess = getConversationAccess(destination);
378
398
  const credentialSubject = getCredentialSubject({
379
399
  access: conversationAccess,
380
- requester,
400
+ subject: context.credentialSubject,
381
401
  });
382
402
 
383
403
  const task: ScheduledTask = {
@@ -449,21 +469,49 @@ export function createSlackScheduleUpdateTaskTool(
449
469
  return tool({
450
470
  description:
451
471
  "Edit, pause, resume, or reschedule an existing Junior scheduled task in the active Slack conversation. Use only task IDs returned for this destination. Do not move scheduled tasks across conversations.",
472
+ executionMode: "sequential",
452
473
  inputSchema: Type.Object({
453
- task_id: Type.String({ minLength: 1, description: "ID of the task to update. Must be from this active Slack destination." }),
474
+ task_id: Type.String({
475
+ minLength: 1,
476
+ description:
477
+ "ID of the task to update. Must be from this active Slack destination.",
478
+ }),
454
479
  task: Type.Optional(Type.String({ minLength: 1, maxLength: 4000 })),
455
480
  schedule: Type.Optional(Type.String({ minLength: 1, maxLength: 300 })),
456
481
  timezone: Type.Optional(Type.String({ minLength: 1, maxLength: 80 })),
457
- next_run_at: Type.Optional(Type.String({ minLength: 1, description: "Exact ISO timestamp when changing the next run time." })),
482
+ next_run_at: Type.Optional(
483
+ Type.String({
484
+ minLength: 1,
485
+ description: "Exact ISO timestamp when changing the next run time.",
486
+ }),
487
+ ),
458
488
  recurrence: Type.Optional(
459
- Type.Union([Type.Literal("daily"), Type.Literal("weekly"), Type.Literal("monthly"), Type.Literal("yearly"), Type.Null()], { description: "Provide only for repeating schedules. Omit for one-time requests. Set to null to convert a recurring task to one-time." }),
489
+ Type.Union(
490
+ [
491
+ Type.Literal("daily"),
492
+ Type.Literal("weekly"),
493
+ Type.Literal("monthly"),
494
+ Type.Literal("yearly"),
495
+ Type.Null(),
496
+ ],
497
+ {
498
+ description:
499
+ "Provide only for repeating schedules. Omit for one-time requests. Set to null to convert a recurring task to one-time.",
500
+ },
501
+ ),
460
502
  ),
461
503
  status: Type.Optional(
462
- Type.Union([
463
- Type.Literal("active"),
464
- Type.Literal("paused"),
465
- Type.Literal("blocked"),
466
- ], { description: "Set to active, paused, or blocked to resume, pause, or block the task." }),
504
+ Type.Union(
505
+ [
506
+ Type.Literal("active"),
507
+ Type.Literal("paused"),
508
+ Type.Literal("blocked"),
509
+ ],
510
+ {
511
+ description:
512
+ "Set to active, paused, or blocked to resume, pause, or block the task.",
513
+ },
514
+ ),
467
515
  ),
468
516
  }),
469
517
  execute: async (input) => {
@@ -539,8 +587,13 @@ export function createSlackScheduleDeleteTaskTool(
539
587
  return tool({
540
588
  description:
541
589
  "Delete one scheduled Junior task from the active Slack conversation. Use only task IDs returned for this destination. Do not delete schedules from threads, other channels, or another user's DM.",
590
+ executionMode: "sequential",
542
591
  inputSchema: Type.Object({
543
- task_id: Type.String({ minLength: 1, description: "ID of the task to delete. Must be from this active Slack destination." }),
592
+ task_id: Type.String({
593
+ minLength: 1,
594
+ description:
595
+ "ID of the task to delete. Must be from this active Slack destination.",
596
+ }),
544
597
  }),
545
598
  execute: async ({ task_id }) => {
546
599
  const lookup = await getWritableTask({ context, taskId: task_id });
@@ -570,8 +623,13 @@ export function createSlackScheduleRunTaskNowTool(
570
623
  return tool({
571
624
  description:
572
625
  "Queue an existing active scheduled Junior task to run as soon as possible, without changing its cadence. Use when the user asks to run an existing scheduled task now. Use only task IDs returned for this destination.",
626
+ executionMode: "sequential",
573
627
  inputSchema: Type.Object({
574
- task_id: Type.String({ minLength: 1, description: "ID of the active task to run now. Must be from this active Slack destination." }),
628
+ task_id: Type.String({
629
+ minLength: 1,
630
+ description:
631
+ "ID of the active task to run now. Must be from this active Slack destination.",
632
+ }),
575
633
  }),
576
634
  execute: async ({ task_id }) => {
577
635
  const lookup = await getWritableTask({ context, taskId: task_id });
package/src/types.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import type { AgentPluginCredentialSubject } from "@sentry/junior-plugin-api";
2
+
1
3
  export type ScheduledTaskStatus = "active" | "paused" | "blocked" | "deleted";
2
4
 
3
5
  export type ScheduledRunStatus =
@@ -35,11 +37,7 @@ export interface ScheduledTaskConversationAccess {
35
37
  visibility: "private" | "public" | "unknown";
36
38
  }
37
39
 
38
- export interface ScheduledTaskCredentialSubject {
39
- type: "user";
40
- userId: string;
41
- allowedWhen: "private-direct-conversation";
42
- }
40
+ export type ScheduledTaskCredentialSubject = AgentPluginCredentialSubject;
43
41
 
44
42
  export type ScheduledCalendarFrequency =
45
43
  | "daily"