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..23b679e3 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,17 @@ 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 +642,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`.