fix: reduce supervisor stalls and preserve task delivery

Load Supervisor graphs only when opened, preserve source details on demand, reuse indexed story digests and window selection lists without losing keyboard or focus behavior. Route heavy UI reads through the existing worker, batch scoped queries and construct each publication snapshot once.

Keep remote cancellation responsive during model-route cleanup, remove late routes and retain reported usage after response-cap cancellation. Preserve Telegram pending replies through polling conflicts, show delivery errors and honor the final rate-limit cooldown.

Schema 62 adds lookup indexes without rewriting user content; older clients cannot open upgraded data. Large review publication still blocks Main and remains documented follow-up work. Record the future storage-service direction without performing the architecture migration.

Validation: exact staged code passed isolated full tests (6212 passed, 87 skipped), typecheck and lint. Three-round source performance comparisons, SQLite migration equivalence, native Electron interaction checks and current-source Linux x64 Host validation completed; eight external text-model requests.

Release note: Supervisor opens reviews without loading the entire graph, keeps long selection lists responsive and preserves details and keyboard navigation. Remote cancellation and Telegram reply recovery are more reliable; remote fixes require an Agent build containing this change.
This commit is contained in:
mesalogo
2026-10-05 14:18:33 +08:00
parent e50ca6d149
commit b90ff50f2a
59 changed files with 2108 additions and 265 deletions
@@ -297,6 +297,12 @@ Agent 当前 Runtime installation + 兼容配置
发送 Prompt;原生调用迟到返回的 Session 只关闭自身,不能回收仍服务其他 binding
的共享进程。原生握手本身仍由进程 owner 共享,一个等待者退出不取消其他等待者。
模型路由撤销不等待原生 HTTP 响应后才释放 backend 控制队列;broker 关闭、凭据释放
及 terminal 提交仍按原顺序执行。启动准备保存原 transport 和取消 signal,安装完成时
若已取消,按原 Session/operation 再撤销一次,避免早先清理被迟到安装覆盖。撤销沿用
helper 的 operation 匹配检查,不删除同 Session 的其他 operation。HTTP 沿用 10 秒
超时,进程退出或 transport dispose 中止未完成请求;无需新的持久状态或协议阶段。
原生 `chat.message` 在模型请求前设置该 Session 的工具与模型路由,不再按产品模式
过滤 Shell、编辑或写入;请求级端点与子会话归属仍独立。
OpenCode 1.18.29 的 ACP 不转发未注册子 Session 的权限请求,且原生子代理只继承父
@@ -24,6 +24,7 @@
| [回顾分块调度设计](./review-scheduling-design.md) | 第 0 节定义生产分页、批次持久化、续跑、当前设置及剩余边界;其余章节保留改造前基线、目标设计和原型证据 |
| [故事线模型改造设计](./storyline-model-design.md) | 全局 › 项目 › 功能 › 子线索的故事层级、故事归属与经验抽取阶段、用户调整及实施顺序(目标设计) |
| [实施进度](./progress.md) | 已验证实现、验证证据和当前边界 |
| [Renderer 修复与测量](./frontend-progress.md) | K01、K03、K04 的界面回归、源码 Electron 对照及测量限制 |
| [故事线图谱设计](../story-graph/prd.md) | 图谱对象、环绕时间轴与实体交互 |
生产入口已接通完整来源分页、叶子持久化、持续处理至完成、活动页主动暂停/继续及批次详情;设置可调页大小、批次正文/消息上限、单次响应容量、两个模型超时和共享并发。实际合同及限制见[生产接线](./review-scheduling-design.md#0-生产接线与剩余边界)。真实 DeepSeek 验证已覆盖一个完整的 27 条消息会话;较早的 129 条消息实验仍未完成。两次实验共 5 次付费请求,详情见进度。原始 AbortError 原因尚未由日志确定。
@@ -0,0 +1,99 @@
# Renderer 修复与测量
最终集成补注:本轮暂存内容在排除并行功能的隔离工作树通过完整验收,6,212 项通过、0 失败、87 跳过;类型检查与 lint 通过。下文保留分组与早期失败记录,最终提交验收以[统一记录](../../review/kiss-local-fixes-2026-10-05.md#最终检查)为准。
## 2026-10-05:K01、K03、K04
对应[局部修复清单](../../review/kiss-local-fixes-2026-10-05.md)及[性能原则](../../architecture/performance-principles.md)的 P5、P6、P7、P8、P10。此处只记录 Renderer;Main 查询、Worker、发布事务和数据库验证由[实施进度](./progress.md)记录。
`SupervisorWorkspace` 首次工作回顾不再请求图谱。进入图谱页时按所选结果读取,内存中只保留当前结果的一份图谱;普通页签切换复用,刷新、历史导航和修改后的刷新清除缓存。图谱错误不阻止阅读回顾,失效请求不能重新填入旧缓存。来源正文继续在点击后读取,未改变 IPC DTO。
工作回顾与图谱使用同一份故事读取状态,按范围和刷新版本复用。摘要在 workspace 中按故事快照和时间区间计算一次,子组件直接渲染该结果;父子关系索引替代逐故事全量扫描。保留直接子线索聚合、孤立子线索、跨项目关联、时间边界及排序语义。App 只移除了监督者刷新中的成果列表读取,未拆分全局状态或新增领域 store。
事件、实体、关系列表使用 `useListWindow`,保留选中和焦点行,按实际行高计算占位。新增组件处理跨窗口的方向键、Home/End、Tab/Shift+Tab 和选择,完整记录仍可滚动访问。原生 Electron 回归发现,窗口切换把已渲染行换成估计高度的占位后,末条焦点行会移出视口;共享 hook 已补可见焦点保护,并验证主动滚离焦点行时不会被拉回。该行为也有独立共享 hook 回归。
## 回归证据
修改前运行新用例,三项均按预期失败:概览调用了 `graph`;同一故事快照计算摘要两次;600 个事件全部挂载。修改后,监督者、心跳中心、摘要及共享列表相关回归通过。历史结果、迟到图谱和来源响应、图谱失败与重试、实体修改后重新读取、来源正文按需读取均有覆盖。App 集成回归验证首次和缓存路由返回不再重读成果列表。
最后复查补充了历史结果已删除的回归:显式结果 ID 查询为空时,工作回顾显示可重试错误,重试仍使用原 ID,不退回最新结果,也不请求图谱。该失败分支在修复前已运行并失败,修复后单独验证;不改变下述性能 fixture 的成功结果路径。
`tests/supervisor-layout.electron.test.ts` 在 Vite 中运行生产组件,新增 `?windowed=1&recap=1` 固定数据,包含各 600 条事件、实体和关系。原生键盘遍历跨越未挂载行,检查首尾可达、焦点可见、选中状态、滚动后视口有内容;随后执行来源读取、回顾与图谱切换、历史切换、实体编辑和 390px 窗口检查。常规 1440、1024、390px、浅深主题及密集图谱布局场景继续通过。
测试使用真实 Chromium 布局、鼠标及键盘输入和生产 React 组件。bridge 返回固定合成数据,编辑写入测试内存;这不是生产 Main、SQLite、模型调用的端到端证明。完整 App 路由由 jsdom 集成用例覆盖。未运行模型请求、生产构建、打包或已打包应用探针。
## 源码性能对照
Windows x64,Intel Core Ultra X7 358H,Electron 44.5.1,不降速。基线取独立临时 worktree 的 `e50ca6d1`,复用现有 Supervisor Electron/Vite fixture;Vite 从 worktree 读取基线 Renderer/shared 源码。两版本使用相同合成数据和原生鼠标输入,最终顺序为基线 1、修复 1、基线 2、修复 2、基线 3、修复 3。每次启动独立 Vite 缓存和 Electron 用户目录。
下表每格按三轮列值,单位为 ms。点击反馈为点击事件至两次 `requestAnimationFrame` 的代理指标;就绪帧指帧回调中观察到 `aria-busy=false`,不等于完整内容提交或合成器呈现时刻。长任务来自 Renderer `PerformanceObserver`,未与 Main 查询耗时相加。
| 场景与指标 | 基线三轮 | 修复三轮 |
| --- | --- | --- |
| 已预载路由,2,000 事件,图谱点击反馈 | 346.3 / 325.0 / 188.3 | 29.9 / 38.5 / 24.3 |
| 同场景,图谱长任务时长 | 338+203 / 317+169 / 182+131 | 0 / 0 / 0 |
| 同场景,挂载行数 / 文档元素数 | 2,000 / 8,207 | 24 / 329 |
| 2,000 故事各十事件,首次回顾摘要 CPU | 80.0 / 73.2 / 48.2 | 15.4 / 16.2 / 9.3 |
| 同场景,首次回顾长任务时长 | 83 / 75 / 50 | 0 / 0 / 0 |
| 同场景,图谱返回回顾的摘要 CPU | 140.8 / 117.5 / 88.7 | 0 / 0 / 0 |
| 同场景,缓存路由返回的摘要 CPU | 67.4 / 74.1 / 42.6 | 0 / 0 / 0 |
| 同场景,缓存路由返回点击反馈 | 73.4 / 81.1 / 47.5 | 5.2 / 8.7 / 6.7 |
| 注入 250ms 图谱延迟,首次回顾就绪帧 | 282.7 / 265.1 / 263.8 | 16.2 / 10.9 / 10.6 |
| 未预载模块,八事件,首次回顾出现帧 | 340.9 / 341.9 / 308.1 | 312.5 / 343.8 / 331.1 |
修复版本首次回顾的图谱请求数为零;图谱首次打开才承担模拟延迟。普通图谱/回顾切换不再请求故事,缓存路由返回不再计算摘要。摘要首次的计数包含一次空数据计算和一次已加载快照计算,后续同一快照复用。
未预载模块的首次回顾仍约需 0.31~0.34 秒,包括 Vite 模块加载及 React Suspense 行为,未证明这一项有改善。在该场景随后首次打开八事件图谱时,修复版本第二轮仍出现一次 55ms 长任务;其余修复场景未观察到长任务。三轮样本不足以声明 p95 达标,源码模式也不能代替发布包测量。三个场景均未加载 Three.js,此处没有测量螺旋的 WebGL 初始化。
原始记录及临时测量脚本保留在本机批准的临时目录 `opencode`:`supervisor-comparison.jsonl`、`supervisor-audit-runner.mjs`、`supervisor-audit-driver.mjs`。最终数据标签为 `baseline-final` 和 `fixed-final`;文件还保留较早的对照轮次,不能混算。首次审查和各轮间波动明显,因此记录逐轮数值,不把模块加载、查询等待和渲染 CPU 合并归因为存储。
## 验证状态
- 最终定向 Renderer 与共享列表测试:8 个文件、139 个用例通过。此前包含相关 Main 文件的定向重跑为 368 通过、1 跳过。
- App 首次入口、缓存路由返回及侧栏图谱导航:3 通过。
- 完整 Supervisor 源码 Electron 布局与交互:通过;`npm run lint` 通过。
- 完整 `npm test` 执行结束:515 个测试文件通过、6 个失败、15 个跳过;6,208 个用例通过、7 个失败、87 个跳过,另有一个未处理拒绝。随后修正并重跑了本任务的焦点问题;相关 Main 失败在其负责方更新后重跑通过,Magic Notes 原生布局独立重跑通过。
- 当时 `tests/message-expansion.electron.test.ts` 独立重跑仍触发 90 秒超时;后续定位、修复及定向绿色验证见下节。全量测试尚未整体重跑,不能将定向验证记为全量通过。
- 最终 `npm run typecheck`、`npm run lint`、`git diff --check` 通过。文案扫描无阻断项;复核候选为加载、错误和测量范围的必要限制说明,已保留。
## 2026-10-05:消息展开测试复查
该 Electron 测试在 2,000 和 10,000 条聊天记录场景之后,还执行监督者来源请求的语言切换失效场景。聊天历史使用独立的消息窗口机制,不调用本轮修改的 `useListWindow`;监督者 fixture 只有一个事件,也未进入长列表窗口路径。相同断言下,`e50ca6d1` 和当前源码的普通复跑分别在 16.4 秒和 15.8 秒通过,未复现确定性的 Renderer 性能回退。
加入有界的 100ms 替换图谱响应后,两版本均重现测试同步错误:语言已切换,旧来源按钮仍短暂存在,测试便继续执行;固定两帧后读取按钮的 `disabled` 时,刷新已卸载该节点,抛出空引用错误。测试现在通过 fixture 的来源标题区分第几次图谱响应,等待第二次响应实际渲染后再完成旧来源请求并检查失效结果。初始语言固定为中文,保证切换到英文确实触发刷新。
轮询辅助函数另有一个已验证的超时问题:第二次及后续动画帧中的谓词异常没有进入 Promise 的拒绝路径,Promise 会永久等待,直到外层 90 秒看门狗杀死 Electron。批准临时目录中的 `expansion-wait-probe.mjs` 使用两版本的原始辅助函数和同一“节点消失”输入验证:旧版为 `pending` 加未捕获错误,修复版为 `rejected` 且无未捕获错误。已将谓词异常接入拒绝路径,并保留原有 10 秒等待上限;外层 90 秒上限、DOM 数量、展开状态、键盘和旧来源断言均未放宽。
原先的 90 秒日志没有阶段信息,无法证明它一定由上述某一个等待点触发。此次修复针对已重现的同步错误与未结束的等待 Promise,不将先前超时归因于共享列表或存储。新增阶段耗时及 Renderer 错误输出便于后续定位。没有修改产品 Renderer 或后端代码来绕过测试。
严格 fixture 下,基线完整测试在 15.6 秒通过。当前源码最终合并运行消息展开、监督者布局和共享列表等 10 个测试文件,141 个用例通过,总耗时 69.7 秒;其中消息展开各阶段合计 14.7 秒。两种聊天规模均访问 1,397 条不同记录,最多挂载 15 行、926 个文档元素;保留的展开项从 38 条在删除后归零,原生 Space 收起、输入草稿、监督者迟到来源失效均通过。Supervisor 继续覆盖原生首尾跳转、跨窗口 Tab、焦点可见、主动滚离焦点、历史、来源、编辑及窄窗口。最终类型检查和 lint 通过。
## 2026-10-05:独立复核修正
独立复核发现本轮改动引入了两个问题,均在修复前运行回归确认。
故事加载的旧 `act()` 会保留发起时的 `load()` 回调。A 范围的重命名尚未结束时切到 B,若 A 修改完成后再调用旧回调,它会先递增共享 generation,导致 B 的在途读取也被丢弃。相同范围换刷新版本时,旧回调还会覆盖新结果。新测试分别重现了 B 保持空数据和刷新后显示旧快照;另一个测试确认旧刷新回调会多发一次读取。
`SupervisionStories` 现在在提交时记录当前范围与刷新版本,在递增 generation 和发出读取之前核对回调归属;响应也须属于同一归属对象和请求序号。卸载后清除归属。四个 hook 回归覆盖跨范围修改完成、同范围刷新版本变化、保留回调在刷新后及卸载后的调用,以及当前修改正常重读并在页签返回时复用快照。修复前四项中三项失败,修复后全部通过;没有改变修改请求本身或增加全局状态管理。
列表原先只保留视口附近、选中和焦点行。滚到中间后,从面板按 Tab 会进入第一条已挂载记录;从后续控件按 Shift+Tab 会进入最后一条已挂载记录。原生 Electron 回归实际得到 `event-285` 和 `event-314`,且随后不能按完整列表的边界顺序退出。
`SupervisorSelectionList` 通过已有 `keepIds` 额外保留逻辑首、末行,继续使用原生 Tab 进入和退出,不增加焦点哨兵或拦截外部控件的键盘事件。新增 Electron 场景先选择中间记录、将列表滚到中部,再从真实前后控件用原生 Tab/Shift+Tab 进入。事件、实体、关系三类均检查首末记录、焦点可见、反向退出至前一控件和正向退出至后一控件;随后继续原有方向键、Home/End、跨窗口 Tab、滚动保留、历史、来源和编辑检查。测试准备阶段在同一次浏览器求值中检查并选择中间行,等待选中提交,避免把尺寸重测期间的临时 DOM 当作稳定状态。
最终定向运行包括消息展开、完整 Supervisor 布局、故事加载、workspace、心跳中心、摘要和共享列表:11 个文件、145 个用例通过,总耗时 69.3 秒。App 中相关监督者、心跳和图谱导航另有 11 个用例通过;类型检查和 lint 通过。消息展开仍覆盖 2,000/10,000 条记录,最多挂载 15 行、926 个元素,三个展开状态恢复、原生 Space、草稿保留、删除清理及迟到来源失效均通过。
本次没有运行完整仓库测试、生产构建或模型调用,也没有提交。上文性能表保留其测量时的代码状态;首末行保留会额外挂载至多两行,本次未重跑性能对照,不将旧节点数称为此次修正后的测量结果。原生列表回归中的有界 DOM 断言继续通过。
## 修改归属
本次 Renderer 任务的产品文件为 `App.tsx`(仅删除成果刷新)、`SupervisorWorkspace.tsx`、`SupervisorSelectionList.tsx`、`SupervisionStories.tsx`、`SupervisionStoryDigest.tsx`、`supervision-story-digest.ts`、`supervisor-workspace.css`、`use-list-window.ts`,以及对应的 `App.test.tsx`、`HeartbeatCenter.test.tsx`、`SupervisorWorkspace.test.tsx`、`SupervisionStories.test.tsx`、`supervision-story-digest.test.ts`、`use-list-window.test.tsx`。当前 `SupervisorWorkspace.tsx` 相对 `e50ca6d1` 的修改均来自本任务,包括最后的历史结果缺失检查。
本任务的 Electron 文件为 `tests/supervisor-layout.electron.test.ts`、`tests/message-expansion.electron.test.ts`,以及 `tests/support/` 下的 `supervisor-layout-driver.mjs`、`supervisor-layout-regression.tsx`、`supervisor-selection-driver.mjs`、`message-expansion-driver.mjs`、`message-expansion-regression.tsx`。
| 文档 | 本任务修改 | 并行修改 |
| --- | --- | --- |
| `frontend-progress.md` | 全文 | 无已识别的其他修改 |
| `README.md` | 文档表中的“Renderer 修复与测量”一行 | 未发布回顾遇到会话删除的说明段落 |
| `ui-design.md` | 按需图谱读取、历史和刷新等待规则、列表窗口与键盘规则 | 删除会话后省略来源的活动说明段落 |
其余 Main、Agent、shared DTO、`SupervisorActivity`、`SupervisionReviewStages`、中英文 heartbeat 文案、监督者技术/主进度/需求/逻辑文档、公共评审与路线图、Telegram 和宣传稿修改均不属于本 Renderer 任务。此归属用于区分本任务的改动,未替其他负责方审定或提交其代码。
@@ -4,6 +4,46 @@
当前记录以已验证生产行为为准。监督者尚未覆盖全部 user stories。
## 2026-10-05 监督页面读取与发布局部优化
本节记录 K01 的后端来源投影及 K02、K05、K06,基线为 `e50ca6d1`。生产 IPC 的概览、图谱、故事/经验读取已接入现有只读 Worker,文件数据库失败时不回退 Main 查询。schema 62 只增加概览查询索引;图谱只传来源标题,完整正文仍可按来源 ID 读取。发布只构造一次最终图谱快照,原 Main 事务和原子提交规则保留。接口及顺序规则见[技术设计](./technical-design.md#页面读取与发布2026-10-05)。本节不承担 Renderer 懒加载、摘要和虚拟列表的验证。
先加回归后改实现:最初 6 个用例在旧代码全部失败,分别暴露来源正文随图谱返回、无关项目仍读取全部经验、缺少 UI Worker 路径及重复重写快照等问题。新增文件最终 8 项通过,包含另一连接提交期间的读快照一致性、跨叶子复用身份时首叶子快照与来源引用保留。原子性用例在 checkpoint 写入处注入失败,确认结果、来源、运行状态和 checkpoint 一起回滚,叶子批次可重试。IPC 回归使用真实文件数据库和在临时目录编译的 Worker 源码,断言三个生产 handler 不调用 Main 同步读取;Worker 终止后请求拒绝,来源详情仍返回完整正文。
源码测量复用 `build/run-main-perf.cjs` 的 esbuild/Electron-as-Node 方法。基线从独立临时 worktree 读取,修复版从工作树读取,各运行三轮,第二轮反转版本及数据量顺序。Windows x64,Core Ultra X7 358H,Electron 44.5.1/Node 24.21.0,无 CPU 降速;同机还有并行开发活动,保留全部波动。每个版本每轮使用独立合成 SQLite,100 个会话、20 份结果,最新结果持有全部图谱事实;小/中/大样本分别为 2,000/20,000/40,000 条消息和 200/2,000/8,000 个事件及来源,消息与来源正文各 2,048 个 ASCII 字节。最大库约 212 MB。这是压力分布,不代表用户库。
每个读取入口测首次调用和三个立即重复调用,期间用 `setImmediate` 记录调用方事件循环最大间隔。首次概览包含懒启动 Worker;它不是冷 OS 缓存测量。下表为大样本每轮四次调用中的最大 Main 间隔,单位 ms:
| 入口 | 基线三轮 | 修复后三轮 |
| --- | --- | --- |
| 概览 | 389.03/471.16/872.84 | 2.45/2.10/3.17 |
| 结果图谱 | 583.03/571.86/646.16 | 12.04/12.29/15.79 |
| 故事及经验 | 82.92/77.06/72.28 | 8.56/6.94/19.74 |
修复后大样本概览的首次完成耗时为 94.01/89.65/117.37 ms,暖调用中位数为 0.74/0.74/1.05 ms。图谱首次完成仍为 453.89/476.75/518.11 ms,暖调用中位数为 573.05/551.27/686.15 ms,小时密度仍需读取消息正文。故事及经验首次完成为 90.99/84.18/95.01 ms;SQL 数量从 1,204 降至 6,但全局返回的 17,201 条 SQL 行和完整成员响应保留,不能据此声称整个故事请求变快。
大样本图谱 JSON 从 25,742,067 降至 7,143,667 字节;概览保持 7,901 字节,故事响应保持 3,317,180 字节。数值是 UTF-8 JSON 大小,不是 Electron IPC 实际传输字节。以上计时覆盖生产仓储方法、真实 Worker 及线程传输;不含 Renderer 解析/渲染、窗口 IPC 序列化、点击到下一帧,也不把几个探针耗时相加为端到端结果。
发布另用独立合成库,每批 20 个真实消息来源、20 个事件和 20 个实体,实际执行清单初始化、叶子保存和 `saveSupervisionResult`。本次复测保留并行加入的删除会话校验,测试来源确实存在。测量期间关闭自动 WAL checkpoint,以分离事务本身,未改变产品配置或落盘同步级别。三轮交替结果如下,单位 ms:
| 批数/来源数 | 基线同步耗时 | 修复后同步耗时 |
| --- | --- | --- |
| 10/200 | 27.86/28.77/30.96 | 28.60/18.33/24.88 |
| 50/1,000 | 194.20/228.55/223.33 | 146.28/151.44/127.86 |
| 100/2,000 | 686.15/729.16/729.95 | 271.02/286.14/303.41 |
100 批的快照 UPDATE 从 101 次降至 1 次,快照 SQL 从 444.31/473.67/463.00 降至 14.85/15.49/14.78 ms;修复后 COMMIT 为 27.40/30.89/33.01 ms,整个事务仍为 271.00/286.11/303.39 ms,事件循环最大间隔为 271.06/313.32/303.73 ms。小批次波动大于收益,不声明稳定提速。所有发布样本的 checkpoint 数都与来源数相等。合并后的叶子保存有一次 21.93 ms 峰值,也超过 16 ms,未修改其校验或放宽断言。
迁移验证直接复制基线 worktree 生成并已关闭的 schema-61 合成库,再运行生产 `upgradeAssistantStorage`。55 张用户表、32,947 行及 rowid 的逐行哈希在升级前后相同;20 份概览、2,000 条来源标题及完整正文、100 个故事、200 条经验的结果与排序等价,另比对项目及无关项目读取。升级耗时 61.73 ms,文件由 95,526,912 增至 95,711,232 字节;只报告 `upgrading`,没有内容转换或压缩,重复升级不再执行。`integrity_check` 为 `ok`、外键检查为空,基线文件 SHA-256 未变。单元测试另覆盖旧库有空闲页时也不回收。
最终定向命令包含 `supervision-ui-reads`、`supervision-history`、`supervision-timeline`、`supervision-stories`、`supervision-experiences`、`supervision-review`、`supervision-worker`、`readonly-query-reader`、`assistant-storage-upgrade`、`assistant-database` 和 `ipc` 的测试文件:11 文件、422 项通过、1 项原有跳过。`tests/supervisor-layout.electron.test.ts` 与 `tests/magic-notes-layout.electron.test.ts` 独立复跑,2 项通过。`npm test` 全量曾有 6 项失败、6,209 项通过、87 项跳过:两个新发布 fixture 未保留真实会话、旧 IPC fixture 只模拟一次 429 却要求整个请求失败,以及并行删除和两项 Electron 布局回归。发布 fixture 已改用真实消息;IPC fixture 覆盖重试耗尽后再次执行恢复;上述失败文件的最终复跑均通过。未将这次复跑记成全量重跑通过。
最终 `npm run typecheck`、`npm run lint` 和 `git diff --check` 通过。类型检查发现新 fixture 的阶段字面量不在合同中,已改为既有 `saving`,该文件 8 项再次通过。两份后端文档的中文审校扫描阻断项为 0;人工复核保留数据范围、性能预算和未测项说明。
原始脚本、18 组读取样本及迁移报告保留在系统临时 `opencode/supervisor-kiss-comparison-DzVb85`,读取汇总为 `entry-summary.json`,迁移报告为 `migration-integrity.json`。并行删除校验合并后的 18 组发布样本位于其 `publication-current/publication-measurements.json`;此前同目录外层的发布报告只代表合并前源码。
剩余预算问题:故事读取有一次 19.74 ms 的 Main 间隔,超过 16 ms;大图谱仍要等待数百毫秒的 Worker 查询;首次 Worker 启动也会增加小库概览等待。Main 最终发布仍有约 271~303 ms 的同步阻塞,并未达到性能原则。业务写入迁移、独立存储服务、IPC 实际序列化与并发写锁等待不在本次已验证范围,后续方向见[桌面存储职责](../../architecture/desktop-storage-direction.md)。本轮不迁移全部写入,也不放宽预算。外部模型调用为 0;没有读取用户正文、修改用户库、读取环境文件、生产构建、打包或启动已打包应用,没有提交。
## 2026-10-05 监督模型临时失败重试
对应 FR-S6。`runSupervisionModel` 已接入一次可取消的临时失败重试,覆盖监督者各模型阶段;错误分类、清理及每次尝试的超时规则见[技术设计](./technical-design.md#模型阶段超时与调度)。直连文本请求保留 HTTP status,并交出失败流中已报告的用量。OpenCode/Continue、直连传输重试和叶子/导航输出重试未改。监督者仍在桌面 Main 直连模型,不经过远程 Agent 或模型桥。
@@ -9,7 +9,7 @@
| 产品设计 | [监督者应用产品设计](./supervisor-prd.md) |
| 行为规则 | [监督者逻辑设计](./logic-design.md) |
| 界面设计 | [监督者 UI 设计](./ui-design.md) |
| 实施进度 | SQLite schema 50(心跳介入与监督建议见[心跳触发与建议](#心跳触发与建议2026-09-30));当前算法、设置和验收边界见[生产接线](./review-scheduling-design.md#0-生产接线与剩余边界) |
| 实施进度 | SQLite schema 62;页面读取与索引见[页面读取与发布](#页面读取与发布2026-10-05),当前算法、设置和验收边界见[生产接线](./review-scheduling-design.md#0-生产接线与剩余边界) |
本文回答如何在现有 GoodBuddy 桌面端中实现监督者。它不改变产品范围,也不把模拟 Demo 当作生产数据模型。
@@ -33,7 +33,7 @@ schema 43 在结果上增加 `graph_snapshot_json`,保存当次实体名称、
升级从现有事件实体关联、实体变化和来源引用恢复结果成员,不猜测名称匹配;旧数据缺失的归属或已经覆盖的内容无法还原,原对象仍保留。新结果即使实体没有事件或来源,也会保存完整成员。结果内容与运行、事件、来源在同一事务提交。
`overview({ target? })` 返回稳定的 `id`、`storyLineId` 及来源 ID。指定 Conversation/Task 时,Main 读取目标实际项目,查询同时要求目标来源匹配、结果 scope 为 global 或包含该项目;LIMIT 在过滤之后。卡片展示结果真实范围,global 结果不会伪装成单会话摘要。`graph({ resultId, storyLineId? })` 校验二者归属,只返回该结果的事件、对象和来源;显式 ID 无效时不回退到最新结果。继续讨论按结果主键读取,并验证来源属于该结果。
`overview({ target? })` 返回稳定的 `id`、`storyLineId` 及来源 ID。指定 Conversation/Task 时,只读 Worker 读取目标实际项目,查询同时要求目标来源匹配、结果 scope 为 global 或包含该项目;LIMIT 在过滤之后。卡片展示结果真实范围,global 结果不会伪装成单会话摘要。`graph({ resultId, storyLineId? })` 校验二者归属,只返回该结果的事件、对象和来源标题;显式 ID 无效时不回退到最新结果。继续讨论按结果主键读取,并验证来源属于该结果。
工作回顾保存所选结果,切换或刷新使用请求序号忽略迟到图谱。侧栏按目标重建卡片,清空来源和预览,并忽略旧请求;工作栏 tasks 实例的 `targetRef` 可保存 conversation/task 固定目标,取消固定后恢复跟随当前选择。该绑定只控制监督卡片,不改变任务列表的项目范围。继续讨论预览显示实际目标名称和 ID,发送回调显式携带该会话 ID,由 Main 使用该会话的 Runtime 与项目。
@@ -45,6 +45,20 @@ schema 43 在结果上增加 `graph_snapshot_json`,保存当次实体名称、
已确认记忆作为当前背景,不参与新增正文覆盖。最多四条、每条 500 字符,时间来自真实更新时间;模型不能把背景引用为本批新增证据。旧结果继续保留原来源类型和 locator。监督正文上限现用于分批,余段继续处理;心跳报告的有界输入语义没有改变。
## 页面读取与发布(2026-10-05)
对应 K01 的来源投影及 K02、K05、K06。`supervision:overview`、`supervision:graph`、`supervision:stories` 的生产 IPC 校验后分别调用 `listSupervisionResultsAsync`、`getSupervisionGraphAsync`、`getSupervisionStoriesAsync`。它们复用 `readonly-query-worker` 的 AssistantDatabase 独立只读连接,每次领域读取包在一个 `readSnapshot` 内;故事响应中的故事、经验、未归属计数及撤销状态来自同一 SQLite 快照。调用前已提交的写入对下次读取可见。
文件数据库缺少 Worker 配置、启动失败、崩溃或处于原有退避期时,请求直接失败,页面沿用既有错误与重试显示;这三个入口不使用 `readWithFallback`,不在 Main 重跑重查询。内存 SQLite 测试保留同步分支。原工具 `readStoryGraphAsync`、来源详情及其他存储入口保持各自原有实现,没有增加存储进程或通用协议。
结果图谱和 scope 图谱的 `sources` 都只查询共享合同已有的 `id`、`title`、`occurred_at`,并按原表 rowid 顺序返回。正文、来源类型、locator 等仍完整保存在 `supervision_sources`,通过 `supervision:source`/`source-context` 按 ID 读取;历史结果、所有事件、对象和关联均保留。概览是否请求图谱由 Renderer 的页面加载规则决定,后端不截断图谱内容。
schema 62 在既有迁移事务中新增 `supervision_sources(result_id, occurred_at, source_type, id)` 和 `supervision_results(created_at)` 索引。前者供每份概览的首来源定位,后者支持结果时间及 rowid 倒序读取。升级沿用启动存储 Worker,保留表内容和 rowid,不重写正文,不触发历史转换或 `VACUUM`。新版本仍遵守既有的禁止旧客户端打开更高 schema 规则。
故事查询先按 scope 选择故事,再一次读取这些故事的事件成员;经验查询在 SQL 中筛选仍有形成事件、且与所选项目有关的经验,再一次读取其全部有效事件关联。经验的跨项目关联不会随项目筛选被裁掉。两类列表各至多两条 SQL,原列表和成员排序、人工调整状态及完整内容访问保留。
发布继续使用 Main 原有 `BEGIN IMMEDIATE` 事务。每个叶子写入后收集当时的实体、关系及来源引用,末尾只写一次 `graph_snapshot_json`;重复持久身份仍采用原 SQL 分组规则,保留首叶子快照值和全部叶子详情。结果、运行状态、来源、时间线与 checkpoint 仍一起提交或回滚。没有将写入迁到 Worker,也没有把一个完整结果分多次提交。大批量发布仍超过 Main 16 ms 预算,实测与剩余限制见[实施进度](./progress.md#2026-10-05-监督页面读取与发布局部优化)。独立存储服务的职责迁移继续留在[目标方向](../../architecture/desktop-storage-direction.md)。
## 监督者模型连接
`ApplicationSettings.supervisorModelProfileId` 只保存已有连接 UUID,`null` 或旧设置缺少字段时跟随 `RuntimeSettings.defaultModelProfileId`。Main 的 `resolveSupervisorRuntime` 从 `getResolvedSettings()` 读取连接和已有凭据,校验文本协议及认证后,用 `createModelProfileRuntime` 创建无工具直连 Runtime。Renderer 仅接收公开连接信息,不新增凭据存储或 IPC 方法。
@@ -61,7 +75,7 @@ schema 43 在结果上增加 `graph_snapshot_json`,保存当次实体名称、
`runSupervisionModel` 为提取、导航、故事、经验和建议共用一次临时错误重试,错误范围见[逻辑规则](./logic-design.md#监督者模型选择)。每次尝试使用新的 request/conversation ID 和隐藏任务,正文缓冲单独创建;用量按各次任务保存。失败后先清理计时器并释放临时会话,再等待;等待继续持有监督池槽位,受父 signal 和池取消控制,结束或失败后释放槽位。`error` 事件的 `status: cancelled` 优先于错误文字;抛出的结构化错误检查 status、statusCode 和 cause,字符串只识别明确的 HTTP 状态或已知网络/流中断格式。
直连文本请求保留 HTTP 错误的 status,并在响应流失败前交出已收到的用量;没有提供方用量时不估算补记。重试决策只在监督包装层,直连传输原有重试、OpenCode 和 Continue 策略不变。429 使用同一 500ms 等待,不新增 Retry-After、UI 或配置项。
直连文本请求保留 HTTP 错误的 status,并在响应流失败前交出已收到的用量;没有提供方用量时不估算补记。监督响应超容量后立即 abort 并清空正文,最多继续消费 1 秒,仅保存用量事件,不累积后续文本;随后请求关闭迭代器、释放临时会话和池槽位,迟到事件不再写入。容量错误保持原文,不被随后到达的超时或流错误替换,也不重试;已缓冲完成响应和中止响应均沿用原 call ID 记账。重试决策只在监督包装层,直连传输原有重试、OpenCode 和 Continue 策略不变。429 使用同一 500ms 等待,不新增 Retry-After、UI 或配置项。
`supervisorModelConcurrency` 为 1 至 4 的整数,默认 1。报告和监督整理共用 Main 内 `SupervisionModelPool` FIFO 池,普通聊天不入池;降低上限不终止在途请求。报告槽位从领取前持有至报告完成或无变化提交后,在下游监督开始前释放,避免并发 1 时相互等待。监督服务外层运行仍串行;该池不是完整分块调度器,也不提供跨聊天与监督的提供商全局并发、RPM、TPM 或 Retry-After 控制。
@@ -12,6 +12,8 @@
工作回顾与故事线图谱在主页签下方共用回顾上下文栏。历史选择器最多占 280px,收起时只显示生成日期时间,随后是“刷新”和“新回顾”,不在右侧重复范围和覆盖区间。展开复用共享锚定单选菜单,每项显示生成时间及该结果自身的真实范围、覆盖起止时间,长说明换行;当前项带选中标记,触发按钮的无障碍描述保留所选结果的完整上下文。即使只有一条结果也显示选择器。切换页签和图谱模式时,这一栏的位置、内容和选择保持一致,空态及读取失败时仍可访问。
首次打开工作回顾只读取回顾及其范围的故事摘要,不等待图谱。进入故事线图谱时才读取所选结果的图谱;读取期间在图谱页显示加载状态,返回工作回顾仍可阅读及选择历史。工作回顾与图谱共用已读的故事数据,普通页签切换不重复读取。当前结果的图谱在切换页签时保留;切换历史结果、显式刷新、实体或关系修改后的刷新会使旧图谱失效。来源列表只需标题和时间,点击来源才读取完整正文。
点击“新回顾”才展开范围、时间、发起按钮和重新整理菜单。表单位于上下文栏下方,以次级表面区分新请求与已选结果;再次点击收起并保留输入。工作回顾正文铺满页面壳层,不另设阅读宽度上限。容器宽于 860px 且有辅助内容时,左侧约占三分之二,显示工作总结和本次变化;右侧显示未解决事项、故事进展和经验,以弱分隔线分区。没有辅助内容时只保留全宽正文。窄屏按正文、辅助内容顺序排列为单列。摘要保留原有段落和换行,故事摘要保留“显示更多”及进入图谱的选择。
新回顾运行中保留已显示结果及其日期、范围,范围、时间、发起和刷新控件保持可用。手动发起、完成、暂停、取消、无变化和失败统一使用应用 Toast;重复点击发起提示已有运行,不新建或排队。同一回顾操作的通知互相替换,关闭通知不暂停或取消执行。成功和信息沿用共享自动消失时间,错误由用户关闭。页面不保留操作状态条或错误横幅,加载历史结果、切换页签和重新进入页面不触发回顾通知。
@@ -94,9 +96,9 @@ Modal 按[共享表单 Modal 规则](../../../UI-DESIGN.md#615-表单-modal)复
没有自动计划也能手动回顾;成功结果显示自身冻结的范围、时间和未解决事项,可选择历史结果或进入图谱。来源筛选由本次回顾范围和时间限制;图谱详情只列出所选事件的关联来源,实体或关系列出相关事件的来源。独立的来源类型选择器尚未实现。
选择历史结果后,图谱读取该结果的成员和内容,历史菜单显示其回顾区间和生成时间。切换时清空旧图谱和详情并显示加载状态,迟到请求不得替换新选择。确认、修订和移除反馈更新当前所选结果,后续自动回顾不改写旧结果。
选择历史结果后,历史菜单显示该结果的回顾区间和生成时间,旧图谱和来源详情清空。在图谱页选择历史时立即读取对应成员和内容;在工作回顾页选择时,图谱读取留到下次进入图谱页。迟到请求不得替换新选择。确认、修订和移除反馈更新当前所选结果,后续自动回顾不改写旧结果。
切换语言或手动回顾完成引起刷新时,同时解除被替代的来源读取、实体和关系操作的等待状态。新图谱读取完成后,历史选择、刷新和详情操作恢复可用;旧请求迟到时既不显示旧内容,也不能解除后来发起的请求的等待状态。
切换语言或手动回顾完成引起刷新时,同时解除被替代的来源读取、实体和关系操作的等待状态。工作回顾在回顾读取完成后恢复历史选择和刷新;图谱页等待对应图谱读取完成后恢复操作。图谱失败只在图谱页提示并提供重试,不阻止阅读已读回顾;旧请求迟到时既不显示旧内容,也不能解除后来发起的请求的等待状态。
手动“回顾当前进展”新建回顾,活动页“继续”恢复原 run;运行中再次发起按上文显示提示,停止清理结束后才能开始新回顾。失败保留上次结果、本次范围和成功批次;已取消回顾不能继续。手动操作不推进自动位置,删除报告也不重置自动位置。
@@ -131,6 +133,8 @@ Modal 按[共享表单 Modal 规则](../../../UI-DESIGN.md#615-表单-modal)复
左侧使用共享 PageTabs 切换“事件 / 实体 / 关系”,显示各类数量,一次只展示一类列表,无记录时显示本次回顾的分类空态。页签固定在顶部,列表占满剩余高度并独立滚动。切换页签不清除当前图谱选择;从画布、事件浏览或详情选择对象时同步切到对应页签。支持方向键、Home/End 和面板键盘聚焦。
事件、实体和关系长列表复用共享列表窗口,只挂载视口附近的行,并保留选中行和焦点行。记录总数、完整名称及末条记录均可访问,不以截断记录提速。列表内上、下方向键移动焦点,Home/End 到首、末条,Enter 或 Space 选择;Tab/Shift+Tab 按完整记录顺序移动,在首尾离开列表。键盘移动跨过未挂载的行时,先挂载目标行再聚焦。行高测量或窗口变化不得把刚聚焦的可见行移出视口;用户主动滚走时保留焦点行,但不把滚动位置拉回。
使用 `supervisor` 页面壳层,最大内容宽度为 `1440px`。当前生产图谱左侧为紧凑的事件、实体和关系列表,中间为 SVG 画布,右侧为详情与来源。桌面列宽为 `216px minmax(0, 1fr) 280px`,画布获得主要空间。外圈事件按发生时间排序后等距逆时针排列,不表示实际时间间隔;起止之间保留缺口和方向箭头。内部显示真实实体名称,实线仅连接实际事件与实体,虚线仅连接实际实体关系。
高度按窗口剩余可见空间计算,底部保留约 24px 边距(不小于页面底部内边距)。平铺图最低 660px,时间螺旋最低 320px;低于最小高度时页面允许纵向滚动。中间画布不内部滚动,工具栏与事件浏览栏之外的空间给图;左侧列表和右侧详情各自滚动。两种视图的图例均使用共享 InlineHelp 的纯 i 图标,保留可访问名称和 title;点击或 Enter/Space 固定展开,再次点击、Escape、点击或聚焦外部关闭。平铺可见数量仍在画布角落;螺旋左下角不显示圈长及圈数,这些信息收进图例。窗口尺寸及上方表单展开、收起后重新计算可用高度。
@@ -371,6 +371,12 @@ Runtime 包。2026-09-19 完整桌面验证覆盖联合包安装、CN 项目创
阶段。实现规则见[共享进程与 Session 所有权](../assistant-workbar/runtime-process-reuse-technical-design.md#61-连接与所有权),
本次源码 Host 验证及调用计数见[统一执行进度](../unified-execution/progress.md#2026-10-05-runtime-生命周期审查修复)。
同日 K07 补齐模型路由准备后的取消:broker 关闭及终态提交保持原顺序,原生路由
撤销的 HTTP 等待移出全局控制队列。安装晚于取消完成时,以捕获的 transport、Session
和 operation 再次撤销该路由;原生 helper 只删除 operation 匹配的项。路由 HTTP 请求
仍有 10 秒上限,transport 退出或释放时中止未完成请求。当前源码的 OpenCode/Continue
实机证据见[局部修复验证](../unified-execution/progress.md#2026-10-05-k07k08-局部修复)。
## ACP 与断线
- 管控面只负责有界 JSON-RPC:连接、查询、启动、取消、关闭和重连。数据面只负责
+4
View File
@@ -138,6 +138,8 @@ Token 使用现有加密凭据存储,保存后不在普通设置快照中返
已生成并保存的结果沿用现有发件箱有限重试,发送失败与任务执行失败分别记录。补发回复不重新调用模型或重跑任务。用户阻止 Bot 等不可恢复错误应停止无效重试。
已有 webhook 或另一实例占用轮询时,已确认 Bot 身份且目标匹配的待发回复仍可发送;接收冲突本身不能永久终止这些回复。发送内部重试达到上限后,发件箱的下一次尝试仍须遵守最近一次 429 响应的等待时间,停用可取消等待。
同一驱动实例内且分段进度仍在缓存时,多段回复从未发送部分继续。进度不跨重启持久化,重启或缓存淘汰后补发可能重复已发送段落;网络中断造成平台接收结果不明时也可能重复。首版不承诺平台级发送幂等。
### FR-10 启停与历史保留
@@ -162,6 +164,8 @@ Telegram 待接收更新最多保留 24 小时,超过平台保留期的离线
状态和错误推送只更新状态区域,不覆盖尚未保存的 Token 或白名单草稿。解决轮询冲突后可重新保存当前配置以启动连接;单独测试连接成功不改变接收状态。
接收正常而结果发送失败时,保留已连接状态并显示脱敏的发送错误。后续健康轮询不清除发送提示,投递成功后清除;接收也发生错误时优先展示接收原因,恢复后保留尚未清除的发送提示。
状态通过文字表达,不能只靠颜色。错误保留可操作原因,如 Token 无效、网络不可达、webhook 占用或另一实例正在轮询。测试成功、发送失败和模型失败使用各自准确文案,避免统一显示“连接失败”。
界面沿用 [UI-DESIGN.md](../../../UI-DESIGN.md) 的控件、通知、键盘和可访问性规则,不新增独立视觉体系。
@@ -55,6 +55,32 @@ npm test -- tests/telegram-channel.electron.test.ts
补充检查:钉钉错误码断言加入后,单独复跑 9 项通过;修改的 TS 文件 ESLint、Renderer typecheck 和 `git diff --check` 通过。早期全仓检查受到并行修改中的类型与 lint 错误阻塞,负责方修正后最终检查均通过;完整测试为 6,136 项通过、0 失败、87 跳过。汇总见[阶段修复验收](../../review/stage-audit-fixes-2026-10-05.md#最终检查)。
## 2026-10-05 K09 投递修复
本轮只修改 Telegram 驱动、Manager 及其测试,复用现有服务重试、SQLite 发件箱和状态 IPC。未改数据库结构、Main IPC、Runtime 或 Agent;本地与远程任务完成后的通道结果均使用同一发送路径,无需独立 Agent 修复。
修复前先运行持久回归:`telegram-delivery.test.ts` 的 3 项 loopback HTTP/SQLite 用例和 Manager 的状态用例共 4 项失败。启动 webhook 冲突时,同 Bot 的待发回复未发出且变为终止记录;403 后状态仍为 `running` 且无错误;连续 429 的最后响应要求等待 3 秒,下一次发送却只间隔约 1.010 秒。扩展的 Electron 设置 fixture 也在“已连接时显示发送失败”断言处失败。
修复后,启动阶段保留 `getMe` 确认的身份,已有 webhook 时发送同 Bot 的待发回复,其他 Bot 的记录仍被拒绝,轮询状态仍报告冲突。发送错误经脱敏后由现有状态区域显示,健康轮询不清除;发送成功清除投递提示但不覆盖轮询错误。驱动在内部重试耗尽后仍完成最后一次 `retry_after` 等待,再返回发件箱;新增取消用例验证等待可中断、记录保持 `pending / attempts=0`,重新启用后只补发一次且不运行 executor。
| 检查 | 最终结果 |
| --- | --- |
| 9 个通道/Renderer 聚焦文件 | 127 项通过,包含 4 项 loopback HTTP/SQLite 回归 |
| 现有 Electron 设置 fixture | 1 项通过;生产设置 UI、Preload、IPC、Manager、驱动与 SQLite,403 提示、已连接状态、Token/白名单草稿、重载恢复均通过 |
| 修改的 6 个 TS 文件 ESLint | 通过 |
| `npm run typecheck` | Main、Agent 通过;Renderer 被并行工作区的 `App.tsx:174` 未使用 `refreshArtifacts` 导入阻塞,未修改该文件 |
最终聚焦命令:
```powershell
npm exec -- vitest run src/main/channels/telegram-delivery.test.ts src/main/channels/telegram-channel-driver.test.ts src/main/channels/channel-manager.test.ts src/main/channels/channel-service.test.ts src/main/channels/channel-settings-store.test.ts src/main/channels/sqlite-channel-state.test.ts src/renderer/src/ChannelSettingsSection.test.tsx src/renderer/src/channel-status-store.test.ts src/main/channels/dingtalk-channel-driver.test.ts
npm exec -- vitest run tests/telegram-channel.electron.test.ts
npm exec -- eslint src/main/channels/telegram-channel-driver.ts src/main/channels/channel-manager.ts src/main/channels/telegram-delivery.test.ts src/main/channels/telegram-channel-driver.test.ts src/main/channels/channel-manager.test.ts tests/support/telegram-main.ts
npm run typecheck
```
Electron 最终运行使用 2 次本地 HTTP 模型响应;连同修复前失败运行和中间验证,本轮共 6 次本地 fixture 请求、0 次外部 Provider 请求、0 次真实 Telegram 收件人消息。未读取 `.env`,未运行生产构建。完整测试、全仓 lint 及合并后的类型检查由协调方执行;本节不声明全仓验收通过或性能提升。
## Main 性能对比
基线为 `78afec77758f3c175f8e2b80e5be4327e13b4689` 的临时 detached worktree,当前为包含 Telegram 改动的工作区。按基线、当前交替执行 `npm run perf:main` 各 3 次。环境为 Windows x64、Intel Core Ultra X7 358H、Electron 44.5.1、Node 24.21.0;每轮使用隔离数据库,包含 1000 个会话、20008 条生成消息、24000 条活动记录,每个场景预热后测量 15 次,无外部模型调用。
@@ -6,7 +6,7 @@
设置页经生产 Preload 和 IPC 调用 `ChannelManager`,由 `ChannelSettingsStore` 保存配置。Manager 按需加载 `TelegramChannelDriver`,向其注入 Electron `net.fetch`;驱动默认网络实现也使用 `net.fetch`。请求走 Electron 网络栈及系统代理,不依赖 Node 的 `HTTPS_PROXY` 配置,不使用 grammY 或 Telegraf。
驱动先调用 `getMe` 与 `getWebhookInfo`,拒绝已有 webhook,不删除外部配置。测试连接只做这两项检查,不启动轮询。启用后使用 `getUpdates`,`timeout=30`、`limit=100`、`allowed_updates=['message']`;普通请求超时 15 秒,轮询请求超时 40 秒。临时错误以 1~30 秒退避重连,429 按 `retry_after` 等待;其他 4xx 停止轮询并报告错误。
驱动先调用 `getMe` 与 `getWebhookInfo`,已有 webhook 时拒绝启动轮询,不删除外部配置。`getMe` 验证的 Bot 身份独立保留;启动 webhook 冲突与运行中的轮询冲突均不阻止身份匹配的已保存回复发送,也不因此终止其发件箱记录。测试连接只做这两项检查,不启动轮询或改写运行中的身份。启用后使用 `getUpdates`,`timeout=30`、`limit=100`、`allowed_updates=['message']`;普通请求超时 15 秒,轮询请求超时 40 秒。临时错误以 1~30 秒退避重连,429 按 `retry_after` 等待;其他 4xx 停止轮询并报告错误。
## 身份、执行与保存
@@ -29,7 +29,7 @@
生成图片后,executor 先把 `artifactIds` 写入桌面会话,再向 `ChannelService` 返回通道契约中的状态、正文、错误和附件。图片字节保留在附件中;桌面成果 ID 不进入严格校验的通道结果,避免成功任务被误报为“无效结果”。该转换位于本地和远程 Runtime 事件处理之后,不改变 Runtime 或 Agent 协议。
驱动按最多 4096 个 UTF-16 码元拆分纯文本,并避开代理对中间位置;不设置 `parse_mode` 或引用原消息。分段进度只在驱动内存保存,最多 128 条,停止时清空。单次发送的 429 最多等待重试 3 次,仍失败则交回发件箱处理。跨重启补发与不确定网络结果允许产生重复段落。
驱动按最多 4096 个 UTF-16 码元拆分纯文本,并避开代理对中间位置;不设置 `parse_mode` 或引用原消息。分段进度只在驱动内存保存,最多 128 条,停止时清空。单次发送遇到 429 最多额外尝试 3 次;最后一次仍被限流时,先等待该响应的 `retry_after`,再将失败交回发件箱,避免服务层提前重发。等待沿用调用方与服务生命周期的取消信号;停止期间不增加失败计数。跨重启补发与不确定网络结果允许产生重复段落。
停用会中止轮询、发送等待、活动执行与排队工作,等待活动操作结束后报告停止。清除凭据时设置页同时提交 `enabled=false`;底层拒绝启用但无 Token 的配置。Token 替换先验证,失败不停止旧服务。项目与历史不因停用或清除凭据删除。
@@ -37,6 +37,8 @@
Manager 通过 `{ channel, status }` 增量 IPC 推送状态,忽略旧服务实例的迟到事件。Telegram 连接状态由驱动维护,发送成功不会覆盖轮询错误。状态值为 `disabled`、`starting`、`running`、`stopped`、`error`;重连使用 `starting` 加错误说明,未配置由界面结合凭据状态显示。
Manager 在当前服务实例内保留最近一次发送错误,经既有脱敏函数处理后,附在连接状态的错误说明中。轮询正常时仍显示已连接,同时提示 `Telegram sending failed: ...`;健康轮询不会清除该提示。若同时有轮询错误,优先显示轮询原因;恢复接收后仍可看到未清除的发送错误。后续投递成功清除发送提示,停用或替换服务释放该实例的错误状态,无新增持久化记录或 IPC 字段。
Renderer 的 `channel-status-store.ts` 订阅事件,selector 只向状态区域提供对应通道数据。首次快照不会覆盖已收到的较新事件;状态更新不重新加载整份设置,不重置 Token 或白名单草稿。配置页复用共享页签、开关、项目设置字段和通知控件。
首次配置说明、空白名单提示和发送者 ID 字段帮助均明确:ID 可先留空,先启用并保存通道,连接后私聊自己的 Bot 发送 `/whoami`,取得本人数字用户 ID 后填入并再次保存。仅测试连接不启动消息接收;用户名和 Bot ID 不能代替发送者 ID。白名单为空时仍只提供帮助,不接受 AI 请求。
@@ -1,5 +1,71 @@
# 进度与证据
## 2026-10-05 K07/K08 局部修复
最终集成补注:排除并行删除功能后,待提交内容的隔离全量测试为 6,212 项通过、0 失败、87 跳过,类型检查及 lint 通过。下文分组阶段的失败轮次不是最终结果;完整证据见[KISS 验收](../../review/kiss-local-fixes-2026-10-05.md#最终检查)。
K07 已修复共享 Runtime 在模型路由清理阶段阻塞控制队列、取消后迟到安装留下路由的
问题。broker 关闭与终态提交顺序保留,原生路由撤销在队列外完成;迟到安装按原
transport/Session/operation 再次清理。transport 退出中止未完成路由 HTTP 请求。
规则见[Session 所有权](../assistant-workbar/runtime-process-reuse-technical-design.md#61-连接与所有权)。
K08 在监督正文超容量时立即取消,清空正文并只消费用量事件,清理等待上限 1 秒。
原容量错误优先于后续流错误与超时,不重试、不重复记账;超时以后返回的事件不写库。
该路径由桌面直连模型执行,远程项目的监督也使用同一实现;具体用量规则见
[监督模型调度](../conversation-supervision/technical-design.md#模型阶段超时与调度)。
### 回归与检查
首轮七项持久回归在修复前全部失败:取消/截止时间后的 peer 查询仍等待路由释放,
迟到安装残留,OpenAI/Anthropic 已收到的 12 input/3 output 用量丢失,以及超限后的
记账消费与清理边界不符。修复后增加缓冲完成响应、transport dispose/exit 回归,
共十一项新增用例通过。检查取消终态只有一条、peer 仍 running、无重复 Prompt、
用量只有一个 call,以及迟到事件不会在池槽释放后写入。
```text
npx --no-install vitest run src/main/assistant/supervision-model-retry.test.ts src/main/assistant/supervision-production.test.ts src/main/agent/model-runtime.test.ts src/agent-daemon/runtime-acp-backend.test.ts src/agent-daemon/agent-owned-acp-prompt.test.ts src/agent-daemon/protocol-server.test.ts
npm run typecheck
npm run lint
```
上述六文件 243 项通过、1 项跳过;12:22 的 Main/Agent/Renderer 类型检查及全仓 lint 通过。
并发工作树的完整 `npm test` 为 6,214 项通过、5 项失败、87 项跳过,耗时 870.20 秒;
失败位于监督布局、IPC 的 provider 重试、监督恢复和快照读取测试,K07/K08 文件均通过。
原始输出为批准临时目录中的 `k07-full-tests-1791174423486.log` 及同名 `.json`。
这轮结果不能当作全仓验收通过;未修改其他任务负责的数据库、IPC 或界面文件。
12:45 单独复跑 IPC provider 场景通过(1 项通过、190 项按筛选跳过);期间其他任务仍在
修改测试,后一次类型检查报 `supervision-ui-reads.test.ts` 的 `publishing` phase 类型
错误。最终整体检查需由合并工作树的负责方复跑,不能用本模块的通过结果替代。
### Linux x64 当前源码
复用前阶段 `a01-runtime-host` 的凭据与 SSH 固定身份入口,仅以 esbuild 生成当前
Desktop driver、Agent daemon 和 CLI/helper 测试 bundle。使用 Host 已安装的原生
OpenCode/Continue,在 `/root/tmp/gb-lifecycle-*` 专用目录运行,未安装生产包或更换
Host current。阻塞点只由 harness 注入;路由安装、撤销及 409 缺失校验通过真实 helper。
| 场景 | peer 查询 | 取消完成 | 外部文本 HTTP 请求 | 结果 |
| --- | ---: | ---: | ---: | --- |
| 首轮 OpenCode | 51 ms | 206 ms | 2 | 取消及迟到路由清理通过;后续工具恢复触及旧夹具的两次调用上限 |
| 最终 OpenCode | 13 ms | 176 ms | 3 | peer 完成、工具执行、detach/恢复、测试 Agent 提升、终态重放通过 |
| 最终 Continue | 46 ms | 194 ms | 3 | 同上 |
准确合计 **8 次外部文本提供商 HTTP 请求、0 次图片提供商调用**,八个响应均 HTTP 200。
最终每个 Runtime 包含一次短文本 peer 调用和两次工具/恢复调用,预算在 harness 中
显式设为三次;被取消的启动未调用提供商。三个运行结束时所属进程均为 0,额外的
只读 SSH/SFTP 检查确认本轮时间范围内的专用目录残留为 0,该检查新增模型调用 0 次。
延迟是单次场景观察,不作为 p95 或桌面性能预算结论。
最终 Host 事件通过现有批处理器和隔离 SQLite 校验:OpenCode 41 条事件、21 个
checkpoint;Continue 16 条事件、8 个 checkpoint;重放去重通过。没有读写用户生产库。
原始脚本位于 `C:/Users/jiang/AppData/Local/Temp/opencode`:
`k07-runtime-host-build.cjs`、`k07-runtime-host-run.cjs`、`k07-host-cleanup-build.cjs`。
日志位于其 `k07-runtime-host/` 子目录:`opencode-1791173985798.log`、
`opencode-1791174187014.log`、`continue-1791174187010.log`、`inspect-1791175306647.log`。
验证不覆盖签名安装包、macOS、Linux arm64、完整 UI 操作或生产规模性能;远程修复需
部署包含本次源码的 Agent,当前未提交或发布。
## 2026-10-05 Runtime 生命周期审查修复
A01(远程启动阻塞控制队列)和 A02(本机 OpenCode 提交失败后等待静默 SSE)已修复。