Skip to content

[PR] fix(hermes): tool_search still binds session identity and model budget when deferring plugin tools (/acp panel + 500K wi | fix(hermes): tool_search 延迟插件工具时仍绑定会话身份与模型预算(/acp 面板 + 500K 窗口) - #4856

Merged
ranxianglei merged 4 commits into
ranxianglei:masterfrom
yanhuojiyusheng:2026-10-26_hermes-acp-session-binding
Oct 27, 2026

Conversation

@yanhuojiyusheng

Copy link
Copy Markdown
Contributor

Fixes #4855

问题

Hermes 默认开启 tools.tool_search,model_tools.py:517-534 把插件 toolset 换成 tool_search/describe/call 桥,出站 tools[] 里没有 compress/decompress/acp_status。_plugin_tools_visible()(hermes-plugin/__init__.py:1567-1576)因此为假,on_llm_request 在 :1602-1607 整个 return None —— 连 x-bili-plugin、x-bili-plugin-conversation、model、window 四个头一起丢。代理只能铸 pfa-* 匿名会话(src/session.ts:1330),于是 /acp 用 local-* 查状态 404 并误报 "No model request yet",窗口也从 500K 回落 200K。

压缩本身没坏(pfa-* 下确实在 fold),坏的是身份与预算。

做法:身份/预算与压缩执行者解耦

新增可选协议头 x-bili-plugin-compression: proxy:

结果:会话仍绑在 local-* 上,/acp 有面板、窗口走真实 500K;压缩继续由代理注入执行(工具仍在 tool_search 后面,代理模式本来就需要自己注入才跑得起来,见 src/server/lane-decisions.ts:264-278)。

为什么不按「有 tool_call 桥就转 native」

桥的存在不证明当前会话能调出全部 ACP 工具(模型可能从不 describe compress)。那样做会把插件头盖在一个实际压不了的会话上,比现状更糟。

测试

  • tests/plugin-protocol.test.ts 新用例 4 条腿:①deferred(仅 read_file)→ 上游收到注入的 compress/decompress/acp_status 且保留 read_file,status?conversationId=sess-defer 200、pluginAgent=hermes、contextLimit=500000;②部分快照(只有 compress)→ compress 不重复注入、其余补齐;③无 override 头 → 纯插件道不注入;④只有 conversation + compression、无 x-bili-plugin marker → 会话仍 404。
  • tests/hermes-native.test.ts 新增 defer-fallback 驱动场景(无工具 / 仅 compress / 全可见三种 mw 调用),断言前两者都带 proxy、全可见时不带。

本地:npm run typecheck 0 错;npm run build 成功;node --import tsx --test tests/plugin-protocol.test.ts tests/pi-subagent-channel.test.ts tests/plugin-native-binding.test.ts 45/45 pass;tests/hermes-native.test.ts 51/51 pass(均为 RED→GREEN)。

实机验证(Hermes TUI + 本 PR 构建):/__bili/plugin/status?conversationId=local-b6dc… 由 404 变 200,pluginAgent=hermes、windowSource=plugin、contextLimit=500000、runtimeInfo.source=hermes-native。

风险 / 已知边界

  • 该会话会持续走「代理注入压缩」通道(与今天实测行为一致,只是身份/预算修好了);要真 native 需 Hermes 不再把 plugin toolset 放进 tool_search,不在本 PR 范围。
  • _plugin_tools_visible() 只读 tools[].function.name|name;模式误判会导致同名工具重复注入(被 404 腿的断言卡住)。
  • 切到新身份后,旧 pfa-* 的块状态不复用(首轮一次性重付),属预期。

按 AGENTS §7.4,本 PR 触及会话绑定/身份语义,需人工 review,不自动合。

@ranxianglei ranxianglei changed the title fix(hermes): tool_search 延迟插件工具时仍绑定会话身份与模型预算(/acp 面板 + 500K 窗口) [PR] fix(hermes): tool_search still binds session identity and model budget when deferring plugin tools (/acp panel + 500K wi | fix(hermes): tool_search 延迟插件工具时仍绑定会话身份与模型预算(/acp 面板 + 500K 窗口) Oct 26, 2026
@yanhuojiyusheng
yanhuojiyusheng force-pushed the 2026-10-26_hermes-acp-session-binding branch from 8b99a39 to 0cc265f Compare October 26, 2026 14:24
ranxianglei pushed a commit to yanhuojiyusheng/billion-context that referenced this pull request Oct 26, 2026
…4856 review)

The author's rebase landed on 05c379e; master has since advanced via ranxianglei#4837 (kernel-guard lockfile exemption). Additive sync per AGENTS §7.2 — no history rewrite.
@ranxianglei

Copy link
Copy Markdown
Owner

🤖 Powered by ework · qwen3.8-27b

(ai: vllm-qwen/qwen3.8-27b)

查重: 不重复。按失败签名(hermes tool_search / x-bili-plugin-compression / pfa-* 匿名会话)检索 open+closed 的 issue 与 PR:仅相关项为 #4855(本 PR 的来源 issue);#2398 已关闭且机制不同(Responses 预算记账);#4694/#4702 是 pi subagents 配置,无关。

标签: bug / hermes / scope/wiring / p2(缺陷修复;hermes 车道;协议接线层;能用但面板与窗口错——压缩本身不受影响,故 p2 不 p1)。

审核结论(八问)

  1. 是什么问题:Hermes 默认把 ACP 压缩工具延迟到 tool_search 后面时,插件把身份头、会话头、model、window 四个头全部丢掉,代理只能铸匿名会话——/acp 面板 404、窗口从 500K 回落 200K。压缩本身没坏。
  2. 什么原因导致的:插件「全有或全无」的头逻辑把「压缩工具是否可见」和「是否盖身份/预算头」绑死在一起——两个本独立的职责被一个布尔值决定,不可见时整个 return None,身份与预算被连坐。
  3. 影响范围与严重程度:所有开启默认 tools.tool_search=auto 的 Hermes 原生会话(即绝大多数 Hermes 用户):/acp 误报 "No model request yet"、窗口按 200K 而非真实 500K 计预算(压缩偏早触发但不坏数据)。可用性不受损 → p2。
  4. 怎么修复的:身份/预算与压缩执行者解耦——新增可选协议头 x-bili-plugin-compression: proxy:代理侧要求同时存在 x-bili-plugin marker 才认,据此把请求降为代理注入压缩(复用 会话卡死自愈机制:卡死模式检测与代理侧主动补救 #2155/[Pi / pi-subagents] 默认内置角色的 tools 白名单阻挡 ACP:bili 自动加载成功,但上下文工具仍不可调用 #2268 既有降级形态);插件侧从 manifest 读能力,工具不可见但能力已广播时照常盖身份/model/window 头再追加 proxy。老代理不广播 → 分支不可达,零行为变化。「有 tool_call 桥就转 native」被正确拒绝——桥的存在不证明模型能调出全部工具,我的独立判断与此一致。
  5. 是否有回归问题:逐项审过——① 无新头的旧插件/旧代理行为逐字节不变(测试腿③④钉住;hermes-native 空能力场景钉住 legacy 路径);② 会话内模式切换为 per-request 无状态判定,与 会话卡死自愈机制:卡死模式检测与代理侧主动补救 #2155/[Pi / pi-subagents] 默认内置角色的 tools 白名单阻挡 ACP:bili 自动加载成功,但上下文工具仍不可调用 #2268 既定形态一致,可自愈;③ pluginMode 其余消费方(ccrStamp/imageRescale/demoteGate/prepare*)对代理模式请求的处理与今天 de-facto 通道(匿名 pfa-* 本就是代理模式)一致,无语义漂移;④ 伪造面:新头无 marker 不生效(腿④钉 404),未超出 marker 既有授权;⑤ 唯一实际回归风险点(golden 契约缺再生导致 CI 红)已修复并本地复现验证。未发现无意回归。
  6. 是否破坏缓存:不涉及缓存/重试/游标/去重键。切到 local-* 身份后旧 pfa-* 块状态不复用,首轮一次性重付——预期代价,PR 已声明。
  7. 是否改了设计方向:轻微增量——插件协议多一个可选能力字段(manifest headers.compression)与一个请求头;双压缩模式架构及其绑定规则不变。
  8. 是否引入新配置:无用户面配置/env/CLI;仅协议层字段,经 manifest 自动广播,无需迁移。

审核发现与处置(小问题直接修,均已推上 PR 分支)

分支维护说明

审核期间 PR head 被换成了基于较旧 master(05c379efd)的 rebase 版本,而当前 master 已推进到 24e71f6(含 #4837 等)。我把当前 master 合并进 PR 分支(additive sync,未改写历史),最终 head 8faf94b83,其文件树与我本地验证过的绿色状态逐字节一致。另报一个事实供 owner 知悉:审核期间该提交一度出现在 master tip、随后又不在(master 被重写),PR 目前仍未合入。

验证

typecheck 干净;定向套件(wire-contract-golden / plugin-protocol / hermes-native / pi-subagent-channel / plugin-native-binding / lane-decisions-matrix)191/191 pass;全量回归以远程 CI 为准(新 head 上重跑中)。

判定:方向正确、机制健全、测试双向有牙(gate 纪律的正反两侧都钉住了)、唯一拦路项(golden 红)已修并推上 PR 分支。CI 转绿后可合——按 §7.4 触及会话绑定/身份语义,需人工 review 与手动合并:#4856

@ranxianglei ranxianglei added bug Something isn't working hermes 改动范围:内核之外的主机侧接线(src/agent 适配器、server 路由、注入拦截)/范围兜底/客户端标签 p2 scope/wiring labels Oct 26, 2026
yanhuojiyusheng and others added 4 commits October 27, 2026 08:36
… the ACP tools (ranxianglei#4855)

Hermes defaults tools.tool_search to "auto": model_tools.py:517-534 rewrites the
plugin toolset into the tool_search/describe/call bridge, so the outgoing request
never carries compress/decompress/acp_status. _plugin_tools_visible() therefore
returns False and on_llm_request bailed out entirely — dropping x-bili-plugin,
x-bili-plugin-conversation, the model and the context-window headers. The proxy
then minted a pfa-* anonymous session (session.ts:1330): /acp's status lookup
404'd and read as "No model request yet", and the window fell back to 200K.

Decouple identity/budget from whoever executes compression. The proxy advertises
x-bili-plugin-compression in the manifest headers; the plugin stamps "proxy" only
when that capability is advertised AND the ACP tools are absent from the request,
and the proxy honors it only alongside the x-bili-plugin marker (same degradation
shape as ranxianglei#2155's plugin-channel death and ranxianglei#2268's pi-subagent role whitelist).
Identity and budget now bind to the conversation; the proxy keeps owning the
compress injection while the tools stay deferred.

Tests: plugin-protocol gains a 4-leg case (deferred/subset/no-override/no-marker —
the last one must still 404) and hermes-native a defer-fallback driver scenario.
…pression (ranxianglei#4855)

The PR added the optional protocol header x-bili-plugin-compression, broadcast in the plugin manifest under headers.compression, but did not regenerate the wire-contract golden snapshots, so tests/wire-contract-golden.test.ts went red on manifest-default.json and manifest-optin.json (CI: 3 of 6 test shards failing on exactly those two tests; parent commit was fully green). Regenerated with scripts/update-wire-contract-goldens.ts; the diff is additive only — one new key per manifest snapshot. Ledger grows, nothing shrinks.
…ompression-channel comments (ranxianglei#4855)

All six comments added by this PR cite ranxianglei#4657, which is an unrelated closed PR (hermes proxy-misdetection fix, Fixes ranxianglei#4656). The anchor must point at the issue being fixed, ranxianglei#4855. Comment text otherwise untouched.
…l/compression) (ranxianglei#4855)

The manifest example listed only agent/conversation/contextWindow/instructionsMutable — it already missed maxOutput and model before this PR, and now also misses headers.compression. Complete the example to match the real manifest and add one sentence documenting the optional per-request compression override semantics.
@yanhuojiyusheng
yanhuojiyusheng force-pushed the 2026-10-26_hermes-acp-session-binding branch from 8faf94b to 75c8170 Compare October 27, 2026 00:36
@ranxianglei
ranxianglei merged commit e4cc685 into ranxianglei:master Oct 27, 2026
51 checks passed
@github-actions

Copy link
Copy Markdown
Contributor

📦 Merged Build Published

This merge to master was published as 0.0.873-master.e4cc685 (rolling master tag).

npm install -g billion-context@0.0.873-master.e4cc685  # this exact build
npm install -g billion-context@master  # always the newest merged build

npm view billion-context@master prints the current version at any time.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working hermes 改动范围:内核之外的主机侧接线(src/agent 适配器、server 路由、注入拦截)/范围兜底/客户端标签 p2 scope/wiring

Projects

None yet

2 participants