Skip to content
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@ Versions follow the merge of a `*_release-v*` branch; CI publishes to npm on tag
## [Unreleased]

### Changed
- **会话详情「交接文档」(handoff) web 渲染升级:Markdown 正文 + 语法高亮 + 角色卡片(eWork 风格)**:此前 handoff 页把每条消息正文按纯文本转义直出——assistant 回复里的粗体/行内代码/链接全是字面量,代码块原样显示 ``` 围栏,工具输入输出是一大块无语言、无高亮、无复制按钮的 pre。现在:① 消息正文(含思维链)按 Markdown 渲染,服务端 `markdownToHtml`(博客文章、handoff 兜底)与浏览器 IIFE 共用同一实现——新共享核心 `src/web/mdrender.ts`(转义优先、零运行时依赖,dist 零依赖守卫不变,`.toString()` 内联进 IIFE,附自包含契约测试防转译破坏);② 代码块按语言高亮(json/diff/js/ts/py/sh 等手写词法器,GitHub 明/暗双配色,未知语言退化为纯文本转义,注入面全转义);③ 消息改角色卡片布局(user/assistant/tool 左边框 + 徽章,思维链默认收起,工具卡输入/输出标签 + 语言标注 + 复制按钮 + JSON 美化,≤20 行默认展开);④ 文档头元信息(标题/session id/token 数/压缩块)不再被客户端跳过,渲染为灰底 key-value 盒;新增 GFM task list 复选框。回归:tests/mdrender.test.ts(18 用例,含 esbuild-minify 复刻 tsup 产线的内联契约)、tests/web-handoff-roles.test.ts 适配新 markup(#2065 语义不变量保留:正文 `### N.N` 标题不成为角色分隔线)、tests/web-sessions.test.ts(XSS/unsafe-link/handoffHtml 断言原样穿过新实现),全部 web 沙箱测试套件绿。
- **AGENTS.md 瘦身(owner 指令 2026-11-08):76 KB → 50.6 KB(−34%),规则零丢失,编号不变**。KDD 分两级:Tier A(#2/#4/#6/#7/#8/#10,每会话操作规则)留 AGENTS.md 全文;Tier B(#1/#3/#5/#9/#11–#18)全文逐字搬入新文件 `reference/design-decisions.md`(append-only 台账,**KDD 编号 = 稳定公共 API,原样保留**),AGENTS.md 每条留 1–3 行操作性后果 + 指针。Issue 分类与标签规则整节搬入 `reference/triage.md`(p1 口径与总账讨论链接留在正文)。E2E/Release 细则收敛为指向既有 `reference/testing.md` / `reference/release.md` 的硬规则。被反向引用的 heading 锚点(`#4-git-safety-rules-mandatory` 等 6 处)文本原样不动。
- **系统架构文档体系落地(owner 指令 2026-11-08):`docs/architecture/` 成为实时维护的架构枢纽**:新增 `docs/architecture.md` / `docs/architecture.zh-CN.md` 总枢纽(整体架构图中英双版转正 + 8 模块总表),首个样板模块页 `docs/architecture/storage.{md,zh-CN.md}`(统一存储:职责/原理/工作机制/硬不变量,配模块图中英双版)。文件级地图 `reference/architecture.md` 补齐 125 个缺失条目(新存储层 / marker / fastpath / lanes 等全部新模块)并升级为 CI 强制全量覆盖。新增 CI 车道 `architecture-docs`(`tools/check-architecture-docs.mjs`):地图覆盖全 `src/` 文件、总表↔页面成对、图 svg 优先 + png 备选齐备、SVG 基础良构(未转义 `&` 曾让英文图线上破图)。AGENTS.md「Documentation Layout」新增实时维护规则:改结构 ⇒ 同 PR 更新页 + 图 + 总表。其余 7 个模块页(pipeline / lanes / kernel / identity / fastpaths / webui / update)同一 PR 全部补齐——总表 8 行全 ✅(每页 = 中英双版文档 + 中英双版图)。

### Fixed
- **压缩树图:节点标题不再一闪即逝,点击压缩块能真正看到被压缩的消息内容**:此前树节点只有原生 SVG `<title>` 悬停提示——选中节点或 5s 轮询重画时 DOM 一变提示就消失,实测「显示一下马上消失」;右侧 Block details 里块的消息行虽带消息编号(mNNNNN)但内容全是一句「消息已不在会话中」(宿主原生压缩把消息从上下文剪掉后,字节无处可取)。现在:① 悬停提示改为页面内常驻浮层(.g-tip)——跟随指针、显示完整块号/tier/消息范围/完整主题/tokens/消息数,选中节点或轮询重画都不再打断,离开图形区才隐藏;悬停时节点卡片上的主题行同时原地扩宽(~20 字 → 20 字上限的完整截断,离开复原);② 5s 轮询只回传瘦节点(无主题/摘要/tokens),重画前先与详情富块按 blockId 合并——节点标题、浮层元信息、消息数不再在每次 tick 后退化成光秃秃的块号;③ 服务端新增**冻结快照水合**:压缩块落盘时已把原始消息逐条存进 blockContents(`[role]`/`[role • tool]` 头 + 原文,pi 宿主 user 消息还带 mNNNNN 标记行)——水合按**标记锚定**对齐:pi 宿主打进 user 消息正文的 mNNNNN 标记是身份戳,每个标记用两条独立证据之一锚死——**恒等锚**(标记号=该位当前 ref,从未被重编号)或**冻结 span 算术锚**(标记的冻结编号 − 块冻结 span 起点 = 快照位序;span 与标记同批写入,冷加载 restamp(#5196 parked 段换新号)重编了整块 ref 也照样成立——实测 b27 全块 127 条因此恢复,旧规则因标记号已不在 ref 表里整块拒水合);锚点之间的段仅在条数平衡时按位 zip。标记既非恒等也非算术锚(冻结上下文混入外来消息/span 无法证明)、锚点乱序、无锚非叶块(快照 span 的是 effective 超集)一律拒绝,未证明的位置保留诚实的 missing 标记——宁可不显示,不冒错位张冠李戴的风险(预览 + 点击展开取原文,`/message?ref=` 同享该层)。实测大会话(253 块/9771 个直接消息引用):7789 条(80%)恢复出真实预览与原文,其余保持 missing。回归:tests/web-blocks-tree.test.ts 冻结快照水合 7 用例(按位水合 / 条数不符拒 / 标记移位旧锚 / 宿主剪块中消息的锚定恢复 / restamp 整块恢复 / 算术锚优先于巧合 ref 碰撞 / 无锚标记整块拒(含 click-to-open 双向断言)),源码级断言更新为合并后重画(renderCollapSvg(merged)。
- **交接文档「Compressed block summaries」附录不再被吞进最后一条消息卡片**:客户端 handoff 渲染器只认 `### user/assistant/tool` 三种角色分隔线(#2065 语义,正文内其他 `###` 标题是内容),export 端 #845 追加在对话之后的 `## Compressed block summaries` 整节(长会话可达 80+ 个 `### Block bN` 条目、数百行)因此全部落进最后一个 assistant 卡片,看起来像「压缩消息被追加成一条巨型消息」。现在客户端把 Conversation 之后的 `## ` 级标题识别为独立分节:`### Block bN — topic` 逐条渲染为小块卡片(mono 块号 + 主题截断标题 + tier/token 元行 + Markdown 正文),角色卡片只保留真实对话。回归:tests/web-handoff-roles.test.ts 新增附录分节用例(角色卡片数不膨胀、条目内容保真、#2065 不变量不受影响)。
- **会话历史的「条数」维度与超大未压缩请求:按真实维度拦下,判死信息点名超重字段(#5000 #5794 #5885)**:#5000 的 26,903 条消息会话(55.2MiB)穿过了**所有**字节/token 闸门、在上游死于消息条数上限——因为条数这个维度从来没有任何闸(它无法被任何字节/token 估算表达:每条消息还各带 ~69B 的 m-ref 标记,26,903 条 ≈ 1.8MB 纯标记)。现在新增按上游配置的**条数闸**(`network.maxMessages` / `BILI_MAX_MESSAGES`,默认 0=关闭——不存在全局「通用上限」,条数上限因上游而异):超限直接 413,信息点名 COUNT 维度并给出可执行指引(重启会话或回退到更早的时点;折叠在一轮 preflight 内关不掉 ~20K 条的缺口,所以是纯判死、不做条数折叠),且在 token 触发器**之前**运行——token 达标但条数超标的体(正是 #5000 的形状)也只有这道闸看得见。同时所有超窗判死信息(#5932 的三道计量闸)现在**点名超重的是哪个字段**——按 #554 计量器同源分桶(messages / tools[] / system / other),不再只给一个总数:#5000 的形状会显示 `tools[] ~X`,巨大粘贴/工具结果会显示 `messages ~X`,运维一眼知道该删什么。另新增 **32MiB 解码摄入成本带**(`network.ingestBodyBytes` / `BILI_INGEST_BODY_BYTES`):#5794 的 51.2MiB/29,619 条请求每轮独占事件循环 5.5–7.2s(本机 CPU 54.6% → 747s FATAL 自重启、全员缓存冷),成本带在 JSON 解析扫掠**之前**按解码后字节 413(stage=ingest)——任何可解析的**文本为主**的 32MiB+ 体都已是 ≥~8M token,超出所有真实模型窗口,放行必然烧钱再死。带图片的体在所有车道豁免(base64 像素按设计不进 token 计量——#496/#1800/#857「图片单独计费、绝不在原始字节上判死」;#3909 的 ~48MiB 图片密集 chat 会话与 #3701 的 ~38.5MiB WS 截图体都是受支持工作负载,由槽位预算/收件上限/车道自身的预发送字节预算+丢弃逻辑治理;Responses 车道无论如何整体豁免);并发槽位预算与 5GiB 收件上限继续独立生效。回归:tests/issue5000-count-gate(3 e2e:token 达标+条数超标→413 零上游 / 无 model 路径 / 条数达标照常转发)、tests/issue5794-ingest-cap(3 e2e:超带文本→413 stage ingest / 带内照常 / 超带带图不拦)、tests/side-request-isolation 分桶计量单测;既有 332 用例(含 #3701/#857/#496/#4079/#4185/#2490 全部回归面)全绿。
- **Pi 原生插件(内联)模式:后台 SQLite 完整性检查 worker 的一次性 ExperimentalWarning 不再泄漏进宿主 TUI(#5940)**:Node 在每线程首次使用 `node:sqlite` 时打印一次 `ExperimentalWarning: SQLite is an experimental feature`,而后台 quick_check worker(`scheduleBackgroundIntegrityCheck`)创建 Worker 时没有任何 stderr 处理——Node 默认 worker stdio 把 worker 的 stderr 直通宿主终端,检查恰在中途会话运行时通知就落在 agent TUI 里(复现:billion-context@0.1.213 + Pi 1.5.0 原生插件模式 + Node v24.7.0,检查本身通过)。修复=源头捕获:构造 worker 时 `stderr: true`(仅 Node 展开;bun worker 无可捕获 stderr、bun:sqlite 不发此类通知,bun 路径逐字节不变),构造后立即消费捕获流直至其寿命结束(数据可能在 verdict 之后才到,消费同时防管道背压挂死);窄状态行过滤器只丢已知 SQLite 通知块(header ±`(node:<pid>)` 前缀 + `--trace-warnings` 提示/缩进堆栈尾),其余每行走 `loggerLog("warn")` 记为 `[storage] integrity worker stderr for <db>: <line>`——内联车道 stderr 腿对 TUI 静音,保留行进 durable bili.log;独立 bili 保持 file+stderr 面。无全局 `--no-warnings`、无检查弱化,verdict/alarm/缓存语义不变。残留(PR 已诚实披露):父进程自己开机期的同一通知副本不受此改动影响(拦截宿主进程 warning 属侵入他人运行时,划出本 PR 边界)。tests/integrity-worker-stderr.test.ts(过滤器按 #3617 双向 + 真 worker 接线子进程断言 + teeth probe);新套件已登记 scripts/live-files.mjs(SPAWN_RE tripwire 纪律,#1248)。
- **The log/publish secret scrubber now catches JSON-quoted credentials and Groq/Hugging Face keys.** `redactSecretsInText` (every log line, and via `redactForPublish` the diagnostics blob that lands in public GitHub issues) required `:`/`=` right after the key name, so the JSON/dict shape upstream error bodies and config dumps use — `"api_key": "…"`, `"password": "…"`, `'access_token': '…'` — slipped through unless the value happened to start with a known prefix such as `sk-`. An optional quote after the key name now matches it; `gsk_…` (Groq) and `hf_…` (Hugging Face) keys, both on the public-host list, are masked like `sk-…`. Usage-count keys (`"prompt_tokens"`, `"tokens"`, `"max_tokens"`) stay verbatim. Regression: `tests/log-mask.test.ts`.
Expand Down
2 changes: 1 addition & 1 deletion docs/architecture/webui.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Everything a human sees about the proxy: the session browser, logs, config, upda

**Data sources.** The live session pool plus storage rows (tier tree, #3917); `acp_status` ranges/nudge are recomputed LIVE per render, never cached stale (`acp-status.ts`); the cache ledger economics (`cache-ledger.ts`, #1536); billing at USD prices with the payback view (#4899); notice dismissal receipts in unified storage (kv_store namespace `notices`).

**Render.** Server-rendered HTML shell (`page.ts`) + one JS bundle (`client.ts`) + styles; `i18n.ts` carries the zh-CN/en message tables (dead keys are test-guarded); `markdown.ts` is escape-safe; `report.ts` builds dependency-free MD+SVG session reports; `diagnostics.ts` produces the paste-ready blob for issue reports (#3040).
**Render.** Server-rendered HTML shell (`page.ts`) + one JS bundle (`client.ts`) + styles; `i18n.ts` carries the zh-CN/en message tables (dead keys are test-guarded); `mdrender.ts` is the single escape-first Markdown + syntax-highlight core (hand-rolled lexers for json/diff/js/ts/py/sh, GitHub light+dark palettes, zero runtime deps — inlined into the browser IIFE via `.toString()` exactly like the JSONC helpers), and `markdown.ts` wraps it for the server side (blog articles + handoff fallback); `report.ts` builds dependency-free MD+SVG session reports; `diagnostics.ts` produces the paste-ready blob for issue reports (#3040).

**Notices.** `mergedNotices()` (`src/announcements.ts`) merges the three sources; the remote feed is this repo's `announcements/remote.json`, polled on the ~3-minute watcher cadence, fail-open (fetch problems keep the last-known-good snapshot; model traffic is never affected); builtin notices are dead-key-guarded by `tests/notice-registry.test.ts`.

Expand Down
2 changes: 1 addition & 1 deletion docs/architecture/webui.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

**数据源。** 活会话池 + 存储行(层树,#3917);`acp_status` 的 ranges/nudge 每次渲染**现算**、从不缓存陈旧(`acp-status.ts`);缓存账本经济性(`cache-ledger.ts`,#1536);USD 价格计费与回本视图(#4899);通告关闭回执在统一存储(kv_store namespace `notices`)。

**渲染。** 服务端渲染 HTML 壳(`page.ts`)+ 一个 JS 包(`client.ts`)+ 样式;`i18n.ts` 承载 zh-CN/en 文案表(死键有测试守卫);`markdown.ts` 转义安全;`report.ts` 生成零依赖 MD+SVG 会话报告;`diagnostics.ts` 产出可粘贴的诊断块供报 issue(#3040)。
**渲染。** 服务端渲染 HTML 壳(`page.ts`)+ 一个 JS 包(`client.ts`)+ 样式;`i18n.ts` 承载 zh-CN/en 文案表(死键有测试守卫);`mdrender.ts` 是唯一的转义优先 Markdown + 手写语法高亮核心(json/diff/js/ts/py/sh 词法器,GitHub 明/暗双配色,零运行时依赖——与 JSONC 辅助函数完全相同的方式经 `.toString()` 内联进浏览器 IIFE),`markdown.ts` 在服务端(博客文章 + 交接文档 fallback)包一层;`report.ts` 生成零依赖 MD+SVG 会话报告;`diagnostics.ts` 产出可粘贴的诊断块供报 issue(#3040)。

**通告。** `mergedNotices()`(`src/announcements.ts`)合并三个源;远端 feed 是本仓 `announcements/remote.json`,按 ~3 分钟 watcher 节拍轮询、fail-open(取不到就留最后已知良好快照;模型流量永不受影响);内置通告由 `tests/notice-registry.test.ts` 死键守卫。

Expand Down
3 changes: 2 additions & 1 deletion reference/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,8 @@ billion-context/
│ ├── changelog.ts # GitHub Releases feed for the panel changelog (#3719)
│ ├── sessions-data.ts # Read-only session browsing (live pool + on-disk, #1420); tier tree + lazy block content (#3917)
│ ├── logs-query.ts # GET /__bili/logs selection engine
│ ├── markdown.ts # Escape-safe Markdown → HTML (#1420)
│ ├── markdown.ts # Escape-safe Markdown → HTML (#1420) — thin wrapper over mdrender.ts
│ ├── mdrender.ts # Shared escape-first Markdown + hand-rolled syntax-highlight core (server render + browser IIFE via .toString() inline, zero deps)
│ ├── page.ts # Server-rendered HTML page shell (CA cert hint, i18n)
│ ├── client.ts # Web UI client JS bundle
│ ├── styles.ts # Web UI CSS
Expand Down
Loading
Loading