@zibby/workflow-templates 0.3.0 → 0.4.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.
@@ -0,0 +1,134 @@
1
+ /**
2
+ * sentry-triage — three-schema state model.
3
+ *
4
+ * Triage flow:
5
+ * 1. fetch_issues (LLM + sentry skill) — pull recent unresolved
6
+ * issues from Sentry
7
+ * 2. filter_noise (custom execute) — drop known-noise patterns
8
+ * (browser-extension URLs, ResizeObserver loops, etc.) WITHOUT
9
+ * paying an LLM call per issue
10
+ * 3. classify (LLM) — classify the survivors as
11
+ * NOISE / LOW / MEDIUM / HIGH / CRITICAL with reasoning
12
+ * 4. dispatch_alerts (custom execute) — sub-graph dispatch to
13
+ * notify-slack OR notify-lark for issues above severityThreshold
14
+ *
15
+ * Why sub-graph dispatch (not built-in notify here):
16
+ * - notify-slack / notify-lark are reusable across MANY parent
17
+ * workflows (sentry-autofix, sentry-incident, cron-summary, etc.).
18
+ * - Adding a new channel (Discord, Teams) means adding a new child
19
+ * workflow — sentry-triage code never changes.
20
+ * - In-process sub-graph dispatch is ~5ms overhead in cloud, so the
21
+ * architectural cleanliness costs nothing.
22
+ */
23
+
24
+ import { z } from 'zod';
25
+
26
+ export const SEVERITY_LEVELS = /** @type {const} */ (['NOISE', 'LOW', 'MEDIUM', 'HIGH', 'CRITICAL']);
27
+
28
+ export const sentryTriageInputSchema = z.object({
29
+ // ── Sentry source ────────────────────────────────────────────────
30
+ organizationSlug: z.string().min(1)
31
+ .describe('Sentry organization slug (the URL segment after sentry.io/organizations/).'),
32
+ projectSlug: z.string().min(1)
33
+ .describe('Sentry project slug — limits triage to a single project.'),
34
+ environment: z.string().default('production')
35
+ .describe('Sentry environment tag to filter by (defaults to production).'),
36
+ sinceMinutes: z.number().int().min(5).max(1440).default(60)
37
+ .describe('Look back this many minutes for newly-firstSeen issues. Hourly cron → 60.'),
38
+
39
+ // ── Triage thresholds ────────────────────────────────────────────
40
+ severityThreshold: z.enum(SEVERITY_LEVELS).default('MEDIUM')
41
+ .describe('Only dispatch alerts for issues at or above this severity. Drop the rest.'),
42
+ maxIssues: z.number().int().min(1).max(100).default(20)
43
+ .describe('Cap issues processed per run. Protects against an unexpected error storm.'),
44
+
45
+ // ── Where to send alerts ────────────────────────────────────────
46
+ notifyWorker: z.enum(['notify-slack', 'notify-lark']).default('notify-slack')
47
+ .describe(
48
+ 'Which child workflow to dispatch alerts to. Both must be deployed in the same project ' +
49
+ 'as this triage workflow. Pick whichever messaging platform your team uses.',
50
+ ),
51
+
52
+ // For notify-slack
53
+ slackChannel: z.string().min(1).max(120).optional()
54
+ .describe('Slack channel id (C012345) or #name. Required when notifyWorker=notify-slack.'),
55
+ slackMentions: z.array(z.string().max(60)).max(10).optional()
56
+ .describe('Mentions to append on CRITICAL alerts only, e.g. ["<!subteam^S0ONCALL>"].'),
57
+
58
+ // For notify-lark
59
+ larkReceiveId: z.string().min(1).max(120).optional()
60
+ .describe('Lark chat id (oc_…), open id (ou_…), or email. Required when notifyWorker=notify-lark.'),
61
+ larkMentions: z.array(z.string().max(200)).max(10).optional()
62
+ .describe('Lark @-mention strings for CRITICAL alerts.'),
63
+
64
+ model: z.string().default('auto')
65
+ .describe('LLM model override for classify_issues. Default auto-selects.'),
66
+ });
67
+
68
+ export const sentryTriageContextSchema = z.object({
69
+ // Runner-injected
70
+ workspace: z.string().optional()
71
+ .describe('Workspace path — set by runner. Triage doesn\'t need it but graph.run requires it.'),
72
+
73
+ // Node outputs (mid-graph, keyed by node name)
74
+ fetch_issues: z.object({
75
+ issues: z.array(z.object({
76
+ id: z.string(),
77
+ shortId: z.string().optional(),
78
+ title: z.string(),
79
+ culprit: z.string().optional(),
80
+ level: z.string().optional(),
81
+ status: z.string().optional(),
82
+ count: z.union([z.string(), z.number()]).optional(),
83
+ userCount: z.number().optional(),
84
+ firstSeen: z.string().optional(),
85
+ lastSeen: z.string().optional(),
86
+ permalink: z.string().optional(),
87
+ metadata: z.object({
88
+ type: z.string().optional(),
89
+ value: z.string().optional(),
90
+ filename: z.string().optional(),
91
+ }).optional(),
92
+ })),
93
+ fetchedAt: z.string().optional(),
94
+ }).optional(),
95
+
96
+ filter_noise: z.object({
97
+ kept: z.array(z.any()),
98
+ dropped: z.array(z.object({
99
+ id: z.string(),
100
+ reason: z.string(),
101
+ })),
102
+ }).optional(),
103
+
104
+ classify: z.object({
105
+ classifications: z.array(z.object({
106
+ issueId: z.string(),
107
+ severity: z.enum(SEVERITY_LEVELS),
108
+ confidence: z.number().min(0).max(1).optional(),
109
+ reasoning: z.string().optional(),
110
+ suggestedAction: z.string().optional(),
111
+ ruleMatched: z.string().optional(),
112
+ })),
113
+ }).optional(),
114
+
115
+ dispatch_alerts: z.object({
116
+ dispatched: z.array(z.object({
117
+ issueId: z.string(),
118
+ severity: z.enum(SEVERITY_LEVELS),
119
+ status: z.enum(['sent', 'skipped', 'failed']),
120
+ detail: z.string().optional(),
121
+ messageTs: z.string().optional(),
122
+ messageId: z.string().optional(),
123
+ })),
124
+ summary: z.object({
125
+ total: z.number(),
126
+ sent: z.number(),
127
+ skipped: z.number(),
128
+ failed: z.number(),
129
+ }),
130
+ }).optional(),
131
+ });
132
+
133
+ export const sentryTriageStateSchema =
134
+ sentryTriageInputSchema.merge(sentryTriageContextSchema);