# Changelog

## 0.1.0-beta.12 — 无绑定自动创建修复 + 10/8 批次（2026-10-08）

- BUG-064（本版）：创建后身份验证的一次性探测竞态——新会话页异步写入 localStorage 身份键，
  一次性读竞态读到空 → 每次自动创建都记「身份 UNKNOWN」残留（实测 5 条，跨 5 会话，
  导致自动创建全局暂停）。修复：waitForPrincipal() 有界轮询（20s/500ms）+ 失败原因区分。
- 并入 10/8 批次修复：BUG-057（瞬时徽标不收回）、BUG-058/059（复制路径与 auto 只取代码块）、
  BUG-060（local-chatgpt: 中间路由）、BUG-061（PROVISIONAL 徽标文案）、BUG-062（提交假阴性）、
  BUG-056（提交谎报成功）与三态提交探针。

## 0.1.0-beta.11 — 修复 submit() 谎报发送成功（BUG-056，2026-10-04）

长文本提示词被 UI 静默丢弃（输入框清空、无新消息）时，submit() 曾把"输入框变空"当成发送成功，
调用方最终把上一轮旧回复当成新答案返回。提交成功判据改为身份证明：只有出现新的用户消息 id
才算送达；被销毁的草稿显式报失败。

## 0.1.0-beta.10 — 横幅瘦身：PNG 930KB → WebP 30KB（2026-09-28）

readme.png（2172×724，930KB）转 WebP q86 后 30KB（-97%），画质肉眼无损；
包体积 1.2MB → ~300KB。README（中英）与 `pi.image` 画廊预览同步指向 readme.webp。

## 0.1.0-beta.9 — 统一横幅与画廊预览（2026-09-28）

- 单横幅 readme.png（2172×724）替换 logo+hero 四图：GitHub / npm / Pi 画廊三面一致
- package.json 新增 `pi.image`：Pi 官方画廊（pi.dev/packages）卡片预览
- 本版首次携带该资产发布（此前 CDN 404，三个面的配图均不可见）

## 0.1.0-beta.8 — 适配 2026-09 UI 二波（BUG-055，2026-09-25）

ChatGPT 当晚再次调整 UI：`重新生成回复` 从操作条消失、助手操作条换了类名（用户条反占旧类）、
一条回复可能拆成多个 markdown 节点。后果：ask 等待超时（回复不可见）、copy 只取回 3 字。

- 单元锚点改为内容型单选回退链（评价回复 → 朗读 → regen），不再依赖类名
- 回复正文取最长 markdown 节点；过滤虚拟化空壳单元
- 真机验证：copy 3 字 → 14612 字；ask 900s 超时 → 端到端通过

## 0.1.0-beta.7 — 修复新 UI 代码块复制源识别（BUG-054，2026-09-25）

新 UI 的代码块渲染为 wrapper > (工具条, body > code)，**没有 <pre>**；而复制源分类与代码块枚举
完全按 pre 匹配，导致代码复制按钮静默降级为 other（copyMode=code 不可用、sources 缺失该项）。

- nearCode：无 pre 形状以代码块自有的「自动换行 / wrap」按钮为锚（散文 inline code 不触发）
- collectCodeBlocks：pre + wrap 锚定 code 合并后按文档序排序去重（ordinal 保真）
- 真机复验：同一按钮 other → code/codeblock

## 0.1.0-beta.6 — 修复平台阻断采样误报（BUG-053，2026-09-25）

beta.5 的平台阻断检查把「对话正文」当成了页面状态：canvas 回复的文档标题是真实 h1/h2 且位于
main 内，正文讨论 CAPTCHA / 验证码 / 限额等词时，正常页面被误判为平台阻断，ask 会在副作用前
拒绝发送（方向安全，但误伤正常路径）。

- 采样面收窄为页面状态信号：alert/dialog/toast（排除 transcript / 气泡 / writing-block）
  + document.title + captcha iframe
- 新增采样面回归断言；真机验证：同页 platformBlock 由 CAPTCHA → null
- brb-full-smoke --send 端到端 38/0（投递 5132ms / 首回复 9526ms / relay 复制通道正确）

