Repository navigation
Description
背景(为什么开这张单)
两起独立事件指向同一个根因:bili 没有一份"消息身份"的权威记录。
- Two independent compactors fight each other: host-side kernel compaction spawns a fresh anonymous session inside bili, which then EMERGENCY-folds every turn and forwards a payload still above the host trigger — a self-sustaining loop #5488(已修):WorkBuddy 宿主端压缩把基线洗掉 → bili 误当外来内容 → 应急折叠死循环。修法只能靠猜宿主压缩了没有。
- 大上下文增量解析(watermark fastpath,
rollout.features.pluginFullResendWatermark):当前靠 dsh 等进程内插件替 bili 打戳(x-bili-pf-count/x-bili-pf-tail-ids)。闭源客户端打不了 bili 的戳,永远进不了这个模式。
2026-11-03 下载官方真包拆源码后,源码级证明这个前提不成立——客户端本来就在原生发整套会话/消息 id + 显式压缩信号。本单专门做三件事:①收集各客户端的原生身份方案 ②理清 "session+msgid = 消息身份标志 + 增量解析依据" 的目标逻辑 ③按层落地,不再逐客户端打补丁。
① 已确认事实:WorkBuddy / CodeBuddy CLI(源码级证据)
来源:WorkBuddy 5.9.3 CN 官方包(codebuddy.cn/v2/update 接口取包),内核为 Electron app.asar.unpacked/cli/dist/codebuddy-lite-wb.mjs,自报版本 2.165.0(与 #5488 报告版本一致,另有 2.168.0 双构建)。
传输面:模型请求走 OpenAI-SDK 直连(baseURL 链 CODEBUDDY_BASE_URL || endpoint || productConfig),另有 Responses-API 分支;内核无 Anthropic wire 通道(anthropic 字样仅见于密钥清洗列表/沙箱依赖)。#5488 走 ANTHROPIC_BASE_URL 挂载,说明外壳还有翻译层——该层是否丢弃自定义头,M1 实锤。
请求头族(统一模型请求装配点无条件装配,静态未见按目标域过滤的分支):
| 头 | 粒度 | 语义 |
|---|---|---|
X-Conversation-ID |
页面/会话级,稳定 | 客户端内部字段名就叫 workbuddy.session_id(telemetry 映射原文) |
X-Session-ID |
账号会话级 | 跨重启稳定 |
X-Conversation-Request-ID |
轮级,每轮新值 | workbuddy.prompt_request_id |
X-Conversation-Message-ID(= X-Request-ID) |
消息级,uuid 去连字符 | 重试间保持稳定(可作回放键) |
X-Root-Request-ID / X-Parent-Conversation-ID |
主链 / 子代理血统 | 子 agent 分支可隔离 |
压缩显式信号(applyCompactionHeaders,仅在宿主刚压缩过的回合附着):
X-Compaction: manual | reactive | auto(源码映射:用户手动=manual、prompt-too-long=reactive、自动=auto)X-Context-Compacted(该上下文已被压过)
压缩信封文本(内容层兜底识别,客户端自带四组正则):主形 <conversation_history_summary><summary>…</summary></conversation_history_summary>(压缩器自产流式块,带 isCompactInternal:true)、短形 <cb_summary>、data-role="compact-summary"、标签族 previous_*/analysis/summary。
宿主指纹:x-codebuddy-request: 1(官方风控闸门头)、X-IDE-Name/X-IDE-Type: WorkBuddy、X-IDE-Version、body agent:"cli"。
② 其他客户端采集表(本单的持续产物,逐行填)
| 客户端/版本 | 接入通道 | 会话级 | 消息级 | 重试稳定性 | 压缩信号 | 验证状态 |
|---|---|---|---|---|---|---|
| dsh(bili 进程内) | — | pfa 内容哈希(bili 自己打的) | bili cluster id(bili 打的) | — | bili 自知 | 现状基线 |
| WorkBuddy 5.9.x / 内核 2.165.0 | OpenAI(+Responses 分支) | X-Conversation-ID | X-Conversation-Message-ID | ✅稳定 | X-Compaction 三值 | ✅ 源码证实 |
| claude-code 2.1.247(二进制逐字节核验) | ✅ 双通道:头 X-Claude-Code-Session-Id(session UUID,进 SDK defaultHeaders,对任意 baseURL 无条件发)+ body metadata.user_id JSON({device_id, account_uuid, session_id, parent_session_id?,ti?,tk?};env CLAUDE_CODE_EXTRA_METADATA 可扩展) |
toolu_*(tool_use/tool_result block,重试稳定、会话内永久,但模型每轮换新→只能作消息内锚点不能作跨轮锚点);turn 粒度 id(promptId)只在本地 jsonl,不上线 |
✘ 无头/字段;正文唯一信号 = 压缩后首条 user 消息固定英文前缀 "This session is being continued from a previous conversation that ran out of context…"(可伴 "Recent messages are preserved verbatim." 行与 transcript 路径行);次级印证 = 部分 querySource 的 1h TTL cache_control 使压缩后 token 计数断崖 | ✅ 二进制反汇编 @字节偏移(header 构造器 Ak() off:210421061、metadata 构造器 cB() off:214464660、压缩后续写文案 Nhe() off:211527680)+ 本机 ~/.claude/projects/*.jsonl 实测(uuid/parentUuid/isSidechain/promptId/leafUuid 字段链) | ||
| codex 0.147.0(Rust 二进制全量字符串 + 本机 118 个 rollout JSONL / sqlite 实测) | ✅ session_id(=rollout 文件名=sqlite 主键,uuidv7,resume 走 thread resume RPC)+ thread 系(thread_id/child_thread_id/root_thread_id/forked_from_id;头 x-codex-parent-thread-id、x-openai-subagent、x-codex-installation-id=installation_id 文件值) | ✅ 四家唯一 wire 原生逐条消息 id:input[] 每类 item 带原生 id 原样回传——user/developer msg_<uuidv7>(客户端造)、assistant `msg_<hex32 |
hex64>(服务端发)、工具调用 ctc_+call_id、输出 ctco_、推理块 rs_(附可回放密文 encrypted_content/encrypted_function_args);每 item 另有 internal_chat_message_metadata_passthrough.turn_id(uuidv7);请求级 previous_response_id(加速续接,失效即整包回退重放:"Previous response was not found. Retrying the full request.")+ 头 x-client-request-id` |
✔ 逐字节稳定(历史持久化于 rollout JSONL + sqlite thread_items/thread_turns,重试读回同一批记录重放;输出 id 归属上游,断线重生成会换新) | [model_providers.bili-comfly] base_url=http://127.0.0.1:8787/bili/… wire_api="responses",本 build 无 OPENAI_BASE_URL env(0 命中);id/头不按 host 条件化(高置信推测,建议抓包封版) |
|
| opencode 1.14.47(源码级确认) | OpenAI 系任意 base URL / ACP | 有:x-session-affinity(ses_,对所有 provider 无条件发;fork 另有 x-parent-session-id) |
wire 无(body 零暴露 msg id;三级体系 ses_/msg_/prt_ 全在内部 SQLite,重试跨轮逐字节稳定);ACP 通道把 msg_ 原样外露为 messageId | ✔(持久化,runLoop 每步读回同一批记录重放) | ✘ 无显式头/字段;内容标记:user 问 "What did we do so far?" + 锚定式 markdown 摘要()+ 尾部原文;独立 prune 机制清老 tool output("[Old tool result content cleared]") | ✅ sst/opencode tag v1.14.47 全量源码(llm.ts:366-390 头部构造点、message-v2.ts:777+ 映射、compaction.ts) |
| Gemini CLI 0.67.x(源码级确认) | 无 OpenAI 兼容路径,全走 Google GenAI REST(GOOGLE_GEMINI_BASE_URL);接入只能实现 GenAI 协议代理 | wire(通用路径)无:GenAI body 无 id 字段、头里 x-gemini-api-privileged-user-id 实为机器安装 id 名不副实;仅 Code Assist(Login with Google)路径 body 带 user_prompt_id+request.session_id(交互模式下 prompt_id≡sessionId,粒度冒用) |
内部有:sessionId(v4 UUID,进程级可 resume)、MessageRecord.id(JSONL 本地专用,从不上线)、prompt_id(轮级=sessionId) | ✔(重试闭包捕获固定 snapshot,prompt_id/sessionId 恒定,maxAttempts 10+mid-stream 3) | ✘ 无显式信号;压缩后历史 = [普通 user turn(摘要文本内含 <state_snapshot> 标签),model "Got it. Thanks for the additional context!",…近期轮];纯截断回退连此标记都没有 |
✅ google-gemini/gemini-cli @0ac57c02 全量源码(contentGenerator.ts:302-372 头部组装、chatCompressionService.ts:662-672、code_assist/converter.ts:129-178) |
采集方法固定三步:静态(bundle/二进制 strings/开源源码)→ 动态(挂 bili 抓一轮,全量打印收到的头)→ 交叉(重放同一请求验证 id 稳定性)。
③ 目标逻辑(三层分离,替代逐客户端补丁)
原则:bili 只信两样东西——宿主原生的身份戳,和自己手里转发过的字节。缺任何证据 ⇒ 回退现有 legacy 全解析(fail-safe 不变)。
- L1 身份层(新增单一事实源)
- 会话键:宿主原生 conversation/session id 优先;缺失才用现有 pfa 内容哈希(降级不报错)
- 消息 id:宿主原生优先;首次完整 parse 时记"宿主 id ↔ bili cluster id"绑定表
- 轮次界碑:宿主 request-id 出新值;缺失退化为消息数增量
- 压缩事件:宿主显式头优先(X-Compaction/X-Context-Compacted);缺失退化为现有信封嗅探正则
- L2a 折叠决策(Two independent compactors fight each other: host-side kernel compaction spawns a fresh anonymous session inside bili, which then EMERGENCY-folds every turn and forwards a payload still above the host trigger — a self-sustaining loop #5488 的消费方):会话键稳定 ⇒ 基线不再被拆段清空;压缩事件到来 ⇒ 静默重定基线(不是应急折叠)。断环。
- L2b 增量解析(watermark fastpath 的消费方):gate/tail 槽改读 L1——宿主原生 msgid 占位(契约里 "?" 槽本就合法,v3 语义:"对齐证明不得卡此通道");字节指纹为最高档自证:bili 持有上一轮自己转发出去的完整 body,前缀 span 直接比对,不等或带压缩标 ⇒ reseed。闭源宿主由此拿到与插件宿主同量级的收益。
- L2c 回放去重:TTL 内相同 msgid = 重试重放,不当新消息处理(顺带消掉 Two independent compactors fight each other: host-side kernel compaction spawns a fresh anonymous session inside bili, which then EMERGENCY-folds every turn and forwards a payload still above the host trigger — a self-sustaining loop #5488 的一半风险面)。
L1 先以纯观测形式落地(把收到的这族头全部写进回合元数据 + debug 日志,零行为变更)——它同时是 M1 的实锤工具与后续所有消费方的地基。
里程碑
- M0 采集表填齐(WorkBuddy ✅;其余每行独立推进,互不阻塞)
- M1 L1 观测层上线(纯日志;附:代理模式下自定义头实际到达性的实锤,含 ANTHROPIC_BASE_URL 翻译层掉头风险)
- M2 L2a 并入 Two independent compactors fight each other: host-side kernel compaction spawns a fresh anonymous session inside bili, which then EMERGENCY-folds every turn and forwards a payload still above the host trigger — a self-sustaining loop #5488 修复
- M3 L2b 并入 fastpath(实验性开关内,默认行为不变)
相关
- Two independent compactors fight each other: host-side kernel compaction spawns a fresh anonymous session inside bili, which then EMERGENCY-folds every turn and forwards a payload still above the host trigger — a self-sustaining loop #5488(死循环事故与其修复载体)
- [Bug][dsh] 20k-message / 81 MiB-inbound session: host aborted 6× in 2 h — the #3050 resident-AST OOM is not bounded by
sessions.hardMaxBytes(Windows, 0.1.210) #5403 / [Bug][dsh] Plugin lane performs a full re-parse and resends the entire body every round (81 MiB → hundreds of MB AST), w | [Bug][dsh] plugin 车道每轮全量解析重发 body(81 MiB→数百 MB AST)是 #5403 OOM 的生产者:用水位线断言替换逐条复核,服务端只解析新增尾段 #5418(watermark fastpath v3/v4 语义出处:"hosts compress/rewrite, so no alignment proof may gate this channel") - v0.1.210..master 审计(该窗口无回归,本单为增量能力,非缺陷修复)
Activity
[-][设计] 客户端原生的 session/msgid 统一身份层(身份采集表 + 增量解析重构)[/-][+][Design] Client-Native Unified Identity Layer for Session/MsgID (Identity Collection Table + Incremental Parsing Refacto | [设计] 客户端原生的 session/msgid 统一身份层(身份采集表 + 增量解析重构)[/+]ranxianglei commentedon Nov 3, 2026
(ai: vllm-qwen/qwen3.8-27b)
查重: 不重复。 检索了 open+closed 的 issue/PR(WorkBuddy / watermark / identity / msgid / compaction interop)。相关项与差异:
x-conversation-id认作稳定会话 id,OPEN 待人审):只修了会话级识别、且只覆盖一个客户端——不碰消息级 id、不碰压缩显式信号、不碰增量解析。本单 L1/L2a/L2b/L2c 正是把那条「加一行识别」的路径泛化成单一事实源,M2 吸收它;sessions.hardMaxBytes(Windows, 0.1.210) #5403/[Bug][dsh] Plugin lane performs a full re-parse and resends the entire body every round (81 MiB → hundreds of MB AST), w | [Bug][dsh] plugin 车道每轮全量解析重发 body(81 MiB→数百 MB AST)是 #5403 OOM 的生产者:用水位线断言替换逐条复核,服务端只解析新增尾段 #5418(fastpath v3/v4 语义出处)+ 在途的 watermark foundation increment PR:M3 的消费方地基。本单不改其语义,只在实验性开关内增加「读宿主原生 msgid + 字节指纹自证」。评估(feature triage 独立审核)
方向已由你拍板。我的独立审核结论:合理、与现有架构一致、建议按层推进,补三个判断:
sessions.hardMaxBytes(Windows, 0.1.210) #5403 的 OOM 教训自相矛盾。本轮计划(交付物)
标签: scope/wiring · dsh · workbuddy · codebuddy · claude-code · codex · opencode · p3 —— 设计/功能单非缺陷类不打 bug;scope 落协议接线/会话绑定面;客户端标签对应采集表各行;p3 因本单自身无直接可用性缺陷(底层 p1 事故在 #5488 单独跟踪)。
ranxianglei commentedon Nov 3, 2026
L2b 梳理:增量解析(watermark fastpath)机制全解 + 对原生 msgid 的推论
读完整份
src/server/fastpath.ts(520 行)+lane-decisions.ts+prepare-openai.ts调用点后的梳理,锚点均为当前 master。1. 机制一句话
dsh/插件模式下宿主每轮重发全部历史(这是宿主行为,非 bili 设计);legacy 每轮都要完整解析多 MiB 前缀(#5403 OOM 对)。fastpath 用"水位线"断言前缀未变:命中时只解码 head 片段(前 8 元素)+ window[K..T),中间 cores 直接复用 seed;正确性靠确定性 wire identity(#3642,content-hash 集群 id
h_+16hex)保证——复用不是启发式,与重解析数学等价。2. 证据链(由强到弱,全部服务端可自证)
id + JSON.stringifyheadEnd),headMatchesCore?槽合法(v3 裁决)、与持久化的上一轮尾部 id 做连续性检查x-bili-pf-seq戳回,客户端下轮回显x-bili-pf-count;不一致 → ahead/no-progress(credited);desync 一轮自愈miss 判定是全枚举分类(gate-off/breaker-held/no-stamp/ahead/no-progress/tail-ids-shape/tail-ids-prev/multimodal/unseeded/seed-stale/head-drift/…——每一类都是显式 verdict 而不是笼统失败)。
声明的信任边界(fastpath.ts:27,§7.4 人工评审标记):同 count 的前缀编辑不可检测 = 已接受的代价;破坏性改写(count 变化)必被 k-ahead/k-behind 抓住;v4 把检测搬进宽度算术(idActive ⇔ len==T−seed.stampT+1 ∧ model 匹配)。
3. 门控与状态
watermarkGateAllowed = featureOn && pluginMode && !titleGen && !incrementalTurn && !breakerArmed(lane-decisions.ts:324)。注意 incrementalTurn=true 的请求被排除——那条 lane 本来就发 delta,无需水位线。rollout.features.pluginFullResendWatermark默认 percent:5;envBILI_ROLLOUT_PLUGIN_FULL_RESEND_WATERMARK=1/0强制。WeakMap<Session,PFSeed>(注释明说不是会话真相,只是免重解析缓存)+ 持久化 session ext"pf:state"{t,m,misses,disabledAt,l}——重启后计数与断路器续跑。4. 对本 ticket(L1 原生身份层)的直接推论
(a) dsh 回显的三个输入在闭源客户端都有对等物:轮次边界 ← request-id 新鲜度;head 位置锚点 ← 宿主 msgid↔core id 绑定表;tail 槽 ← 绑定得着填真 id、绑定不着填
?(恰好就是 v3 合法槽语义)。即闭源场景不缺信号,缺的是 L1 绑定层把它们接进来。(b) §7.4 盲点在同源场景可升级:cores 本身是发送内容的字节等价表示(content-hash id),bili 只要持久化上一轮 core-id 序列的摘要,本轮 head 检查之外再比一次中段摘要,即可把"同 count 前缀编辑"从'接受代价'升级为'检测到即受控 reseed'(成本 O(n) 字符串比对,远低于重解析多 MiB body)。这条建议列入 M3 子项。
(c) WorkBuddy 式显式压缩头(X-Compaction: manual/reactive/auto)让 k-behind 判定变成先验事件:直接触发受控 reseed,省掉 miss-credit 试错轮——断路器的存在恰恰说明每次误判都在花一次真实 full parse + 一次客户端水印同步代价。
(d) 原则不变:宿主声明 ≠ 对齐证明。原生 id 只做身份/事件信号,永不绕过字节校验(与 v3 "no alignment proof may gate this channel" 裁决一致);任何一环证据缺失 ⇒ legacy fail-safe 原样兜底。L1 先以纯观察上线,顺便实证 #5488 悬案里的 ANTHROPIC translator 是否剥自定义头。
ranxianglei commentedon Nov 3, 2026
(ai: vllm-qwen/qwen3.8-27b)
收到,L2b 梳理与我在 master 上读到的代码一致(fastpath.ts 520 行、
watermarkGateAllowed门、finalize 每轮纪律、多模态永久降级——逐条对上)。四点推论全部接受,其中三点直接落进里程碑清单:M3 子项(并入本单跟踪,M3 实施时逐条过八问)
?(v3 合法槽语义原样复用),轮次界碑读 request-id 新鲜度;X-Compaction/X-Context-Compacted到来直接触发受控 reseed,省掉 miss-credit 试错轮(每轮试错 = 一次真实 full parse + 一次水印同步 RTT)。(d) 原则钉死:原生 id 只做身份/事件信号,永不绕过字节校验;任何一环证据缺失 ⇒ legacy fail-safe 原样兜底。这条我会在 M3 实现里写成硬约束(对齐证明通道只增不减)。
当前进度(M1 进行中):观测模块已写完(
src/native-identity.ts,五族头:session/turn/msgid/compaction/fingerprint;bili 自打的 x-bili-* 刻意排除,不给插件车道加噪音),正挂入 handle.ts 单点——body 解码之后、passthrough/bypass 分支之前,覆盖所有 HTTP 模型请求(含 cert-MITM 终止后的明文流);WS 车道(opencode v2 responses-over-websocket)M1 明确不覆盖,PR 里如实标注。零行为变更用 hermetic A/B 测试钉死:带/不带这族头两次请求的出站字节逐字相等 +x-conversation-id单独出现时不升格为身份源(#5526 落地前的既有行为保持)。顺带一个与 (d) 相关的现状事实:
buildForwardHeaders今天只剥 host/content-length,WorkBuddy 这族头已经原样透传到模型端点了——bili 只是没用它们。所以 M1 上线不改变上游可见面,只改变 bili 自己看得见什么。ranxianglei commentedon Nov 3, 2026
(ai: vllm-qwen/qwen3.8-27b)
L2a 梳理与 master 代码逐条对上(evaluateNudge 三条件、plugin 模式 nudge 强制关、injectNudge 挂最后 assistant、preflight emergencyCompress force+全非保护前缀、#5488 循环机理与 netSaved=-67127 指纹)。三段方案接受,补三个实现层判断:
① 触发器要分「边沿」和「电平」。按你源码级证据,X-Compaction 只在宿主刚压缩过的那个回合附着(事件,边沿);X-Context-Compacted 是「该上下文已被压过」(状态,电平,后续回合可能持续带)。Stage 1 的触发必须只用边沿:X-Compaction 出现即事件;X-Context-Compacted 只做佐证/状态标注,单独出现不触发重定基线——否则一个持续带电平头的宿主会让 bili 每个回合都当「刚发生压缩」。这个边沿/电平区分正是 M1 观测层要先实锤的:真流量里这两个头各附着几轮、是否成对,M1 上线后从
[native-id]日志直接读出,再写 Stage 1 的触发谓词。② Stage 2 大概率不用发明新路径——复用既有的「历史被替换」机制。把事件回合当 resume/restart 处理,语义上就是仓库里已有的 fork-adoption/resume-inheritance 面(
maybeAdoptResume/maybeAdoptForkBlocks,handle.ts 的 resume-inheritance miss 日志,#1333/#1362 derivedFrom lineage)。L2a 的本质是:用宿主的显式事件驱动这条既有路径,替代现在的内容启发式猜测。旧块标 superseded 不删,正好落在 kernel「id 永不复用」契约内(refs 账本完整,decompress 血统可追溯)。这样 Stage 2 的改动面收敛在「事件→既有 adoption 入口」的接线,而不是新造一套重定基线。③ 时序:M2 = Stage 1+2,依赖两件事先行——#5526(x-conversation-id 会话键稳定)合入 + M1 字段数据确认头的附着模式。缺任一腿都退化为现状(fail-safe 成立,你的第三段判断我认同:三条腿各自独立降级)。
里程碑清单更新:M2 = {① 边沿触发的事件识别(谓词等 M1 数据)② 静默重定基线(走既有 adoption 面)③ x-conversation-id 稳定键(#5526)};M3 = {① gate/tail 读 L1 msgid ② core-id 序列摘要检测同 count 前缀编辑 ③ 显式压缩头先验 reseed}。M1 观测层继续推进中。
ranxianglei commentedon Nov 3, 2026
(ai: vllm-qwen/qwen3.8-27b)
三锚模型(头 8 元素=前锚,tail-id=尾锚,中段指纹=中锚)接受——它把信任边界从「同 count 不可检测」精确推到「count 内任意编辑可检测」。对存储方案有一个范围修正,理由链如下:
中段指纹应当锚在进程内 seed 上,不落持久化 ext。 关键事实:seed cores 本身活在
WeakMap<Session,PFSeed>(注释明说不是会话真相、只是免重解析缓存),重启后必然 unseeded → 走 legacy 全解析 → 下轮 reseed+restamp,客户端一个 RTT 收敛——这是 fastpath 的既有设计。既然 cores 不跨重启,只持久化摘要也恢复不了命中资格(有摘要没 cores,复用无从谈起),落盘摘要数组的唯一效果是给会话 ext 加一块随 T 线性增长的数据(marathon 会话数千元素,每轮重写一行),买不到任何检测能力。所以:middle-drift,与现有全枚举分类同纪律。计算成本落在既有的结构扫描上,不是新解析遍。 fastpath-scan.ts 本来就是单遍字符遍历、零分配地测出每个元素的 span——中段的原始 wire 字节就在扫描手里。对每个 span 的原始字节算 FNV,不 JSON.parse、零新增分配。这正是保住 fastpath 性能收益的关键:若为验证而解析中段多 MiB,收益归零。
误报方向是安全的:若某宿主对历史做良性重序列化(逐轮字节不稳),raw-span 指纹会反复失配 → 反复 reseed → 最坏情况 = 现状行为 + 一次额外扫描,且
middle-driftverdict 日志让这种宿主立刻可见、可针对性豁免。反方向(漏报)不存在:任何字节改动必翻该元素哈希。这与 provider 前缀缓存自身的假设同构——95–97% 命中率的前提就是中段字节稳定,不稳定本来就是异常信号。边界披露:此子项改写 fastpath.ts:27 声明的信任边界(「同 count 前缀编辑不可检测 = 接受代价」→「检测到即受控 reseed」),属 §7.4 人工评审面,M3 PR 会显式列旧→新。排序确认:M1(观测,进行中)→ M2(L2a 事件识别+静默重定基线,断 #5488 环)→ M3(L2b 三锚,实验性开关内)。
ranxianglei commentedon Nov 3, 2026
M1 设计稿:宿主身份观察层(纯观测,零行为变化)
状态:设计稿,待拍板。范围:只采集、不上报、不改字节。
1. 一句话
在 bili 唯一的 relay 入口加一个纯采集器:每条模型请求到达时,认出它来自哪个客户端(WorkBuddy / codex / Claude Code / opencode / Gemini / 其他),抽出该客户端的原生身份头/字段与压缩信号,量出 id 稳定性,打日志并持久化进 session ext。不改变任何转发给上游的字节,不加任何 gate,默认关(
BILI_ROLLOUT_NATIVE_IDENTITY_OBSERVATION=1开启)。目的:把本 issue 采集表里所有「字符串级推测、待动态抓包」的条目变成实测数据(codex 头确切拼写、client_metadata KV、cc 主线 metadata 格式、各家压缩前缀真实触发)。2. 采集点(单点)
src/server/handle.ts——协议四分支判定(urlPath 后缀 → openai / anthropic / responses / google,L377–388)且decodeRequestBody完成之后(~L396)插一个observeNativeIdentity(...)调用。理由:req.headers,经既有headerValue())+ 解码后 body 都完整可用;3. 指纹与模式表(代码常量,后续可 env JSON 覆盖)
新文件
src/server/native-identity-profiles.ts:每个 profile ={ name, match(规则), headers:{名→粒度}, bodyPaths:[[...]], compactionPrefixes:string[] }。首发五家:X-Conversation-ID头X-Conversation-Message-ID、请求级X-Request-ID)X-Compaction/X-Context-Compacted(五家最强,直接当事件用)x-codex-installation-id或 (protocol==responses ∧x-client-request-id)x-codex-window-id/-turn-metadata/-parent-thread-id(拼写以实测为准,先按候选集采)msg_/ctc_/ctco_/rs_;每 item 的internal_chat_message_metadata_passthrough.turn_id;请求级previous_response_id轮换/responses/compact结尾(独立事件)X-Claude-Code-Session-Idmetadata.user_id.session_id(双通道互验,不一致 = incident 事件)toolu_出现次数当消息内锚点计数(不落全文)x-session-affinity(值形如ses_*)x-parent-session-id(事件)ses_/msg_/prt_串出现次数(期望恒 0,一旦非 0 = 上游协议变化,告警)<previous-summary>标签<state_snapshot>前缀(截断回退路径连这个都没有——恰好用来区分两条回退路)模式表为什么做成常量表而不是散落硬编码:五家压缩文案各自来自独立的二进制取证,存在随客户端版本漂移的风险,做成一张可热更、带单元测试钉住的表,将来加一家/改一条 = 改表 + 测试,不动架构。env 覆盖口(
BILI_NATIVE_IDENTITY_PROFILES_JSON)只做加法不改减(内置表是底)。4. 数据模型(session ext,沿用 fastpath 原语)
存储 =
store.setSessionExt(sessionId, "nidv:obs", json)(同src/server/fastpath.ts:186的写法,key 命名跟随pf:state惯例)。Schema:隐私红线:ext 里只存 sha1 前 8 位摘要(installation-id 类 PII 不以明文落盘);日志同样截断输出(
sess=a74ab59a…);全值只在请求生命周期内存里活。尺寸预算:整段 JSON 上限 ~4KB,events FIFO 保 32 条连续去重——对齐 gc-vacuum 时期的 ext 尺寸卫生规矩,不会长成一个新病根。M1 没有任何 breaker:观察层永远不参与决策,不存在误杀面。5. 观测输出
nidv: client=codex sess=a74ab59a… msgΔ=+12 evt=-;6. 非目标(红线清单)
7. 开关声明(家规:process.yaml 单一事实源)
knobs.ts+website/config-reference/process.yaml登记rollout.features.nativeIdentityObservation(bool,默认 false,envBILI_ROLLOUT_NATIVE_IDENTITY_OBSERVATION=1/0);PR 内附tools/gen-config-docs.mjs generate的重生成产物(CONFIGURATION.md + zh-CN)——#5374 的教训:碰配置必须连文档再生成交一起,否则 CI docs-sync 永久红。web 配置页快速按钮 = 明确后置(M1 先在 owner 机 env 验证价值,值得常驻再加 UI)。8. 测试计划与出口判据
单测:① 5 个 profile 各一组 fixture(头/body → 期望抽取,含负例:cc 的 metadata 字段畸形 JSON 吞错不抛);② 稳定性矩阵(重复相同→yes / 变更→no / 重启续接:预载旧 ext 断 prev 连续性);③ 前缀匹配器 × 四条压缩文案 + 对抗样本(文案出现在中段而非基线切换后的首个 user turn 不得命中);④ byte-stable 不变量(off/unknown ⇒ golden 零 diff);⑤ 时延冒烟(1MB body,p99 Δ≤1ms 实证)。
出口判据(满足即解锁 M2 绑定表 + tail 槽):owner WSL 盒启用约一周,混流 dsh(wb)+ cc + opencode + codex,采集表三个「待动态抓包」条目全部拿到实测回填;≥20 轮连续会话的稳定性统计;wb/cc/codex 三家各至少真实触发 1 次 compact 且本层检测命中 ≥1。
9. 工作量
~350–450 LOC(profiles 常量 ~120 + obs 核心 ~150 + 开关/文档/测试其余),单 PR,标准流程:branch → 本地全套件 → push → 自审评论挂 PR → owner 合并。
ranxianglei commentedon Nov 3, 2026
(ai: vllm-qwen/qwen3.8-27b)
L2c 设计接受,补三个实现层修正——其中第①条会改变去重键的定义:
① 去重键必须是 (session, msgid, body 指纹) 三元组,不能只是 (session, msgid)。 「同 msgid」有两种截然不同的宿主行为:(a) 真重放——请求字节逐字相同(网络层重试);(b) 调整后重发——宿主拿到错误(如 prompt-too-long)后改了 body 再发,msgid 可能沿用也可能换新。#5488 场景里 WorkBuddy 正是压缩/改写后再发,(b) 是真实存在的流量。若只按 msgid 去重,会把宿主的恢复性调整当成重放吞掉——等于替宿主做了它没做的决定。按三元组:字节相同 ⇒ 真重放,可安全去重;字节不同 ⇒ 新回合正常处理,且「同 msgid 不同字节」本身是高价值观测信号(M1 日志直接可见)。
② v1 应做「状态推进幂等」,不做「返回缓存响应」。 两个硬理由:其一,重试的语义是「第一次没收到响应」——第一次的流可能已半交付(SSE 传到一半 RST),回放录制的字节 = 把半个流当完整响应发出去;而且第一次的上游调用可能已计费完成,第二次仍须真实转发上游,去重省不了这次调用,能省的只有 bili 侧的状态副作用。其二,成本归属:[acp-usage]/billing 账目按「一回合一上游调用」记账,无调用回放响应会打乱面板成本归因。所以 L2c 的安全形态 = 重复 (session,msgid,body) 到达时,重新转发上游、但围栏掉会被二次应用的状态突变(nudge 基线覆写、ref 账本推进、块写入中的具体哪几项有害,需要 #5488 日志取证钉死,不猜)。
③ 因此建议把 L2c 拆成两步走(与仓库「门控双向断言」纪律一致):
[native-id] replay标记行(msgid + body 是否逐字相同)——这同时是①里「同 id 不同字节」信号的采集器,也是后续围栏的取证基础;TTL:60s 是合理起点但应由 M1 字段数据校准(各宿主重试退避的真实分布,WorkBuddy 的指数退避上界),写进 M1 观测的验收项而不是现在拍死。排序认同:L2c 三条腿独立于 L2a/L2b,L2c-obs 甚至可以先于 M2 上线。
ranxianglei commentedon Nov 3, 2026
(ai: vllm-qwen/qwen3.8-27b)
对,#3248 之后我按「双采集点、单模块」落,确认理解:
① 主采集点 = 增量车道入口(dsh/omp/opencode/pi 的 delta 回合)。这里比旧设计稿多拿到一样东西:msgIds 信封本身就是宿主原生消息级 id 数组(#4491 通道)——legacy 路径上根本没有的逐元素身份证据,L2b 绑定表的直接原料。压缩前缀检测也落在 delta 小载荷上,成本更低。
② 次采集点 = handle.ts relay 咽喉(我已在分支上挂好的那个点)。覆盖全量重发流量:WorkBuddy(#5488 那类闭源宿主,无进程内插件)、codex、Claude Code(ANTHROPIC_BASE_URL 代理模式)、gemini、opencode 纯代理模式——以及插件宿主回落全量重发的轮次。两个点写同一个模块、同一份
nidv:obsext schema(按 session id 归并);同一会话同一轮只会走其中一条路,read-modify-write 竞争窗口极小,走既有 setSessionExt 原子性即可,不加额外锁。两个实现层修正(其余设计稿照单执行:profile 表、ext schema、rollout 默认关、
nidv:行格式、五条红线、测试计划):sessions.hardMaxBytes(Windows, 0.1.210) #5403 消掉的开销。改为有界固定模式文本扫描(indexOf/计数,不解析):codex item id 前缀计数、cc metadata 路径、压缩前缀出现位置(记录 offset + 所属元素 role)。成本 O(n) 字符遍历,与 fastpath-scan 同量级,1MB p99≤1ms 冒烟实测背书;且 bodyBuffer 在任何轮次(含 fastpath 命中轮)都持有完整原始字节,两个采集点对所有轮次覆盖均匀,不与 fastpath 内部交互。开工:先读增量车道入口与 knobs/ext 三个锚点,然后按设计稿重构当前分支(profiles 表 + 双点接线 + 开关/文档再生成 + 扩展测试),单 PR 交付。
fix(obs): dual_mismatch event stores digests of both channels, never …
refactor(obs): satisfy unused-exports CI gate + drop no-op catch assi…
ranxianglei commentedon Nov 3, 2026
(ai: vllm-qwen/qwen3.8-27b)
M1(L1 观测层)已按你的设计稿落地,PR 已开: https://github.com/ranxianglei/billion-context/pull/5576(单 commit
878aad4ec,9 文件 +1138 行,纯新增;本地 typecheck 干净、定向测试 19/19 + 邻域 fastpath/rollout/incremental-lane 69/69 绿,全量回归交 CI)。配置面(按纪律单列):
rollout.features.nativeIdentityObservation默认 0(关),envBILI_ROLLOUT_NATIVE_IDENTITY_OBSERVATION=1/0;另有一个非门控的扩展 envBILI_NATIVE_IDENTITY_PROFILES_JSON(画像表按名追加,内置为底)。rollout.yaml seed + CONFIGURATION.md en/zh 已用 gen-config-docs 再生。迁移:无(纯增量、默认关)。八问:
sessions.hardMaxBytes(Windows, 0.1.210) #5403 删掉的开销,且 fastpath 命中轮 body 根本不解析,扫描反而统一覆盖)+ sha1-8 摘要红线(原始 id 值不落盘不入日志,测试钉住)+ extnidv:obs(稳定性矩阵/事件 FIFO ≤32/4KB 预算)+ 每请求一行 debug(nidv: client=… sess=… msgΔ=+N evt=…,压缩命中升 WARN)。pf:state同套 house pattern 守卫互不干扰)、golden 套件零 diff。未发现回归。sessions.hardMaxBytes(Windows, 0.1.210) #5403;③记录键从画像自身会话证据派生(n:<sha1-8>),不走 bili 身份链——[PR] fix: recognize CodeBuddy/WorkBuddy x-conversation-id as stable session identity (#5488) #5526 合入前 WB 会被键在账号级 x-session-id 上,同一账号多个会话合并会毁掉稳定性统计;④延迟断言用 CI 可移植的 20ms 上界,本地实测 p99≈2.1ms @ 1MiB codex 形 body(1ms 目标按现场数据跟踪,不拿共享 CI runner 的墙钟当断言)。一个实现期发现(与你的假设有出入):ext 持久化依赖统一存储 sidecar——#3818 起默认是
full,出厂即可用;但显式 opt-out 到 legacy 的实例会优雅降级为"只有日志行"(模块按 fastpath 同款守卫处理,降级不报错、不丢日志行)。你的设计稿假设 ext 恒可用,这是唯一一处实现偏离假设的地方,已在 PR 里披露并钉了降级测试。下一步:等你合并 + WSL 机混流(dsh/wb + cc + opencode + codex)开 env 跑一周现场数据(§8 退出判据:≥20 轮稳定性统计、wb/cc/codex 各 ≥1 次真实 compact 检出、回填采集表三个"待动态抓")。M2(并入 #5488 修复)等 #5526 合入 + 现场数据到位后按里程碑推进;M3 动 fastpath 信任边界,届时单独走 §7.4 人审。本轮无待决策事项,不需要 🔶。
收工判定:M1 交付完毕,状态 = 待合并 + 待现场数据。
ranxianglei commentedon Nov 3, 2026
(ai: vllm-qwen/qwen3.8-27b)
M1 PR CI 状态补充:#5576 首跑三个表面守卫红(docs-coverage / unused-exports / lifecycle-ledger-guard),均为自伤——新增的
BILI_NATIVE_IDENTITY_PROFILES_JSON未登记文档/LIFECYCLE、若干内部类型多带了 export。已修于第二 commitd908da405(env 入 env-only.yaml seed + CONFIGURATION en/zh 再生 + LIFECYCLE §1 立账带解除条件;8 个无跨文件消费者的导出降为内部)。重跑现状:14 个门 job 全绿(含三守卫),test 矩阵分片在跑,11 条 e2e 车道在 d908da4 已全部绿。已在 #4599 留一行结论。等矩阵收尾即可合。ranxianglei commentedon Nov 3, 2026
(ai: vllm-qwen/qwen3.8-27b)
M0 采集表进展(静态分析已落进 PR #5576 的画像表;动态抓包留待现场周):
<previous-summary>前缀<state_snapshot>前缀说明:①"画像已编码"指 PR #5576 的
native-identity-profiles.ts——现场周发现拼写/形状偏差时可用BILI_NATIVE_IDENTITY_PROFILES_JSON按名修正,不用发版;②三个"待动态"项(重试稳定性、codex/opencode 行为、cc metadata 实锤)由 WSL 机混流一周收口,这正是 M1 观测层要产出的数据;③静态推断与源码/strings 证实的区分已逐行标清,不混报。fix(obs): drop dead extraHeaders field never consumed by extraction
feat(fastpath): v5 identity-only head verification per owner ruling 2…
v6: canonical-ledger hit decision — rewrite-passthrough, R5 diagnosti…
v6: document the mid-insertion proof requirement of rewrite-passthrou…
v6: canonical-ledger hit decision — rewrite-passthrough, R5 diagnosti…
v6: document the mid-insertion proof requirement of rewrite-passthrou…
fix(ci): pass architecture-docs + unused-exports gates after master m…