OpenProgram Docs
OpenProgram · 运行时 / WebUI · 工程设计

Web 运行时可靠性与性能设计

本文统一记录 Web 页面刷新、会话加载、WebSocket 消息分发、后台错误展示、EventBus 通知、异步 Job 状态、静态资源加载和本地状态目录的已验证问题,并给出相互兼容的目标合同、任务边界和量化验收标准。

受众:Runtime / Server / Web 维护者 状态:TASK 1 partial;TASK 3 complete;TASK 6 complete;其余任务待执行 证据:源码审计、真实会话测量、浏览器测量、定向测试
主结论:持久错误状态尚未实现、刷新时重复发送完整会话,以及 EventBus notify 可能同步阻塞或丢失仍是 P0。命令异常隔离和首组管理 action 迁移已经完成两切片,但其余旧错误路径与恢复合同仍待实现。WebSocket 广播无界问题已有完成规格审查、质量审查和完整 Python gate 的 TASK 3 实现。TASK 6 已完成本地依赖、静态压缩、延迟表面切分和资源预算实现,并通过两类审查、完整 Python gate、默认 App 可视验收和本地 browser trace。
范围边界:本文不修改 Agent 推理逻辑、DAG 存储语义、模型提供商协议、权限模型或多用户身份模型。已有专题文档继续负责各自的详细语义;本文只定义它们在 Web 运行时可靠性和性能方面必须共同满足的合同。
01

实测基线

以下数据来自当前默认状态目录、当前最大会话、直接执行会话加载处理器以及禁用浏览器缓存的本地页面加载。它们是后续前后对比的固定基线,不代表所有机器的绝对时间。

最大的 session_loaded
11.66 MB
单个未压缩 JSON 帧;消息约 9.75 MB,Graph 约 1.91 MB。
Hydration wall time
≈313 ms
单次直接执行 handle_load_session;不含网络、浏览器解析与 React 状态写入。
Visible / graph rows
112 / 1,790
页面只展示 112 条消息,但首次加载仍发送完整 Graph。
冷启动静态传输
3.42 MB
66 个资源、43 个脚本;响应未使用 gzip 或 Brotli。
Cold navigation
≈123 ms
DOMContentLoaded 约 39 ms;本机 HTTP 本身不是主要等待项。
Worker 常驻内存
≈437 MB
短采样 CPU 从约 4.1% 降到 0.3%,未观察到持续 CPU 饱和。
State directory
6.3 GB
chrome-profile 约 6.1 GB;sessions 约 8.5 MB。
验证基线
80 + 115
Python 定向 80 项与 Web 115 项通过;现有测试未覆盖本文列出的关键缺口。
测量方法限制后续使用
会话载荷使用真实 SessionDB 数据和只记录发送内容的 WebSocket 替身执行公开 handler。关闭冷 context stats,避免把独立后台刷新计入首帧。同一会话执行 before/after;记录总帧、messages、graph、context tree 各自字节。
浏览器加载默认端口页面,缓存关闭,读取 Navigation/Resource Timing 和响应头。内置诊断浏览器没有默认 App 的 owner credential,因此未执行已认证聊天操作。用于静态资源体积、CSP、压缩和脚本数量验收;真实聊天由默认 App 验收。
EventBus 日志统计全局与会话 events.jsonl 的类型和字节占用。日志内容随真实使用变化。后续比较事件行数、重复 ID、大型字段和写入延迟。
worker 资源对默认 worker 进行短时间进程采样并统计状态目录。不是长时间 workload profile。用于排除持续 CPU 饱和;正式实现后增加事件循环延迟和队列指标。
02

当前数据流与失效位置

当前系统同时存在浏览器 WebSocket、原始 WS frame、EventBus、AgentEvent、SessionDB 和未接入生产的 MessageStore。下图标出用户操作从前端到持久化及后台事件返回前端的主要路径。

当前 Web 运行时数据流 浏览器命令进入 WebSocket handler,调用 session、runtime 和 job action。后台事件通过 EventBus 的 ws.frame 订阅者广播。红色节点表示当前缺少错误展示、背压或增量恢复。 Web / Electron 渲染进程 use-ws · 会话存储 · UI WebSocket 接收循环 error contract 不完整 WS action handlers 会话 · 运行时 · 设置 JobRunner / channels 工作线程 · 状态事件 Session hydration 同步全量 messages + graph EventBus notify 同步 / async 可丢 SessionDB / 事件日志 durable rows · JSONL Global broadcast 无有界队列 / 不检查 Future 聊天流式 reducer rAF 合并 · 行回收 Generic UI dispatch type=error 无统一消费者 MessageStore v2 seq / delta 已存在但未接入 commanddispatchspawn/status load_sessionemitws.frame persist/logsend_textchat frames

红色表示已验证缺口;黄色表示设计与实现语义不一致;绿色表示当前已有合理优化。MessageStore v2 是可复用候选,不应再引入第三套增量协议。

03

问题清单与用户影响

优先级依据数据丢失、错误不可见、事件循环阻塞、资源无界增长和普通用户触发概率确定。每项都指向当前源码,不把设计提案写成已经实现的事实。

