Sentry Triage 插件实战指南:在 GitHub Copilot 画布中扫描、分级并派发线上 Sentry 错误
【免费下载链接】awesome-copilotCommunity-contributed instructions, agents, skills, and configurations to help you make the most of GitHub Copilot.项目地址: https://gitcode.com/GitHub_Trending/aw/awesome-copilot
本文围绕 Awesome Copilot 仓库中的 Sentry Triage 插件,完整讲解它的安装方式、前置条件、画布使用流程、Agent 结构化交接工具,以及扫描分级与安全校验的底层实现原理。读完本文,你将掌握如何把 Sentry 实时错误流接入 Copilot 画布,一键按紧急程度分组、用通俗语言重写错误标题、识别已跟踪问题,并把选中的问题交接给 Copilot 创建跟踪 Issue 或起草修复 PR。
插件概览:从 "Sentry 报错" 到 "有人跟进" 的一站式画布
Sentry Triage 是一个面向 on-call(值班)场景的 GitHub Copilot 画布扩展,定位见 插件 README 和 扩展 README:
- 实时扫描:直接扫描某个 Sentry 组织的实时 Issue(可选收窄到单个项目),并按"为什么需要关注"分组展示;
- 通俗标题:一键把原始 Sentry 错误标题改写为一句面向用户的可读摘要;
- 跟踪识别:已经通过本插件归档(带
sentry-triage标签)的问题,在后续扫描中会被自动识别为"已跟踪"; - Create issue(📝 创建 Issue):只归档或复用跟踪 Issue,不写代码、不开分支、不建 PR;
- Fix with Copilot(🔧 用 Copilot 修复):归档/复用跟踪 Issue,并额外拉起一个专门会话起草修复 PR;
- 设置面板:在创建任何内容前,确认仓库、本地检出、基线分支和 Issue 跟踪器的目标。
插件元数据在 plugin.json 中定义:版本 1.1.0,MIT 许可,关键字覆盖canvas、copilot-canvas、error-triage、incident-response、on-call、sentry,其extensions声明指向./extensions/sentry-triage(即本仓库的 extensions/sentry-triage 目录)。
安装:一条命令装插件
通过 Awesome Copilot 的插件源安装(见 插件 README):
copilot plugin install sentry-triage@awesome-copilot该插件是 Awesome Copilot 声明扩展名称与版本。
直接部署源码(用户级 / 项目级)
如果你希望不经过插件市场,直接把扩展源码放到对应作用域(见 扩展 README):
- 用户级:把整个
extensions/sentry-triage目录放到~/.copilot/extensions/sentry-triage/; - 项目级:放到仓库的
.github/extensions/sentry-triage/。
运行时依赖与自动安装
画布在运行时依赖sentrynpm 包(库模式使用),该依赖不随扩展源码打包。缺失时画布仍能打开,并显示一个 setup gate(设置门):
- 点击Install dependencies按钮,扩展会在自己实际安装的目录内执行
npm install(路径无需用户猜测); - 依赖装好后若未登录,点击Sign in with Sentry按钮,浏览器自动打开授权并回跳——全程无需终端操作;
- 两项都完成后设置门自动清除,无需重载或重新调用 Copilot。
对应的手动等价步骤为:
# 用户级安装 cd ~/.copilot/extensions/sentry-triage # 或项目级:从仓库根目录 cd .github/extensions/sentry-triage # 或已通过 copilot plugin install 安装时,进入已安装插件内的扩展目录 cd <installed-plugin-path>/com.github.copilot/extensions/sentry-triage npm install npx sentry auth login之后在 GitHub Copilot App 中重载扩展,再打开sentry-triage画布。依赖声明在 package.json 中:@github/copilot-sdk1.0.11、sentry0.42.2,Node.js 引擎要求>=22,ESM 入口为extension.mjs。
前置条件与 Sentry 连接
使用前需要满足以下条件(见 扩展 README):
- Node.js 22 或更新版本;
- GitHub Copilot App 的画布 / UI-extensions 实验特性已启用;
- Sentry 登录:画布通过 Sentry CLI 的库模式读取 Issue,无需配置 MCP Server。
凭据方式:一键登录或环境变量 Token
交互式环境:在设置门中点Sign in with Sentry完成 OAuth 登录,无需终端;
非交互环境:导出环境变量
export SENTRY_AUTH_TOKEN=<your-token>
登录或 Token 需要以下权限范围:event:read、org:read、project:read。画布每次打开都会校验登录状态,未登录时显示设置门并给出确切原因。
从源码看,连接探测由 preflight.mjs 负责:它通过 Sentry CLI SDK 发起一次真实的auth.whoami()调用,成功即视为已连接;抛错则根据错误分类决定设置门展示的引导类型:
SENTRY_PACKAGE_MISSING:提示安装依赖(专属 gate 分支);SENTRY_ENV_TOKEN_ACTIVE:环境变量 Token 生效时,登录按钮无意义,直接快速失败;- 未认证 / 401 / 403:显示"Sentry isn't connected yet."或"凭证过期或无效,请重新登录";
- 网络瞬时故障(
econnreset、timeout、enotfound等):标记为transient,checkConnections()会以[400, 700, 1100]毫秒的退避间隔最多重试 3 次;认证类失败绝不重试。
值得注意的是,GitHub 连接故意不做预检:Fix with Copilot的交接由 Agent(session.sendAndWait)执行,Agent 自己有 GitHub 访问能力,因此画布只需关心 Sentry 一侧的连通性。
在正确的仓库作用域打开画布
打开画布时,请从目标仓库作用域的 Copilot 会话进入,这样画布能自动检测仓库、本地检出和基线分支。如果你在错误的仓库打开了画布,请先打开Settings确认目标,再点击Create issue或Fix with Copilot。
源码层面,仓库/分支的自动探测逻辑在 extension.mjs:
- 启动时读取环境变量
GITHUB_REPOSITORY、GITHUB_BASE_REF、GITHUB_SERVER_URL,或从扩展进程工作目录的 git remote 解析; - 由于扩展进程通常以
~/.copilot为 cwd(并非驱动中的仓库),seedDefaultsFromSession()会通过session.rpc.metadata.snapshot()获取驱动会话的真实工作目录(最多重试 6 次、间隔 150ms),并从中重新推导 repo、host 与 base branch; - 显式的
GITHUB_*环境变量始终优先,不会被会话探测覆盖。
基线分支的解析细节
baseBranchFromPath()使用git symbolic-ref refs/remotes/origin/HEAD读取远端默认分支,并且只剥离固定前缀refs/remotes/origin/——如果误用split('/').pop(),分支名release/2026会被错误截成2026,从而指向不存在的基线。
使用画布:四步完成一次值班分诊
画布使用流程(见 扩展 README):
- 从你要工作的仓库打开画布;
- 确认 Sentryorganization(从登录自动检测;多组织账号在下拉框中选择)。在组织未设置前,Scan issues保持禁用;
- 可选:用自动补全字段收窄到单个project,留空则扫描整个组织;
- 查看按关注原因分组的问题列表。切换Plain-English titles开关,把原始 Sentry 错误换成可读摘要;
- 选择问题,在工具栏中选择📝 Create issue或🔧 Fix with Copilot。
扫描窗口
画布默认扫描最近 24 小时窗口,也支持更宽的时间窗。scanIssues(org, project, period)(见 sentry.mjs)会严格按用户选择的窗口查询,而不会悄悄放宽。
分组逻辑:按"为什么需要关注"分级
这是插件最核心的价值:把成百上千条原始错误,收敛成三个按优先级排序的分组。纯函数categorize()(见 sentry.mjs)是唯一的分类器:
| 分组 | 判定条件 | 展示原因 |
|---|---|---|
| 🔄 Regressions(回归) | Sentry 自己标记的is:regressed | "Sentry flagged this as regressed" |
| 📈 Escalating(持续恶化) | Sentry 标记的is:escalating,或未被标记但属于高龄高影响(ageHours >= 24且users >= 40或events >= 100) | "Sentry flagged this as escalating" 或 "Active within the ..." |
| 🆕 New Critical(新增关键) | ageHours < 24且users >= 2(严格小于边界,边界归属上一条) | "First seen ..." |
分级阈值以命名常量明确定义:HIGH_IMPACT_USERS = 40、HIGH_IMPACT_EVENTS = 100、NEW_MAX_AGE_HOURS = 24、NEW_MIN_USERS = 2。每个分组内按users降序、再按events降序排列,保证影响最大的问题在最前面。
扫描链路:代码拥有数据,模型只做润色
scanIssues()的完整链路是:
- 主查询
is:unresolved,按最近出现排序,分页上限PRIMARY_LIMIT = 1000(单一 API 页只有 100 条,若用默认值会让高影响的老问题掉出面板); - 并行补查
is:unresolved is:regressed与is:unresolved is:escalating(上限PRIORITY_LIMIT = 1000),并做一次 250ms 的重试;400(Sentry 拒绝该过滤)或持续传输失败会被记录到warnings,而不是静默当作"无回归"; - 按 key 合并去重(主列表优先),再交给
categorize(); - 当所选窗口为空且窗口不是 90d 时,额外做一次 90d 宽查询,统计窗口外还有多少未解决 Issue(
older/olderCapped),用于空状态引导用户放宽窗口。
设计要点是:哪些问题存在、计数、如何分类,全部由代码从 Sentry CLI SDK 实时返回驱动,模型从未被询问"有哪些问题",因此画布不可能展示记忆的、过期的数据。SDK 返回的是解析后的 JSON 对象(不是 markdown),所以 sentry.mjs 中的适配器(mapIssue、mapOrgs、mapProjects)与分类器都是纯函数,便于离线单测;只有listOrgs/listProjects/findProject/scanIssues通过 sentryClient.mjs 访问网络。
组织与项目枚举的工程细节
- 组织枚举
listOrgs()永不抛错,认证失败也返回空数组,保证设置表单总能渲染; - 项目枚举
listProjects()以 100 条/页分页(最多 20 页),并带 8 秒墙钟预算;利用runSerial把整段分页遍历作为一次原子 SDK 操作(Sentry CLI 的next游标依赖全局 per-command 状态,页与页之间不能交错);onPage回调支持把前 ~100 个项目约 1 秒内流式推给 UI 自动补全; - 超大组织(如数千项目)会在预算内返回已收集的部分,避免自动补全一直转圈。
通俗标题(Plain-English titles)与跟踪识别
通俗标题:混合式重写
扫描结果先以原始标题立即发布到画布(不等模型,避免用户盯着加载动画),随后模型在后台把每条标题改写为一句 15 词以内的用户可见症状描述。这一逻辑集中在enrichPlainEnglish():
- 单轮模型改写上限
ENRICH_MAX_ISSUES = 60条(按优先级排序截取),其余卡片使用从标题派生的兜底摘要——一个坏模型回合永远不会清空或误报某张卡片; - 提示词明确禁止使用类名、异常类型名、堆栈术语、文件路径或内部符号;
- 模型不打印摘要,而是通过结构化工具
submit_issue_summaries回传(按请求 token 存储到summariesInbox);即使回合超时,只要工具已同步提交的数据仍会被应用,不会白白丢弃。
跟踪识别与"可能相关"线索
同一轮模型回合内还做了两件事:
- 跟踪检测:在目标仓库搜索带
sentry-triage标签的 Issue,标题包含 Sentry key 即判定为已跟踪,并进一步查找其关联 PR(PR 可能位于不同仓库)。结果通过submit_tracking回传; - 相关线索:搜索标题携带相同原始错误文本、但并非本画布跟踪 Issue 的问题(如手工登记的 incident),以较弱的"possibly related"提示展示,绝不冒充权威的"👀 Tracked"徽章,通过
submit_related回传。
这两类结果是模型报告,在渲染为权威徽章前,扩展层会强制校验:issue 的 URL 必须是目标仓库内/issues/<n>形状、PR URL 必须是 PR 锚定仓库内/pull/<n>形状,且 host 匹配受信任主机(详见下文安全章节);校验失败的记录整条丢弃或剥离 PR 字段。
Agent 结构化工具:不往时间线里打印 JSON
画布向 Agent 暴露了 4 个结构化交接工具(见 扩展 README),Agent 调用工具而不是把 JSON 打印到时间线:
| 工具 | 用途 |
|---|---|
submit_issue_summaries | 回传通俗标题映射(token → { issueKey: sentence }) |
submit_tracking | 回传已跟踪 Issue/PR 映射 |
submit_related | 回传"可能相关"的 GitHub Issue |
submit_work_pr | 修复会话完成后回传 PR 编号/URL/状态,让画布卡片更新为实时 PR 链接 |
其中submit_work_pr依赖一个每工作项生成的workToken:修复会话通过提示词把 token 原样回传,画布据此把 PR 更新落到发起该工作的确切画布实例与问题上——仅靠 Sentry short key 匹配会把两个恰好共享 key(如都有API-123)的组织交叉串线。工作卡片还带 5 分钟对账机制(WORK_RECONCILE_MS):若我们的等待超时但修复会话从未产出 PR,卡片会从卡死的working态翻转为可重试的错误态,并释放 token。
交接动作:Create issue 与 Fix with Copilot
提示词构建在 extension.mjs 的buildWorkPrompt()中,两条路径的差异清晰可见:
📝 Create issue(仅跟踪)
提示词要求模型:先做 Step 0 去重(搜索已存在的开放跟踪 Issue,有则复用),再打开跟踪 Issue;明确禁止修复 Bug、写代码、建分支、开 PR 或拉起任何其他会话。Issue 必须携带标记:标题含[sentry-triage][KEY]前缀、正文含完整 Sentry URL、并打上sentry-triage标签(不存在则先创建)。返回 JSON 形如:
{"status":"done","issue":{"number":123,"url":"https://..."}}🔧 Fix with Copilot(Issue + 修复会话)
在 Step 0 去重之外("正在被处理"仅指存在开放 PR,关闭/合并的 PR 不算,会重新开工),额外执行 Step 2:通过create_session工具在当前项目拉一个独立专用会话,命名如Fix KEY:
- 支持指定模型(
model参数)与执行位置execution_location(cloud或local); - 云模式:目标仓库与基线分支来自设置面板(受信任);本地模式:修复会话始终运行在画布自身检出的 "Current project",PR 落在当前检出的受信任仓库——曾存在"由模型提供 project_id 路由修复会话"的路径,因其授权写操作所依据的仓库只能来自同一轮不可信的 Sentry 回合,已被移除;
- 修复会话被要求先读仓库自身的 Copilot/Agent 规范(
.github/copilot-instructions.md、匹配的.github/instructions/*.instructions.md、AGENTS.md/CLAUDE.md),复现 Bug、实现针对性修复、补测试、开 DRAFT PR 并链接 Issue; - 修复会话最终必须回传形如
PR ready for Sentry KEY: call submit_work_pr with key "...", workToken "...", prNumber ..., prUrl ..., prState "draft"的消息,画布据此更新卡片; - 当前会话只负责归档 Issue 和交接,不等待PR 完成,立即返回会话 id/name:
{"status":"handed-off","issue":{"number":123,"url":"https://..."},"session":{"id":"session-id","name":"Fix API-123"}}跟踪器扩展:Linear / Jira
Step 1 的 Issue 流程按所选跟踪器分发(设置面板可选):GitHub 走默认路径;Linear / Jira 通过各自已连接的 MCP server 归档(画布不预检这两个连接,Agent 具备该访问能力);未知跟踪器回退为按 id/label 的通用指令。线性/Jira 归档同样要求标题或描述携带 key 与完整 Sentry URL,并尽量打sentry-triage标签。
安全设计:面向不可信数据与不可信模型的双重防线
画布把来自 Sentry 的错误数据(标题、消息、key、URL)视为不可信输入,并针对"写权限提示词注入"做了多层防御,值得借鉴:
- URL 白名单净化
safeSentryUrl():只有能被new URL()解析为http:/https:的字符串才放行,并返回规范化的url.href(换行/控制字符会被百分号编码或拒绝),其他(空白、javascript:、注入的散文)一律折叠为空; - 仓库引用严格校验
repoRefNumber()/urlInRepo():模型报告的 Issue/PR 链接只有在 host 精确等于受信任主机(来自 Actions 环境或本地 git remote,绝不来自模型或 Sentry 文本)、owner/repo 精确匹配目标仓库、路径为/issues/<n>或/pull/<n>且 n 是正的安全整数时才被采信;路径只校验会放行https://attacker.example/<owner>/<repo>/pull/1这样的伪装链接;正则锚定(?:\/|$)边界防止/pull/123evil被截断成有效 id; - 仓库名规范化
normalizeRepo():owner 限字母数字与连字符(≤39 字符,首尾不能是连字符),repo 限字母数字与./-/_(≤100 字符,但拒绝.、..),防止owner/repo?tab=x这类伪装通过宽松的"一个斜杠"检查; - Sentry 数据注入保护:提示词中所有 Sentry 来源字段(key、标题、原因、URL、摘要)先经
sanitizeForPrompt()收敛为单行有界数据;写权限提示词内用-----BEGIN SENTRY DATA (untrusted)-----包裹数据块,明确指示模型把整块当作惰性数据而非指令,并固定目标仓库、标签、key、workToken 为画布设定、不可被数据块更改; - Issue key 净化
safeIssueKey():只保留[A-Za-z0-9._-]并截断到 64 字符,空结果回退unknown-issue,防止在"..."引号上下文中逃逸; - 结果解析防伪
parseWorkResult():无法解析出结构化结果时返回空对象,绝不从散文里刮取#123之类的数字当成功凭据——否则"Could not create issue #123"会被误判为成功创建。
并发与超时:单会话上的回合串行化
SDK 只驱动单条会话对话,两个交错的sendAndWait会互相污染。因此 extension.mjs 用一条 promise 链(sessionTurnChain)把所有模型回合串行化,并实现了一个讲究的超时语义:
- 调用方超时(
TURN_TIMEOUT_MS = 240000)不提前释放锁——sendAndWait的超时只约束"我们等多久",不会中止模型在会话上的在飞工作;超时后通过session.abort()取消回合,锁一直持有到回合真正终结(中止或完成),防止下一个回合与它交错产生重复副作用; TURN_HARD_TIMEOUT_MS = 300000作为兜底传给sendAndWait本身,即使abort()未能终结回合,锁也会释放,画布不会永久卡死;- 计时器只在回合真正开始执行(而非排队)时启动,避免排队中的回合误中止正在运行的上一回合。
画布源码结构速览
扩展目录 extensions/sentry-triage 的文件职责(见 扩展 README):
extension.mjs— 画布声明、Agent 交接工具、编排与安全校验(约 2000 行,是本插件逻辑核心);server.mjs— 支撑画布 webview 的回环 HTTP 服务器;state.mjs— 每个画布实例的状态与渲染协调;styles.mjs— 画布样式;sentry.mjs/sentryClient.mjs— Sentry CLI 库模式访问:扫描 Issue、枚举组织与项目;preflight.mjs— Sentry 登录/连接检查与设置门;prefs.mjs— 持久化用户偏好;escape.mjs— 提示词/HTML 转义辅助;components/—page.mjs、card.mjs、category.mjs三个 webview 组件;assets/preview.png— 扩展画廊预览图;package.json— ESM 入口与运行时依赖;copilot-extension.json— Copilot 扩展名称/版本元数据。
小结
Sentry Triage 插件的完整工作闭环是:Sentry 实时数据 → 代码侧确定性扫描分级 → 模型仅负责标题润色与跟踪识别 → 画布卡片展示 → 用户选择交接方式 → Agent 通过结构化工具归档 Issue / 拉起修复会话 → PR 回传画布更新。它用"代码拥有数据、模型只做语言润色"的架构杜绝了过期数据,用双 repo 锚定与 URL 校验建立了跨 repo 写操作的安全边界,用回合串行化与超时对账保证了长时间值班场景下的稳定性。对正在搭建 AI 值班分诊流水线的团队而言,这份源码(尤其是 sentry.mjs 的分类器与 extension.mjs 的安全层)本身就是一份高质量的可参考实现。该插件遵循 MIT 许可证,可自由查看与复用。
【免费下载链接】awesome-copilotCommunity-contributed instructions, agents, skills, and configurations to help you make the most of GitHub Copilot.项目地址: https://gitcode.com/GitHub_Trending/aw/awesome-copilot
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考