@sentry/junior-scheduler 0.72.0 → 0.73.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.
package/dist/index.js CHANGED
@@ -125,7 +125,8 @@ function buildScheduledTaskRunPrompt(args) {
125
125
  // src/store.ts
126
126
  import {
127
127
  agentPluginCredentialSubjectSchema,
128
- destinationSchema
128
+ destinationSchema,
129
+ isSlackDestination
129
130
  } from "@sentry/junior-plugin-api";
130
131
 
131
132
  // src/cadence.ts
@@ -662,7 +663,7 @@ function parseStoredTask(value) {
662
663
  }
663
664
  const record = value;
664
665
  const destination = destinationSchema.safeParse(record.destination);
665
- if (!destination.success) {
666
+ if (!destination.success || !isSlackDestination(destination.data)) {
666
667
  return void 0;
667
668
  }
668
669
  const credentialSubject = record.credentialSubject === void 0 ? void 0 : agentPluginCredentialSubjectSchema.safeParse(record.credentialSubject);
@@ -1050,7 +1051,8 @@ import { Type } from "@sinclair/typebox";
1050
1051
  import {
1051
1052
  AgentPluginToolInputError,
1052
1053
  agentPluginCredentialSubjectSchema as agentPluginCredentialSubjectSchema2,
1053
- destinationSchema as destinationSchema2
1054
+ destinationSchema as destinationSchema2,
1055
+ isSlackDestination as isSlackDestination2
1054
1056
  } from "@sentry/junior-plugin-api";
1055
1057
  var TASK_ID_PREFIX = "sched";
1056
1058
  var MAX_LISTED_TASKS = 50;
@@ -1058,30 +1060,36 @@ var DEFAULT_SCHEDULE_TIMEZONE = "America/Los_Angeles";
1058
1060
  function throwToolInputError(error) {
1059
1061
  throw new AgentPluginToolInputError(error);
1060
1062
  }
1061
- function requireActiveDestination(context) {
1062
- const parsed = destinationSchema2.safeParse(context.destination);
1063
+ function requireActiveConversation(context) {
1064
+ const parsed = destinationSchema2.safeParse(context.source);
1063
1065
  if (!parsed.success) {
1064
- const destination = context.destination;
1066
+ const source = context.source;
1065
1067
  const issues = parsed.error.issues;
1066
- if (!destination || destination.platform !== "slack") {
1067
- throwToolInputError("No active Slack destination is available.");
1068
+ if (!source || source.platform !== "slack") {
1069
+ throwToolInputError("No active Slack conversation is available.");
1068
1070
  }
1069
1071
  if (issues.some((issue) => issue.code === "unrecognized_keys")) {
1070
1072
  throwToolInputError(
1071
- "Active Slack destination must not include unknown fields."
1073
+ "Active Slack conversation must not include unknown fields."
1072
1074
  );
1073
1075
  }
1074
1076
  if (issues.some((issue) => issue.path[0] === "channelId")) {
1075
- throwToolInputError("Active Slack destination channel is invalid.");
1077
+ throwToolInputError("Active Slack conversation channel is invalid.");
1076
1078
  }
1077
1079
  if (issues.some((issue) => issue.path[0] === "teamId")) {
1078
- throwToolInputError("Active Slack destination workspace is invalid.");
1080
+ throwToolInputError("Active Slack conversation workspace is invalid.");
1079
1081
  }
1080
- throwToolInputError("No active Slack destination is available.");
1082
+ throwToolInputError("No active Slack conversation is available.");
1083
+ }
1084
+ if (!isSlackDestination2(parsed.data)) {
1085
+ throwToolInputError("No active Slack conversation is available.");
1081
1086
  }
1082
1087
  return parsed.data;
1083
1088
  }
1084
1089
  function requireRequester(context) {
1090
+ if (context.requester?.platform !== "slack") {
1091
+ throwToolInputError("No active Slack requester context is available.");
1092
+ }
1085
1093
  const userId = context.requester?.userId?.trim();
1086
1094
  if (!userId || userId.toLowerCase() === "unknown") {
1087
1095
  throwToolInputError("No active Slack requester context is available.");
@@ -1132,13 +1140,13 @@ function sameDestination(task, destination) {
1132
1140
  return taskDestination.platform === "slack" && taskDestination.teamId === destination.teamId && taskDestination.channelId === destination.channelId;
1133
1141
  }
1134
1142
  async function getWritableTask(args) {
1135
- const destination = requireActiveDestination(args.context);
1143
+ const destination = requireActiveConversation(args.context);
1136
1144
  const task = await createSchedulerStore(args.context.state).getTask(
1137
1145
  args.taskId
1138
1146
  );
1139
1147
  if (!task || task.status === "deleted") {
1140
1148
  throwToolInputError(
1141
- "Scheduled task was not found in the active destination."
1149
+ "Scheduled task was not found in the active Slack conversation."
1142
1150
  );
1143
1151
  }
1144
1152
  if (!sameDestination(task, destination)) {
@@ -1281,7 +1289,7 @@ function createSlackScheduleCreateTaskTool(context) {
1281
1289
  )
1282
1290
  }),
1283
1291
  execute: async (input) => {
1284
- const destination = requireActiveDestination(context);
1292
+ const destination = requireActiveConversation(context);
1285
1293
  const requester = requireRequester(context);
1286
1294
  const nowMs = Date.now();
1287
1295
  const timezone = input.timezone ?? getDefaultScheduleTimezone();
@@ -1340,7 +1348,7 @@ function createSlackScheduleListTasksTool(context) {
1340
1348
  annotations: { readOnlyHint: true, destructiveHint: false },
1341
1349
  inputSchema: Type.Object({}),
1342
1350
  execute: async () => {
1343
- const destination = requireActiveDestination(context);
1351
+ const destination = requireActiveConversation(context);
1344
1352
  const tasks = await createSchedulerStore(context.state).listTasksForTeam(
1345
1353
  destination.teamId
1346
1354
  );
@@ -1358,12 +1366,12 @@ function createSlackScheduleListTasksTool(context) {
1358
1366
  }
1359
1367
  function createSlackScheduleUpdateTaskTool(context) {
1360
1368
  return tool({
1361
- 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.",
1369
+ description: "Edit, pause, resume, or reschedule an existing Junior scheduled task in the active Slack conversation. Use only task IDs returned for this conversation. Do not move scheduled tasks across conversations.",
1362
1370
  executionMode: "sequential",
1363
1371
  inputSchema: Type.Object({
1364
1372
  task_id: Type.String({
1365
1373
  minLength: 1,
1366
- description: "ID of the task to update. Must be from this active Slack destination."
1374
+ description: "ID of the task to update. Must be from this active Slack conversation."
1367
1375
  }),
1368
1376
  task: Type.Optional(Type.String({ minLength: 1, maxLength: 4e3 })),
1369
1377
  schedule: Type.Optional(Type.String({ minLength: 1, maxLength: 300 })),
@@ -1459,12 +1467,12 @@ function createSlackScheduleUpdateTaskTool(context) {
1459
1467
  }
1460
1468
  function createSlackScheduleDeleteTaskTool(context) {
1461
1469
  return tool({
1462
- 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.",
1470
+ description: "Delete one scheduled Junior task from the active Slack conversation. Use only task IDs returned for this conversation. Do not delete schedules from threads, other channels, or another user's DM.",
1463
1471
  executionMode: "sequential",
1464
1472
  inputSchema: Type.Object({
1465
1473
  task_id: Type.String({
1466
1474
  minLength: 1,
1467
- description: "ID of the task to delete. Must be from this active Slack destination."
1475
+ description: "ID of the task to delete. Must be from this active Slack conversation."
1468
1476
  })
1469
1477
  }),
1470
1478
  execute: async ({ task_id }) => {
@@ -1487,12 +1495,12 @@ function createSlackScheduleDeleteTaskTool(context) {
1487
1495
  }
1488
1496
  function createSlackScheduleRunTaskNowTool(context) {
1489
1497
  return tool({
1490
- 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.",
1498
+ 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 conversation.",
1491
1499
  executionMode: "sequential",
1492
1500
  inputSchema: Type.Object({
1493
1501
  task_id: Type.String({
1494
1502
  minLength: 1,
1495
- description: "ID of the active task to run now. Must be from this active Slack destination."
1503
+ description: "ID of the active task to run now. Must be from this active Slack conversation."
1496
1504
  })
1497
1505
  }),
1498
1506
  execute: async ({ task_id }) => {
@@ -1535,9 +1543,13 @@ function shouldSkipRun(task, run) {
1535
1543
  }
1536
1544
  function createSchedulerToolContext(ctx) {
1537
1545
  return {
1538
- credentialSubject: ctx.credentialSubject,
1539
- destination: ctx.destination?.platform === "slack" ? ctx.destination : void 0,
1540
- requester: ctx.requester,
1546
+ credentialSubject: ctx.slack?.credentialSubject,
1547
+ source: ctx.source.platform === "slack" ? {
1548
+ platform: "slack",
1549
+ teamId: ctx.source.teamId,
1550
+ channelId: ctx.source.channelId
1551
+ } : void 0,
1552
+ requester: ctx.requester?.platform === "slack" ? ctx.requester : void 0,
1541
1553
  state: ctx.state,
1542
1554
  userText: ctx.userText
1543
1555
  };
@@ -1785,12 +1797,13 @@ function createSchedulerPlugin() {
1785
1797
  return defineJuniorPlugin({
1786
1798
  manifest: {
1787
1799
  name: "scheduler",
1800
+ displayName: "Scheduler",
1788
1801
  description: "Scheduled Junior task management and heartbeat dispatch"
1789
1802
  },
1790
1803
  legacyStatePrefixes: ["junior:scheduler"],
1791
1804
  hooks: {
1792
1805
  tools(ctx) {
1793
- if (!ctx.destination || ctx.destination.platform !== "slack" || !ctx.requester?.userId) {
1806
+ if (ctx.source.platform !== "slack" || ctx.requester?.platform !== "slack") {
1794
1807
  return {};
1795
1808
  }
1796
1809
  const context = createSchedulerToolContext(ctx);
@@ -1,18 +1,18 @@
1
- import { type AgentPluginCredentialSubject, type Destination, type Requester, type AgentPluginState, type AgentPluginToolDefinition } from "@sentry/junior-plugin-api";
1
+ import { type AgentPluginCredentialSubject, type AgentPluginState, type AgentPluginToolDefinition, type SlackDestination, type SlackRequester } from "@sentry/junior-plugin-api";
2
2
  export interface SchedulerToolContext {
3
3
  credentialSubject?: AgentPluginCredentialSubject;
4
- destination?: Destination;
5
- requester?: Requester;
4
+ requester?: SlackRequester;
5
+ source?: SlackDestination;
6
6
  state: AgentPluginState;
7
7
  userText?: string;
8
8
  }
9
9
  /** Create a tool that stores a scheduled task for the active Slack context. */
10
10
  export declare function createSlackScheduleCreateTaskTool(context: SchedulerToolContext): AgentPluginToolDefinition<any>;
11
- /** Create a tool that lists scheduled tasks for the active Slack destination. */
11
+ /** Create a tool that lists scheduled tasks for the active Slack conversation. */
12
12
  export declare function createSlackScheduleListTasksTool(context: SchedulerToolContext): AgentPluginToolDefinition<any>;
13
- /** Create a tool that edits a scheduled task in the active Slack destination. */
13
+ /** Create a tool that edits a scheduled task in the active Slack conversation. */
14
14
  export declare function createSlackScheduleUpdateTaskTool(context: SchedulerToolContext): AgentPluginToolDefinition<any>;
15
- /** Create a tool that removes a scheduled task from the active Slack destination. */
15
+ /** Create a tool that removes a scheduled task from the active Slack conversation. */
16
16
  export declare function createSlackScheduleDeleteTaskTool(context: SchedulerToolContext): AgentPluginToolDefinition<any>;
17
17
  /** Create a tool that marks an existing scheduled task due immediately. */
18
18
  export declare function createSlackScheduleRunTaskNowTool(context: SchedulerToolContext): AgentPluginToolDefinition<any>;
package/dist/types.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { AgentPluginCredentialSubject, Destination } from "@sentry/junior-plugin-api";
1
+ import type { AgentPluginCredentialSubject, SlackDestination } from "@sentry/junior-plugin-api";
2
2
  export type ScheduledTaskStatus = "active" | "paused" | "blocked" | "deleted";
3
3
  export type ScheduledRunStatus = "pending" | "running" | "completed" | "failed" | "blocked" | "skipped";
4
4
  export interface ScheduledTaskPrincipal {
@@ -47,7 +47,7 @@ export interface ScheduledTask {
47
47
  createdBy: ScheduledTaskPrincipal;
48
48
  conversationAccess?: ScheduledTaskConversationAccess;
49
49
  credentialSubject?: AgentPluginCredentialSubject;
50
- destination: Destination;
50
+ destination: SlackDestination;
51
51
  executionActor?: ScheduledTaskExecutionActor;
52
52
  lastRunAtMs?: number;
53
53
  nextRunAtMs?: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sentry/junior-scheduler",
3
- "version": "0.72.0",
3
+ "version": "0.73.0",
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.72.0"
26
+ "@sentry/junior-plugin-api": "0.73.0"
27
27
  },
28
28
  "devDependencies": {
29
29
  "@types/node": "^25.9.1",
package/src/plugin.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  defineJuniorPlugin,
3
- type Destination,
4
3
  type Dispatch,
5
4
  type AgentPluginToolDefinition,
6
5
  type PluginOperationalReportContent,
6
+ type SlackDestination,
7
7
  type ToolRegistrationHookContext,
8
8
  } from "@sentry/junior-plugin-api";
9
9
  import { buildScheduledTaskRunPrompt } from "./prompt";
@@ -54,10 +54,16 @@ function createSchedulerToolContext(
54
54
  ctx: ToolRegistrationHookContext,
55
55
  ): SchedulerToolContext {
56
56
  return {
57
- credentialSubject: ctx.credentialSubject,
58
- destination:
59
- ctx.destination?.platform === "slack" ? ctx.destination : undefined,
60
- requester: ctx.requester,
57
+ credentialSubject: ctx.slack?.credentialSubject,
58
+ source:
59
+ ctx.source.platform === "slack"
60
+ ? {
61
+ platform: "slack",
62
+ teamId: ctx.source.teamId,
63
+ channelId: ctx.source.channelId,
64
+ }
65
+ : undefined,
66
+ requester: ctx.requester?.platform === "slack" ? ctx.requester : undefined,
61
67
  state: ctx.state,
62
68
  userText: ctx.userText,
63
69
  };
@@ -183,7 +189,7 @@ function formatTimestamp(timestampMs: number | undefined): string {
183
189
  : "none";
184
190
  }
185
191
 
186
- function destinationLabel(destination: Destination): string {
192
+ function destinationLabel(destination: SlackDestination): string {
187
193
  if (destination.channelId.startsWith("D")) {
188
194
  return "Direct Message";
189
195
  }
@@ -358,15 +364,15 @@ export function createSchedulerPlugin() {
358
364
  return defineJuniorPlugin({
359
365
  manifest: {
360
366
  name: "scheduler",
367
+ displayName: "Scheduler",
361
368
  description: "Scheduled Junior task management and heartbeat dispatch",
362
369
  },
363
370
  legacyStatePrefixes: ["junior:scheduler"],
364
371
  hooks: {
365
372
  tools(ctx) {
366
373
  if (
367
- !ctx.destination ||
368
- ctx.destination.platform !== "slack" ||
369
- !ctx.requester?.userId
374
+ ctx.source.platform !== "slack" ||
375
+ ctx.requester?.platform !== "slack"
370
376
  ) {
371
377
  return {} as Record<string, AgentPluginToolDefinition<any>>;
372
378
  }
@@ -4,11 +4,12 @@ import {
4
4
  AgentPluginToolInputError,
5
5
  agentPluginCredentialSubjectSchema,
6
6
  destinationSchema,
7
+ isSlackDestination,
7
8
  type AgentPluginCredentialSubject,
8
- type Destination,
9
- type Requester,
10
9
  type AgentPluginState,
11
10
  type AgentPluginToolDefinition,
11
+ type SlackDestination,
12
+ type SlackRequester,
12
13
  } from "@sentry/junior-plugin-api";
13
14
  import { buildCalendarRecurrence, parseScheduleTimestamp } from "./cadence";
14
15
  import { sanitizeScheduledTaskPrincipal } from "./identity";
@@ -25,8 +26,8 @@ import type {
25
26
 
26
27
  export interface SchedulerToolContext {
27
28
  credentialSubject?: AgentPluginCredentialSubject;
28
- destination?: Destination;
29
- requester?: Requester;
29
+ requester?: SlackRequester;
30
+ source?: SlackDestination;
30
31
  state: AgentPluginState;
31
32
  userText?: string;
32
33
  }
@@ -44,26 +45,32 @@ function throwToolInputError(error: string): never {
44
45
  throw new AgentPluginToolInputError(error);
45
46
  }
46
47
 
47
- function requireActiveDestination(context: SchedulerToolContext): Destination {
48
- const parsed = destinationSchema.safeParse(context.destination);
48
+ function requireActiveConversation(
49
+ context: SchedulerToolContext,
50
+ ): SlackDestination {
51
+ const parsed = destinationSchema.safeParse(context.source);
49
52
  if (!parsed.success) {
50
- const destination = context.destination as Partial<Destination> | undefined;
53
+ const source = context.source as Partial<SlackDestination> | undefined;
51
54
  const issues = parsed.error.issues as readonly SchemaIssue[];
52
- if (!destination || destination.platform !== "slack") {
53
- throwToolInputError("No active Slack destination is available.");
55
+ if (!source || source.platform !== "slack") {
56
+ throwToolInputError("No active Slack conversation is available.");
54
57
  }
55
58
  if (issues.some((issue) => issue.code === "unrecognized_keys")) {
56
59
  throwToolInputError(
57
- "Active Slack destination must not include unknown fields.",
60
+ "Active Slack conversation must not include unknown fields.",
58
61
  );
59
62
  }
60
63
  if (issues.some((issue) => issue.path[0] === "channelId")) {
61
- throwToolInputError("Active Slack destination channel is invalid.");
64
+ throwToolInputError("Active Slack conversation channel is invalid.");
62
65
  }
63
66
  if (issues.some((issue) => issue.path[0] === "teamId")) {
64
- throwToolInputError("Active Slack destination workspace is invalid.");
67
+ throwToolInputError("Active Slack conversation workspace is invalid.");
65
68
  }
66
- throwToolInputError("No active Slack destination is available.");
69
+ throwToolInputError("No active Slack conversation is available.");
70
+ }
71
+
72
+ if (!isSlackDestination(parsed.data)) {
73
+ throwToolInputError("No active Slack conversation is available.");
67
74
  }
68
75
 
69
76
  return parsed.data;
@@ -72,6 +79,9 @@ function requireActiveDestination(context: SchedulerToolContext): Destination {
72
79
  function requireRequester(
73
80
  context: SchedulerToolContext,
74
81
  ): ScheduledTaskPrincipal {
82
+ if (context.requester?.platform !== "slack") {
83
+ throwToolInputError("No active Slack requester context is available.");
84
+ }
75
85
  const userId = context.requester?.userId?.trim();
76
86
  if (!userId || userId.toLowerCase() === "unknown") {
77
87
  throwToolInputError("No active Slack requester context is available.");
@@ -99,7 +109,7 @@ function isDmChannel(channelId: string): boolean {
99
109
  }
100
110
 
101
111
  function getConversationAccess(
102
- destination: Destination,
112
+ destination: SlackDestination,
103
113
  ): ScheduledTaskConversationAccess {
104
114
  if (isDmChannel(destination.channelId)) {
105
115
  return { audience: "direct", visibility: "private" };
@@ -139,7 +149,7 @@ function getCredentialSubject(args: {
139
149
 
140
150
  function sameDestination(
141
151
  task: ScheduledTask,
142
- destination: Destination,
152
+ destination: SlackDestination,
143
153
  ): boolean {
144
154
  const taskDestination = task.destination;
145
155
  return (
@@ -153,14 +163,14 @@ async function getWritableTask(args: {
153
163
  context: SchedulerToolContext;
154
164
  taskId: string;
155
165
  }): Promise<ScheduledTask> {
156
- const destination = requireActiveDestination(args.context);
166
+ const destination = requireActiveConversation(args.context);
157
167
 
158
168
  const task = await createSchedulerStore(args.context.state).getTask(
159
169
  args.taskId,
160
170
  );
161
171
  if (!task || task.status === "deleted") {
162
172
  throwToolInputError(
163
- "Scheduled task was not found in the active destination.",
173
+ "Scheduled task was not found in the active Slack conversation.",
164
174
  );
165
175
  }
166
176
 
@@ -369,7 +379,7 @@ export function createSlackScheduleCreateTaskTool(
369
379
  ),
370
380
  }),
371
381
  execute: async (input) => {
372
- const destination = requireActiveDestination(context);
382
+ const destination = requireActiveConversation(context);
373
383
  const requester = requireRequester(context);
374
384
 
375
385
  const nowMs = Date.now();
@@ -426,7 +436,7 @@ export function createSlackScheduleCreateTaskTool(
426
436
  });
427
437
  }
428
438
 
429
- /** Create a tool that lists scheduled tasks for the active Slack destination. */
439
+ /** Create a tool that lists scheduled tasks for the active Slack conversation. */
430
440
  export function createSlackScheduleListTasksTool(
431
441
  context: SchedulerToolContext,
432
442
  ) {
@@ -436,7 +446,7 @@ export function createSlackScheduleListTasksTool(
436
446
  annotations: { readOnlyHint: true, destructiveHint: false },
437
447
  inputSchema: Type.Object({}),
438
448
  execute: async () => {
439
- const destination = requireActiveDestination(context);
449
+ const destination = requireActiveConversation(context);
440
450
 
441
451
  const tasks = await createSchedulerStore(context.state).listTasksForTeam(
442
452
  destination.teamId,
@@ -455,19 +465,19 @@ export function createSlackScheduleListTasksTool(
455
465
  });
456
466
  }
457
467
 
458
- /** Create a tool that edits a scheduled task in the active Slack destination. */
468
+ /** Create a tool that edits a scheduled task in the active Slack conversation. */
459
469
  export function createSlackScheduleUpdateTaskTool(
460
470
  context: SchedulerToolContext,
461
471
  ) {
462
472
  return tool({
463
473
  description:
464
- "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.",
474
+ "Edit, pause, resume, or reschedule an existing Junior scheduled task in the active Slack conversation. Use only task IDs returned for this conversation. Do not move scheduled tasks across conversations.",
465
475
  executionMode: "sequential",
466
476
  inputSchema: Type.Object({
467
477
  task_id: Type.String({
468
478
  minLength: 1,
469
479
  description:
470
- "ID of the task to update. Must be from this active Slack destination.",
480
+ "ID of the task to update. Must be from this active Slack conversation.",
471
481
  }),
472
482
  task: Type.Optional(Type.String({ minLength: 1, maxLength: 4000 })),
473
483
  schedule: Type.Optional(Type.String({ minLength: 1, maxLength: 300 })),
@@ -573,19 +583,19 @@ export function createSlackScheduleUpdateTaskTool(
573
583
  });
574
584
  }
575
585
 
576
- /** Create a tool that removes a scheduled task from the active Slack destination. */
586
+ /** Create a tool that removes a scheduled task from the active Slack conversation. */
577
587
  export function createSlackScheduleDeleteTaskTool(
578
588
  context: SchedulerToolContext,
579
589
  ) {
580
590
  return tool({
581
591
  description:
582
- "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.",
592
+ "Delete one scheduled Junior task from the active Slack conversation. Use only task IDs returned for this conversation. Do not delete schedules from threads, other channels, or another user's DM.",
583
593
  executionMode: "sequential",
584
594
  inputSchema: Type.Object({
585
595
  task_id: Type.String({
586
596
  minLength: 1,
587
597
  description:
588
- "ID of the task to delete. Must be from this active Slack destination.",
598
+ "ID of the task to delete. Must be from this active Slack conversation.",
589
599
  }),
590
600
  }),
591
601
  execute: async ({ task_id }) => {
@@ -615,13 +625,13 @@ export function createSlackScheduleRunTaskNowTool(
615
625
  ) {
616
626
  return tool({
617
627
  description:
618
- "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.",
628
+ "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 conversation.",
619
629
  executionMode: "sequential",
620
630
  inputSchema: Type.Object({
621
631
  task_id: Type.String({
622
632
  minLength: 1,
623
633
  description:
624
- "ID of the active task to run now. Must be from this active Slack destination.",
634
+ "ID of the active task to run now. Must be from this active Slack conversation.",
625
635
  }),
626
636
  }),
627
637
  execute: async ({ task_id }) => {
package/src/store.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  agentPluginCredentialSubjectSchema,
3
3
  destinationSchema,
4
+ isSlackDestination,
4
5
  type AgentPluginReadState,
5
6
  type AgentPluginState,
6
7
  } from "@sentry/junior-plugin-api";
@@ -363,7 +364,7 @@ function parseStoredTask(value: unknown): ScheduledTask | undefined {
363
364
  }
364
365
  const record = value as Partial<ScheduledTask>;
365
366
  const destination = destinationSchema.safeParse(record.destination);
366
- if (!destination.success) {
367
+ if (!destination.success || !isSlackDestination(destination.data)) {
367
368
  return undefined;
368
369
  }
369
370
  const credentialSubject =
package/src/types.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import type {
2
2
  AgentPluginCredentialSubject,
3
- Destination,
3
+ SlackDestination,
4
4
  } from "@sentry/junior-plugin-api";
5
5
 
6
6
  export type ScheduledTaskStatus = "active" | "paused" | "blocked" | "deleted";
@@ -72,7 +72,7 @@ export interface ScheduledTask {
72
72
  createdBy: ScheduledTaskPrincipal;
73
73
  conversationAccess?: ScheduledTaskConversationAccess;
74
74
  credentialSubject?: AgentPluginCredentialSubject;
75
- destination: Destination;
75
+ destination: SlackDestination;
76
76
  executionActor?: ScheduledTaskExecutionActor;
77
77
  lastRunAtMs?: number;
78
78
  nextRunAtMs?: number;