P0 会造成用户请求结果不可知、关键事件丢失、无界资源增长或跨会话阻塞P1 稳定复现的性能、诊断或兼容缺陷P2 容量和长期运维问题,不阻塞前两类修复
ID优先级当前行为用户可见影响根因与证据目标状态
R1P0公共 dispatcher 以及 agent/channel/settings 管理 action 的未处理存储异常已在发送前写入 profile-scoped UserErrorStore,并发送最终低敏 operation_error;runtime/session 的顶层 type="error"、project 的空结果或内嵌 error、channel validation/QR 的专用错误形态和 chat reply error turn 仍保留,前端尚无持久全局 error store。已迁移管理操作不再无提示,且传输失败后仍有服务端记录;其余旧形态仍缺少一致展示、确认和刷新恢复。server.py 统一处理未捕获 action 异常;user_errors.py 保存有界低敏记录;ws_actions/ 仍有待迁移形态。所有用户相关失败进入统一错误合同,并按 scope 持久显示。
R2P0公共 dispatcher 的 unknown action 与 handler exception 已生成最终低敏 operation_error,每次失败都有独立 error/correlation ID、明确 scope 和 UTC 时间;服务端已提供 principal-scoped list、exact-ID acknowledge 和多窗口 recovery 协议;前端继续兼容旧 action_error。现有 handler failure 不再误报为缺少后端处理器;发送前记录允许后续恢复;未知或未来 code 使用不含 wire message/error 的通用失败提示。server.py 使用共享安全构造器、记录 correlation metadata,并在持久化失败时以 state_recovery_required 关闭连接;user_error.py 实现严格分页、确认关联和同 principal 恢复广播;action-error.ts 负责新旧 envelope 归一化。validation code、其余 action 迁移与前端 hydration/展示仍属于 TASK 1 后续切片。
R3P0agent/channel 的列表、增删、binding 和 alias 存储异常不再转换为空数组或静默成功;异常由公共 receive loop 持久化并返回一个低敏 operation_error,且不发送对应正常列表或成功通知。协议层现在可以区分正常空状态与该组管理故障;页面不会收到替换旧数据的正常响应,但尚无 degraded 标记和刷新恢复。agent.py、channel.py 的存储异常上抛到公共 dispatcher;记录按已认证 principal 隔离。持久记录和日志 correlation ID 已实现;degraded 标记、确认和恢复仍由 TASK 1 后续切片完成。
R4P0Job broadcaster 已发送 error,Branches 状态和 AttachCard 没有保存/显示详情。后台任务只显示 errored,刷新后无法知道原因和是否可重试。runner.py 与 branches/index.tsx 字段不对称。错误摘要随 Job 实体持久化;卡片显示安全原因、重试属性和日志关联 ID。
R5P0连接恢复和会话切换发送完整 session_loaded。大会话刷新传输和解析超过 11 MB,页面响应时间随历史增长。session.py 一次发送 messages、graph、context tree;客户端重连直接调用 load_session。首屏 manifest + 当前链;Graph、上下文树和大字段按需;重连按 revision/seq 增量恢复。
R6P1会话加载在 WS event loop 内同步聚合、排序、建图和 JSON 序列化。一个大会话的刷新可延迟同一 worker 上其他 socket 的命令处理。_fn_run_siblings 中反复 all_msgs.index,整体 handler 无计算/发送分离。纯计算快照在线程执行;异步层只负责按序发送和状态校验。
R7P0TASK 3 候选实现已将生产连接收敛到单一 sender,并对 queued + in-flight 帧施加 256 帧 / 4 MiB 上限。慢客户端的同时 send_text 从 RED 的 400 降为 1;普通更新可合并,终态使用关键预留。ws_delivery.py 与公共 handler 慢客户端回归。complete;规格、质量和完整 Python gate 均为 PASS。
R8P1重连固定每 2 秒尝试,成功后重新加载整个会话。服务重启或网络异常时多个页面会同步重试并重复传输完整状态。use-ws.ts。指数退避、随机抖动、在线恢复后发送已知 revision/seq;不重复生成 UI 错误。
R9P0EventBus notify 的普通 handler 在 emitter 线程同步执行;async handler 无 event loop 时被跳过。慢插件阻塞 Agent;部分后台事件没有任何消费者收到。events/bus.py。notify 总是进入专用队列;gate 保持同步并执行明确预算。
R10P1事件日志仍同步写;tool.before 的重复行已经修复并通过两类复审;注册表测试仍漏掉跨行 emit。高频工具输出仍增加同步 IO;未注册事件仍可能无法由测试发现。event_log.py、test_event_gate.py。实现异步批量日志、AST 注册检查和大字段摘要化。
R11P1TASK 6 已移除产品运行时 CDN 资源并将 marked/KaTeX 纳入构建;Starlette gzip responder 为可压缩响应协商 gzip,并按响应 MIME 排除已压缩格式。源码、legacy template、构建产物和 HTTP public entry 已有自动化回归;browser home 不再自动请求 Google favicon;规格、质量、完整 Python gate、默认 App 和本地 browser trace 均通过。markdown-render.ts 与 frontend.py。complete;保持 self-only CSP。
R12P2Chrome profile 没有容量展示和保留策略;当前约 6.1 GB。长期使用后占用磁盘,用户无法从产品中判断来源或安全清理范围。最大目录为浏览器 on-device model、Default profile 和 model store。展示分类容量;只提供显式、可恢复的清理操作;不自动删除登录态。
04

统一错误合同与前端展示

“后台报错前端必须显示”定义为:所有影响用户请求、持久状态、后台任务或可恢复操作的失败都必须进入可观察错误记录。它不要求把每个内部降级或重试都显示成 Toast,也不允许把 traceback、文件内容或凭据发送到浏览器。下列 schema 是唯一最终 wire 合同;TASK 1 服务端已为公共 dispatcher 和首组管理 action 生成完整字段、在发送前记录,并实现 principal-scoped list、exact-ID acknowledge 和多窗口 recovery。前端 hydration、展示和其余错误源仍按后续切片实现。

// WebSocket envelope;nullable 字段使用 null,不省略语义必需字段
{
  "type": "operation_error",
  "data": {
    "error_id": "err_opaque_unique_id",
    "request_id": "client_request_id_or_null",
    "scope": "session | job | settings | channel | agent | transport | system",
    "code": "stable_machine_code",
    "message": "safe user-facing summary",
    "action": "safe_action_or_null",
    "session_id": "session_id_or_null",
    "operation_id": "action_job_execution_id_or_null",
    "retryable": false,
    "severity": "info | warning | error | fatal",
    "correlation_id": "opaque log lookup id",
    "occurred_at": "ISO-8601 UTC"
  }
}
错误合同字段/状态唯一语义
request_id由客户端为请求/响应 action 生成;成功或失败必须回传同一值并结束 pending。operation_id 标识服务端实体,不能代替 request ID。
error_id每次失败唯一。Toast 去重键为 scope + code + session_id + operation_id,5 秒内相同键只显示一次;错误记录仍分别保留。
UserErrorStore使用当前 profile 下独立 SQLite 文件,按已认证 principal 隔离;创建时间起最多保留 7 天且每个 principal 最多 1,000 条。开放记录按 occurred_at、error_id newest-first 严格分页,确认操作按 principal + exact error_id 事务性幂等关闭。发送必须发生在事务提交之后;记录失败时不发送伪 durable envelope,而关闭连接并要求 state recovery。
severityinfo 只进入诊断;warning 显示局部 degraded 状态;error 显示卡片和一次 Toast;fatal 显示持久阻断状态并停止自动重试。
生命周期请求成功只结束对应 request_id 的 pending。acknowledge_user_error 在请求连接内依次发送带同一 request_id 的 user_error_acknowledged 和 {type:"operation_recovered", data:{error_ids:[...], scope, operation_id, occurred_at}};持久关闭后,其他同 principal 窗口独立接收同一 recovery,跨连接不承诺相对顺序,请求连接发送失败也不得阻止其他窗口恢复。服务器必须列出要关闭的具体 error_id,前端不得按模糊 code 批量关闭;重复确认可再次发送同一 exact-ID recovery 以使窗口收敛,数据库关闭保持幂等。用户确认或实体删除也只关闭明确 ID;普通后续成功不能隐式清除不相关错误。
Job 失败job_status(status=errored) 是 Job 状态的唯一 wire 事件,前端由它生成 error-store 记录;不得再为同一失败发送第二个 operation_error。operation_error 只用于 Job action 请求本身失败。
事件类别前端行为持久性不得包含
当前会话请求失败对应卡片或控制项附近显示原因;首次接收显示一次 Toast。写入会话错误记录;刷新后仍可见。traceback、原始环境变量、未过滤 provider body。
后台 Job 失败会话列表显示错误标记;AttachCard/Jobs 显示摘要和可重试状态。跟随 Job 实体持久化。子进程完整 stdout、文件全文。
设置或管理操作失败保留上一个已知状态,显示操作失败;不把失败解释为空列表。错误中心保留到用户确认或后续成功。credential value、token、绝对 secret path。
可恢复内部降级默认不 Toast;相关表面显示 degraded 状态,诊断面可查询。结构化日志与指标。面向用户的重复提示。
传输断开显示连接状态;自动恢复期间不重复产生相同错误。仅在超过恢复预算或有未确认操作时进入错误中心。每次重连尝试对应一个 Toast。
服务端规范化

单一安全 payload 构造器

