Skip to content

fix(desktop): 补齐桌面端身份头 + chat 的 sse-gzip-recovery(修复 chat 410 freebuff_update_required) - #36

Merged
HengXin666 merged 2 commits into
HengXin666:mainfrom
xiao398008:fix/freebuff-410-desktop-identity-headers
Oct 18, 2026
Merged

HengXin666 merged 2 commits into
HengXin666:mainfrom
xiao398008:fix/freebuff-410-desktop-identity-headers

Conversation

@xiao398008

Copy link
Copy Markdown
Contributor

现象

POST /api/v1/chat/completions 一律返回 410(freebuff_update_required,official 通道下 body 为空),而 session/admission 与 agent-runs 都正常。同机、同账号用官方客户端(Desktop 0.0.173 / CLI 0.2.27)对话正常,说明不是账号或出口问题。

根因

上游对 chat 一跳加了客户端身份门禁:请求缺少桌面端身份组时被判为过时客户端。与官方请求逐字段比对后,缺口三处:

位置 官方 本仓(改前)
session/admission x-freebuff-client: desktop、x-freebuff-client-version: <桌面版号>、x-freebuff-install-id 缺
session/admission x-freebuff-wallet-spend-limit: session 0
chat x-freebuff-sse-gzip-recovery: 1 缺

取值来源:官方桌面端 0.0.173 的 resources/orchestrator/orchestrator.js(常量 FREEBUFF_CLIENT_HEADER / FREEBUFF_CLIENT_VERSION_HEADER / FREEBUFF_INSTALL_ID_HEADER / FREEBUFF_SSE_GZIP_RECOVERY_HEADER,其中 client-version 取 FREEBUFF_APP_VERSION),并用同机同号的抓包逐字段 diff 复核。

改动

  • src/upstream/fingerprint/official-fingerprint.ts:补身份组头与 install-id;wallet-spend-limit 缺省 'session'
  • cli-bridge/lib/endpoints/chat-payload.ts:chat 补 x-freebuff-sse-gzip-recovery: 1
  • src/upstream/upstream-contract.ts:登记两个新头(过契约门禁)

验证

  • 实测:/v1/chat/completions → 200 + 完整流式回复;日志无 upstream chat non-ok
  • npm run check:gates 全绿(18/18)、check-upstream-contract 通过、typecheck 通过

备注

x-freebuff-client-version 缺省值 0.0.173 是抓包时的桌面版号;桌面端发版后需同步更新该缺省(也可用 FREEBUFF_APP_VERSION 覆盖)。

关联 #35。

上游对 chat 一跳加了客户端身份门禁: 缺少桌面端身份头时
/api/v1/chat/completions 一律 410 freebuff_update_required(official 通道下
body 为空), 而 session/admission 与 agent-runs 都正常.

- officialSessionHeaders: 补 x-freebuff-client: desktop、
  x-freebuff-client-version(取 FREEBUFF_APP_VERSION, 缺省按当前桌面版
  0.0.173)与 x-freebuff-install-id(有值时); wallet-spend-limit 缺省改
  'session'
- cli-bridge chat 头: 补 x-freebuff-sse-gzip-recovery: 1
- upstream-contract: 登记两个新头

取值对齐官方桌面端 0.0.173 orchestrator 的 FREEBUFF_CLIENT_HEADER /
FREEBUFF_CLIENT_VERSION_HEADER / FREEBUFF_INSTALL_ID_HEADER /
FREEBUFF_SSE_GZIP_RECOVERY_HEADER, 并与同机同号抓包逐字段 diff 复核.

实测: /v1/chat/completions 200 + 完整流式回复; 日志无 upstream chat non-ok;
npm run check:gates 全绿.

关联 HengXin666#35.
@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

修复桌面端身份头缺失导致的聊天接口 410 错误

🐞 Bug fix 🕐 20-40 Minutes

Grey Divider

AI Description

• 为会话请求补齐桌面端身份头,并将钱包额度缺省值对齐官方客户端。
• 为聊天请求补充 SSE gzip 恢复标记,修复上游返回的 410 错误。
• 将新增头部纳入上游契约门禁;本次未新增自动化测试。
Diagram

graph TD
  R["客户端请求"] --> S["会话头装配"] --> A["会话准入"] --> H["聊天头装配"] --> C["聊天接口"] --> E["SSE 响应"]
