Skip to content

feat(cli): list connected browsers when a selector matches nothing - #313

Merged
iuyo5678 merged 3 commits into
Tencent:mainfrom
PerryLink:feat/selector-miss-lists-candidates
Oct 6, 2026
Merged

iuyo5678 merged 3 commits into
Tencent:mainfrom
PerryLink:feat/selector-miss-lists-candidates

Conversation

@PerryLink

@PerryLink PerryLink commented Sep 21, 2026 •

Copy link
Copy Markdown
Contributor

When bsk session start --browser <id-or-label> cannot match a connected browser, the error now includes the current connected-browser list. This lets the caller inspect available instance IDs and labels without a separate discovery command.

Behavior

  • Reuse the existing browser snapshot and table renderer used by multiple_browsers_online.
  • Include the snapshot in data.browsers for JSON callers, and show the instance ID, browser, label, and session count in human-readable output.
  • Explain that the list cannot distinguish a mistyped selector from an offline browser. Callers should verify or reconnect the intended browser before choosing another instance.
  • Keep the existing not_found code, error message, hint, and exit code. When no browser is connected, continue omitting data.
  • Leave browser selection, retry behavior, and successful session starts unchanged. A selector miss does not allocate a session.

Scope and compatibility

This improves diagnostics for a missing selector; it does not infer the intended Chrome profile or automatically choose a replacement browser. The current DSH plugin error adapter discards data, so displaying these candidates to DSH users remains a follow-up.

StartSessionError::BrowserNotFound changes from a unit variant to a struct variant. Rust callers that construct or match that variant need to adapt; the existing CLI error code and message remain unchanged.

The branch includes main at 3f10983. Test fixtures now use the current BrowserStatusEntry.unresponsive field and BrowserClient.liveness structure.

Validation

Five focused tests cover candidate payloads, the empty-registry response, human-readable output and ordering, and the real in-process selection path with no session left behind.

Local validation on macOS:

  • cargo fmt --all -- --check — passed.
  • cargo clippy --workspace --all-targets --locked --offline -- -D warnings — passed.
  • cargo test --workspace --locked --offline — 835 passed, 1 ignored, 0 failed.
  • node scripts/check-crate-skill.mjs with offline Cargo settings — passed.

These local checks do not replace the required CI checks on the updated PR.

Refs #213. This is a diagnostic improvement, not a complete solution for preserving a user's profile requirement across agent calls.

`session.start --browser` takes an extension instance id or a unique
label, and rejects a selector that matches nothing with `not_found` /
"requested browser is not connected". That error carried no payload, so
the caller could not tell a typo from an offline target, and could not
see which instances were actually connected.

The two sibling outcomes of the same selector resolution already carry
their candidates: `multiple_browsers_online` emits a `browsers` snapshot
and the ambiguous-label case emits `instance_ids`, and the CLI already
renders a connected-browsers table and a candidate bullet list for those
two. `not_found` was the third case, and the only one with nothing to
show.

Attach the same snapshot to `BrowserNotFound`, which becomes a struct
variant, and render the connected-browsers table for it. The message is
unchanged, and an empty registry still emits no `data` payload, so that
wire shape and the existing `details:` line are untouched.
@iuyo5678

Copy link
Copy Markdown
Collaborator

Thanks for the contribution. I reviewed the change against the latest main, and I don’t see any major implementation issues that should block merging, assuming the required CI checks pass. Reusing the existing browser snapshot and table renderer keeps this improvement focused and consistent with the current behavior.

A few suggestions to strengthen the PR:

  • Add an integration test for the actual selector-miss path. The new tests cover error mapping and rendering separately. Exercising a real session.start request against registered browsers would also verify that the candidate list reaches the caller and that a failed selection creates no session.
  • Clarify the scope of the improvement. The list helps users diagnose a missing selector, but it cannot determine whether the selector was mistyped or the intended browser is offline. It would be helpful to remind callers to verify or reconnect the intended browser before choosing another instance.
  • Note the current DSH limitation. The DSH error adapter currently drops data, so its users won’t see the new candidates yet. A brief note would keep expectations accurate; support there can be a follow-up.

Overall, I think this is a useful improvement and worth merging once CI is green. These suggestions are intended to strengthen coverage and clarify the user-facing behavior.

Review follow-up on Tencent#313.