helper 只生成经过过滤的 error data,并负责字段校验、日志关联和 session routing。WS action/channels/system 将该 data 包装为 operation_error;JobRunner 把同一 data 嵌入唯一的 job_status,不生成第二个 error envelope。

前端错误存储

单一状态入口

use-ws 只解析并交给 error store;错误卡片、会话标记和 Toast 从同一记录派生,避免不同组件各自解释 code。

Action correlation

明确请求完成

需要请求/响应的 action 使用 request ID。成功和失败都结束 pending 状态,任何按钮都不能无限保持 loading。

兼容适配器

旧帧逐步迁移

迁移期间前端把现有 error、action_error 和 Job error 适配到统一记录;后端逐项切换后删除兼容分支。

05

会话加载与刷新设计

目标是让首屏成本由当前可见内容决定,而不是由整个会话历史决定。持久化仍以 SessionDB/DAG 为准;Web wire 只传页面当前需要的投影。

阶段返回内容触发条件缓存键
Session manifest标题、状态、head、revision、当前链 ID、计数、功能状态、错误摘要。切换会话或无有效 manifest。session_id + revision
Viewport messages当前可见链尾部消息和必要的 compaction/attach 引用。首屏或滚动到未加载范围。session_id + head_id + cursor
Graph summary节点 ID、边、角色、状态和最小标签,不包含消息正文与工具结果。用户打开 DAG 或分支面板。session_id + graph_revision
Node/detail单节点上下文、完整工具输出、runtime tree、附件详情。用户展开或导航到具体节点。session_id + node_id + content_revision
Reconnect delta已知 revision 后的持久提交,以及每个运行中实体已知 seq 后的状态和终态事件。连接恢复且服务器仍保留连续增量。客户端提交 known_session_revision 和 known_stream_seqs: {entity_id: seq}
Full recovery新的 manifest 和有限 viewport,不是完整历史。revision 不兼容、delta 已淘汰或客户端状态无效。重置本地 session projection
唯一来源:SessionDB/DAG 是持久状态的唯一权威来源。现有 MessageStore 只复用 block、seq 和 delta 计算代码,不成为第二个持久 store;不能复用的独立持久化和 singleton 接口在 TASK 4 删除。所有 full recovery 都从 SessionDB projection 生成。
协议量定义保留与回退
session_revision每个 session 单调递增的整数。一次 SessionDB/DAG 原子提交只增加一次;manifest、message header、Graph summary 和持久错误都标记其读取 revision。永久随 session 持久化。客户端 revision 高于服务端或 schema 不兼容时拒绝 delta 并返回新 manifest。
stream_seq每个运行中 message/job 独立单调递增;只描述尚未形成持久提交的 text/tree/progress 增量。当前 worker 每实体保留最近 256 个 delta 或 15 分钟,以先达到者为准。worker 重启把未完成实体持久标记 error/aborted,客户端随后执行 manifest recovery。
delta journal由持久提交生成的 from_revision → to_revision projection change;不保存完整正文副本。内存保留最近 512 次提交或 15 分钟。缺口超过保留范围时返回 manifest + 有限 viewport。
viewport cursor稳定二元组 (created_at, message_id),按当前 head 链逆序分页;首屏最多 100 条且未压缩 JSON 最多 512 KiB,以先达到者为准。向历史方向分页;head 改变时 manifest 给出新 cursor。单条消息超过剩余预算时只返回 header、摘要和 detail marker。
detail cursor单节点/工具结果每次最多 1 MiB 未压缩内容;更大结果按 UTF-8 安全边界分块并返回 next cursor。每次读取都重新执行 session/node/caller 授权;不得通过 cursor 跨节点或跨 session。
计算与发送的分离边界

同步线程读取不可变 SessionDB 快照、建立 ID 索引、计算当前链与 Graph summary,并返回普通数据对象。异步 handler 在发送前再次校验 session revision;若期间发生写入,发送 manifest 后追加 delta,或放弃旧快照重新读取。线程内不得直接操作 WebSocket。

需要删除的重复计算

预先创建 id → insertion_index,替代排序中的 all_msgs.index;兄弟分组按 predecessor 一次建立;同一请求只读取一次 session row;Graph summary 与消息正文分开序列化。

大字段与兼容

保留当前 32 KiB 工具结果 marker 和 get_full_tool_output 权限检查。进一步把 context tree、runtime blocks、Graph node content 和附件 metadata 纳入按需读取。旧客户端仍可请求旧 action,但服务端需要设置硬载荷上限并在超过时返回升级提示,而不是继续生成无界帧。

06

WebSocket 队列、顺序与恢复

浏览器标准 WebSocket API 不提供自动背压,因此服务器必须限制自己的发送队列。每个连接由一个 sender task 顺序发送,生产线程只执行线程安全的 enqueue。

帧类别示例队列规则溢出行为
不可丢终态error、question、chat final、job terminal、ack、permission result保持顺序,不覆盖,不跨 session 合并。无法在预算内排队时关闭连接并标记需要 state recovery;不得静默丢弃。
可合并增量text/thinking delta、tree update、progress、context stats按 session + entity + type 保留最新或合并相邻文本。删除被更新取代的旧增量,并记录 coalesced 计数。
快照sessions list、branches、settings、graph summary同一 key 只保留最新未发送快照。替换旧快照;不影响终态。
控制pong、auth/status、reload required小型独立预算,防止被大消息完全延迟。认证或协议错误立即关闭;pong 可丢弃旧值。

队列上限与关键预留

每连接限制 256 帧或 4 MiB,以先达到者为准;其中 64 帧和 1 MiB 只供不可丢终态。关键帧入队前先清除可合并增量,不能占用普通快照的旧版本。

发送所有权

只有 sender task 调用 ws.send_text。断开时取消 sender、释放队列并注销 focused session;其他线程不保留 WebSocket Future。

重连

指数退避并加入随机抖动;页面重新可见或用户明确重试时可提前触发。成功后先恢复协议状态,再恢复会话投影。

观测指标

记录 queue_frames、queue_bytes、oldest_age、coalesced、send_failures、disconnect_reason 和 recovery_mode,不记录帧正文。

关键帧恢复合同:source 在 enqueue 前持久化 chat final、job terminal、用户可见 error、question 和 permission request。所有用户可见 error 同时写入有界 UserErrorStore,按 error_id 查询,最多保留 1,000 条或 7 天;session manifest 只携带相关未关闭 ID,重连通过 list_user_errors(cursor) 读取详情。transport close 由客户端根据 close/timeout 本地生成,不依赖服务端错误帧。chat ack 只在用户消息落盘后发送;其他 request ack 必须能通过 request_id 幂等查询或重放。若关键预留仍满,服务器关闭该连接并标记 state_recovery_required;客户端重连后从 manifest、UserErrorStore、pending-question registry、Job store 和 request result 查询恢复,不能依赖未发送的内存帧。
MDN WebSocket

标准 WebSocket API 不提供自动背压;继续使用兼容性最好的 WebSocket,但必须在应用层限制队列。

MDN bufferedAmount

客户端可用 bufferedAmount 监测未发送字节,但该值不能替代服务端每连接队列和恢复协议。

Python asyncio.Queue

采用正数 maxsize 的 FIFO 队列;跨线程入口使用 event loop 的线程安全调度,不直接跨线程操作 Queue。

07

EventBus 执行模型与日志

EventBus 保留两种明确语义:gate 是同步决策;notify 是异步观察。二者不能共用“当前线程直接调用 handler”的执行方式。

属性GateNotify
用途在动作发生前允许或拒绝。记录、更新 UI、触发后续非阻塞工作。
执行位置emitter 线程;默认总预算 events.gate_timeout_ms=50。EventBus 自有 event loop;同步 consumer 在线程池执行,async consumer 在该 loop 执行。
失败维持现有 fail-open,并写结构化警告和 gate 结果。隔离消费者;重试只由消费者策略决定;不得静默跳过。
顺序注册顺序。同一 session + entity 有序;不同 key 可并发。
背压不排队;超过预算时跳过剩余 gate 并记录。有界队列;关键事件不能被普通遥测占满。
日志事件和 gate verdict 合并成一条记录。异步批量写;大型 payload 使用长度、摘要和受控片段。
Notify 队列合同规则
顺序键优先使用 session_id + entity_id;只有 session 时使用 session;两者都没有时使用 type + origin。同一键严格 FIFO,不同键可并发。
容量普通队列最多 4,096 事件;关键状态队列最多 512 事件。关键类型是 operation error/recovered、question、job terminal、turn end 和标记为 critical 的 ws.frame。
满队列progress/context stats 等遥测按键合并或丢弃并增加明确指标;关键队列满时 source 的持久状态仍是恢复依据,bus 写入 emergency loss marker 并通知 Web 连接执行 state recovery。不得只打印 stderr 后继续假定已投递。
关闭与重启正常关闭最多排空 2 秒;剩余 notify 记录 shutdown loss marker。关键业务状态在 emit 前持久化,重启后由各 store 的 recovery 扫描恢复,不把 EventBus 当 durable queue。
Gate 安全边界EventBus gate 只承载插件/工作流的附加决策,维持 fail-open。权限、认证、sandbox 和凭据校验不使用该 fail-open gate,继续由各自强制边界拒绝。
日志唯一性事件根据 session 是否存在只写 session log 或 global log 之一;同一目标文件中一个 event ID 一行。gate event 在 verdict 完成后写一次,不能先由 notify 写一行。
Registry integrity

AST 级注册检查

测试解析 Python AST 中的 emit_safe、make_event 和常量事件名,覆盖跨行调用;动态名称必须显式登记例外和消费者。

Single record

事件只记录一次

tool.before 的观察与 gate 共享同一 Event;最终日志包含 gate 字段。不能先写普通行再写 verdict 行。

Payload policy

高体积字段摘要化

tool.after.result_text 默认记录字节数、错误状态、哈希和有限预览。完整结果仍在 SessionDB/工具结果存储,不复制到事件日志。

UI bridge

错误不是原样转发所有事件

EventBus 到 Web 的桥只转发经过注册的用户相关 envelope。内部事件日志和用户错误流相关联,但不能直接暴露任意 payload。

08

前端渲染、刷新与静态资源

当前流式 reducer 已按 animation frame 合并更新,MessageRow 按 ID 订阅并对长列表执行行回收。这些机制继续保留。优化重点是减少进入 store 的数据、消除无效外部依赖和限制首次资源体积。

区域保留修改验证
流式消息每个 animation frame 合并 text/thinking;单消息 selector。终态到达前先 flush 对应 pending delta;错误记录与回复状态原子更新。高频 delta 下每帧最多一次相关 store update;终态内容不缺失。
长列表MessageRow memo、viewport 行回收、稳定 ID 数组。首屏只 hydrate viewport 数据;Graph 面板不再强迫 chat store 保存完整 Graph 正文。1,000+ 节点会话首屏 DOM 和 React commit 不随完整历史线性增长。
错误 UI现有 Toast 组件和会话 unread 状态。增加 error store、持久错误卡片和后台会话错误标记;Toast 去重。当前/后台/刷新后三种场景都有自动化断言。
marked / KaTeXMarkdown 和数学语法行为。从前端 bundle 本地导入,删除 CDN loader 和 WS 初始化等待。严格 script-src 'self' 下零 CSP violation;离线可渲染。
静态响应同源静态 export 和缓存头。为文本、JS、CSS、SVG 增加 gzip;后续可加入构建期 Brotli 预压缩。Accept-Encoding 协商正确,已压缩内容不重复压缩,SSE 不压缩。
代码分割Next.js 静态 export。按 settings、DAG、browser、programs 等不在首屏的表面延迟加载。首次传输与最大 chunk 进入预算;交互后加载有明确 pending 状态。
Starlette GZipMiddleware

采用官方 ASGI middleware 的最小体积和压缩级别配置;排除 SSE 和已经压缩的格式。

MDN CSP script-src

'self' 与 CDN runtime script 不兼容。选择本地依赖,不放宽 CSP,也不增加 unsafe-inline/unsafe-eval。

拒绝 WebSocketStream 迁移

浏览器支持范围不足,而且不能解决服务端队列、持久状态、错误合同和增量恢复。保留标准 WebSocket 并补齐应用层控制。

09

运行指标、容量和状态目录

性能改动必须能在默认 worker 上持续测量。指标只记录计数、耗时、字节和关联 ID,不记录提示词、模型回复、工具输出或凭据。

WebSocket

  • 活动连接数和每连接队列字节。
  • send latency、失败数、coalesced 数。
  • 重连次数、恢复模式、全量恢复原因。

Session hydration

  • manifest/messages/graph 各自字节。
  • DB read、projection、serialize、send 耗时。
  • viewport hit、detail fetch、delta fallback 数。

EventBus

  • notify enqueue 与 handler duration。
  • 队列深度、失败、重试和丢弃原因。
  • gate 总耗时与超过预算次数。

Storage

  • sessions、logs、chrome cache、models、IndexedDB 分类容量。
  • 用户触发清理前展示目标和影响。
  • 默认不删除 Cookie、登录态、扩展数据和会话。
清理边界:浏览器 Profile 是用户状态,不得使用后台定时任务或启动钩子自动删除。可安全重建的 cache/model 与身份/扩展/IndexedDB 必须分开列出,并要求用户显式确认具体类别。
10

实施任务与依赖

每个任务独立提交、独立规格审查和质量审查。TASK 1 的命令隔离、首组管理 action 迁移、服务端持久化与恢复协议已经分切片实现,但整体仍为 partial;TASK 3 与 TASK 6 已经通过两类审查、完整 gate 和各自的运行时验收,状态为 complete;TASK 5 的 gate single-record 局部也已通过两类复审,但 TASK 5 整体仍未完成。后一个任务不得依赖未审查的前一个工作区状态。

TASK 1

统一错误 envelope 与前端错误状态 partial

公共 dispatcher 异常隔离、agent/channel/settings 管理存储异常迁移、服务端 UserErrorStore 以及 list/ack/recovery 协议已经实现;最终 envelope 在进入发送队列前按已认证 principal 持久化,记录有 1,000 条/7 天上限,存储失败要求 state recovery。确认使用 exact error ID,结果按 request ID 关联,并只向同 principal 窗口广播恢复。Web 兼容 operation_error/action_error,TUI 使用低敏文本消费两种 frame。当前会话卡片、后台标记、Toast 去重、前端 reconnect hydration 和其余旧错误路径仍未实现。聊天回复现有 error bubble 保持兼容;Job 实体错误由 TASK 2 处理。

TASK 2

后台 Job 错误完整展示

让 Job error 从实体、广播、列表加载到 AttachCard/Branches 全程保留;终态错误在刷新后仍存在。失败信息使用安全摘要,完整 traceback 只写日志。

TASK 3

每连接有界发送队列 complete

在 server 层新增 _webui/ws_delivery.py,建立 sender 生命周期、帧分类、合并规则、终态保障和断连清理;所有广播路径只 enqueue。慢客户端、发送失败、跨线程并发、队列溢出、终态预留和 in-flight 容量回归已通过,未修改 events/bus.py。

server.pyapps/server/.../_webui/ws_delivery.py
TASK 4

Session projection 与增量恢复

先固定载荷测量和协议合同,再分离同步计算与异步发送;实现 manifest、viewport messages、Graph summary、detail fetch 和 reconnect delta。评估并复用或删除未接入的 MessageStore。

TASK 5

EventBus notify 与日志执行模型 单记录切片完整

gate event 的 observer 通知阶段已经延迟磁盘写入,verdict 完成后只写一条含 gate 字段的记录,并已通过独立规格与质量复审。其余范围仍包括:notify 转移到专用执行环境;gate 保持同步;日志异步批量写;摘要化大型 payload;用 AST 验证事件注册完整性。兼容现有 plugin hook 公开签名。

TASK 6

本地前端依赖、压缩与资源预算 complete

CDN runtime loader 和 legacy template 标签已删除,marked/KaTeX 由构建本地提供,产品 CDN 图标与 browser home 自动 favicon 请求改为本地文字标识;Starlette gzip responder 按权重协商并按响应 MIME 排除已压缩格式;center-tab 非首屏表面使用动态导入并显示 pending 状态;构建预算、HTTP 回归、完整 Python gate、默认 App 可视验收和五次禁用缓存的本地 browser trace 均已通过。

markdown-render.tsfrontend.pyapps/web/scripts/runtime/check-static-resource-budget.mjs
TASK 7

容量诊断与显式清理

增加只读容量分类和诊断输出;设计 cache/model 的显式清理入口;身份和浏览器用户数据保持默认不清理。该任务不阻塞错误与刷新性能修复。

任务depends_on可并行关系职责排除
TASK 1无可与 TASK 3、6、7 并行只定义通用 action error/error store;不实现 Job 实体状态。
TASK 2TASK 1完成 TASK 1 后可与 TASK 4 并行job_status 唯一承载 Job 终态;复用 error store,不再新增第二种 Job error frame。
TASK 3无可与 TASK 1、6、7 并行只修改 server 的连接队列和 ws.frame enqueue adapter;不修改 events/bus.py 的 notify 调度。
TASK 4TASK 3可与 TASK 2、5 并行SessionDB/DAG 是唯一持久来源;删除不能复用的 MessageStore 持久接口。
TASK 5TASK 1、TASK 3与 TASK 4 的代码文件不重叠唯一拥有 events/bus.py、registry 和 event log;Web bridge 使用 TASK 1 envelope、TASK 3 enqueue。
TASK 6无可独立执行不改变 CSP 信任边界,不修改 WebSocket 协议。
TASK 7无可独立执行默认只读诊断;删除入口需要单独交互设计和明确确认。
11

量化验收矩阵

目标值是实现合同,不是当前已达成的数据。每项需要自动化测试或可重复测量命令,不能只靠人工观察。

公共入口场景验收标准证据类型
任意已注册 WS actionhandler 抛异常同一 socket 收到安全 operation_error;连接继续;UI 在 250 ms 内显示;日志可按 correlation ID 定位。服务端 receive-loop component test + Web reducer/UI test。
列举、删除、设置和通道操作底层存储异常不得返回正常空集合或静默成功;旧数据保留并标 degraded,或显示明确失败。逐 action 合同测试。
JobRunner后台任务异常后刷新Job、AttachCard 和会话错误标记显示同一安全原因;retryable 一致。持久化 + WS + React 集成测试。
最大已知会话首次加载无客户端缓存首个未压缩 wire projection ≤ 1 MiB;服务端 event-loop 占用 ≤ 50 ms;其余计算在线程执行。固定会话 fixture 的 payload/time budget test。
同一会话重连客户端已知 revision,缺失不超过 64 个 delta不发送完整 history;恢复帧 ≤ 256 KiB;终态、错误和问题均存在。断连/恢复协议测试。
慢 WebSocket 客户端持续产生 stream delta 与终态队列不超过 256 帧或 4 MiB;增量可合并;终态不丢;无法恢复时明确关闭并要求重新同步。受控慢 sender component test。
EventBus notify慢同步 plugin + worker thread async subscriberemitter 的 enqueue p99 ≤ 1 ms;async subscriber 不因缺少当前 loop 被跳过;失败不阻塞其他消费者。并发 benchmark + 行为测试。
Event log一次带 gate 的 tool.before事件只选择 session 或 global 一个目标日志;该目标中同一 event ID 只出现一行,包含 gate verdict,大型结果不写入完整正文。真实事件文件断言。
Web 首次加载缓存关闭、接受 gzip零 CSP violation;零 CDN runtime 请求;首次 transfer ≤ 2 MiB;最大压缩 JS chunk ≤ 350 KiB。默认 App browser trace + HTTP headers test。
状态目录诊断Profile 超过配置提醒值准确分类容量;未获明确确认不执行删除;清理报告列出删除类别和可恢复性。临时 profile 文件树测试。

测量口径与阈值依据

指标起止点与环境样本阈值依据
会话 wire bytes后端 json.dumps 结果的 UTF-8 未压缩字节;默认 profile 的固定大型脱敏 fixture,关闭冷 context stats。每个候选提交 5 次,报告中位数和最大值;消息/graph/detail 分项。1 MiB 是当前 11.66 MB 的首阶段硬上限,并保证本地 JSON parse 和远程传输可控;单 detail 另有 1 MiB 分块上限。
event-loop 占用从 async handler 进入到第一次 await send 前,仅统计 event-loop 线程 wall time;projection 在线程的时间单独报告。固定 fixture 20 次,报告 p50/p95/max。50 ms 对齐交互任务不长期占用主循环的预算;不是整个页面完成时间。
错误 UI 延迟浏览器收到完整 WS frame 到错误卡片出现在 DOM;不含网络。20 次,报告 p95。250 ms 允许一次状态更新和渲染,同时明显低于现有 2 秒重连周期。
Notify enqueueemit 入口到事件成功进入 bus 自有队列,不含 consumer。单线程和 8 worker 并发各 10,000 次,报告 p50/p95/p99。p99 ≤ 1 ms 确保普通 notify 不显著增加工具和 turn 执行时间。
静态 transfer默认 App、缓存关闭、无浏览器扩展注入;统计 document origin 资源的 encodedBodySize。冷加载 5 次,报告中位数和最大值。2 MiB 是当前 3.42 MB 的首阶段预算;350 KiB 约束单个压缩 JS chunk,防止一个延迟表面进入首包。
CSP只统计 OpenProgram document origin 产生的 violation;浏览器扩展或开发工具注入单独报告。冷加载、打开数学消息、进入设置和 DAG 各一次。产品自身零 violation 是安全和功能合同。
容量分类以文件系统实际 allocated/logical bytes 为准,并列出分类规则;符号链接不跨出 state root。固定临时树和真实只读诊断。分类合计与根目录差异不超过 1% 或 1 MiB,以较大者为准。
基线可重复性:本页的 11.66 MB、313 ms、123 ms、437 MB 和测试计数来自一次当前工作区审计,未绑定可提交 fixture,因此只能作为观察基线。TASK 1/3/4/5/6 的 RED 首先增加固定 fixture、脚本或浏览器 trace 配置;实现报告只能用这些新入口给出正式 before/after。
12

兼容风险、采用与排除

选择处理理由与边界
标准 WebSocket + 应用层有界队列已选设计浏览器兼容性稳定;服务端仍需要明确队列、合并和恢复语义。
现有 MessageStore seq/delta部分复用只复用 block/seq/delta 计算;SessionDB/DAG 已确定为唯一持久来源,独立 store 接口删除。
统一 operation_error已选设计前端需要稳定 machine code 和安全文本;旧错误帧通过短期 adapter 兼容。
EventBus notify 专用执行环境已选设计满足非阻塞观察合同;gate 不迁移,继续同步。
本地打包 marked/KaTeX已选设计与严格 CSP、离线使用和可重复构建一致。
放宽 CSP 允许 CDN拒绝增加远程脚本信任和可用性依赖,不能解决首次连接等待。
WebSocketStream / WebTransport 全面迁移拒绝浏览器支持或复杂度不符合当前范围,也不能替代持久恢复和错误合同。
全局 React store/组件重写拒绝当前已有 rAF 合并、单消息订阅和行回收;未证明它们是主要限制。
自动删除 Chrome Profile拒绝可能移除登录态、IndexedDB、扩展或其他用户数据;只允许显式分类清理。
迁移风险:会话加载协议改变会影响刷新顺序、分支切换、运行中卡片、问题重放和 send queue。TASK 4 必须先记录所有现有 frame 顺序与 public-entry RED,再逐层替换;不能直接把整个 async handler 放入 to_thread。
13

实现状态附录

本节集中区分当前已有能力、部分能力和未实现设计。上文的目标合同不因当前状态而弱化。

能力状态当前证据剩余工作
统一 operation_error 与 error store服务端恢复协议公共 dispatcher 和已迁移的 agent/channel/settings 管理存储异常使用同一最终低敏 operation_error;服务端按已认证 principal 在发送前写入独立 SQLite UserErrorStore,有 1,000 条/7 天上限、并发写保护和 correlation ID;list_user_errors 严格分页,acknowledge_user_error 按 exact ID 幂等关闭并关联请求,同 principal 窗口收到 exact-ID recovery;Web 与 TUI 已有兼容消费入口。实现前端 hydration、scope 展示与去重,并迁移其余旧错误路径。
聊天回复错误 bubble已有主 chat dispatcher 的 error turn 会终结 placeholder 并进入现有渲染路径。与统一错误中心关联,避免重复 Toast。
WebSocket 命令级异常隔离四切片unknown action、未处理 handler exception 和已覆盖的管理存储异常先持久化、再返回低敏 operation_error;合法 request_id/session_id/action/operation_id 可关联,scope 明确,日志共享 correlation ID;错误 list/ack/recovery 已按 authenticated principal 隔离;Web 兼容旧 action_error,TUI 不展示 wire message/error,并隔离其他 session 的错误。补齐 validation code,迁移 runtime/session/project/channel QR 等旧路径,并实现前端恢复和展示。
工具输出按需读取已有超过 32 KiB 的工具结果在 wire 中截断,展开时按 session/node/caller 授权读取。把 Graph、context tree 和其他重复大字段纳入 projection。
流式渲染合并与长列表回收已有text/thinking delta 按 animation frame 合并;MessageRow 使用稳定 selector;长列表计算 live rows。补性能预算,确保新的 error/delta store 不破坏这些性质。
R6 请求内 sibling navigation 索引局部完成fn-run 使用一次枚举生成稳定排序位置;普通 chat 的 sibling_navigation_index 按请求建立 ID、目标 sibling group 与 deepest-leaf child 索引,只为当前可见 chain 涉及的完整 sibling group 排序,并只物化该 chain 的导航。公开 handle_load_session 回归同时约束 sibling 分组与 child 遍历为线性扫描,并覆盖相同时间戳和深层相邻分支;纯 DAG helper 回归覆盖重复/空 ID、runtime/tool/code、spawn、dangling predecessor 与 cycle。2,000 条消息、100 个可见节点的合成算法测量中位数由 134.076 ms 降为 1.355 ms;50,000 条离屏 sibling 的稀疏场景保持约 15 ms 与 2.75 MiB 峰值内存。两类独立复审均通过。R6 其余会话加载计算/发送分离、单次 session row snapshot、projection/delta 恢复和完整 event-loop 预算仍未实现;本行不表示 R6 整体完成。
后台 Job error 字段部分Runner 广播和 TypeScript 类型包含 error。状态存储、卡片渲染、刷新恢复和 Toast/错误中心关联。
会话增量恢复未接入MessageStore 模块定义 seq/delta/full,但生产 server/web 没有使用。按已确定的 SessionDB/DAG 单一来源实现 projection recovery,并删除独立 store 接口。
每连接 WebSocket 背压complete每个生产连接使用单一 sender;queued + in-flight 总量不超过 256 帧 / 4 MiB;慢客户端并发发送峰值从 400 降为 1。规格审查、质量审查和完整 Python gate 均为 PASS。无。
EventBus gate 日志唯一性局部完成已注册 gate 类型通过 emit 通知 observer 时延迟磁盘写入;同一个 Event 在 emit_gate 完成 verdict 后只写一条含 gate 字段的 JSONL。公共回归同时约束 observer 仍只收到一次;规格与质量复审均为 PASS。每批 1,000 个 paired tool.before 的同步日志微基准中位数由 105.15 ms 降为 58.48 ms,写入由 2,000 行降为 1,000 行;该结果不代表完整 EventBus 或端到端延迟。TASK 5 的异步 notify、批量日志、大型 payload 和 AST registry 检查仍未实现。
EventBus 异步 notify 保证未实现普通 handler 同步执行;无 loop 的 async handler 被跳过。TASK 5。
本地 marked/KaTeX 与静态压缩complete源码与 legacy template 零产品 CDN 资源;marked/KaTeX 本地导入;JS/CSS/SVG gzip、加权协商、identity、小响应、MIME 排除、既有 encoding 和 SSE 回归通过;browser home 使用本地文字图标;构建预算为 2,089,594 B,最大构建 gzip JS chunk 为 186,752 B。五次本地禁用缓存加载的传输中位数和最大值均为 1,221,805 B,最大脚本传输均为 187,384 B;每次 65 个请求全部为本地 origin,零加载失败、零 CSP/console 错误。远程部署环境的 browser trace 不属于本地 TASK 6 完成条件,部署时另行验证。
Profile 容量诊断与清理未实现只能通过文件系统检查容量。TASK 7。

TASK 1 长期合同与实施切片

边界长期合同当前实现
Wire envelope第 04 节 schema 是唯一最终合同;nullable 字段必须显式为 null,不得把 exception 或命令 payload 复制到 wire。公共 WebSocket dispatcher 的 unknown action、未处理 handler exception 和首组管理 action 已生成全部最终字段;chat reply 自有 error turn 不在该路径重复生成。
关联身份request_id 是客户端生成的单次命令关联值,不作为权限或幂等凭据;服务端只回显非空、无控制字符且不超过 128 个字符的值。session_id 和 action 同样只回显安全字符串。缺失或非法关联值返回 null,不得回显原始对象、超长文本或内部异常。
错误分类unknown_action 表示协议或版本不匹配且不可重试;handler_error 表示命令处理失败并保守标为不可重试。后续 action 必须使用注册 code,前端未知 code 使用通用低敏提示。当前公共路径按 session、agent、settings、channel 或 system 分类;不把 exception message、traceback、路径、凭据或命令 payload 放入 wire/store,完整 traceback 只进入本地结构化日志。
兼容新前端同时消费 operation_error 与旧 action_error;新服务端不发送双帧。Web 静态资源和 worker 属于同一发布单元,协议不承诺新服务端配旧前端。保留 actionErrorNotice 与 consumeActionError 导出作为源码兼容别名,内部归一化为同一个 notice。
持久恢复UserErrorStore 以 error_id、session 和 request 建索引,记录按已认证 principal 隔离;list、acknowledge 与 reconnect recovery 使用明确 ID。SQLite store、1,000 条/7 天界限、persist-before-send、严格分页 list、exact-ID acknowledge、请求关联结果和同 principal recovery 已实现;前端 hydration 尚未实现,因此当前不声明刷新恢复完成。
展示当前会话内错误、后台错误标记和 Toast 共享去重键;聊天回复已有 error bubble 保持原语义。dispatcher 命令失败继续显示一个 Toast;不同 session 的错误不得进入当前会话专属视图。
第一切片验收:真实 _websocket_handler 对 unknown action 和 handler exception 返回 operation_error,回显合法 request_id,拒绝非法元数据,保持连接处理后续 ping;新前端对新旧帧生成相同低敏 notice,未知 code 不显示 wire message/error。
第二切片验收:agent 的 list/add/delete/set-default、channel account/binding/session-alias 的 list/add/remove/attach/detach,以及 settings 的 get/set 存储异常均通过真实 _websocket_handler 生成且只生成一个低敏 operation_error,不发送对应正常空集合或成功结果,并继续处理后续 ping。TUI 同时消费新旧命令错误 frame,不渲染 wire message/error;带有其他有效 session ID 的错误不会写入当前 transcript,只有当前 session 的 chat action failure 会结束对应 turn。
第三切片验收:公共 dispatcher 为每次失败生成并严格校验最终 12 字段 data,先按已认证且格式有效的 owner principal 提交到 profile-scoped SQLite,再进入连接发送队列;发送失败不丢服务端记录,持久化失败不发送伪 durable frame 并以 state_recovery_required 关闭连接。存储回归覆盖 reopen、principal 隔离、线程与多进程首次并发写、跨 principal 过期清理、容量、7 天边界和低敏 wire/store。
第四切片验收:list_user_errors 只返回 authenticated principal 的开放记录,按 newest-first 严格分页;acknowledge_user_error 在请求连接内按确认结果、exact-ID recovery 的顺序发送,并独立通知同 principal 的其他窗口,不会通知其他 principal,请求连接发送失败也不阻止其他窗口恢复。重复或未知 exact ID 保持幂等且不创建错误;非法字段、畸形 JSON 和非对象 JSON 生成低敏 invalid_request,连接继续处理后续命令。存储、组件和协议回归共 35 项通过。

TASK 3 任务简报与实施证据

证据项已核对内容
批准来源本页 WebSocket 队列、TASK 3 与量化验收条款;任务从当时的 main 基线启动。
生产文件server.py、_webui/ws_delivery.py、ws_actions/webtab.py、routes/misc.py。
测试文件test_websocket_delivery.py、test_websocket_command_lifecycle.py、test_webtab_control.py、test_web_owner_auth.py。
必须行为单一 sender;跨线程只入队;queued + in-flight 有界;文本与状态合并;终态关键预留;溢出和发送失败要求 state recovery;断连完成所有 waiter。
兼容与排除保留 _ws_connections、await ws.send_text 和精确 socket 控制的现有语义;不修改 EventBus、Session projection、前端重连或错误合同。
RED通过真实 _websocket_handler 和 _broadcast 注入 400 个更新,旧实现的同时 send_text 峰值为 400,期望值为 1。
GREEN 与受影响验证聚焦命令 37 项通过;WebUI 与 web-use 扩大验证 448 项通过、1 项跳过,另有 1 个已复现的基线失败。
审查规格审查 PASS;质量审查 PASS。两轮确认的 waiter、失败语义、终态分类和 in-flight 容量 finding 都有定向回归。
完整 gateRuff PASS。确定性基线问题修复并完成独立审查后,Python 完整执行为 6,243 passed、19 skipped、7 deselected、1 xfailed、零 failed、零 errors。
交付状态complete。规格、质量和完整 Python gate 均为 PASS;已进入隔离集成分支,尚未合入 main 或推送。

TASK 6 任务简报与实施证据

证据项已核对内容
批准来源本页前端与资源合同、TASK 6、Web 首次加载验收和静态 transfer 测量口径;任务从 TASK 3 基线提交创建独立工作区。
生产文件apps/server/openprogram_server/_webui/frontend.py;apps/web/app/layout.tsx;components/app-shell.tsx、components/chat/messages/markdown.ts、components/settings/provider-icon.tsx;lib/format-utils/markdown.tsx、lib/global.d.ts、lib/net/use-ws.ts、lib/runtime-bridge/conversations.ts、lib/runtime-bridge/markdown-render.ts;public/html/index.html;apps/web/package.json 与根 package-lock.json。删除 lib/external-libs.ts 和 lib/cdn-globals.d.ts。
验证文件tests/component/webui/runtime/test_webui_frontend.py;apps/web/scripts/runtime/check-local-runtime-assets.mjs、check-static-resource-budget.mjs;同步更新 check-chat-ui.mjs、check-markdown-sanitize.mjs 和 check-socket-access.mjs。
必须行为所有产品 runtime asset 使用本地资源;marked 与 KaTeX 不等待外部 loader;browser home 不自动请求外部 favicon;CSP 继续使用 self-only 信任边界;JS/CSS/SVG 按大小写不敏感且遵守 q 权重的 Accept-Encoding 使用 Starlette gzip responder;小响应、已压缩响应 MIME、SSE 和已有 Content-Encoding 不压缩或不重复压缩;非首屏 center-tab 表面按需加载并显示 pending 状态;构建产物进入固定 2 MiB / 350 KiB 预算。
兼容与排除没有修改 WebSocket 协议、Session projection、EventBus 或错误合同;useMarkdownReady 保留兼容导出;OwnerAuth 的 CSP 定义未放宽。
RED初始 HTTP public-entry fixture 在大静态 JS 请求中缺少 Content-Encoding,执行结果为 1 failed / 13 passed;本地 runtime 资源脚本在 CDN loader 上失败;资源预算脚本在缺少生产构建产物时失败。规格修复 RED 以真实 PNG/WOFF2 请求复现重复 gzip,为 2 failed / 16 passed。质量修复 RED 新增无后缀 API MIME 与 q 权重入口,为 6 failed / 19 passed;browser 组件检查在 Google favicon helper 上失败。
GREEN 与受影响验证修复后 frontend 25 项全部通过,覆盖 JS/CSS/SVG gzip、identity、小响应、静态与无后缀 API MIME 排除、大小写与 q 权重、SSE 与已有 encoding;与 OwnerAuth/CSP 合并为 67 项通过。Web unit 115 项、browser/bookmarks/local-runtime source check、TypeScript 和生产构建通过。
资源预算chat.html 的保守构建产物检查统计 110 个初始及 CSS 引用资源,gzip level 6 编码合计 2,089,594 B;最大压缩 JS chunk 为 186,752 B,均低于固定预算。
审查与修复规格初审确认请求后缀排除无法覆盖无后缀 public response;首轮修复后继续保留 Starlette responder。质量初审确认三项:排除条件必须基于响应 MIME、Accept-Encoding 必须处理大小写与 q 权重、browser home 不得自动请求 Google favicon;质量修复分别增加 MIME-aware responder、显式 gzip 优先于 wildcard 的协商和本地 DOM 文字图标。最终规格限定复审 PASS,质量限定复审 PASS。
完整 gate确定性基线问题已分别修复并通过独立规格与质量审查;随后 Python 完整执行为 6,243 passed、19 skipped、7 deselected、1 xfailed、零 failed、零 errors。此前偶发的 models.dev 失败与后台线程错误没有复现。
交付状态complete。规格、质量、完整 Python gate、默认 App 刷新与可视验收均为 PASS;五次本地禁用缓存 trace 均低于 2 MiB / 350 KiB 预算,且无外部请求、加载失败或 CSP/console 错误。远程服务器 trace 和推送未执行。

当前审计验证入口

PYTHONPATH=apps/server:. .venv/bin/python -m pytest \
  tests/component/agent/turns/test_event_bus.py \
  tests/component/agent/turns/test_event_gate.py \
  tests/unit/agent/behavior/test_event_bridges.py \
  tests/unit/agent/dispatcher/test_error_turn_finalized.py \
  tests/component/agent/jobs/test_async_job_recovery.py \
  tests/component/webui/chat/test_session_tool_output_transport.py \
  tests/unit/webui/test_webui_chat_dispatcher.py -q

cd apps/web && npm test
cd apps/web && npx tsc --noEmit
python3 -m scripts.docs_site.build
python3 -m scripts.docs_site.checklinks

这些入口证明当前受支持路径没有被文档改动破坏,不证明本文目标已经实现。各 TASK 必须先增加对应的错误 UI、慢客户端、projection、队列、EventBus 并发或资源预算 RED。

相关权威文档

实现证据只在对应任务经过规格审查、质量审查和完整验证后更新。本文不记录日期或提交历史;演变过程由 Git 保存。

有界会话历史

会话加载将最新记录页与更早的历史分开。页面保留其捕获的分支头和稳定的最早消息游标。一页包含完整显示行及其 caller 后代,最多 50 个顶层行,目标序列化预算为 512 KiB。无法拆分的超大行单独返回。分页不修改已存储历史或模型上下文。历史读取不改变焦点、实时会话头、执行状态或问题重放。

Codex 通过分页历史 API 分离线程元数据、轮次和条目。DeepSeek Harness 提供按消息对齐的页面和序列游标。本实现采用稳定分支快照、向前读取历史和增量实时更新,保留 OpenProgram 现有的分支图和执行恢复合同。不引入另一套会话存储,也不改变模型上下文压缩。

仅用于传输的 extra 对象省略重复提升的字段。现有工具输出预览保留经授权展开完整输出的能力。超过传输帧预算的逻辑历史和图响应以有序、逐次等待完成的分片发送;客户端在分派前重建完整响应,并按首片到达顺序与后续实时通知保持排序。延迟响应共享每连接 6400 万个 UTF-16 代码单元、8 个未完成传输和 1,024 个逻辑响应的预算。这样可以处理单个大行,而不静默丢弃数据或扩大 4 MiB 发送队列。普通实时更新独立继续。分片缓冲区属于单个连接,断连时丢弃。重连加载新的最近页,并使用现有执行恢复机制。

客户端按稳定标识在前面插入更早的行,不替换更新的实时内容,也不移动当前选中会话。分页请求捕获当前加载代次,在重新加载、切换分支或更换 socket 后被忽略。不可用游标产生明确的可重试错误,而不是反复重连。插入更早消息时保持滚动位置。图保持完整以支持现有分支和压缩控件;服务端历史投影仍扫描捕获的会话。存储索引和图的延迟渲染是单独的优化,本次不声称完成。

验收:复制出的故障会话可打开且不发生队列溢出;每个逻辑页保留准确内容及 caller 归属;页面帧保持有界,包括 Unicode 和单个超大行;游标耗尽时不丢失或重复消息;陈旧响应不能覆盖实时或更新状态;断连释放未完成缓冲;分支、压缩、工具展开和执行恢复仍可用。实现状态:已实现并经过独立审查。联合验证通过 8,591 项 Python 测试、635 项 Web 测试、4 项浏览器测试和 190 项 CLI 测试,以及 Desktop、静态、类型、构建和文档检查。已安装 App 分五页获取全部 26 个原始显示行,无重复、乱序或断连;观测到的最大传输帧为 51,564 字节。重新加载和心跳正常。

References: Codex 历史协议; DeepSeek Harness 历史.

长会话布局与加载

有界历史协议将传输大小与会话记录长度分离。长会话还需要避免递归栈溢出的图构建:此前的深度遍历对每个节点追溯全部祖先,并递归访问会话链。包含 1,500 个节点的线性会话在首个页面交付前就会超过 Python 递归限制。

图深度现在先通过带记忆缓存的父链分类计算,再进行迭代前序遍历。父路径进入环的节点仍作为独立根节点,保留现有防御行为。分叉、重试、派生分支和调用子节点保留当前行顺序及偏移。父链分类只访问每个节点一次;遍历不依赖 Python 调用栈。后续消息规范化只对回复建立一次索引。泳道列布局维护每条泳道的宽度和已占用的最大列,不再重复扫描已经放置的节点。

会话消息聚合和按字节预算准备页面在事件循环外执行,需等待的 WebSocket 发送仍在所属循环执行。分支图构建也在该循环外执行。快照和增量事件顺序不变。现有内存会话索引仍是读取的权威来源;本次不引入持久缓存、模式迁移或历史删除。

验收通过公开图构建器和会话加载处理器测试长链、大量分叉和非法环。小图输出必须符合现有布局合同,包含 10,000 个节点的会话记录必须能加载有界最近页且不发生递归失败。完整图和初始存储索引的开销仍与会话大小成正比;本次不声称实现恒定内存存储或基于索引的会话记录投影。有界历史章节中的 Codex 和 DeepSeek 参考资料仍用于分页对比。

实现状态:已实现并经过独立审查。公开布局和会话加载测试覆盖 10,000 个节点的长链、大量分叉和非法环;保留完整图输出和有界会话记录页面。联合验证及已安装 App 的长历史重放通过。初始图和存储内存仍随会话大小增长。

Last updated · 2026-10-10