## 0.1.0-beta.5 — canvas 回复支持（BUG-052）+ 合规整改落地（2026-09-25）

### 修复：BUG-052 — canvas（写作块）回复取不回内容

ChatGPT 把长文档渲染进写作块（canvas）后，复制链路失效（"扫到 1 个候选：code:复制"）。
零语义假设探针实测后重构：canvas 工具条复制 = 该 canvas 自有 markdown；turn 级复制 = 全部
canvas 合并全文。

- canvas 成为显式回复表面；多 canvas 文本 = 全部拼接（此前只取最后一个）
- 身份分离：turn 操作条按钮不再被 canvas 内 <pre> 误判为 code；CopyKind 增 canvas
- 优先级链：单 canvas → 工具条 → turn → DOM 降级；多 canvas → 仅 turn；无 turn 源即 fail-closed
- 整表面校验 matchesCanvasSource（长度门槛 + 首/中/尾三窗口）；普通回复路径不放宽
- Chrome + Edge 三通道真机验证（23816 / 15517 / 15446 字）

### 合规整改（三条落地）

- **定位与措辞**：README 中英文 independent/unofficial 与"不绕过平台限制"声明；setup 向导新增
  第三方服务 + 数据流两屏提示（取消 = 零远端副作用）；文档宣传语防火墙（DOC-GATE-10）
- **数据流声明**：新增 docs/data-flow.md（Pi → ChatGPT → BRB → Pi → 你的 provider；本机
  持久化清单；日志脱敏规则）；诊断脱敏补 Cookie 掩码
- **平台边界不变量**：CAPTCHA / 安全挑战 / 限流 / 额度 / 登录态统一检测（PlatformBlocker），
  ask 与建会话副作用前强制检查并停止；doctor 显式上报；私有接口/规避签名静态扫描；
  发布门禁新增平台边界五问

## 0.1.0-beta.4 — 适配 ChatGPT 2026-09 新 UI（文本握手修复，BUG-051）

### 背景

2026-09-24 晚 ChatGPT Web 全量推送新版 UI：所有 testid/role 锚点（conversation-turn、
data-message-author-role、send-button、copy-turn-action-button）被移除，轮次结构改为
exchange-pair（用户气泡与助手回复同容器），且交互处理器不再响应不可信点击。
beta.3 在新 UI 下 `ask`/`copy` 全面失效（fail-closed 拦下，无盲发）。

### 修复（全部 Chrome + Edge 双端真机复验）

- **双模解析层**：legacy 锚点优先、新 UI 回退；exchange 单元以「重新生成回复」按钮为锚
  （每完成轮恰一个），对虚拟化容器结构无关。
- **交互全面改 trusted**（CDP Input.dispatchMouseEvent / keyDown）：复制、发送回退、停止；
  Enter 提交换成实测可用的序列。
- **投递验证假阴性修复**：消息 id 改为内容哈希锚定——索引型合成 id 在虚拟化重挂载下漂移，
  曾导致「消息已送达却报未送达」。
- **复制链路**：scope 正确切分（文本=markdown 节点 / 按钮=exchange 单元）、kind 校验对齐
  宿主晋升规则、枚举与 sources 过滤对齐、编辑器查找排除 writing-block 与屏外节点。
- 配置提示：遗留 `copyLabel: "^复制回复"` 与新 UI 的裸标签「复制」不匹配会导致复制源
  找不到——去掉该配置即可（新 UI 下无需设置）。

## 0.1.0-beta.3 — 浏览器选择（Chrome / Edge）+ Pi 包画廊发现（2026-09-24）

### 浏览器选择（轮次 B1–B4）

- 支持范围冻结为 **Google Chrome 与 Microsoft Edge** 两家；`browser` 策略为 `auto | chrome | edge`。
- 新增 `/brb browser`：不带参数时显示当前策略与已解析到的浏览器（含可执行文件），
  带 `auto | chrome | edge` 时写入策略。**显式选择会让已解析的粘性结果失效**，
  下次操作重新解析——粘性结果是观察，不是偏好；只有显式选择（`/brb browser` 或
  `/brb config browser=…`）会清除它，自动解析永远不清。
- 首次使用前的预检会解析并启动所选浏览器。
- `doctor` 只报真实事实：端口归属与代理归因不再猜测（配合 BUG-045 的三态文案）。

### Pi 包画廊发现

- 声明 `pi-package` 关键字——Pi 文档规定的画廊发现准入条件。

## 0.1.0-beta.2 — README 图片在 npm 页面上可显示（2026-09-24）

The npm package page rendered the README's relative image paths as broken
(`![logo](docs/assets/logo.png)`), because npm resolves them against its own site and the
development repository is private (so `raw.githubusercontent` is not an option either).
Both language READMEs now reference the assets through an absolute CDN URL
(`cdn.jsdelivr.net/npm/@yedan1122/brb/...`), and a new docs gate (`DOC-GATE-09`) keeps
them honest: every CDN reference must point at this package and at a file that really
exists (mutation-verified — breaking a path turns the gate red).

## 0.1.0-beta.1 — documentation refresh (2026-09-24)

Docs-only release that fixes what the first beta's README could not: `0.1.0-beta.0`
shipped saying "not yet published; this command currently fails", which the npm page
rendered verbatim. This version states the published state, gives the working
`pi install npm:@yedan1122/brb@beta` command, and syncs quick-start / release-manual
status. No code change: the extension surface is identical to `0.1.0-beta.0`.

## Extension label surface (2026-09-23, owner ruling)

The pi extension header now shows **@yedan1122-brb** instead of the previous `brb`: a root
`index.ts` forwarder was added, `pi.extensions` now points at `./index.ts`, and the entry
joined `files` (pack 54 -> 55) and `tsconfig.include`. No behavior change: the root file
only re-exports `./src/brb/index.ts` (one line, no logic), which in turn forwards to the
host implementation. The transitional `chatgpt-web-bridge` junction was removed so the
directory cannot be discovered twice.

## Command menu defaults to English (2026-09-23, owner ruling)

The `/brb` menu now speaks English by default: the top-level description and every
subcommand label/usage (completion list, enum parameters, argument skeletons) are
English. The Chinese display is provided by the **@yedan/zh-slash-menu** extension,
which rewrites the completion labels from their English originals only inside the
`/brb ` argument context and exposes a global `/zh-menu on|off|status` switch. No
behaviour change: the two structural assertions that pinned the Chinese wording now
assert the English equivalents.

## BUG-030 target identity (2026-09-22, rulings #43/#44)

Auto-create now snapshots browser targets before creation and pins the returned target
through attach, handshake, provisional registry commit, first real send, and URL sync.
Existing or drifted targets fail closed as `INVALID_TARGET`/`TARGET_DRIFT`/`TARGET_GONE`
without an orphan or retry; legacy config and active-tab scans are diagnostic only. Empty
conversation ids are explicit `PROVISIONAL` target bindings and upgrade atomically to
`BOUND` only when the same target materializes a new `/c/<id>`. Orphans now carry durable
states and `/brb orphan clear <id>` resolves a pending local blocker without deleting a
remote conversation. Offline regression N1–N11 covers the lineage and recovery gates.

## 0.1.0-beta.0 (2026-09-21, BRB-AUTOBIND-ONBOARDING / ruling #42-B)

Added opt-in onboarding for unbound Pi threads. `autoCreateOnUnbound` is a backward-compatible
tri-state config (`ask` / `on` / `off`, default `ask`); the first eligible bound operation asks
once and persists the answer. Accepted operations reuse the canonical create/handshake/commit
pipeline, preserve capability/grant and daily-limit accounting, record provenance (`source:
auto|start|manual`), block pending orphans and duplicate in-flight creates, and fail closed on
ambiguous creation, identity mismatch, handshake failure, or registry commit failure. No real
ChatGPT messages are part of this offline validation round.

## 0.1.0-beta.0 (2026-09-20, verdict #33 fence-balance gate — safety hardening)

