From 3bbd6ab19fec1f6eaf67069b6d7075b5768c7d52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Chaonan=E2=80=9D?= Date: Thu, 27 Aug 2026 22:22:30 +0800 Subject: [PATCH 1/2] fix: treat protocol version differences as upgrade reminders Agents were stopping on popup and status copy that looked like a hard incompatibility. Keep the connection usable, reframe exit 5 as an unknown method, and ask users to upgrade instead of aborting. Co-authored-by: Cursor --- .../src/entrypoints/popup/App.test.tsx | 35 +++++++++++++++++++ apps/extension/src/entrypoints/popup/App.tsx | 7 ++-- crates/bsk-cli/skill/SKILL.md | 4 +-- crates/bsk-cli/src/cli/doctor.rs | 22 ++++++------ crates/bsk-cli/src/cli/status.rs | 4 +-- .../i18n/src/locales/en-US/extension.json | 8 ++--- .../i18n/src/locales/zh-CN/extension.json | 8 ++--- skill/SKILL.md | 4 +-- 8 files changed, 64 insertions(+), 28 deletions(-) diff --git a/apps/extension/src/entrypoints/popup/App.test.tsx b/apps/extension/src/entrypoints/popup/App.test.tsx index ecbcae00..daec6379 100644 --- a/apps/extension/src/entrypoints/popup/App.test.tsx +++ b/apps/extension/src/entrypoints/popup/App.test.tsx @@ -247,6 +247,41 @@ describe("App", () => { expect(copyButton.getAttribute("disabled")).not.toBeNull(); expect(screen.getByText("连接后可用")).toBeTruthy(); }); + + it("treats protocol drift as still connected and prompts an upgrade", async () => { + mockUseConnectionState.mockReturnValue({ + snapshot: { + ...baseSnapshot, + state: "version_skew", + instanceId: "03c3e47f", + handshake: { + server: "bh", + version: mockDaemonVersion, + protocol_version: "1.0", + }, + }, + statusState: "version_skew", + setLabel, + setConnectionEnabled, + }); + + render(); + + expect(screen.getByText("已连接")).toBeTruthy(); + expect(screen.getByText("可升级")).toBeTruthy(); + expect(screen.queryByText("协议不一致")).toBeNull(); + expect(screen.queryByText("Action needed")).toBeNull(); + expect(screen.queryByText("兼容")).toBeNull(); + const warning = screen.getByText(/协议版本不同,请及时升级/); + expect(warning.textContent).toContain("CLI 协议"); + expect(warning.textContent).toContain("扩展协议"); + + openRecordView(); + const copyButton = screen.getByRole("button", { name: "复制录制指令" }); + expect(copyButton.getAttribute("disabled")).toBeNull(); + fireEvent.click(copyButton); + expect(navigator.clipboard.writeText).toHaveBeenCalledTimes(1); + }); }); describe("control hints toggle", () => { diff --git a/apps/extension/src/entrypoints/popup/App.tsx b/apps/extension/src/entrypoints/popup/App.tsx index 3092a064..0756e9cb 100644 --- a/apps/extension/src/entrypoints/popup/App.tsx +++ b/apps/extension/src/entrypoints/popup/App.tsx @@ -79,6 +79,7 @@ export function App() { }, [copiedTick]); const isSkewed = statusState === "version_skew"; + const connectionLive = statusState === "connected" || isSkewed; const daemonVersion = snapshot.handshake?.version ?? "—"; const daemonProtocol = snapshot.handshake?.protocol_version ?? "—"; const extensionVersion = snapshot.extensionVersion || "—"; @@ -90,7 +91,7 @@ export function App() { setCopiedInstanceId(true); }; - const recordReady = statusState === "connected" && Boolean(snapshot.instanceId); + const recordReady = connectionLive && Boolean(snapshot.instanceId); const recordPurpose = purposeDraft.trim(); const recordStartUrl = startUrlDraft.trim(); const recordCommand = snapshot.instanceId @@ -206,12 +207,12 @@ export function App() { {isSkewed && (

{t("popup.versionSkewWarning", { extensionProtocol: PROTOCOL_VERSION, - daemonProtocol, + cliProtocol: daemonProtocol, })}

)} diff --git a/crates/bsk-cli/skill/SKILL.md b/crates/bsk-cli/skill/SKILL.md index d6dd1e2e..9a1ac987 100644 --- a/crates/bsk-cli/skill/SKILL.md +++ b/crates/bsk-cli/skill/SKILL.md @@ -29,7 +29,7 @@ Drive the user's **real Chromium browser** (with their logins and cookies) throu ## Prerequisites 1. `bsk` on `PATH` (Rust CLI from browser-skill) -2. browser-skill **extension** loaded in Chromium and connected (popup shows green) +2. browser-skill **extension** loaded in Chromium and connected (popup shows green, or an upgrade reminder — still connected; continue) 3. Any `bsk` command auto-starts background services as needed; use `bsk doctor` if anything fails ## Mandatory workflow @@ -299,7 +299,7 @@ bsk record stop [--output trace] # terminal fallback if the browser panel is u | `2` | Protocol / transport — service unreachable, IPC failure | `bsk doctor`; check extension connected; retry the command | | `3` | Browser / CDP execution failed | Retry; simplify selector; check tab still open | | `4` | Timeout | Increase `--timeout`; try `--wait-until domcontentloaded` | -| `5` | Version skew (CLI vs extension) | Upgrade/reinstall matching versions | +| `5` | Unknown RPC method | This command is not implemented in the current build. Continue the task with other commands. If a newly added command is missing, suggest `bsk update`. | Human errors print `error:` + `hint:` on stderr; `--json` includes `code`, `message`, `hint`, `exit_code`. diff --git a/crates/bsk-cli/src/cli/doctor.rs b/crates/bsk-cli/src/cli/doctor.rs index 3d0c57a9..3b6bc195 100644 --- a/crates/bsk-cli/src/cli/doctor.rs +++ b/crates/bsk-cli/src/cli/doctor.rs @@ -351,10 +351,10 @@ fn check_version_compatible(status: Option<&StatusResult>) -> CheckResult { } } -/// `bsk doctor` check: every connected browser should use the same -/// protocol version as the daemon. Minor protocol drift is accepted by -/// the daemon but flagged here so the user can see who needs updating -/// (M10.4). +/// `bsk doctor` check: connected browsers should speak a protocol the +/// daemon accepts. A different protocol string is still a live +/// connection — report Ok so agents keep working. The detail is an +/// upgrade reminder, not a blocker. /// /// Review M2 (round-1 minor): when no browsers are connected, the /// check has nothing to compare against, so it now reports @@ -396,13 +396,12 @@ fn check_browsers_protocol_compatible(status: Option<&StatusResult>) -> CheckRes }) .collect::>() .join(", "); - CheckResult::fail( + CheckResult::ok( name, format!( - "{} browser(s) have protocol minor drift from the daemon: {stale}", + "{} browser(s) report a different protocol version (still usable — continue, and upgrade soon): {stale}", status.version_skew_browsers.len() ), - "upgrade the browser-skill extension or bsk CLI so both sides use the same protocol version", ) } @@ -581,7 +580,7 @@ mod m2_tests { } #[test] - fn browsers_check_reports_fail_when_skew_present() { + fn browsers_check_reports_ok_when_skew_present() { let status = fake_status( vec![BrowserStatusEntry { instance_id: "alpha".into(), @@ -605,9 +604,10 @@ mod m2_tests { }], ); let check = check_browsers_protocol_compatible(Some(&status)); - assert_eq!(check.status, CheckStatus::Fail); + assert_eq!(check.status, CheckStatus::Ok); assert!(check.detail.contains("alpha")); - assert!(check.hint.is_some()); + assert!(check.detail.contains("still usable — continue, and upgrade soon")); + assert!(check.hint.is_none(), "a protocol-version note must not hint to stop"); } #[test] @@ -635,7 +635,7 @@ mod m2_tests { }], ); let check = check_browsers_protocol_compatible(Some(&status)); - assert_eq!(check.status, CheckStatus::Fail); + assert_eq!(check.status, CheckStatus::Ok); assert!( check .detail diff --git a/crates/bsk-cli/src/cli/status.rs b/crates/bsk-cli/src/cli/status.rs index 77855f0e..f6a92868 100644 --- a/crates/bsk-cli/src/cli/status.rs +++ b/crates/bsk-cli/src/cli/status.rs @@ -65,7 +65,7 @@ fn render_human(s: &StatusResult) { let bold = "\x1b[1m"; let reset = "\x1b[0m"; eprintln!( - "{bold}{yellow}warning:{reset} {} browser(s) have protocol version drift from the daemon{reset}", + "{bold}{yellow}note:{reset} {} browser(s) report a different protocol version; still usable — continue, and run `bsk update` soon{reset}", s.version_skew_browsers.len() ); for skew in &s.version_skew_browsers { @@ -75,7 +75,7 @@ fn render_human(s: &StatusResult) { skew.label.clone() }; eprintln!( - " {} ({}) — protocol ext {} vs daemon {} (app ext v{}, daemon v{}) — please align protocol versions", + " {} ({}) — protocol ext {} vs CLI {} (app ext v{}, CLI v{}) — still usable; please upgrade", skew.instance_id, label, display_protocol(&skew.client_protocol_version), diff --git a/packages/i18n/src/locales/en-US/extension.json b/packages/i18n/src/locales/en-US/extension.json index b8f8fa1a..62851c84 100644 --- a/packages/i18n/src/locales/en-US/extension.json +++ b/packages/i18n/src/locales/en-US/extension.json @@ -6,27 +6,27 @@ "disconnected": "Disconnected", "connecting": "Connecting…", "connected": "Connected", - "version_skew": "Protocol mismatch", + "version_skew": "Connected", "disabled": "Connection off" }, "stateDetail": { "disconnected": "Open BrowserSkill to connect.", "connecting": "Connecting to BrowserSkill…", "connected": "Ready for automation tasks.", - "version_skew": "Some features may be unavailable." + "version_skew": "Protocol versions differ. Please upgrade." }, "stateBadge": { "disconnected": "Standby", "connecting": "Igniting", "connected": "Ready", - "version_skew": "Action needed", + "version_skew": "Upgradable", "disabled": "Off" }, "connectionToggleTitle": "BrowserSkill connection", "controlHintsToggleTitle": "Control hints", "controlHintsToggleHint": "Show the status pill and orange glow while the Agent controls a page.", "controlHintsInfoLabel": "About control hints", - "versionSkewWarning": "Extension protocol v{{extensionProtocol}}, daemon v{{daemonProtocol}}.", + "versionSkewWarning": "Extension protocol v{{extensionProtocol}}, CLI protocol v{{cliProtocol}}. Protocol versions differ — please upgrade.", "upgradeAvailable": "Upgradable", "launcher": { "title": "Quick actions" diff --git a/packages/i18n/src/locales/zh-CN/extension.json b/packages/i18n/src/locales/zh-CN/extension.json index 51c8f33b..338d5df1 100644 --- a/packages/i18n/src/locales/zh-CN/extension.json +++ b/packages/i18n/src/locales/zh-CN/extension.json @@ -6,27 +6,27 @@ "disconnected": "未连接", "connecting": "连接中…", "connected": "已连接", - "version_skew": "协议不一致", + "version_skew": "已连接", "disabled": "连接已关闭" }, "stateDetail": { "disconnected": "请先打开 BrowserSkill。", "connecting": "正在连接 BrowserSkill…", "connected": "可接收自动化任务。", - "version_skew": "部分能力可能不可用。" + "version_skew": "协议版本不同,请及时升级。" }, "stateBadge": { "disconnected": "Standby", "connecting": "Igniting", "connected": "Ready", - "version_skew": "Action needed", + "version_skew": "可升级", "disabled": "Off" }, "connectionToggleTitle": "BrowserSkill 连接", "controlHintsToggleTitle": "控制提示", "controlHintsToggleHint": "Agent 控制页面时显示提示条和橙色闪光。", "controlHintsInfoLabel": "控制提示说明", - "versionSkewWarning": "扩展协议 v{{extensionProtocol}},daemon 协议 v{{daemonProtocol}}。", + "versionSkewWarning": "扩展协议 v{{extensionProtocol}},CLI 协议 v{{cliProtocol}}。协议版本不同,请及时升级。", "upgradeAvailable": "可升级", "launcher": { "title": "快捷功能" diff --git a/skill/SKILL.md b/skill/SKILL.md index d6dd1e2e..9a1ac987 100644 --- a/skill/SKILL.md +++ b/skill/SKILL.md @@ -29,7 +29,7 @@ Drive the user's **real Chromium browser** (with their logins and cookies) throu ## Prerequisites 1. `bsk` on `PATH` (Rust CLI from browser-skill) -2. browser-skill **extension** loaded in Chromium and connected (popup shows green) +2. browser-skill **extension** loaded in Chromium and connected (popup shows green, or an upgrade reminder — still connected; continue) 3. Any `bsk` command auto-starts background services as needed; use `bsk doctor` if anything fails ## Mandatory workflow @@ -299,7 +299,7 @@ bsk record stop [--output trace] # terminal fallback if the browser panel is u | `2` | Protocol / transport — service unreachable, IPC failure | `bsk doctor`; check extension connected; retry the command | | `3` | Browser / CDP execution failed | Retry; simplify selector; check tab still open | | `4` | Timeout | Increase `--timeout`; try `--wait-until domcontentloaded` | -| `5` | Version skew (CLI vs extension) | Upgrade/reinstall matching versions | +| `5` | Unknown RPC method | This command is not implemented in the current build. Continue the task with other commands. If a newly added command is missing, suggest `bsk update`. | Human errors print `error:` + `hint:` on stderr; `--json` includes `code`, `message`, `hint`, `exit_code`. From c48a53269eba840baeff54b97d131bac09ed5797 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Chaonan=E2=80=9D?= Date: Thu, 27 Aug 2026 22:50:01 +0800 Subject: [PATCH 2/2] style: rustfmt doctor protocol-version test asserts cargo fmt --check failed on two long assert lines in PR CI. Co-authored-by: Cursor --- crates/bsk-cli/src/cli/doctor.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/crates/bsk-cli/src/cli/doctor.rs b/crates/bsk-cli/src/cli/doctor.rs index 3b6bc195..23b679e3 100644 --- a/crates/bsk-cli/src/cli/doctor.rs +++ b/crates/bsk-cli/src/cli/doctor.rs @@ -606,8 +606,15 @@ mod m2_tests { let check = check_browsers_protocol_compatible(Some(&status)); assert_eq!(check.status, CheckStatus::Ok); assert!(check.detail.contains("alpha")); - assert!(check.detail.contains("still usable — continue, and upgrade soon")); - assert!(check.hint.is_none(), "a protocol-version note must not hint to stop"); + assert!( + check + .detail + .contains("still usable — continue, and upgrade soon") + ); + assert!( + check.hint.is_none(), + "a protocol-version note must not hint to stop" + ); } #[test]