ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Qwen Code Web Shell 会话 Issue 绑定:基于 PR closing references 的派生式快照机制

Qwen Code Web Shell 会话 Issue 绑定:基于 PR closing references 的派生式快照机制 Qwen Code Web Shell 会话 Issue 绑定基于 PR closing references 的派生式快照机制【免费下载链接】qwen-codeAn open-source AI coding agent that lives in your terminal.项目地址: https://gitcode.com/GitHub_Trending/qw/qwen-code本指南深入剖析 qwen-code开源终端 AI 编程代理中会话 → Issue绑定能力的完整实现它不新增写入点而是让 daemon 的 PR 状态刷新定时器从 GitHub 的closingIssuesReferences派生每个已绑定 PR 所关闭的 Issue快照进现有.pr.jsonsidecar并在 Web Shell 侧栏的会话 tooltip、搜索中呈现。读完你将对数据模型、GraphQL 抓取、sweep 收敛、线协议投影与测试验证有完整把握。为什么需要从已绑定 PR 派生Issue在 会话绑定 PR 号 落地之后Web Shell 侧栏已经能回答哪个会话产出了 PR #N。但维护者的主力工作流bugfix / triage / autofix develop-issue都从 issue 出发、最终以 PR 收口侧栏回答不了哪个会话在处理 issue #N。Issue 和 PR 的语义有本质区别见 设计文档PR 是会话的产出会话自己在 GitDialog 里点击 Create PRdaemon 存在一个精确的写入点Issue 是会话的意图Web Shell 里没有任何结构化写入点/bugfix #N只是提示词参数create-issueskill 是模型自己跑gh issue createPR 与 Issue 共用编号空间扫描提示词或分支名反推会误绑。因此核心设计决策是派生而非绑定GitHub 上 PR 与 Issue 的关联已经存在——PR 正文里的Fixes #N就是closingIssuesReferences。唯一高精度的来源就是 GitHub 自己的 closing references无需新增写入点、无需新增 sidecar、无需环境开关。数据模型SessionPr 的可选 issues 快照sidecar 条目结构SessionPr条目增加可选字段issues持久化在会话 JSONL transcript 旁的chatsDir/sessionId.pr.jsonsidecar 中由 session-pr-service.ts 读写{ number: 10303, url: https://github.com/QwenLM/qwen-code/pull/10303, createdAt: ..., state: open, issues: [ { number: 10293, url: https://github.com/QwenLM/qwen-code/issues/10293, state: open } ] }字段语义与约束issues缺省表示尚未抓取空数组表示抓过没有。二者区别决定 sweep 是否还需要为该 PR 发查询详见下文收敛逻辑。Issuestate取值open/completed/not_plannedGitHub 的stateReason为NOT_PLANNED与DUPLICATE时都归一为not_planned映射逻辑见 github-pr-issues.ts 的mapIssueState。每个 PR 最多保留 10 个 issue常量SESSION_PR_ISSUE_LIST_LIMIT 10在 github-pr-issues.ts 中声明GraphQL 查询也只取first: 10。url 校验与 PR url 完全一致只接受 http(s)、长度上限 2048SESSION_PR_URL_MAX_LENGTH、无控制字符。该规则在 core sidecar 校验、bridge 类型、SDK 的isDaemonSessionPrInfo见 sdk-typescript/src/daemon/session-pr.ts三层同步实现——因为 bridge 会把 url 插进 stderr 审计行控制字符会伪造日志。派生数据跟着来源走PR 条目被 cap 淘汰时 issue 一起走同 PR 重绑upsertSessionPr/ bridgeupdateSessionMetadata保留已有issues跨仓库同号 PR 不继承客户端永远不能写issues。从源码看sidecar 的读取端isValidSessionPrList会对整个列表做运行时形状校验任一 entry 不合法包括issues超过 10 条、issue state 不在白名单、url 非法会导致整份列表读取失败因此写入端upsertSessionPr等会主动拒绝带毒条目避免一次坏写入抹掉全部历史绑定。抓取实现一条按编号别名查询的 GraphQL新增 core 工具fetchGitHubPullRequestIssues(cwd, env, numbers)github-pr-issues.ts通过execFile调用gh api graphql利用 gh 自带的{owner}/{repo}占位符解析当前仓库按编号别名查询query($owner: String!, $name: String!) { repository(owner: $owner, name: $name) { p10303: pullRequest(number: 10303) { number url closingIssuesReferences(first: 10) { nodes { number url state stateReason } } } } }为什么不在gh pr list --json上追加字段设计文档明确给出了三条理由这也是保留原 list 查询零风险的原因该字段不带 issue state仍需第二条查询实测--state all --limit 500加该字段从 4.9s 涨到 6.7sgh 超时 10s现有 sweep 的 list 查询已贴近超时上限按编号查询不受 500 条窗口限制老 PR 也能补齐。批处理与错误分类每次调用最多 100 个别名GITHUB_PR_ISSUES_BATCH_SIZE 100超出分批任一批失败整体返回failed。gh 对 NOT_FOUND 别名绑定指向别的仓库的同号 PR以非零退出码返回但 stdout 仍带其它别名的完整数据——包装器在 stdout 有 JSON 时照常解析见runGhGraphql未解析的别名直接缺席由调用方的 url 守卫兜底。返回结果是一个可辨识联合discriminated unionok/not_a_repo/cli_unavailable/repo_unresolved/failed与既有的fetchGitHubPullRequests对齐。注意 parsePullRequestIssuesResponse 对顶层errors[]的区分只有path: [repository, alias]的 NOT_FOUND 才代表仓库没有该 PR其它局部错误服务端错误置空某别名、子字段错误置空 closingIssuesReferences一律抛错转为failed防止瞬时错误被洗成ok后永久写入空快照。仓库在 GitHub 侧已不存在path: [repository]且data.repository为 null则归为结构性的repo_unresolved。Sweep 集成refreshWorkspaceSessionPrStates的第二阶段refreshWorkspaceSessionPrStatespackages/cli/src/serve/server/session-pr-refresh.ts每个 workspace 每轮执行 8 步扫描 sidecar挑出state ! merged || issues undefined的绑定open/closed 的 closing references 会随正文编辑变化merged 但无快照的是升级前的存量只补一次。若存在非 merged 绑定跑原有 slimgh pr list --state all刷 PR state保持不变。对第 1 步的编号去重后跑一次按编号 GraphQL 拿 issues。每个 sidecar 一次updateSessionPrStates原地写入 state issuesurl 不匹配不写state/issues任一缺省则保留原值都无变化则不写文件返回 0 且不触发写入。收敛convergemerged 且无快照、又永远不可能被查询解析到的绑定写入空快照issues: []渲染上等同于没有否则它会永远重新进入查询违背全 merged 且有快照零调用的不变量。触发条件查询成功但仓库不认识该编号外仓同号 PR、查询结构上不可能无 gh / 无 git root、平台根本没有 closing referencesAone。瞬时失败failed不收敛。可解析性过滤用列表查询返回的 PR url 推出本 workspace 仓库的host/owner/repo键url 不在该仓库下的绑定不进入查询与 Aone 的 refreshable 过滤对称merged 的直接本地收敛列表没跑或为空则放行进查询由逐别名 NOT_FOUND 收敛。宽松匹配只限 sweep 内部查询结果与绑定的匹配按host/owner/repo 编号容忍www.、http:、/files后缀、大小写命中后以绑定自己的 url 写入——sidecar 的写入门仍按 canonical url 比对其它同 PR 判定重绑、session-list 合并不放宽。GraphQL 响应的顶层errors[]必须检查只有path: [repository, alias]的 NOT_FOUND 才代表仓库没有该 PR其它局部错误一律当failed。gh 因无远端 / 远端不是 GitHub 而解析不了仓库stderr 里的 placeholder 解析报错或仓库在 GitHub 侧已不存在都归为结构性的repo_unresolved参与收敛。成本全 merged 且已有快照的 workspace零调用否则多一条约 1–3s 的 GraphQL。已合入 PR 的 issue 之后被 reopen 不再跟踪与merged 是终态同一取舍。此外 sweep 还做了几件防御性工作sidecar 提交通过 archive lane 与同一会话的归档/删除串行化assertCanCommit在写入前检查 sidecar 是否已被删除防止复活runtime.generationGuard保证被替换/移除的 workspace 不再用过期 env 跑 gh 或提交 sidecar。定时器startSessionPrRefreshTimer的首轮延迟 60sFIRST_RUN_DELAY_MS之后按QWEN_SESSION_PR_REFRESH_MINUTES间隔默认 5 分钟DEFAULT_SESSION_PR_REFRESH_INTERVAL_MS执行unref()化避免拖住进程退出。线协议与展示类型与投影bridgeSessionPrInfo/ SDKDaemonSessionPrInfo增加issues?所有 sidecar → 线协议投影统一走 core 新增的toSessionPrInfo原先散落在 session-list / session.ts / dispatch / backfill / bridge 共 8 处手写的{number, url, state?}。mergeSummaryPrsissues与state一样以 sidecar 为准live entry 停在绑定时刻且只在 canonical url 相同时才采用——跨仓库重绑的 live 变更与 sidecar 写入之间旧 sidecar 仍指向别的仓库的同号 PR。写入侧类型bridgeSessionMetadataUpdate.pr、SDK 三个updateSessionMetadata参数用Omit…, issues客户端绑 issue 是编译错误运行时 bridge 也只从已知快照重建issues。SDK 侧同时提供isDaemonSessionPrInfo运行时守卫拒绝javascript:这类非 http(s) issue url。Web Shell 展示SessionDetailsTooltip.tsx 在 PR 行之后列出 issue按 url 去重stacked PR 关同一个 issue 只列一次复用 GitHub 视觉词汇open 绿色 circle-dot、completed 紫色 circle-check、not planned 灰色 circle-slash可见文本为Issue #Nsr-only 追加状态。侧栏搜索 sessionSearch.ts 的sessionMatchesGitQuery命中 issue 号带不带#都行。会话行 badge保持只显示 PR避免#N并排歧义。时延模型GitDialog 创建 PR 后issue 在下一轮 sweep 出现首轮 60s 延迟、之后 5 分钟QWEN_SESSION_PR_REFRESH_MINUTES语义不变0禁用 sweep缺省/非法值回退默认 5 分钟。关键决策回顾派生而非绑定唯一高精度的来源是 GitHub 自己的 closing references显式输入绑定/bugfix #N拦截、欢迎页入口留作后续路线届时再考虑独立 sidecar。挂在 PR 条目上而非独立 sidecar派生数据的生命周期等于来源 PR独立文件会引入第二套 cap、归档移动和写入 lane。保留gh pr list不动sweep 的 list 查询已经贴近超时上限issue 单独走按编号 GraphQL。影响文件层文件coreservices/session-pr-service.tsSessionPrIssue、校验、toSessionPrInfo、updateSessionPrStates扩展、utils/github-pr-issues.ts新增daemonserve/server/session-pr-refresh.tssweep 第二阶段、session-list.ts、routes/session.ts、acp-http/dispatch.ts、routes/session-pr-backfill.ts投影bridgebridgeTypes.ts、bridge.ts类型、重绑保留、投影SDKdaemon/types.ts、daemon/session-pr.ts、daemon/index.tsweb-shellSessionPrStateIcon.tsx(css)、sidebar/SessionDetailsTooltip.tsx、sidebar/sessionSearch.ts、i18n.tsx测试上述各层 collocated 单测测试与验证E2E 计划配套 E2E 测试计划见 .qwen/e2e-tests/2026-08-27-webshell-session-issue-binding.md覆盖从 core 契约到真实 GitHub 的四个分组Baseline dry-run用已发布的 CLIqwen --version验证基线行为——绑定一个正文含Fixes #N的 PR 的会话tooltip 只显示 PR侧栏搜N找不到会话sidecar 条目无issues字段。Group Acore 契约cd packages/core npx vitest run src/services/session-pr-service.test.ts src/utils/github-pr-issues.test.tsissues列表可 round-trip非 http(s) issue url、未知 issue state、超过 10 个 issue 都会使 sidecar 整体失效同 PR 重绑保留快照跨仓库重绑丢弃updateSessionPrStates带或不带state都能写issues无变化则跳过写入GraphQL 包装器每个 PR 一个pullRequest(number:)别名OPEN →openCLOSEDCOMPLETED →completedCLOSEDNOT_PLANNED / CLOSEDDUPLICATE →not_plannedgh 对 NOT_FOUND 编号非零退出时保留已解析别名按 100 分批缺 gh 二进制映射为cli_unavailable。Group Bdaemon sweep 与列表cd packages/cli npx vitest run src/serve/server/session-pr-refresh.test.ts、src/serve/server.test.ts -t sidecar、cd ../acp-bridge npx vitest run src/bridge.test.ts -t SessionPrs|re-bind|client-supplied、cd ../sdk-typescript npx vitest run test/unit/sessionPr.test.tsopen 绑定一轮写入 state issues 且createdAt不动无快照的 merged 绑定只做一次按编号查询且不发 list 查询快照后两者都不再发外仓绑定永远不接收本仓库的 issueissue 查询失败仍刷新 stateretired-generation 守卫覆盖新查询会话列表优先用 sidecarissues而非 live entrybridge 回显 seeded issues 并在仅 state 重绑时保留SDK 守卫接受三个 issue state 并拒绝javascript:issue url。Group C真实 gh 对真实仓库从配置了gh auth的 GitHub 仓库 checkout 中种子一个临时 runtime dir包含绑定 open PR引用 open issue、merged PR引用 closed issue、外仓 PR 号的三个会话然后从构建好的 cli dist 调两次refreshWorkspaceSessionPrStates。期望第一轮updated: 2两个真实 PR 的 sidecar 携带state: open/state: completed的 issue外仓条目不动第二轮updated: 0每轮都在 10s gh 超时内完成。Group DWeb Shellcd packages/web-shell npx vitest run client/components/sidebar/SessionDetailsTooltip.test.tsx client/components/sidebar/sessionSearch.test.ts并手动验证qwen serve打开 Web Shell在 Git dialog 创建正文含Fixes #N的 PR等第一轮 sweepdaemon 启动 60s 后、之后每 5 分钟悬停会话行。期望 tooltip 在 PR 行下按绿色 circle-dotissue open/ 紫色 checkcompleted/ 灰色 slashnot planned列出Issue #N链接可打开 issuestacked PR 关同一 issue 只列一次侧栏输入N或#N能搜到会话会话行 badge 仍只显示 PR。范围边界明确不做显式 issue 绑定入口与独立 issue sidecar从提示词 / 分支名 / commit trailer 反推 issue 号会话行 badge 显示 issue回填路由立即抓 issue下一轮 sweep 兜底已合入 PR 的 issue 被 reopen 后的状态跟踪。这套派生式快照设计的本质是把 GitHub 平台上已经存在的结构化关联closing references作为唯一事实源用零新增写入面的方式让 daemon 的低频 sweep 顺路补齐 issue 维度最终在 Web Shell 的 tooltip 与搜索中形成 PR / Issue 双向可查的会话语义。【免费下载链接】qwen-codeAn open-source AI coding agent that lives in your terminal.项目地址: https://gitcode.com/GitHub_Trending/qw/qwen-code创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表