### Changed

Directory-installed Pi extensions are now identified as `brb` in Pi's extension header.

checkReplyShape counted fences with Math.ceil(n/2), so a reply whose single
fence never closed passed the one-block gate. Added ReplyShape.balanced
(fence parity); conforms now requires codeBlocks===1 AND balanced; the fatal
gate rejects unclosed fences with E_BLOCKS ("代码围栏未闭合，无法证明回复
完整"). BUG-015 code-source exemption preserved (blocksApplicable=false
skips fence checks). Existing VERIFY advisory locked by test (quality
warning, not fatal). No model-semantics heuristics added — remote content
deviation stays an observation, not a local gate. Suite 1577 / 35 checks.

## 0.1.0-beta.0 (2026-09-20, BUG-018 reply-lifecycle fix — safety)

BUG-018 (BUG-008 family relapse): waitForReply could return a reply PREFIX as
the complete answer when the stop button was never observed for the whole
generation (mount gap / selector drift / hidden-tab lag) — `lastStreamingAt`
started at `startedAt`, so "never saw streaming" was treated as "generation
ended". Live: MCP returned `HAND` while the DOM held the full `HANDSHAKE_OK`.
Fix: sawStreaming lifecycle tracking; without it, completion requires a 4x
quiet window + an independent UI signal (copy-turn-action-button action bar);
stall escape hatch still bounds the wait. Offline regression with scripted
state sequences driving the real waitForReply, including mutation
reverse-verification (pre-fix code reproduces got "HAND"); MCP transport
excluded via a 10KB byte-for-byte stdio fidelity test. Suite 1563 / 35 checks.

## 0.1.0-beta.0 (2026-09-20, 用户侧文案三原则执行, verdict #28)

Four minimal user-facing language improvements (no protocol changes):
1. Recoverable errors now carry a concrete next step ("没有找到 chatgpt.com
   标签页。下一步：打开 ChatGPT 页面后重试。"; session-identifier errors point
   at retrying in a fresh session). Only genuinely recoverable errors got an
   action — never mechanical advice.
2. Internal jargon lightened on the control surface: "advisory（不阻断）" →
   "提示（不阻断）" (diagnostic keywords stay in doctor/errors).
3. Control envelope compressed: "确定性 parser 判定的控制命令，不是普通对话"
   → "已解析的控制命令；按动作定义执行" (assertion updated in
   control-hook-test).
4. Three copywriting principles recorded in AGENTS.md: facts first; errors =
   cause + executable next step; successes state what now holds. 1507 checks.

## 0.1.0-beta.0 (2026-09-20, open 可发现性 + BUG-017)

Verdict #27: footer clickability is not implementable in the current Pi TUI API
(setStatus/setFooter are text-only; no extension-level global keybinding), so
the badge stays the observation surface and /webgpt open is the action surface.
Open now resolves the CURRENT THREAD's binding by default (no-arg), matching
what the badge shows; still refuses id-less URLs (never a second creation
entry); fail-closed hint when unbound. Label/usage updated.

BUG-017 (FIXED_PENDING_VERIFY): config lost its proxy field (DEFAULT proxy is
undefined; file was rebuilt) and open/relaunch ensureBrowser calls omitted
proxy — the automation browser went direct and chatgpt.com showed
"无法访问此网站" (chrome-error), breaking open/ask/plan/handoff fail-closed.
Fixed: proxy restored in config; open and recovery relaunch now pass
proxy: config.proxy; structural guard asserts all 4 ensureBrowser call sites
carry the proxy. 1507 assertions / 33 checks.

## 0.1.0-beta.0 (2026-09-19, idle badge 常驻徽标)

WEBGPT-IDLE-BADGE-v1 (verdict #26): an always-on footer badge showing the three
facts to scan before trusting a handoff — automation browser running, the
ChatGPT conversation this pi thread is bound to, and whether that page's tab is
open (浏览器✓/× · <short id> · 页签✓/—; 未绑定; 状态? fails closed on failed
observation; 页签— is a recoverable condition, never an error).

New idleStatus.ts pure module: formatIdleStatus + two-layer per-session
coordinator (IDLE badge / TRANSIENT task text). All 9 setStatus call sites now
go through setTransientStatus/restoreIdleStatus; the old clear(undefined)
became restore — a finished task shows the badge again instead of a blank line
(loop stop included). Refreshes are event-driven only: session_start, turn_start
(a refresh during a task only re-caches, never clobbers live progress), and
after WebGPT operations that change state (relay/tool finally, binding
backfill). session_shutdown drops badge state for good. No timers/heartbeats.
Tools/idle-status-test.mjs: 22 checks (4-state wording, override/restore,
per-session isolation, no-timer structural pin). 1501 assertions / 33 checks.

## 0.1.0-beta.0 (2026-09-19, 工具路径底部状态对齐)

Natural-language triggers route through the webgpt tool (ask/plan), which only
sent a coarse onUpdate — no Pi TUI bottom status, while /webgpt ask/plan (relay)
showed the full live flow. Parity fix (verdict #25): the tool ask/plan path now
calls ctx.ui.setStatus (sent → live Ns/M字 via page.ask onTick → 回复完成/提取
方案 → cleared in finally on every exit path). Shared formatWaitingStatus
formatter used by both call sites; command-path strings unchanged.
New tests/tool-status-test.mjs (7 structural checks incl. negative guard that
result-style actions stay result-style). 1478 assertions / 33 checks.

## 0.1.0-beta.0 (2026-09-19, handoff 输出形状提示)

New conversation bindings had no inherited "single code block" habit, so
handoff/report replies came back as prose with many small code blocks (the
extraction falls back to a whole-turn copy, lossless but not clean). The
handoff/report envelope now asks ChatGPT for a single Markdown fenced block
when a plan/receipt is being produced (soft request, not a contract —
RESPONSE_CONTRACT stays the only hard shape requirement). control-hook-test
asserts the hint is present.

## 0.1.0-beta.0 (2026-09-19, 双语 README)

Bilingual product entry: README.md (English, default/public) + README.zh-CN.md
(simplified Chinese, full parity). Language switcher links at the hero; product
surface sections and public facts are marker-annotated; check-docs gained a
README_PAIR check whose intent is to compare the two entries (files exist, mutual
links, section-marker sets equal, fact values equal, relative links valid). The old mixed README's Chinese developer
reference moved to docs/pi-reference.zh-CN.md; the developer-inventory/WIRE-SPEC
assertions that scanned README.md now scan that reference (coverage preserved).

> 后续更正（2026-09-20，审计基线 `33a14a9`）：上面说的 README_PAIR 比对**当时并未真正生效**——
> 其 6 处 `check()` 调用（5 类断言）实参顺序写反，条件被当成标签、标签被当成条件，因此恒为 PASS
> （连 `docs/assets/logo.png` 相对链接失效也照报 PASS）。这里保留的是**设计意图**，
> 其**有效性**尚未成立；详见 `docs/internal/release-gap.md` finding E。

## 0.1.0-beta.0 (2026-09-19, 整理后)

Product README as a Pi Agent extension entry. Internal docs moved to
docs/internal/ (HANDOFF-STATE, PROVENANCE-AUDIT); BUGS/WIRE-SPEC stay at root
per contract (MOVE_DEFERRED_BY_CONTRACT, recorded in docs/internal/INDEX.md).

## 0.1.0-beta.0 (2026-09-19, 原始)


First public beta packaging (not yet published).

- Package: `@yedan1122/pi-chatgpt-web-bridge`, Pi manifest `pi.extensions: ["./index.ts"]`.
- Product surface: `/webgpt setup` onboarding wizard (7 steps, kill switch), slash
  completion, status dashboard line.
- Safety: side-effect registry + capability gates, send-boundary freshness barrier,
  delivery reconciliation, A17 account affinity, bounded recovery (A13/A3),
  diagnostics redaction.
- Tests: full offline suite green (the assertion/check counts are printed by
  `npm test` itself; this entry does not pin numbers that drift).