Loading
High-Level Assessment

沿用现有会话和聊天请求头装配路径、只补齐抓包确认的差异,是此次修复范围内最直接的方案;无需改变流式处理或鉴权架构。桌面版本可通过 FREEBUFF_APP_VERSION 覆盖,但硬编码的 0.0.173 缺省值需随官方发版维护。

Files changed (3) +18 / -1

Bug fix (2) +12 / -1
chat-payload.ts为上游聊天请求添加 SSE gzip 恢复标记 +2/-0

为上游聊天请求添加 SSE gzip 恢复标记

• 在聊天请求头中加入 x-freebuff-sse-gzip-recovery: 1,对齐官方客户端的请求形态。

cli-bridge/lib/endpoints/chat-payload.ts

official-fingerprint.ts补齐会话请求的桌面端身份头 +10/-1

补齐会话请求的桌面端身份头

• 会话请求现在携带 desktop 身份及版本,并在配置了安装 ID 时携带该 ID。POST 请求的钱包额度缺省值由 0 改为 session。

src/upstream/fingerprint/official-fingerprint.ts

Other (1) +6 / -0
upstream-contract.ts将新增请求头登记到上游契约 +6/-0

将新增请求头登记到上游契约

• 新增客户端版本头和 SSE gzip 恢复头常量,并将两者加入业务头清单,以供契约门禁校验。

src/upstream/upstream-contract.ts

@qodo-code-review

qodo-code-review Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (0) 📎 Requirement gaps (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Action required

1. 非 bun 回落路径的 chat 仍缺恢复标记 🔴 P0 ●●● Strong 🐞 Bug ≡ Correctness ⭐ New
Description
本轮 note 写明 proxy.js 的 chat「按桌面形态补 x-freebuff-sse-gzip-recovery: 1」,但 src/
里没有任何地方发这个头:forward.ts 的 legacy upstream.raw('/api/v1/chat/completions') 用的是
filterRequestHeaders 加 officialChatHeaders(),两者都不带它,只有 cli-bridge 的 buildHeaders 补了。设
FREEBUFF_DISABLE_BUN=1,或 official 通道没拿到响应而走 legacy 回落时,chat 请求仍缺本次认定的桌面 chat 头。PR 自己也说没逐项
A/B,所以这条路径可能还是 410 freebuff_update_required,而 note 却说它已经补齐。
Code

.agents/notes/implemented/bug-fix/2026-09-18-official-cli-fingerprint.md[R65-67]

+- `proxy.js` 的 chat 改用 `officialChatHeaders()`,并按桌面形态补
+  `x-freebuff-sse-gzip-recovery: 1`.鉴权头统一来自 `freebuffAuthHeaders()`
+  (只发 `Bearer`).
Relevance

●●● Strong

PR 明确要补齐 chat 指纹;legacy fallback 未复用该头,属于确定的路径遗漏。

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
在 src/ 下 grep sse-gzip-recovery,只命中 upstream-contract.ts 里的常量定义,没有任何出站代码用到它。forward.ts 的 legacy
chat 回落请求头只由 filterRequestHeaders 和 officialChatHeaders 拼成,所以 note 第 65-66 行说的情况与代码不符。

src/proxy/transport/forward.ts[79-100]
src/proxy/transport/forward.ts[145-165]
src/upstream/upstream-contract.ts[87-88]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Node 侧 legacy chat 路径(official 通道未命中或 bun 被禁用时走的路径)没有发 `x-freebuff-sse-gzip-recovery: 1`,只有 cli-bridge 发了。note 却写着 proxy 的 chat 已经补上这个头。

## Fix Focus Areas
- src/upstream/fingerprint/official-fingerprint.ts[200-240]
- src/proxy/transport/forward.ts[79-100]
- .agents/notes/implemented/bug-fix/2026-09-18-official-cli-fingerprint.md[65-67]

## Recommended Fix
在 `officialChatHeaders()` 里加上 `[H_SSE_GZIP_RECOVERY]: '1'`(或在 forward.ts 的 headers 里显式加上),让 legacy chat 与 cli-bridge 的 `buildHeaders` 形态一致;同时在 smoke 测试里断言 legacy chat 带有这个头。

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Dismiss ↗ | View ↗



Remediation recommended

No findings for this group


Informational

No findings for this group

Grey Divider


Resolved findings

1. Desktop headers lack decision history ✓ Resolved
Description
officialSessionHeaders adds desktop identity headers and changes the wallet limit default without
updating the existing fingerprint decision note, while comments in it and buildHeaders put
request-alignment and header-origin rationale in the implementation. When a maintainer revisits
these headers or defaults, the note still describes the older session header set and version policy,
and the implementation comments can be mistaken for the decision record.
Code

src/upstream/fingerprint/official-fingerprint.ts[R116-118]

+    'x-freebuff-client': 'desktop',
+    'x-freebuff-client-version':
+      (process.env.FREEBUFF_APP_VERSION || '').trim() || '0.0.173',
Relevance

●●● Strong

Recent precedent accepts moving rationale into linked notes and adding backlinks near governed
implementation decisions.

PR-#31

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The changed session headers and default are not reflected in the existing fingerprint note. New
comments beside officialSessionHeaders explain alignment with orchestrator.js, version sourcing
and fallback, while a buildHeaders comment attributes the SSE gzip marker to official chat;
together, these show that the rationale remains in implementation comments rather than an updated
note linked from the enforcing code.

Rule 3955925: Keep rationale and history out of implementation comments
src/upstream/fingerprint/official-fingerprint.ts[113-121]
src/upstream/fingerprint/official-fingerprint.ts[162-165]
.agents/notes/implemented/bug-fix/2026-09-18-official-cli-fingerprint.md[9-49]
src/upstream/fingerprint/official-fingerprint.ts[113-115]
cli-bridge/lib/endpoints/chat-payload.ts[184-185]
Skill: hx-agent-notes

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The desktop identity headers and wallet limit default change a guarded upstream contract without updating its existing decision note. Implementation comments also carry the rationale for those choices and for the SSE gzip marker instead of pointing maintainers to a decision record.

## Fix Focus Areas
- src/upstream/fingerprint/official-fingerprint.ts[113-121]
- src/upstream/fingerprint/official-fingerprint.ts[164-165]
- cli-bridge/lib/endpoints/chat-payload.ts[184-185]
- .agents/notes/implemented/bug-fix/2026-09-18-official-cli-fingerprint.md[9-49]

## Recommended Fix
Update the implemented note to describe the current desktop header choices, version fallback, wallet limit default, and their tradeoffs. Move any rationale worth retaining from the implementation comments, including the SSE gzip marker’s origin, into that note or related documentation. Replace rationale-heavy comments with brief links to the relevant documentation beside the code enforcing those choices.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. 桥接端完整对话仍缺桌面身份 ✓ Resolved
Description
新增的 x-freebuff-client-version 只进入 officialSessionHeaders,桥接端的 admit 仍自行构造不含该头的准入请求。调用桥接端的
full 操作时,它先经这条未更新的路径准入,再发送已补恢复标记的 chat 请求,因此这条对话入口仍缺少本次修复所针对的完整身份组。
Code

src/upstream/fingerprint/official-fingerprint.ts[R116-118]

+    'x-freebuff-client': 'desktop',
+    'x-freebuff-client-version':
+      (process.env.FREEBUFF_APP_VERSION || '').trim() || '0.0.173',
Relevance

●●● Strong

Bridge full admission uses a separate header builder and omits the newly required client-version
identity header.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
桥接端有独立的准入头构造器,其中只有 x-freebuff-client 和可选安装 ID,没有新增的版本头;公开的 full 操作确实调用该准入器后继续调用 chat。

src/upstream/fingerprint/official-fingerprint.ts[113-121]
cli-bridge/lib/endpoints/session.ts[29-44]
cli-bridge/lib/upstream/actions.ts[187-205]
cli-bridge/lib/upstream/actions.ts[206-216]
cli-bridge/lib/upstream/actions.ts[238-246]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
主服务的准入请求新增了桌面版本头,但桥接端独立构造的准入请求没有同步,`full` 操作仍走缺头路径。
## Fix Focus Areas
- src/upstream/fingerprint/official-fingerprint.ts[116-121]
- cli-bridge/lib/endpoints/session.ts[29-44]
- cli-bridge/lib/upstream/actions.ts[187-205]
## Recommended Fix
让桥接端准入复用同一份桌面身份配置,至少同步发送 `x-freebuff-client-version`,并验证 `full` 操作从准入到 chat 的请求头。

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. 已有安装身份在准入时被遗漏 ✓ Resolved
Description
officialSessionHeaders 只从 FREEBUFF_INSTALL_ID 读取安装 ID,但现有 Bun 会话路径从桌面端状态文件读取该值。机器上已有
state.json 而未设置该环境变量时,POST 准入不带安装 ID,随后同一会话的 Bun 心跳却带上它,准入所需的桌面身份组仍未补齐。
Code

src/upstream/fingerprint/official-fingerprint.ts[R119-120]

+    ...((process.env.FREEBUFF_INSTALL_ID || '').trim()
+      ? { 'x-freebuff-install-id': (process.env.FREEBUFF_INSTALL_ID || '').trim() }
Relevance

●●● Strong

Admission reads only the environment, while the same Bun session sources install identity from
persisted state.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
新增的 Node 头只检查环境变量;现有 Bun 路径读取状态文件,并用于准入后的 GET 和释放。因此存在无需假设上游响应、即可复现的同会话身份头缺失和不一致。

src/upstream/fingerprint/official-fingerprint.ts[113-121]
src/upstream/client/bun-channel.ts[39-48]
src/upstream/client/bun-channel.ts[214-229]
src/upstream/client/endpoints/session.ts[57-73]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Node 准入仅从环境变量取得安装 ID,而 Bun 会话请求从桌面端状态文件取得,导致同一会话的请求身份不一致。
## Fix Focus Areas
- src/upstream/fingerprint/official-fingerprint.ts[119-121]
- src/upstream/client/bun-channel.ts[39-48]
- src/upstream/client/endpoints/session-headers.ts[69-82]
## Recommended Fix
在会话配置中解析一次安装 ID,并让 Node 准入及 Bun 会话请求使用同一个值;保留无可用安装 ID 时省略该头的行为。

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context sources
✅ Compliance rules (platform): 11 rules
✅ Cross-repo context — repo relationships
Review mode: Auto: 🧠 Deep: 多条会话与鉴权路径同步改动,存在多个独立且易遗漏的逻辑缺陷。
✅ Fix-loop round: fix loop

Grey Divider

Tip of the day
💡 Did you know, you can move or hide resolved findings to focus on what still needs action

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Previous reviews

Review updated until commit c6b121a

Results up to commit 6c08278 ⚖️ Balanced


🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)


Action required

No findings for this group


Remediation recommended

No findings for this group


Informational

No findings for this group


Resolved findings

1. Desktop headers lack decision history ✓ Resolved
Description
officialSessionHeaders adds desktop identity headers and changes the wallet limit default without
updating the existing fingerprint decision note, while comments in it and buildHeaders put
request-alignment and header-origin rationale in the implementation. When a maintainer revisits
these headers or defaults, the note still describes the older session header set and version policy,
and the implementation comments can be mistaken for the decision record.
Code

src/upstream/fingerprint/official-fingerprint.ts[R116-118]

+    'x-freebuff-client': 'desktop',
+    'x-freebuff-client-version':
+      (process.env.FREEBUFF_APP_VERSION || '').trim() || '0.0.173',
Relevance

●●● Strong

Recent precedent accepts moving rationale into linked notes and adding backlinks near governed
implementation decisions.

PR-#31

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The changed session headers and default are not reflected in the existing fingerprint note. New
comments beside officialSessionHeaders explain alignment with orchestrator.js, version sourcing
and fallback, while a buildHeaders comment attributes the SSE gzip marker to official chat;
together, these show that the rationale remains in implementation comments rather than an updated
note linked from the enforcing code.

Rule 3955925: Keep rationale and history out of implementation comments
src/upstream/fingerprint/official-fingerprint.ts[113-121]
src/upstream/fingerprint/official-fingerprint.ts[162-165]
.agents/notes/implemented/bug-fix/2026-09-18-official-cli-fingerprint.md[9-49]
src/upstream/fingerprint/official-fingerprint.ts[113-115]
cli-bridge/lib/endpoints/chat-payload.ts[184-185]
Skill: hx-agent-notes

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The desktop identity headers and wallet limit default change a guarded upstream contract without updating its existing decision note. Implementation comments also carry the rationale for those choices and for the SSE gzip marker instead of pointing maintainers to a decision record.

## Fix Focus Areas
- src/upstream/fingerprint/official-fingerprint.ts[113-121]
- src/upstream/fingerprint/official-fingerprint.ts[164-165]
- cli-bridge/lib/endpoints/chat-payload.ts[184-185]
- .agents/notes/implemented/bug-fix/2026-09-18-official-cli-fingerprint.md[9-49]

## Recommended Fix
Update the implemented note to describe the current desktop header choices, version fallback, wallet limit default, and their tradeoffs. Move any rationale worth retaining from the implementation comments, including the SSE gzip marker’s origin, into that note or related documentation. Replace rationale-heavy comments with brief links to the relevant documentation beside the code enforcing those choices.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. 已有安装身份在准入时被遗漏 ✓ Resolved
Description
officialSessionHeaders 只从 FREEBUFF_INSTALL_ID 读取安装 ID,但现有 Bun 会话路径从桌面端状态文件读取该值。机器上已有
state.json 而未设置该环境变量时,POST 准入不带安装 ID,随后同一会话的 Bun 心跳却带上它,准入所需的桌面身份组仍未补齐。
Code

src/upstream/fingerprint/official-fingerprint.ts[R119-120]

+    ...((process.env.FREEBUFF_INSTALL_ID || '').trim()
+      ? { 'x-freebuff-install-id': (process.env.FREEBUFF_INSTALL_ID || '').trim() }
Relevance

●●● Strong

Admission reads only the environment, while the same Bun session sources install identity from
persisted state.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
新增的 Node 头只检查环境变量;现有 Bun 路径读取状态文件,并用于准入后的 GET 和释放。因此存在无需假设上游响应、即可复现的同会话身份头缺失和不一致。

src/upstream/fingerprint/official-fingerprint.ts[113-121]
src/upstream/client/bun-channel.ts[39-48]
src/upstream/client/bun-channel.ts[214-229]
src/upstream/client/endpoints/session.ts[57-73]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Node 准入仅从环境变量取得安装 ID,而 Bun 会话请求从桌面端状态文件取得,导致同一会话的请求身份不一致。
## Fix Focus Areas
- src/upstream/fingerprint/official-fingerprint.ts[119-121]
- src/upstream/client/bun-channel.ts[39-48]
- src/upstream/client/endpoints/session-headers.ts[69-82]
## Recommended Fix
在会话配置中解析一次安装 ID,并让 Node 准入及 Bun 会话请求使用同一个值;保留无可用安装 ID 时省略该头的行为。

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. 桥接端完整对话仍缺桌面身份 ✓ Resolved
Description
新增的 x-freebuff-client-version 只进入 officialSessionHeaders,桥接端的 admit 仍自行构造不含该头的准入请求。调用桥接端的
full 操作时,它先经这条未更新的路径准入,再发送已补恢复标记的 chat 请求,因此这条对话入口仍缺少本次修复所针对的完整身份组。
Code

src/upstream/fingerprint/official-fingerprint.ts[R116-118]

+    'x-freebuff-client': 'desktop',
+    'x-freebuff-client-version':
+      (process.env.FREEBUFF_APP_VERSION || '').trim() || '0.0.173',
Relevance

●●● Strong

Bridge full admission uses a separate header builder and omits the newly required client-version
identity header.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
桥接端有独立的准入头构造器,其中只有 x-freebuff-client 和可选安装 ID,没有新增的版本头;公开的 full 操作确实调用该准入器后继续调用 chat。

src/upstream/fingerprint/official-fingerprint.ts[113-121]
cli-bridge/lib/endpoints/session.ts[29-44]
cli-bridge/lib/upstream/actions.ts[187-205]
cli-bridge/lib/upstream/actions.ts[206-216]
cli-bridge/lib/upstream/actions.ts[238-246]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
主服务的准入请求新增了桌面版本头,但桥接端独立构造的准入请求没有同步,`full` 操作仍走缺头路径。
## Fix Focus Areas
- src/upstream/fingerprint/official-fingerprint.ts[116-121]
- cli-bridge/lib/endpoints/session.ts[29-44]
- cli-bridge/lib/upstream/actions.ts[187-205]
## Recommended Fix
让桥接端准入复用同一份桌面身份配置,至少同步发送 `x-freebuff-client-version`,并验证 `full` 操作从准入到 chat 的请求头。

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment thread src/upstream/fingerprint/official-fingerprint.ts Outdated
Comment thread src/upstream/fingerprint/official-fingerprint.ts Outdated
Comment thread src/upstream/fingerprint/official-fingerprint.ts Outdated
@qodo-code-review

Copy link
Copy Markdown

Qodo Fixer

✅ Merged (0) · ☑ Fixed (0)

Process

  • No fixes were applied (no_fixes_applied)

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 3 files

Reply to a comment to ask cubic a question or push back. It learns from your replies.
Click Fix with cubic on a comment, or reply @cubic-dev-ai fix this, and cubic writes the fix.

View guided diff | Re-trigger cubic

Comment thread src/upstream/fingerprint/official-fingerprint.ts Outdated
Comment thread src/upstream/fingerprint/official-fingerprint.ts Outdated
Comment thread src/upstream/fingerprint/official-fingerprint.ts
- note(2026-09-18-official-cli-fingerprint): 记录桌面端身份组的取值锚点
  (orchestrator.js)、版本兜底与 wallet 缺省 'session', 并修正已过期的事实
  (x-codebuff-api-key 已移除)。理由只在 note 里, 实现处只留指向本文的引用
- install-id: 新增 src/upstream/client/client-state.ts 的 resolveInstallId()
  (FREEBUFF_INSTALL_ID 优先, 回落官方客户端 state.json, 读不到整头省略);
  Node 会话装配与 bun 会话请求共用同一个值(此前 Node 准入只读 env, 与 bun
  那一跳的身份源分叉)
- 桥接准入(cli-bridge/lib/endpoints/session.ts): 补 x-freebuff-client-version,
  wallet-spend-limit 与桌面对齐为 'session'
- smoke(指纹组): wallet 断言改为 'session', 并钉住桌面身份组
  (client / client-version / install-id)
- bun-channel 拆出 install-id 读取到 client-state.ts, 守住单文件 300 行上限

验证: gates 18/18 ALL PASS; agent-notes verify-all(all gates) 通过;
typecheck rc=0; test:smoke rc=0; bun mock dump 确认桥接准入头集完整
(client / client-version / install-id / wallet=session)。
Comment on lines +65 to +67
- `proxy.js` 的 chat 改用 `officialChatHeaders()`,并按桌面形态补
`x-freebuff-sse-gzip-recovery: 1`.鉴权头统一来自 `freebuffAuthHeaders()`
(只发 `Bearer`).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. 非 bun 回落路径的 chat 仍缺恢复标记 🐞 Bug ≡ Correctness

本轮 note 写明 proxy.js 的 chat「按桌面形态补 x-freebuff-sse-gzip-recovery: 1」,但 src/
里没有任何地方发这个头:forward.ts 的 legacy upstream.raw('/api/v1/chat/completions') 用的是
filterRequestHeaders 加 officialChatHeaders(),两者都不带它,只有 cli-bridge 的 buildHeaders 补了。设
FREEBUFF_DISABLE_BUN=1,或 official 通道没拿到响应而走 legacy 回落时,chat 请求仍缺本次认定的桌面 chat 头。PR 自己也说没逐项
A/B,所以这条路径可能还是 410 freebuff_update_required,而 note 却说它已经补齐。
Agent Prompt
## Issue description
Node 侧 legacy chat 路径(official 通道未命中或 bun 被禁用时走的路径)没有发 `x-freebuff-sse-gzip-recovery: 1`,只有 cli-bridge 发了。note 却写着 proxy 的 chat 已经补上这个头。

## Fix Focus Areas
- src/upstream/fingerprint/official-fingerprint.ts[200-240]
- src/proxy/transport/forward.ts[79-100]
- .agents/notes/implemented/bug-fix/2026-09-18-official-cli-fingerprint.md[65-67]

## Recommended Fix
在 `officialChatHeaders()` 里加上 `[H_SSE_GZIP_RECOVERY]: '1'`(或在 forward.ts 的 headers 里显式加上),让 legacy chat 与 cli-bridge 的 `buildHeaders` 形态一致;同时在 smoke 测试里断言 legacy chat 带有这个头。

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Dismiss ↗ | View ↗

@qodo-code-review

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit c6b121a

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 9 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="test/suites/entries/smoke/parts/api/core/fingerprint.ts">

<violation number="1" location="test/suites/entries/smoke/parts/api/core/fingerprint.ts:114">
P3: `resolveInstallId()` 可能返回空字符串,但会话头装配会把它视为缺失并省略该头;这里应把空值归一化为 `null`,否则 smoke 在空 `installId` 状态文件下失败。</violation>
</file>

<file name=".agents/notes/implemented/bug-fix/2026-09-18-official-cli-fingerprint.md">

<violation number="1" location=".agents/notes/implemented/bug-fix/2026-09-18-official-cli-fingerprint.md:139">
P3: 这段新增断言被归在不存在的 `test/smoke.mjs` 下,开发者无法按说明定位回归用例;请将测试引用更新为实际的 `test/suites/entries/smoke/parts/api/core/fingerprint.ts`。</violation>
</file>

Reply with feedback, questions, or to request a fix.

View guided diff | Re-trigger cubic

)
assert.equal(
admitCall.headers['x-freebuff-install-id'] ?? null,
resolveInstallId(),

@cubic-dev-ai cubic-dev-ai Bot Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: resolveInstallId() 可能返回空字符串,但会话头装配会把它视为缺失并省略该头;这里应把空值归一化为 null,否则 smoke 在空 installId 状态文件下失败。

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At test/suites/entries/smoke/parts/api/core/fingerprint.ts, line 114:

<comment>`resolveInstallId()` 可能返回空字符串,但会话头装配会把它视为缺失并省略该头;这里应把空值归一化为 `null`,否则 smoke 在空 `installId` 状态文件下失败。</comment>

<file context>
@@ -89,9 +91,29 @@ import path from 'node:path'
+  )
+  assert.equal(
+    admitCall.headers['x-freebuff-install-id'] ?? null,
+    resolveInstallId(),
+    '准入的 install-id 必须与 resolveInstallId() 同一来源',
+  )
</file context>
Suggested change
resolveInstallId(),
resolveInstallId() || null,
Fix with cubic

first-tab-discount / `x-fb-timezone`;`x-codebuff-api-key` 断言为"仍带"(已知偏差).
`/1.0.0/`;POST 命中 `.../session/admission`;带 model / wallet-spend-limit
(= `session`)/ first-tab-discount / `x-fb-timezone`;桌面身份组断言
`x-freebuff-client` = `desktop`,`client-version` = `desktopClientVersion()`,

@cubic-dev-ai cubic-dev-ai Bot Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: 这段新增断言被归在不存在的 test/smoke.mjs 下,开发者无法按说明定位回归用例;请将测试引用更新为实际的 test/suites/entries/smoke/parts/api/core/fingerprint.ts。

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At .agents/notes/implemented/bug-fix/2026-09-18-official-cli-fingerprint.md, line 139:

<comment>这段新增断言被归在不存在的 `test/smoke.mjs` 下,开发者无法按说明定位回归用例;请将测试引用更新为实际的 `test/suites/entries/smoke/parts/api/core/fingerprint.ts`。</comment>

<file context>
@@ -103,13 +134,16 @@ tar xzf freebuff-linux-x64.tar.gz && strings -n 6 freebuff > fbref-str.txt
-  first-tab-discount / `x-fb-timezone`;`x-codebuff-api-key` 断言为"仍带"(已知偏差).
+  `/1.0.0/`;POST 命中 `.../session/admission`;带 model / wallet-spend-limit
+  (= `session`)/ first-tab-discount / `x-fb-timezone`;桌面身份组断言
+  `x-freebuff-client` = `desktop`,`client-version` = `desktopClientVersion()`,
+  `install-id` = `resolveInstallId()`;`x-codebuff-api-key` 断言为**不得再带**.
 - 上表四处偏差的原文锚点:二进制 `strings` 提取,命令见本文 Problem 段.
</file context>
Fix with cubic

@HengXin666
HengXin666 merged commit 4fe9a0d into HengXin666:main Oct 18, 2026
2 checks passed
@qodo-code-review qodo-code-review Bot mentioned this pull request Oct 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants