@zibby/workflow-templates 0.4.2 → 0.7.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/index.js +36 -49
- package/notify-notion/brand/notion-logo.svg +4 -0
- package/notify-notion/icon.png +0 -0
- package/package.json +2 -1
- package/sentry-triage/graph.mjs +36 -37
- package/sentry-triage/icon.png +0 -0
- package/sentry-triage/nodes/classify-node.js +97 -12
- package/sentry-triage/nodes/dispatch-node.js +262 -0
- package/sentry-triage/nodes/fetch-issues-node.js +130 -17
- package/sentry-triage/package.json +2 -1
- package/sentry-triage/state.js +26 -69
- package/sentry-triage/nodes/dispatch-alerts-node.js +0 -191
- package/sentry-triage/nodes/filter-noise-node.js +0 -112
- package/sentry-triage/prompts/classify.md +0 -76
- package/sentry-triage/prompts/fetch-issues.md +0 -66
package/index.js
CHANGED
|
@@ -53,8 +53,7 @@ export const TEMPLATES = {
|
|
|
53
53
|
'Composition: character centered, slight forward lean, dynamic motion lines. Mood: cheerful, playful, fast.',
|
|
54
54
|
'NO text, NO browser UI screenshots, NO outline wireframes.',
|
|
55
55
|
].join('\n'),
|
|
56
|
-
|
|
57
|
-
tags: ['testing', 'playwright', 'e2e', 'browser'],
|
|
56
|
+
tags: ['Testing'],
|
|
58
57
|
capabilities: [
|
|
59
58
|
'Preflight LLM extracts assertions from a plain-English spec',
|
|
60
59
|
'Live Playwright execution with screenshots + DOM at every step',
|
|
@@ -106,8 +105,7 @@ export const TEMPLATES = {
|
|
|
106
105
|
'Composition: brackets centered, subtle drop shadow on the canvas. Mood: high-end, technical, confident — like the cover of a developer tool launch.',
|
|
107
106
|
'NO text, NO outline wireframes, NO flat sticker style — this one is DEEP and 3D-rendered.',
|
|
108
107
|
].join('\n'),
|
|
109
|
-
|
|
110
|
-
tags: ['code-analysis', 'jira', 'github', 'test-generation'],
|
|
108
|
+
tags: ['Code Review', 'Testing'],
|
|
111
109
|
capabilities: [
|
|
112
110
|
'Clone repos + snapshot git baseline so changes are diff-able',
|
|
113
111
|
'LLM gate: skip code-gen when ticket can\'t be implemented as-is',
|
|
@@ -150,8 +148,7 @@ export const TEMPLATES = {
|
|
|
150
148
|
'Mood: friendly, approachable, slightly handmade. Like a children\'s book illustration applied to a developer tool.',
|
|
151
149
|
'NO text, NO photo-realism, NO sleek 3D render — this one is hand-drawn and warm.',
|
|
152
150
|
].join('\n'),
|
|
153
|
-
|
|
154
|
-
tags: ['testing', 'test-generation', 'pull-request', 'review'],
|
|
151
|
+
tags: ['Code Review', 'Testing'],
|
|
155
152
|
capabilities: [
|
|
156
153
|
'Skips ticket analysis — feed it the diff directly',
|
|
157
154
|
'LLM explores the codebase to ground test steps in real components',
|
|
@@ -195,8 +192,7 @@ export const TEMPLATES = {
|
|
|
195
192
|
'Mood: focused, energetic, signal-not-noise — the canonical Slack-flavored notification worker.',
|
|
196
193
|
'NO text, NO letters, NO photo-realism, NO sleek 3D render, NO literal Slack wordmark — the colored pinwheel shape is allowed as the brand reference.',
|
|
197
194
|
].join('\n'),
|
|
198
|
-
|
|
199
|
-
tags: ['slack', 'notification', 'alert', 'child-workflow'],
|
|
195
|
+
tags: ['Notifications', 'child-workflow'],
|
|
200
196
|
capabilities: [
|
|
201
197
|
'Severity-coded Block Kit message (low/medium/high/critical)',
|
|
202
198
|
'Code snippet + action button + caller mentions',
|
|
@@ -239,8 +235,7 @@ export const TEMPLATES = {
|
|
|
239
235
|
'Mood: focused, professional, signal-not-noise.',
|
|
240
236
|
'NO Lark / Feishu logo trademark, NO text, NO photo-realism.',
|
|
241
237
|
].join('\n'),
|
|
242
|
-
|
|
243
|
-
tags: ['lark', 'feishu', 'notification', 'alert', 'child-workflow'],
|
|
238
|
+
tags: ['Notifications', 'child-workflow'],
|
|
244
239
|
capabilities: [
|
|
245
240
|
'Severity-coded Lark Interactive Card',
|
|
246
241
|
'Auto-detects receive_id_type from id prefix (chat_id / open_id / email)',
|
|
@@ -279,16 +274,14 @@ export const TEMPLATES = {
|
|
|
279
274
|
slug: 'notify-notion',
|
|
280
275
|
tagline: 'Reusable Notion archiver — durable record for any workflow.',
|
|
281
276
|
iconPrompt: [
|
|
282
|
-
'
|
|
283
|
-
'
|
|
284
|
-
'
|
|
285
|
-
'
|
|
286
|
-
'
|
|
287
|
-
'
|
|
288
|
-
'NO text, NO logo or trademarked marks, NO photo-realism, NO sleek 3D render, NO literal Notion trademark.',
|
|
277
|
+
'A premium, hi-fi app icon for "Notify Notion" — a workflow node that publishes reports to a Notion database. The real Notion brand mark will be composited on top in a post-process step; this prompt generates the BACKGROUND ONLY, with a clear empty area for the overlay.',
|
|
278
|
+
'Visual style: 3D-rendered hero object floating in space, in the style of Apple Vision Pro icons, Linear\'s changelog hero illustrations, or a Stripe product render. Glossy, dimensional, with subtle reflections and a soft rim-light. Same family as the code-analysis marketplace icon.',
|
|
279
|
+
'Subject: a single 3D-rendered page-document hero object made of glossy frosted glass / brushed silver metal, captured in head-on or near-head-on perspective (NOT three-quarter — keep the page face flat to the camera so the logo overlay sits cleanly). The page surface is COMPLETELY EMPTY — no lines, no text, no icons, no markings of any kind. The right edge curls slightly forward like a fresh page being filed, but the front face stays clean. A tiny cyan-teal glow accent sits in the upper-right corner of the page as a "freshly archived" signal.',
|
|
280
|
+
'Background: a deep midnight-navy gradient (#0F172A at the top, #1E1B4B at the bottom), with a single soft cyan glow behind the page and a few faint star-like specks scattered across the canvas. Square format, 1024×1024.',
|
|
281
|
+
'Composition: page centered, page face takes ~60-70% of the canvas and is empty/blank so a logo overlay can sit naturally on its surface. Subtle drop shadow. Mood: high-end, durable, archival — premium devtool aesthetic.',
|
|
282
|
+
'CRITICAL: the page front face must be COMPLETELY CLEAN AND EMPTY. NO N, NO letters, NO horizontal text lines, NO checkmark, NO icons, NO embossing, NO etching, NO decoration on the page surface. Just blank glossy material. NO Notion logo, NO Notion wordmark, NO trademarked marks. NO text anywhere. NO outline wireframes, NO flat sticker style, NO mascot, NO smiling face, NO cartoon. The page surface MUST be empty so a real logo PNG can be composited on it.',
|
|
289
283
|
].join('\n'),
|
|
290
|
-
|
|
291
|
-
tags: ['notion', 'docs', 'reporting', 'knowledge-base', 'archive'],
|
|
284
|
+
tags: ['Notifications', 'Docs', 'Reports'],
|
|
292
285
|
capabilities: [
|
|
293
286
|
'Create a new page in a Notion database (POST /v1/pages)',
|
|
294
287
|
'Append blocks to an existing page (PATCH /v1/blocks/{pageId}/children)',
|
|
@@ -306,49 +299,45 @@ export const TEMPLATES = {
|
|
|
306
299
|
},
|
|
307
300
|
},
|
|
308
301
|
|
|
309
|
-
// ── sentry-triage:
|
|
302
|
+
// ── sentry-triage: hourly LLM triage → Slack OR Lark ─────────────
|
|
310
303
|
'sentry-triage': {
|
|
311
304
|
name: 'sentry-triage',
|
|
312
305
|
displayName: 'Sentry Triage Bot',
|
|
313
|
-
description: 'Hourly Sentry triage
|
|
306
|
+
description: 'Hourly Sentry triage that classifies new issues with an LLM rubric (CRITICAL/HIGH/MEDIUM/LOW/NOISE) and posts above-threshold alerts to your Slack or Lark — whichever you have connected.',
|
|
314
307
|
path: join(__dirname, 'sentry-triage'),
|
|
315
308
|
defaultSlug: 'sentry-triage',
|
|
316
|
-
deps: { zod: '^3.23.0' },
|
|
309
|
+
deps: { zod: '^3.23.0', '@zibby/skills': '^0.1.25' },
|
|
317
310
|
features: [
|
|
318
|
-
'
|
|
319
|
-
'
|
|
320
|
-
'
|
|
321
|
-
'
|
|
322
|
-
'
|
|
323
|
-
'Configurable severityThreshold (don\'t notify on LOW noise)',
|
|
311
|
+
'3-node LLM graph: fetch_issues → classify → dispatch_alerts',
|
|
312
|
+
'Severity rubric with auditable reasoning per issue',
|
|
313
|
+
'Posts to Slack OR Lark (whichever the project has connected — chat_notify OR-group)',
|
|
314
|
+
'LLM dispatcher batches related issues into one message, dedupes near-duplicates',
|
|
315
|
+
'CRITICAL alerts get optional @-mentions; lower severities don\'t',
|
|
324
316
|
'Cron-friendly: hourly schedule, default sinceMinutes=60',
|
|
325
317
|
],
|
|
326
318
|
marketplace: {
|
|
327
319
|
slug: 'sentry-triage',
|
|
328
|
-
tagline: '
|
|
320
|
+
tagline: 'Triage Sentry, ping your team — hourly.',
|
|
329
321
|
iconPrompt: [
|
|
330
|
-
'Hand-painted storybook illustration in a warm gouache style with soft brushwork and gentle painterly texture, featuring the friendly round lighthouse mascot character with two big smiling eyes and a rosy blush on its white-and-coral-striped tower body, perched on a tiny mint-green island and clutching a small glowing purple SHIELD BADGE in front of its body — the badge is a rounded geometric emblem in Sentry\'s signature deep violet (#362D59 / #7553FF) with a stylized white "S"-mark inside it formed from overlapping rounded parallelogram shapes
|
|
322
|
+
'Hand-painted storybook illustration in a warm gouache style with soft brushwork and gentle painterly texture, featuring the friendly round lighthouse mascot character with two big smiling eyes and a rosy blush on its white-and-coral-striped tower body, perched on a tiny mint-green island and clutching a small glowing purple SHIELD BADGE in front of its body — the badge is a rounded geometric emblem in Sentry\'s signature deep violet (#362D59 / #7553FF) with a stylized white "S"-mark inside it formed from overlapping rounded parallelogram shapes.',
|
|
331
323
|
'The lighthouse lantern emits a soft golden beam that catches one glowing amber alert orb while three faded grey noise specks drift harmlessly past, reinforcing the "filter the signal, calm the noise" idea.',
|
|
332
324
|
'Background is a soft sunrise gradient of pale peach at the top blending through buttercream into a gentle wash of dusty lavender at the base, tying the warm scene to the violet of the badge; a few small fluffy pastel clouds float in for friendliness.',
|
|
333
|
-
'Centered composition with the purple shield badge as the immediate focal point in the lower-center, the lighthouse rising behind and slightly above it, beam angled diagonally; plenty of breathing room so the silhouette reads at 64×64
|
|
334
|
-
'Mood is warm, reassuring, optimistic —
|
|
325
|
+
'Centered composition with the purple shield badge as the immediate focal point in the lower-center, the lighthouse rising behind and slightly above it, beam angled diagonally; plenty of breathing room so the silhouette reads at 64×64.',
|
|
326
|
+
'Mood is warm, reassuring, optimistic — friendly night-watch character, NOT tactical or corporate or alarming.',
|
|
335
327
|
'Soft rounded square 1024×1024 canvas with a subtle paper-grain texture.',
|
|
336
|
-
'NO text, NO letters, NO photo-realism, NO sleek 3D render, NO magnifying glass, NO speech bubbles, NO dark navy or near-black backgrounds, NO bug or insect imagery, NO literal Sentry wordmark.',
|
|
328
|
+
'NO text, NO letters, NO photo-realism, NO sleek 3D render, NO magnifying glass, NO speech bubbles, NO dark navy or near-black backgrounds, NO bug or insect imagery, NO literal Sentry / Slack / Lark wordmark.',
|
|
337
329
|
].join('\n'),
|
|
338
|
-
|
|
339
|
-
tags: ['sentry', 'observability', 'on-call', 'triage', 'alerting'],
|
|
330
|
+
tags: ['On-call', 'Bug Triage', 'Notifications'],
|
|
340
331
|
capabilities: [
|
|
341
332
|
'Hourly scheduled triage of new Sentry issues',
|
|
342
|
-
'Deterministic regex filter drops Script error / ResizeObserver / extension noise',
|
|
343
333
|
'LLM severity classifier with auditable rubric',
|
|
344
|
-
'
|
|
345
|
-
'
|
|
334
|
+
'Posts to Slack or Lark — whichever your project has connected',
|
|
335
|
+
'Batches related issues; CRITICAL-only @-mentions for on-call',
|
|
346
336
|
'Configurable severity threshold per deploy',
|
|
347
337
|
],
|
|
348
338
|
conversationStarters: [
|
|
349
339
|
'Triage all new Sentry issues from the last hour',
|
|
350
|
-
'Notify
|
|
351
|
-
'Run hourly and post a summary to our team Slack',
|
|
340
|
+
'Notify the on-call channel when severity is HIGH or above',
|
|
352
341
|
'Page on-call when a CRITICAL error appears in checkout',
|
|
353
342
|
],
|
|
354
343
|
},
|
|
@@ -375,16 +364,14 @@ export const TEMPLATES = {
|
|
|
375
364
|
slug: 'ai-spend-weekly-digest',
|
|
376
365
|
tagline: 'Track and explain your OpenAI / Anthropic / Cursor spending — every Monday morning, in Lark or Slack.',
|
|
377
366
|
iconPrompt: [
|
|
378
|
-
'
|
|
379
|
-
'
|
|
380
|
-
'
|
|
381
|
-
'
|
|
382
|
-
'
|
|
383
|
-
'
|
|
384
|
-
'NO text, NO letters, NO numbers, NO photo-realism, NO sleek 3D render, NO chart axes or grid lines, NO dark navy or near-black backgrounds, NO literal OpenAI / Anthropic / Cursor logos or wordmarks, NO bar charts.',
|
|
367
|
+
'A premium, hi-fi app icon for "AI Spend Weekly Digest" — a workflow that reports cross-vendor LLM spend to engineering leaders.',
|
|
368
|
+
'Visual style: 3D-rendered hero object floating in space, in the style of Apple Vision Pro icons, Linear\'s changelog hero illustrations, or a Stripe product render. Glossy, dimensional, with subtle reflections and a soft rim-light.',
|
|
369
|
+
'Subject: a 3D-rendered stack of three glossy disc-shaped layered chips — like a tiny ascending bar-chart-of-coins — slightly rotated in three-quarter perspective. Each disc is a different premium accent color: top disc warm gold, middle disc cool platinum-silver, bottom disc deep iridescent purple. A single thin trending-up glowing line traces from lower-left to upper-right behind the stack, rendered as a soft neon ribbon (#7553FF violet glow), suggesting cost-over-time at a glance without literal axes.',
|
|
370
|
+
'Background: a deep midnight-navy gradient (#0F172A at the top, #1E1B4B at the bottom), with a single soft violet glow behind the stack and a few faint star-like specks scattered across the canvas. Square format, 1024×1024.',
|
|
371
|
+
'Composition: stack centered, subtle drop shadow on the canvas. Mood: high-end, executive, confident — like the cover image of a finance-ops product launch.',
|
|
372
|
+
'NO text, NO numbers, NO axes or grid lines, NO outline wireframes, NO flat sticker style, NO mascot, NO piggy bank, NO cartoon faces, NO trademarked OpenAI / Anthropic / Cursor logos — this one is DEEP and 3D-rendered.',
|
|
385
373
|
].join('\n'),
|
|
386
|
-
|
|
387
|
-
tags: ['cost', 'finance', 'reporting', 'openai', 'anthropic', 'cursor', 'digest', 'weekly'],
|
|
374
|
+
tags: ['AI Spend', 'Cost Tracking', 'Reports'],
|
|
388
375
|
capabilities: [
|
|
389
376
|
'Pulls org-wide cost+usage from OpenAI, Anthropic, and Cursor admin APIs in parallel',
|
|
390
377
|
'Joins customer attribution from provider-native project / workspace / member metadata',
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
<svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path d="M6.017 4.313l55.333 -4.087c6.797 -0.583 8.543 -0.19 12.817 2.917l17.663 12.443c2.913 2.14 3.883 2.723 3.883 5.053v68.243c0 4.277 -1.553 6.807 -6.99 7.193L24.467 99.967c-4.08 0.193 -6.023 -0.39 -8.16 -3.113L3.3 79.94c-2.333 -3.113 -3.3 -5.443 -3.3 -8.167V11.113c0 -3.497 1.553 -6.413 6.017 -6.8z" fill="#fff"/>
|
|
3
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M61.35 0.227l-55.333 4.087C1.553 4.7 0 7.617 0 11.113v60.66c0 2.723 0.967 5.053 3.3 8.167l13.007 16.913c2.137 2.723 4.08 3.307 8.16 3.113l64.257 -3.89c5.433 -0.387 6.99 -2.917 6.99 -7.193V20.64c0 -2.21 -0.873 -2.847 -3.443 -4.733L74.167 3.143c-4.273 -3.107 -6.02 -3.5 -12.817 -2.917zM25.92 19.523c-5.247 0.353 -6.437 0.433 -9.417 -1.99L8.927 11.507c-0.77 -0.78 -0.383 -1.753 1.557 -1.947l53.193 -3.887c4.467 -0.39 6.793 1.167 8.54 2.527l9.123 6.61c0.39 0.197 1.36 1.36 0.193 1.36l-54.933 3.307 -0.68 0.047zM19.803 88.3V30.367c0 -2.53 0.777 -3.697 3.103 -3.893L86 22.78c2.14 -0.193 3.107 1.167 3.107 3.693v57.547c0 2.53 -0.39 4.67 -3.883 4.863l-60.377 3.5c-3.493 0.193 -5.043 -0.97 -5.043 -4.083zm59.6 -54.827c0.387 1.75 0 3.5 -1.75 3.7l-2.91 0.577v42.773c-2.527 1.36 -4.853 2.137 -6.797 2.137 -3.107 0 -3.883 -0.973 -6.21 -3.887l-19.03 -29.94v28.967l6.02 1.363s0 3.5 -4.857 3.5l-13.39 0.777c-0.39 -0.78 0 -2.723 1.357 -3.11l3.497 -0.97v-38.3L30.48 40.667c-0.39 -1.75 0.58 -4.277 3.3 -4.473l14.367 -0.967 19.8 30.327v-26.83l-5.047 -0.58c-0.39 -2.143 1.163 -3.7 3.103 -3.89l13.4 -0.78z" fill="#000"/>
|
|
4
|
+
</svg>
|
package/notify-notion/icon.png
CHANGED
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zibby/workflow-templates",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.1",
|
|
4
4
|
"description": "Built-in workflow templates for Zibby — browser-test-automation, code-analysis, generate-test-cases, notify-slack, notify-lark, notify-notion, sentry-triage.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -72,6 +72,7 @@
|
|
|
72
72
|
"dependencies": {
|
|
73
73
|
"@anthropic-ai/sdk": "^0.88.0",
|
|
74
74
|
"@zibby/agent-workflow": "^0.4.2",
|
|
75
|
+
"@zibby/skills": "^0.1.27",
|
|
75
76
|
"axios": "^1.15.0",
|
|
76
77
|
"handlebars": "^4.7.9",
|
|
77
78
|
"zod": "^3.23.0 || ^4.0.0"
|
package/sentry-triage/graph.mjs
CHANGED
|
@@ -1,51 +1,41 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* sentry-triage — parent workflow.
|
|
2
|
+
* sentry-triage — parent workflow. Hourly Sentry issue triage.
|
|
3
3
|
*
|
|
4
|
-
* Pipeline:
|
|
4
|
+
* Pipeline (3 LLM nodes, end-to-end agent-driven):
|
|
5
5
|
*
|
|
6
|
-
* fetch_issues
|
|
6
|
+
* fetch_issues (LLM + SKILLS.SENTRY) → list recent unresolved issues
|
|
7
7
|
* ↓
|
|
8
|
-
*
|
|
8
|
+
* classify (LLM, no tools) → label NOISE/LOW/MEDIUM/HIGH/CRITICAL
|
|
9
9
|
* ↓
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* dispatch_alerts (custom execute — sub-graphs to notify-slack OR notify-lark
|
|
13
|
-
* per issue at or above severityThreshold)
|
|
10
|
+
* dispatch_alerts (LLM + SKILLS.CHAT_NOTIFY) → batch + post to Slack OR Lark for
|
|
11
|
+
* issues ≥ SEVERITY_THRESHOLD
|
|
14
12
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* on
|
|
18
|
-
*
|
|
19
|
-
*
|
|
13
|
+
* Why all three nodes are LLM (not deterministic for-loops):
|
|
14
|
+
* - At hourly cadence with ≤20 issues/run, LLM cost is $1.50–$32/mo
|
|
15
|
+
* depending on model. Trivial relative to Sentry / Slack subscriptions.
|
|
16
|
+
* - LLM dispatch can BATCH related issues (5 errors in /checkout/ →
|
|
17
|
+
* 1 consolidated message) and DE-DUP near-duplicates. A
|
|
18
|
+
* deterministic for-loop can't.
|
|
19
|
+
* - outputSchema enforcement guarantees every above-threshold issue
|
|
20
|
+
* either gets a "sent" record or an explicit "failed/skipped" —
|
|
21
|
+
* no silent drops.
|
|
20
22
|
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* 100ms vs 1600s — the architecture is what makes this template
|
|
25
|
-
* cheap enough to run hourly.
|
|
23
|
+
* Customize prompts: each node's prompt lives in its own module under
|
|
24
|
+
* nodes/. Override per-deploy by editing the file or by passing a
|
|
25
|
+
* custom prompt string via inputSchema (planned).
|
|
26
26
|
*/
|
|
27
27
|
|
|
28
|
-
import { readFileSync, existsSync } from 'fs';
|
|
29
|
-
import { join, dirname } from 'path';
|
|
30
|
-
import { fileURLToPath } from 'url';
|
|
31
28
|
import { WorkflowAgent, WorkflowGraph } from '@zibby/core';
|
|
32
29
|
|
|
33
30
|
import { fetchIssuesNode } from './nodes/fetch-issues-node.js';
|
|
34
|
-
import { filterNoiseNode } from './nodes/filter-noise-node.js';
|
|
35
31
|
import { classifyNode } from './nodes/classify-node.js';
|
|
36
|
-
import {
|
|
32
|
+
import { dispatchNode } from './nodes/dispatch-node.js';
|
|
37
33
|
|
|
38
34
|
import {
|
|
39
35
|
sentryTriageInputSchema,
|
|
40
36
|
sentryTriageContextSchema,
|
|
41
37
|
} from './state.js';
|
|
42
38
|
|
|
43
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
44
|
-
function loadPrompt(filename) {
|
|
45
|
-
const path = join(__dirname, 'prompts', filename);
|
|
46
|
-
return existsSync(path) ? readFileSync(path, 'utf-8') : '';
|
|
47
|
-
}
|
|
48
|
-
|
|
49
39
|
export class SentryTriageAgent extends WorkflowAgent {
|
|
50
40
|
buildGraph() {
|
|
51
41
|
const graph = new WorkflowGraph();
|
|
@@ -53,14 +43,22 @@ export class SentryTriageAgent extends WorkflowAgent {
|
|
|
53
43
|
.setInputSchema(sentryTriageInputSchema)
|
|
54
44
|
.setContextSchema(sentryTriageContextSchema);
|
|
55
45
|
|
|
56
|
-
graph.addNode('fetch_issues',
|
|
57
|
-
graph.addNode('
|
|
58
|
-
graph.addNode('
|
|
59
|
-
graph.addNode('dispatch_alerts', dispatchAlertsNode);
|
|
46
|
+
graph.addNode('fetch_issues', fetchIssuesNode);
|
|
47
|
+
graph.addNode('classify', classifyNode);
|
|
48
|
+
graph.addNode('dispatch_alerts', dispatchNode);
|
|
60
49
|
|
|
61
50
|
graph.setEntryPoint('fetch_issues');
|
|
62
|
-
|
|
63
|
-
|
|
51
|
+
// Short-circuit when Sentry returned nothing for this window. The
|
|
52
|
+
// empty-list case is the common idle path (steady-state apps don't
|
|
53
|
+
// throw new errors every hour), and running classify + dispatch on
|
|
54
|
+
// an empty input wastes two Claude calls per run — at hourly cadence
|
|
55
|
+
// across many tenants that adds up. Cleaner to route directly to END
|
|
56
|
+
// at the graph level than to short-circuit inside each downstream
|
|
57
|
+
// node's prompt (which still spends a model round-trip).
|
|
58
|
+
graph.addConditionalEdges('fetch_issues', (state) => {
|
|
59
|
+
const issues = state?.fetch_issues?.issues || [];
|
|
60
|
+
return issues.length === 0 ? 'END' : 'classify';
|
|
61
|
+
});
|
|
64
62
|
graph.addEdge('classify', 'dispatch_alerts');
|
|
65
63
|
graph.addEdge('dispatch_alerts', 'END');
|
|
66
64
|
|
|
@@ -69,10 +67,11 @@ export class SentryTriageAgent extends WorkflowAgent {
|
|
|
69
67
|
|
|
70
68
|
async onComplete(result) {
|
|
71
69
|
const s = result?.state?.dispatch_alerts?.summary || {};
|
|
72
|
-
const
|
|
70
|
+
const classifications = result?.state?.classify?.classifications || [];
|
|
71
|
+
const noise = classifications.filter((c) => c.severity === 'NOISE').length;
|
|
73
72
|
const fetched = result?.state?.fetch_issues?.issues?.length || 0;
|
|
74
73
|
console.log(
|
|
75
|
-
`[sentry-triage] complete — fetched=${fetched}, noise=${
|
|
74
|
+
`[sentry-triage] complete — fetched=${fetched}, noise=${noise}, ` +
|
|
76
75
|
`sent=${s.sent || 0}, skipped=${s.skipped || 0}, failed=${s.failed || 0}`,
|
|
77
76
|
);
|
|
78
77
|
}
|
package/sentry-triage/icon.png
CHANGED
|
Binary file
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* classify node — LLM-driven severity classification.
|
|
3
3
|
*
|
|
4
|
-
* No tools —
|
|
5
|
-
* (
|
|
6
|
-
* NOISE
|
|
4
|
+
* No tools — the LLM sees the rubric AND the concrete issues array
|
|
5
|
+
* (inlined as JSON at render time) and emits one classification record
|
|
6
|
+
* per issue. NOISE detection is part of the rubric itself; no separate
|
|
7
|
+
* pre-filter step.
|
|
7
8
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* guarantees the emitted shape; bad models get a retry with the
|
|
11
|
-
* outputSchema in the prompt.
|
|
9
|
+
* Severity threshold (skip-floor) lives on dispatch, NOT here — this
|
|
10
|
+
* node always classifies every issue. dispatch decides whether to send.
|
|
12
11
|
*/
|
|
13
12
|
|
|
14
13
|
import { z } from '@zibby/core';
|
|
@@ -27,12 +26,98 @@ const ClassifyOutputSchema = z.object({
|
|
|
27
26
|
classifications: z.array(ClassificationShape),
|
|
28
27
|
});
|
|
29
28
|
|
|
29
|
+
const RUBRIC = `You are the classify node of a Sentry triage workflow. Classify each Sentry issue into a severity bucket and explain WHY.
|
|
30
|
+
|
|
31
|
+
The list of issues is appended below as a JSON array. Treat it as authoritative — do NOT call any tool, you have everything you need.
|
|
32
|
+
|
|
33
|
+
# Severity rubric (apply IN ORDER, stop at first match)
|
|
34
|
+
|
|
35
|
+
1. **NOISE** — these never warrant a human ping. Match if ANY:
|
|
36
|
+
- Title is "Script error." (cross-origin opaque error, no stack, useless)
|
|
37
|
+
- Title contains "Non-Error promise rejection captured"
|
|
38
|
+
- Title contains "ResizeObserver loop limit exceeded" or "ResizeObserver loop completed"
|
|
39
|
+
- culprit or metadata.filename URL starts with chrome-extension://, safari-extension://, moz-extension://, webkit-masked-url:// (user's extension crashed, not your code)
|
|
40
|
+
- Title or culprit mentions analytics SDKs: gtag, fbq, _paq, dataLayer, googletagmanager, piwik
|
|
41
|
+
- Title is "AbortError", contains "cancelled", or "Load failed" AND userCount < 3 (user navigated away)
|
|
42
|
+
- Title says "Test ", "Demo ", "[STAGING]" (wrong environment leakage)
|
|
43
|
+
- Stack trace has zero inApp:true frames (3rd-party only — not your code)
|
|
44
|
+
- User-agent in tags indicates a bot (Googlebot, AhrefsBot, etc.)
|
|
45
|
+
|
|
46
|
+
2. **CRITICAL** if ANY of:
|
|
47
|
+
- userCount >= 20 (≥ 20 users affected — real prod impact)
|
|
48
|
+
- culprit or metadata.filename matches /payment|billing|checkout|auth|login|signup|session/i (security/revenue path)
|
|
49
|
+
- level === "fatal" and count >= 10
|
|
50
|
+
- count >= 100 AND firstSeen-to-lastSeen window is < 30 min (active spike)
|
|
51
|
+
|
|
52
|
+
3. **HIGH** if ANY of:
|
|
53
|
+
- userCount >= 5 AND count >= 50
|
|
54
|
+
- level === "fatal" (any count)
|
|
55
|
+
- level === "error" AND userCount >= 3 AND count >= 20
|
|
56
|
+
- Errors in non-critical-but-important paths: settings, profile, search, dashboard, admin
|
|
57
|
+
|
|
58
|
+
4. **MEDIUM** if ANY of:
|
|
59
|
+
- count >= 20 AND userCount >= 2
|
|
60
|
+
- count >= 50 regardless of userCount
|
|
61
|
+
- level === "error" AND count >= 10
|
|
62
|
+
|
|
63
|
+
5. **LOW** — anything else (count < 20 AND userCount < 5, or level === "warning" | "info")
|
|
64
|
+
|
|
65
|
+
# Recommended action per severity
|
|
66
|
+
|
|
67
|
+
- CRITICAL → page_oncall (always notify, always mention rotation)
|
|
68
|
+
- HIGH → notify_channel (notify, no @ unless deploy author known)
|
|
69
|
+
- MEDIUM → notify_channel
|
|
70
|
+
- LOW → digest_only (rolled into a daily summary, not real-time)
|
|
71
|
+
- NOISE → ignore
|
|
72
|
+
|
|
73
|
+
# Output shape
|
|
74
|
+
|
|
75
|
+
For EACH issue in the JSON array below, emit ONE record:
|
|
76
|
+
|
|
77
|
+
\`\`\`json
|
|
78
|
+
{
|
|
79
|
+
"classifications": [
|
|
80
|
+
{
|
|
81
|
+
"issueId": "1234567890",
|
|
82
|
+
"severity": "CRITICAL",
|
|
83
|
+
"confidence": 0.95,
|
|
84
|
+
"reasoning": "12 users affected, culprit handleCheckout (payment path). Likely regression after recent deploy.",
|
|
85
|
+
"suggestedAction": "page_oncall",
|
|
86
|
+
"ruleMatched": "rule 2 (culprit matches /checkout/)"
|
|
87
|
+
}
|
|
88
|
+
]
|
|
89
|
+
}
|
|
90
|
+
\`\`\`
|
|
91
|
+
|
|
92
|
+
# Rules
|
|
93
|
+
|
|
94
|
+
- confidence reflects how cleanly the issue matched. CRITICAL in /payment/ with userCount=50 → 0.95. Borderline → 0.6.
|
|
95
|
+
- reasoning is ONE sentence written for an on-call engineer. Lead with the impact metric.
|
|
96
|
+
- ruleMatched is which numbered rule fired. Helps operators tune the rubric over time.
|
|
97
|
+
- Be consistent: same issue twice should always get the same severity.
|
|
98
|
+
- Temperature 0. Classification, not creative writing.
|
|
99
|
+
|
|
100
|
+
# Do NOT
|
|
101
|
+
|
|
102
|
+
- Classify more issues than appear in the array below.
|
|
103
|
+
- Skip issues — every issue in the array must appear in the output (NOISE included).
|
|
104
|
+
- Use any severity outside NOISE|LOW|MEDIUM|HIGH|CRITICAL.
|
|
105
|
+
- Call any tools.`;
|
|
106
|
+
|
|
107
|
+
const CLASSIFY_PROMPT = (state = {}) => {
|
|
108
|
+
const issues = state?.fetch_issues?.issues || [];
|
|
109
|
+
return `${RUBRIC}
|
|
110
|
+
|
|
111
|
+
## Issues to classify
|
|
112
|
+
|
|
113
|
+
\`\`\`json
|
|
114
|
+
${JSON.stringify(issues, null, 2)}
|
|
115
|
+
\`\`\`
|
|
116
|
+
`;
|
|
117
|
+
};
|
|
118
|
+
|
|
30
119
|
export const classifyNode = {
|
|
31
120
|
name: 'classify',
|
|
32
|
-
// NO skills — this is a pure reasoning step; the LLM has all data
|
|
33
|
-
// it needs in state.filter_noise.kept. Adding skills would let the
|
|
34
|
-
// LLM call Sentry tools for "more context", which we don't want
|
|
35
|
-
// (rubric is supposed to be deterministic).
|
|
36
121
|
outputSchema: ClassifyOutputSchema,
|
|
37
|
-
|
|
122
|
+
prompt: CLASSIFY_PROMPT,
|
|
38
123
|
};
|