- Add an integration test that drives `start_session_recoverable`
  against a registry holding two connected browsers, with a selector
  that matches nothing. It pins both halves the review asked for: the
  candidate list reaches the caller through the daemon's own error
  mapping, and the failed selection leaves the session registry empty.
  The existing tests covered the mapping and the CLI rendering
  separately, so neither could show the two together.
- Say what the candidate table cannot tell. A mistyped selector and an
  offline browser are indistinguishable in the list, so the extras now
  say so, and the ordering test covers the added line.
@PerryLink

Copy link
Copy Markdown
Contributor Author

Thanks for the review — all three points are addressed in 0a679b2, pushed on top of 85d0711.

1. Integration test for the real selector-miss path. Added selector_miss_reaches_caller_and_creates_no_session in daemon/ipc.rs. It builds a BrowserRegistry with two connected browsers and calls start_session_recoverable — the same function the daemon calls for session.start — then asserts both things you named: the candidate list reaches the caller through the daemon's own error mapping, and the failed selection leaves SessionRegistry empty. You were right that the two earlier tests could not show either, since they cover the mapping and the rendering separately. The emptiness assertion also pins the ordering that makes it true today: the selection ? returns before any session id is reserved.

2. Scope of the list. The not_found extras now end with:

the list shows what is connected now; it cannot tell a mistyped selector from an offline browser, so check or reconnect the intended browser before starting on another instance

It renders between the table and the centralised hint:, and the ordering test covers the new line.

3. The DSH limitation is one level above the CLI, and inside this repo. I re-checked this against the current tree instead of restating it, and the finding is narrower than "DSH drops the data":

  • the CLI already emits it — cli/error.rs serializes { code, message, hint, exit_code, data } and sets data: err.data().cloned(), so {"browsers": [...]} is on stdout under --json;
  • the drop happens here, in packages/dsh-plugin-browserskill/src/runner.ts: BskErrorBody already declares data?: unknown, but BskError has no data field and parseBskJson throws with only code, hint and exitCode;
  • and because the tool wrappers re-throw, the host only ever sees error.message — so even carrying data on the error object would not reach a DSH user. The candidates have to go into the message text.

So the follow-up is a small change in this repo rather than a DSH-side one. I kept it out of this PR to leave the diff on the CLI, and I am glad to send it separately if you want it.

One note on the base: this branch is some way behind main, so cargo test -p bsk --lib on native Windows still hits the two skill_install::harness failures we reported as #312, which main has since fixed in 47f5765. They are unrelated to this change and are not in any CI job this PR runs, so I left the base exactly as you reviewed it rather than rebasing. Say the word if you would rather have it refreshed.

Local checks on Windows: cargo fmt --all -- --check clean; cargo clippy --workspace --all-targets reports only pre-existing warnings (file_transfer.rs:417/:426, harness.rs:339, ipc.rs:743, tools_m9_ipc.rs:40), none of them in the added code; the five selector-miss tests pass.

Merge main into the selector-miss branch and update browser test fixtures for the current status and liveness fields.
@iuyo5678
iuyo5678 merged commit 5adf917 into Tencent:main Oct 6, 2026
8 checks passed
PerryLink added a commit to PerryLink/perrylink that referenced this pull request Oct 6, 2026
INSTRUMENTATION.md section 5 requires date-stamped phrasing for anything that moves, and
gives a worked example of the rot. The sentence inherited from the previous edition used
the current tense ("is now the most recent merge this account has anywhere") and had
already rotted before this session touched it -- awslabs/mcp overtook docker/docker-agent
on 10-05. The rewrite in df582e3 repeated the construction and it rotted again within
ninety minutes, when Tencent/BrowserSkill#313 merged.

No figure changes; phrasing only.
PerryLink added a commit to PerryLink/BrowserSkill that referenced this pull request Oct 7, 2026
Tencent#313 lists the connected browsers on a selector miss so the caller can pick a
real instance or label instead of probing every browser in turn. The list
reaches the `bsk --json` envelope as `data.browsers`, but `parseBskJson` reads
only `code`, `message` and `hint`, so it stops at the plugin boundary. Carrying
it on the error object would not help either: the tool layer re-throws and the
host shows `error.message`.

The list now renders into that message, between the summary and the hint, which
is the order the CLI's own renderer uses. It stays on one line because
`tools.ts` keeps only the first line of the message for the action card.

Only `data.browsers` renders. `data` also carries `reason` for the session-busy
retry and the ambiguous-label ids, and those envelopes are unchanged, as is every
other error, timeout, interrupt and success path - checked by running both
revisions over twelve envelopes, of which exactly two differ.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants