@sentry/junior-scheduler 0.67.0 → 0.67.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/identity.d.ts +5 -0
- package/dist/index.js +57 -18
- package/package.json +2 -2
- package/src/identity.ts +64 -0
- package/src/plugin.ts +16 -14
- package/src/prompt.ts +2 -1
- package/src/schedule-tools.ts +5 -4
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { ScheduledTaskPrincipal } from "./types";
|
|
2
|
+
/** Keep scheduler creator metadata from promoting Slack IDs into display names. */
|
|
3
|
+
export declare function sanitizeScheduledTaskPrincipal(principal: ScheduledTaskPrincipal): ScheduledTaskPrincipal;
|
|
4
|
+
/** Render scheduler creator metadata without inventing human profile fields. */
|
|
5
|
+
export declare function scheduledTaskPrincipalLabel(principal: ScheduledTaskPrincipal): string;
|
package/dist/index.js
CHANGED
|
@@ -9,6 +9,50 @@ var SCHEDULED_TASK_SYSTEM_ACTOR = Object.freeze({
|
|
|
9
9
|
id: "scheduled-task"
|
|
10
10
|
});
|
|
11
11
|
|
|
12
|
+
// src/identity.ts
|
|
13
|
+
var SLACK_USER_ID_PATTERN = /^[UW][A-Z0-9]{5,}$/;
|
|
14
|
+
function clean(value) {
|
|
15
|
+
const normalized = value?.trim();
|
|
16
|
+
return normalized ? normalized : void 0;
|
|
17
|
+
}
|
|
18
|
+
function cleanSlackUserId(value) {
|
|
19
|
+
const normalized = clean(value);
|
|
20
|
+
return normalized && normalized.toLowerCase() !== "unknown" ? normalized : void 0;
|
|
21
|
+
}
|
|
22
|
+
function cleanDisplay(value, slackUserId) {
|
|
23
|
+
const normalized = clean(value);
|
|
24
|
+
if (!normalized || normalized.toLowerCase() === "unknown" || normalized === slackUserId) {
|
|
25
|
+
return void 0;
|
|
26
|
+
}
|
|
27
|
+
return SLACK_USER_ID_PATTERN.test(normalized) ? void 0 : normalized;
|
|
28
|
+
}
|
|
29
|
+
function sanitizeScheduledTaskPrincipal(principal) {
|
|
30
|
+
const slackUserId = cleanSlackUserId(principal.slackUserId);
|
|
31
|
+
if (!slackUserId) {
|
|
32
|
+
throw new Error("Scheduled task principal requires a Slack user id");
|
|
33
|
+
}
|
|
34
|
+
const fullName = cleanDisplay(principal.fullName, slackUserId);
|
|
35
|
+
const userName = cleanDisplay(principal.userName, slackUserId);
|
|
36
|
+
return {
|
|
37
|
+
slackUserId,
|
|
38
|
+
...fullName ? { fullName } : {},
|
|
39
|
+
...userName ? { userName } : {}
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function scheduledTaskPrincipalLabel(principal) {
|
|
43
|
+
const author = sanitizeScheduledTaskPrincipal(principal);
|
|
44
|
+
if (author.fullName && author.userName) {
|
|
45
|
+
return `${author.fullName} (@${author.userName})`;
|
|
46
|
+
}
|
|
47
|
+
if (author.fullName) {
|
|
48
|
+
return author.fullName;
|
|
49
|
+
}
|
|
50
|
+
if (author.userName) {
|
|
51
|
+
return `@${author.userName}`;
|
|
52
|
+
}
|
|
53
|
+
return `Slack User ${author.slackUserId}`;
|
|
54
|
+
}
|
|
55
|
+
|
|
12
56
|
// src/prompt.ts
|
|
13
57
|
function escapeXml(value) {
|
|
14
58
|
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
@@ -28,7 +72,7 @@ function renderOptionalLine(name, value) {
|
|
|
28
72
|
function buildScheduledTaskRunPrompt(args) {
|
|
29
73
|
const { run, task } = args;
|
|
30
74
|
const destination = task.destination;
|
|
31
|
-
const creator = task.createdBy;
|
|
75
|
+
const creator = sanitizeScheduledTaskPrincipal(task.createdBy);
|
|
32
76
|
const executionActor = task.executionActor ?? SCHEDULED_TASK_SYSTEM_ACTOR;
|
|
33
77
|
if (!task.task.text?.trim()) {
|
|
34
78
|
throw new Error("Scheduled task text is required");
|
|
@@ -997,15 +1041,15 @@ function requireActiveDestination(context) {
|
|
|
997
1041
|
};
|
|
998
1042
|
}
|
|
999
1043
|
function requireRequester(context) {
|
|
1000
|
-
const userId = context.requester?.userId;
|
|
1001
|
-
if (!userId) {
|
|
1044
|
+
const userId = context.requester?.userId?.trim();
|
|
1045
|
+
if (!userId || userId.toLowerCase() === "unknown") {
|
|
1002
1046
|
throwToolInputError("No active Slack requester context is available.");
|
|
1003
1047
|
}
|
|
1004
|
-
return {
|
|
1048
|
+
return sanitizeScheduledTaskPrincipal({
|
|
1005
1049
|
slackUserId: userId,
|
|
1006
1050
|
...context.requester?.userName ? { userName: context.requester.userName } : {},
|
|
1007
1051
|
...context.requester?.fullName ? { fullName: context.requester.fullName } : {}
|
|
1008
|
-
};
|
|
1052
|
+
});
|
|
1009
1053
|
}
|
|
1010
1054
|
function tool(definition) {
|
|
1011
1055
|
return definition;
|
|
@@ -1578,17 +1622,12 @@ function destinationLabel(destination) {
|
|
|
1578
1622
|
}
|
|
1579
1623
|
return destination.channelId;
|
|
1580
1624
|
}
|
|
1581
|
-
function
|
|
1582
|
-
|
|
1583
|
-
return
|
|
1584
|
-
}
|
|
1585
|
-
|
|
1586
|
-
return author.fullName;
|
|
1587
|
-
}
|
|
1588
|
-
if (author.userName) {
|
|
1589
|
-
return `@${author.userName}`;
|
|
1625
|
+
function operationalAuthorLabel(author) {
|
|
1626
|
+
try {
|
|
1627
|
+
return scheduledTaskPrincipalLabel(author);
|
|
1628
|
+
} catch {
|
|
1629
|
+
return "Invalid Slack creator metadata";
|
|
1590
1630
|
}
|
|
1591
|
-
return `Slack User ${author.slackUserId}`;
|
|
1592
1631
|
}
|
|
1593
1632
|
function cadenceLabel(task) {
|
|
1594
1633
|
if (task.schedule.kind === "one_off") {
|
|
@@ -1660,7 +1699,7 @@ async function buildSchedulerOperationalReport(args) {
|
|
|
1660
1699
|
id: task.id,
|
|
1661
1700
|
values: {
|
|
1662
1701
|
task: task.id,
|
|
1663
|
-
author:
|
|
1702
|
+
author: operationalAuthorLabel(task.createdBy),
|
|
1664
1703
|
destination: destinationLabel(task.destination),
|
|
1665
1704
|
nextRun: formatTimestamp(task.nextRunAtMs),
|
|
1666
1705
|
cadence: cadenceLabel(task)
|
|
@@ -1681,7 +1720,7 @@ async function buildSchedulerOperationalReport(args) {
|
|
|
1681
1720
|
tone: "danger",
|
|
1682
1721
|
values: {
|
|
1683
1722
|
task: task.id,
|
|
1684
|
-
author:
|
|
1723
|
+
author: operationalAuthorLabel(task.createdBy),
|
|
1685
1724
|
destination: destinationLabel(task.destination),
|
|
1686
1725
|
updated: formatTimestamp(task.updatedAtMs)
|
|
1687
1726
|
}
|
|
@@ -1705,7 +1744,7 @@ async function buildSchedulerOperationalReport(args) {
|
|
|
1705
1744
|
values: {
|
|
1706
1745
|
run: run.id,
|
|
1707
1746
|
task: run.taskId,
|
|
1708
|
-
author: task ?
|
|
1747
|
+
author: task ? operationalAuthorLabel(task.createdBy) : "Missing scheduled task",
|
|
1709
1748
|
scheduledFor: formatTimestamp(run.scheduledForMs),
|
|
1710
1749
|
status: run.status
|
|
1711
1750
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sentry/junior-scheduler",
|
|
3
|
-
"version": "0.67.
|
|
3
|
+
"version": "0.67.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.67.
|
|
26
|
+
"@sentry/junior-plugin-api": "0.67.1"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"@types/node": "^25.9.1",
|
package/src/identity.ts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { ScheduledTaskPrincipal } from "./types";
|
|
2
|
+
|
|
3
|
+
const SLACK_USER_ID_PATTERN = /^[UW][A-Z0-9]{5,}$/;
|
|
4
|
+
|
|
5
|
+
function clean(value: string | undefined): string | undefined {
|
|
6
|
+
const normalized = value?.trim();
|
|
7
|
+
return normalized ? normalized : undefined;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function cleanSlackUserId(value: string | undefined): string | undefined {
|
|
11
|
+
const normalized = clean(value);
|
|
12
|
+
return normalized && normalized.toLowerCase() !== "unknown"
|
|
13
|
+
? normalized
|
|
14
|
+
: undefined;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function cleanDisplay(
|
|
18
|
+
value: string | undefined,
|
|
19
|
+
slackUserId: string,
|
|
20
|
+
): string | undefined {
|
|
21
|
+
const normalized = clean(value);
|
|
22
|
+
if (
|
|
23
|
+
!normalized ||
|
|
24
|
+
normalized.toLowerCase() === "unknown" ||
|
|
25
|
+
normalized === slackUserId
|
|
26
|
+
) {
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
return SLACK_USER_ID_PATTERN.test(normalized) ? undefined : normalized;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Keep scheduler creator metadata from promoting Slack IDs into display names. */
|
|
33
|
+
export function sanitizeScheduledTaskPrincipal(
|
|
34
|
+
principal: ScheduledTaskPrincipal,
|
|
35
|
+
): ScheduledTaskPrincipal {
|
|
36
|
+
const slackUserId = cleanSlackUserId(principal.slackUserId);
|
|
37
|
+
if (!slackUserId) {
|
|
38
|
+
throw new Error("Scheduled task principal requires a Slack user id");
|
|
39
|
+
}
|
|
40
|
+
const fullName = cleanDisplay(principal.fullName, slackUserId);
|
|
41
|
+
const userName = cleanDisplay(principal.userName, slackUserId);
|
|
42
|
+
return {
|
|
43
|
+
slackUserId,
|
|
44
|
+
...(fullName ? { fullName } : {}),
|
|
45
|
+
...(userName ? { userName } : {}),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Render scheduler creator metadata without inventing human profile fields. */
|
|
50
|
+
export function scheduledTaskPrincipalLabel(
|
|
51
|
+
principal: ScheduledTaskPrincipal,
|
|
52
|
+
): string {
|
|
53
|
+
const author = sanitizeScheduledTaskPrincipal(principal);
|
|
54
|
+
if (author.fullName && author.userName) {
|
|
55
|
+
return `${author.fullName} (@${author.userName})`;
|
|
56
|
+
}
|
|
57
|
+
if (author.fullName) {
|
|
58
|
+
return author.fullName;
|
|
59
|
+
}
|
|
60
|
+
if (author.userName) {
|
|
61
|
+
return `@${author.userName}`;
|
|
62
|
+
}
|
|
63
|
+
return `Slack User ${author.slackUserId}`;
|
|
64
|
+
}
|
package/src/plugin.ts
CHANGED
|
@@ -12,7 +12,12 @@ import {
|
|
|
12
12
|
type SchedulerOperationalStore,
|
|
13
13
|
type SchedulerStore,
|
|
14
14
|
} from "./store";
|
|
15
|
-
import
|
|
15
|
+
import { scheduledTaskPrincipalLabel } from "./identity";
|
|
16
|
+
import type {
|
|
17
|
+
ScheduledRun,
|
|
18
|
+
ScheduledTask,
|
|
19
|
+
ScheduledTaskPrincipal,
|
|
20
|
+
} from "./types";
|
|
16
21
|
import {
|
|
17
22
|
createSlackScheduleCreateTaskTool,
|
|
18
23
|
createSlackScheduleDeleteTaskTool,
|
|
@@ -197,17 +202,12 @@ function destinationLabel(destination: ScheduledTask["destination"]): string {
|
|
|
197
202
|
return destination.channelId;
|
|
198
203
|
}
|
|
199
204
|
|
|
200
|
-
function
|
|
201
|
-
|
|
202
|
-
return
|
|
205
|
+
function operationalAuthorLabel(author: ScheduledTaskPrincipal): string {
|
|
206
|
+
try {
|
|
207
|
+
return scheduledTaskPrincipalLabel(author);
|
|
208
|
+
} catch {
|
|
209
|
+
return "Invalid Slack creator metadata";
|
|
203
210
|
}
|
|
204
|
-
if (author.fullName) {
|
|
205
|
-
return author.fullName;
|
|
206
|
-
}
|
|
207
|
-
if (author.userName) {
|
|
208
|
-
return `@${author.userName}`;
|
|
209
|
-
}
|
|
210
|
-
return `Slack User ${author.slackUserId}`;
|
|
211
211
|
}
|
|
212
212
|
|
|
213
213
|
function cadenceLabel(task: ScheduledTask): string {
|
|
@@ -301,7 +301,7 @@ async function buildSchedulerOperationalReport(args: {
|
|
|
301
301
|
id: task.id,
|
|
302
302
|
values: {
|
|
303
303
|
task: task.id,
|
|
304
|
-
author:
|
|
304
|
+
author: operationalAuthorLabel(task.createdBy),
|
|
305
305
|
destination: destinationLabel(task.destination),
|
|
306
306
|
nextRun: formatTimestamp(task.nextRunAtMs),
|
|
307
307
|
cadence: cadenceLabel(task),
|
|
@@ -322,7 +322,7 @@ async function buildSchedulerOperationalReport(args: {
|
|
|
322
322
|
tone: "danger",
|
|
323
323
|
values: {
|
|
324
324
|
task: task.id,
|
|
325
|
-
author:
|
|
325
|
+
author: operationalAuthorLabel(task.createdBy),
|
|
326
326
|
destination: destinationLabel(task.destination),
|
|
327
327
|
updated: formatTimestamp(task.updatedAtMs),
|
|
328
328
|
},
|
|
@@ -346,7 +346,9 @@ async function buildSchedulerOperationalReport(args: {
|
|
|
346
346
|
values: {
|
|
347
347
|
run: run.id,
|
|
348
348
|
task: run.taskId,
|
|
349
|
-
author: task
|
|
349
|
+
author: task
|
|
350
|
+
? operationalAuthorLabel(task.createdBy)
|
|
351
|
+
: "Missing scheduled task",
|
|
350
352
|
scheduledFor: formatTimestamp(run.scheduledForMs),
|
|
351
353
|
status: run.status,
|
|
352
354
|
},
|
package/src/prompt.ts
CHANGED
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
type ScheduledRun,
|
|
4
4
|
type ScheduledTask,
|
|
5
5
|
} from "./types";
|
|
6
|
+
import { sanitizeScheduledTaskPrincipal } from "./identity";
|
|
6
7
|
|
|
7
8
|
function escapeXml(value: string): string {
|
|
8
9
|
return value
|
|
@@ -35,7 +36,7 @@ export function buildScheduledTaskRunPrompt(args: {
|
|
|
35
36
|
}): string {
|
|
36
37
|
const { run, task } = args;
|
|
37
38
|
const destination = task.destination;
|
|
38
|
-
const creator = task.createdBy;
|
|
39
|
+
const creator = sanitizeScheduledTaskPrincipal(task.createdBy);
|
|
39
40
|
const executionActor = task.executionActor ?? SCHEDULED_TASK_SYSTEM_ACTOR;
|
|
40
41
|
if (!task.task.text?.trim()) {
|
|
41
42
|
throw new Error("Scheduled task text is required");
|
package/src/schedule-tools.ts
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
type AgentPluginToolDefinition,
|
|
8
8
|
} from "@sentry/junior-plugin-api";
|
|
9
9
|
import { buildCalendarRecurrence, parseScheduleTimestamp } from "./cadence";
|
|
10
|
+
import { sanitizeScheduledTaskPrincipal } from "./identity";
|
|
10
11
|
import { createSchedulerStore } from "./store";
|
|
11
12
|
import { SCHEDULED_TASK_SYSTEM_ACTOR } from "./types";
|
|
12
13
|
import type {
|
|
@@ -68,12 +69,12 @@ function requireActiveDestination(
|
|
|
68
69
|
function requireRequester(
|
|
69
70
|
context: SchedulerToolContext,
|
|
70
71
|
): ScheduledTaskPrincipal {
|
|
71
|
-
const userId = context.requester?.userId;
|
|
72
|
-
if (!userId) {
|
|
72
|
+
const userId = context.requester?.userId?.trim();
|
|
73
|
+
if (!userId || userId.toLowerCase() === "unknown") {
|
|
73
74
|
throwToolInputError("No active Slack requester context is available.");
|
|
74
75
|
}
|
|
75
76
|
|
|
76
|
-
return {
|
|
77
|
+
return sanitizeScheduledTaskPrincipal({
|
|
77
78
|
slackUserId: userId,
|
|
78
79
|
...(context.requester?.userName
|
|
79
80
|
? { userName: context.requester.userName }
|
|
@@ -81,7 +82,7 @@ function requireRequester(
|
|
|
81
82
|
...(context.requester?.fullName
|
|
82
83
|
? { fullName: context.requester.fullName }
|
|
83
84
|
: {}),
|
|
84
|
-
};
|
|
85
|
+
});
|
|
85
86
|
}
|
|
86
87
|
|
|
87
88
|
function tool<TInput = any>(
|