Commit Graph

907 Commits

Author SHA1 Message Date
Peter Steinberger
bba7e30fcc fix(browser): preserve nested timeouts and cancellation across node actions (#114131) 2026-07-26 12:04:52 -04:00
Peter Steinberger
a2c6474377 feat(browser): scoped and structured page extraction (#113938)
* feat(browser): add scoped structured extraction

* fix(browser): keep capture result type module-local and refresh docs map

* fix(browser): keep extract capture helper private
2026-07-25 19:32:44 -07:00
Peter Steinberger
3838d342c2 fix(browser): recover node proxy after failed startup (#113926) 2026-07-25 18:47:11 -07:00
Peter Steinberger
d42c11ec24 fix(browser): one unresponsive shared tab no longer hangs every browser tool (#113921)
* fix(browser): keep tab enumeration responsive

Read tab titles from browser-process CDP target metadata and skip bounded target-info reads so one wedged tab cannot block the profile.

Fixes #113787

Co-authored-by: CBruney <CBruney@users.noreply.github.com>

* fix(browser): parallelize tab metadata reads

Resolve page target metadata concurrently so multiple wedged tabs consume one bounded timeout window.

Co-authored-by: CBruney <CBruney@users.noreply.github.com>

---------

Co-authored-by: CBruney <CBruney@users.noreply.github.com>
2026-07-25 18:35:19 -07:00
Peter Steinberger
7c34185ac2 fix(browser): preserve lazy tool output schema (#113925) 2026-07-25 18:26:55 -07:00
Peter Steinberger
4cdd9b8d85 feat(browser): extract action — answer page questions via a bounded sub-model call (#113861)
* feat(browser): add page extract action

* docs(browser): document extract action

* improve(browser): make extract discoverable in tool description

* fix(browser): keep extract dep types module-local
2026-07-25 15:13:32 -07:00
Peter Steinberger
1bce047953 fix(ci): repair plugin prerelease contracts (#113820) 2026-07-25 12:38:31 -07:00
Peter Steinberger
502d1cf56b refactor(browser): split Playwright interaction tools (#113771)
* refactor(browser): split Playwright interactions

* test(browser): track split lint suppressions

* refactor(browser): inline interaction timeout resolver
2026-07-25 12:17:43 -07:00
Peter Steinberger
0e2c60e605 fix(browser): settle page shares when the relay disconnects (#113803)
Co-authored-by: Peter Steinberger <steipete@golden-gate.local>
2026-07-25 12:04:03 -07:00
Peter Steinberger
24d16b39c8 fix(browser): isolate copilot chat events by active turn (#113781)
Co-authored-by: Peter Steinberger <steipete@golden-gate.local>
2026-07-25 11:54:19 -07:00
Peter Steinberger
4dd8a2be76 improve(browser): batch navigation guard, snapshot delta markers, tool output schema, code-mode recipe (#113749)
* feat(browser): guard batches and annotate snapshot deltas

* docs(browser): document reliable automation loops

* test(browser): align batch guard mocks

* fix(browser): reset snapshot deltas after navigation

* fix(browser): detect document navigation without URL equality

* fix(browser): require stable identity for snapshot deltas

* fix(browser): keep snapshot delta helper private
2026-07-25 10:58:03 -07:00
Peter Steinberger
fe4972b12b fix(browser): keep shared page text inside producer limits (#113768)
Co-authored-by: Peter Steinberger <steipete@golden-gate.local>
2026-07-25 10:41:06 -07:00
Peter Steinberger
dd606796c1 fix(browser): clean up failed guarded navigation routes (#113753)
Co-authored-by: Peter Steinberger <steipete@golden-gate.local>
2026-07-25 09:17:53 -07:00
Peter Steinberger
511d759c50 fix(browser): prevent IME composition from sending copilot messages (#113752)
Co-authored-by: Peter Steinberger <steipete@golden-gate.local>
2026-07-25 09:14:20 -07:00
Peter Steinberger
06548ae57f fix(browser): prevent copilot popup hang when a tab closes (#113744)
Co-authored-by: Peter Steinberger <steipete@golden-gate.local>
2026-07-25 08:39:30 -07:00
Peter Steinberger
332006bc1f test: consolidate OpenClaw test state fixtures (#113576)
* test: consolidate OpenClaw test state fixtures

* test(plugin-sdk): expose isolated test state

Promote the isolated OpenClaw test-state lifecycle through a narrow published Plugin SDK subpath so extension tests no longer import private core helpers. This intentional SDK surface addition is maintainer-approved.

* test: use SDK test-state seam in extensions

Route bundled extension suites through the focused repo-local Plugin SDK test-state entrypoint and remove the Codex projector harness exports made stale by fixture consolidation. Keep the seam out of production builds and published package artifacts while auditing its real consumers in the full-tree deadcode scan.

* test(plugins): map test-state in package boundaries
2026-07-25 05:30:52 -07:00
ooiuuii
591e029095 fix(browser): skip non-executable auto-discovery candidates (#111951)
* fix(browser): skip non-executable candidates

* test(browser): preserve fs constants in mocks

* fix(browser): simplify executable candidate checks

* fix(browser): require regular executable files

* style(browser): format executable candidates

* test(ci): allow sqlite proof on fork runners

* style(test): keep sqlite timeout diff compact

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-25 04:50:40 -07:00
Peter Steinberger
b0603cb443 refactor(fs): adopt shared safe filesystem primitives (#113596)
* refactor(fs): adopt fs-safe helpers

* fix(zalouser): keep stable per-profile QR temp path

* fix(fs): route adoption through owned facades
2026-07-25 03:24:19 -07:00
xingzhou
87b424aca8 fix(browser): control results can contain corrupted text from malformed UTF-8 (#111898)
* fix(browser): reject malformed UTF-8 control responses

* test(browser): cover malformed UTF-8 error responses

* test(browser): strengthen UTF-8 transport coverage

* fix(browser): preserve retry guidance on decode errors

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-25 02:30:36 -07:00
Peter Steinberger
aba0b12014 refactor(browser): split Playwright session manager (#113585) 2026-07-25 01:58:10 -07:00
Peter Steinberger
1e93465a2a refactor(sqlite): enforce one database connection boundary (#113418)
* refactor(sqlite): centralize database opens

* test(sqlite): mock connection owner boundary
2026-07-24 15:15:17 -07:00
Vincent Koc
5ff16901ef refactor(browser): reuse shared error coercion (#113229) 2026-07-24 13:58:29 +08:00
FMLS
b88eeddeed feat(browser): add openclaw browser batch CLI subcommand (#111457)
* feat(browser): add `openclaw browser batch` CLI subcommand

Expose the existing `act:batch` runtime through a dedicated CLI subcommand so
users and scripts can run nested act requests in one call without going through
the agent tool.

- `--actions <json>` for inline JSON, `--actions-file <path>` for file input,
  `--actions-file -` for stdin (1MB cap to bound a runaway pipe)
- `--continue` sets `stopOnError=false`; default keeps the runtime fail-fast
  (stop on first error) behavior, matching the existing batch contract
- `--target-id` forwards to the batch body
- Outer request timeout uses `resolveBrowserActExecutionBudgetMs` so the batch
  budget covers nested actions
- Docs (`docs/tools/browser-control.md` + bundled `browser-automation` skill)
  document the batch CLI, ref lifecycle, targetId conflict handling, and error
  summary format

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(browser): fail batch CLI on action errors

* fix(browser): keep batch stdin reader private

* docs(browser): refresh batch heading map

* test(browser): assert batch JSON via runtime mock

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Mason Huang <masonxhuang@icloud.com>
Co-authored-by: Mason Huang <masonxhuang@proton.me>
2026-07-24 11:33:45 +08:00
Peter Steinberger
1f051da39d refactor(browser): split Chrome MCP lifecycle (#113114) 2026-07-23 10:45:21 -07:00
Peter Steinberger
acd92f6a3d chore(deps): refresh repository dependencies (#112453)
* build(deps): update QA broker dependency

* build(deps): refresh repository dependencies

* build(deps): reconcile rebased shrinkwraps

* test(plugins): remove stale loader test state

* test(deps): stabilize updated dependency coverage

* fix(swift): use caller-isolated TaskLocal overload

* build(deps): regenerate rebased shrinkwraps

* test(msteams): preserve DNS validation in fetch helper

* fix(deps): avoid vulnerable optional image stack

* test(deps): validate generated LRU override

* refactor(ui): extract chat resizable divider

* test(ui): update divider ownership path

* fix(matrix): retain restart-compatible SDK

* style(cron): format update test
2026-07-23 16:17:13 +00:00
Peter Steinberger
525a7f4c96 ci(release): restore full validation after config migrations (#112740)
* fix(browser): preserve scoped CDP authority policy

* test(release): migrate canonical config fixtures

* ci(release): repair acceptance harness drift

* ci(release): extend Anthropic setup budget

* test(release): align remaining CI contracts

* test(release): align Docker and QA contract tests

* docs(release): preserve validation evidence inputs

* test(plugins): align unprepared metadata reads

* ci(release): separate QA live suite filters

* test(release): bind cron MCP cleanup device
2026-07-23 08:46:55 -04:00
LZY3538
954a5089b3 fix(browser): detect Windows browsers when install roots are blank (#111256)
* fix(browser): ignore blank Windows install roots

* fix(browser): complete Windows install root fallbacks

Co-authored-by: LZY3538 <liu.zhenye@xydigit.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-22 14:54:50 -07:00
Hiroshi Tanaka
52f412bf17 fix(browser): tab creation steals window focus during agent automation (#105356)
* fix(browser): tab creation steals window focus during agent automation

Agent-created tabs inherited CDP's foreground default: direct CDP
Target.createTarget omitted the background flag, and the extension
relay's createTab defaulted to active:true, so every agent tab open
activated the new tab (and, on the extension driver, focused the
window), interrupting whatever the human was doing in that browser.

Direct CDP tab creation now requests background:true (agent tab
ownership/selection is target-id based and never depended on
activation), and the extension relay defaults an omitted background
to true while preserving an explicit background:false, matching the
Codex/Claude-in-Chrome model the extension driver mirrors.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* refactor(browser): keep focus fix LOC-neutral

Preserve background tab creation while keeping the oversized CDP and relay modules within the current LOC ratchet.\n\nCodex-Session: 019f5e93-780a-7350-88f9-1986cdb64914

* fix(browser): honor explicit CDP focus requests

Keep background-by-default automation while treating Target.createTarget focus=true as an explicit foreground request in the extension relay.

Codex-Session: 019f5e93-780a-7350-88f9-1986cdb64914

* fix(browser): preserve explicit CDP focus semantics

Apply the background-by-default automation policy only when focus is omitted, preserving focus=false foreground-tab requests as well as focus=true.

Codex-Session: 019f5e93-780a-7350-88f9-1986cdb64914

* fix(browser): preserve create target window focus

Carry the resolved CDP focus intent through the extension relay and explicitly focus the containing Chrome window when requested.\n\nCodex-Session: 019f5e93-780a-7350-88f9-1986cdb64914

* style(browser): refresh relay import order

* test(secrets): use secure node exec fixtures

* test(doctor): secure exec secret fixture

* test(doctor): retain narrowed temp path

* test(secrets): secure remaining exec fixtures

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-21 21:35:40 -07:00
Peter Steinberger
edecdbd05e refactor(config): config-surface reduction tranche 3 — product consolidations (review request) (#111527)
* refactor(config): consolidate media model lists

* refactor(config): unify memory configuration

* refactor(config): consolidate TTS ownership

* refactor(config): move typing policy to agents

* refactor(config): retire product-level config surfaces

* refactor(config): share scoped tool policy type

* chore(config): refresh generated baselines

* fix(config): honor agent typing overrides

* fix(config): migrate sibling config consumers

* refactor(infra): keep base64url decoder private

* fix(config): strip invalid legacy TTS values

* chore(config): refresh rebased baseline hash

* fix(doctor): route legacy messages.tts.realtime voice to talk during tts move

* refactor(config): polish final layout names

* refactor(config): freeze retired tuning defaults

* feat(config): add fast mode default symmetry

* refactor(config): key agent entries by id

* docs(config): update final layout reference

* test(config): cover final layout migrations

* chore(config): refresh final layout baselines

* fix(config): align final layout runtime readers

* fix(config): align remaining readers

* fix(config): stabilize final layout migrations

* fix(config): finalize config projection proof

* fix(config): address final layout review

* docs(release): preserve historical config names

* fix(config): complete keyed agent migration

* fix(config): close final migration gaps

* fix(config): finish full-branch review

* fix(config): complete runtime secret detection

* fix(config): close final review findings

* fix(config): finish canonical docs and heartbeat migration

* fix(config): integrate latest main after rebase

* refactor(env): isolate test-only controls

* refactor(env): isolate build and development controls

* refactor(env): collapse process identity indirection

* refactor(env): remove duplicate config and temp aliases

* docs(env): define the operator-facing allowlist

* ci(env): ratchet production variable count

* fix(env): remove stale provider helper import

* fix(env): make ratchet sorting explicit

* test(env): keep test seam in dead-code audit

* test(env): cover ratchet growth and boundary; document surface budgets

* docs(config): document tier-eval consolidations

* docs(config): clarify speech preference ownership

* test(memory): align retired tuning fixtures

* refactor(memory): freeze engine heuristics

* refactor(config): apply tier-eval tranche

* refactor(tts): move persona shaping to providers

* refactor(compaction): move prompt policy to providers

* test(config): align hookified prompt fixtures

* chore(deadcode): classify test-only exports

* chore(github): remove unused spawn helper

* chore(deadcode): classify queue diagnostics

* chore(deadcode): remove unused lane snapshot export

* chore(plugin-sdk): ratchet consolidated surface

* fix(config): integrate latest main after rebase
2026-07-21 20:28:43 -07:00
Peter Steinberger
4541c27f30 fix(browser): regenerate stale copilot runtime bundle (#112379) 2026-07-21 14:28:56 -07:00
Yiğit ERDOĞAN
1096b74ab7 fix(browser): retire durable tab rows whose browser never returns (#111307)
* fix(browser): retire durable tab rows whose browser never returns

Durable cleanup defers whenever ownership cannot be proven, so a browser
that never comes back at the same cdpUrl leaves its rows behind forever:
each sweep re-claims them, fails the identity lookup, warns, and defers
again. Nothing in the subsystem removes a row by age.

The `browser.session-tabs` namespace is opened with a 5000-row cap and
`reject-new`, so once those rows accumulate to the cap, tracking a new tab
throws PLUGIN_STATE_LIMIT_EXCEEDED. That propagates into the compensation
path in browser-tool-session-tabs.ts, which closes the tab the user just
opened and rethrows -- every `browser open` on that profile then opens a
tab, closes it again, and errors, with no self-healing path.

Bound the retry: when a close attempt reports the target unavailable and
the tab has been unused for longer than the retire window, drop the row
instead of deferring again. A browser returning after that long almost
always carries a fresh instance fingerprint, which retires the row through
the ownership-mismatch path anyway.

closeTrackedBrowserTabsForSessions now accepts `now` like the sweep does,
so lifecycle cleanup can be exercised on a coherent clock.

* refactor(browser): split session tab cleanup claim and test harness

check-lint failed on max-lines: session-tab-registry.ts was at 699 of its
700-line budget and the durable registry test at 982 of 1000, so the retire
branch and its regression test pushed both over.

Extract the cleanup claim bookkeeping (claim, ownership match, delete) into
session-tab-cleanup-claim.ts, and the durable registry test shapes into
session-tab-registry.sqlite.test-helpers.ts, matching the existing
*.test-helpers.ts convention in this directory. No behavior change.

* test(browser): protect unreachable retirement races

Co-authored-by: Yigtwxx <yigiterdogan023@gmail.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-21 10:10:28 -07:00
FMLS
25d6f13a1f test(browser): add cross-operation tab reference parity contract (#111375) 2026-07-20 11:56:57 +08:00
mushuiyu886
34978b5a07 fix(browser): report final URL when opening managed Chrome tabs (#111374) 2026-07-19 10:01:42 -07:00
Peter Steinberger
7aedca21b2 test(release): align built-in timing proofs (#111483) 2026-07-19 09:42:16 -07:00
Peter Steinberger
783a5d21cf refactor(config): purge numeric tuning knobs behind built-in defaults (#111382) 2026-07-19 07:35:45 -07:00
Peter Steinberger
4efcea1fd2 feat(browser): send pages to OpenClaw from the Chrome extension (#111158)
* feat(browser): send pages to the main session from the Chrome extension

One-click page share in the OpenClaw Chrome extension: toolbar popup with an
optional note, page/selection context menu, and Alt+Shift+S. Capture is
selection-first with a readability heuristic, X/Twitter thread extraction, and
Google Docs plain-text export via the user's session cookies. Payloads ride the
existing paired relay WebSocket as a new pageShare message; the gateway-only
page-share sink wraps page text in the external-content safety boundary, then
enqueues a main-session system event and requests an immediate heartbeat
(hooks/wake semantics). Node-hosted relays report a clear unsupported error.

Capture heuristics adapted from Nat Eliason's MIT-licensed send-to-openclaw.

Co-authored-by: Codex <codex@openai.com>

* fix(browser): keep page-controlled metadata inside the share safety boundary

Review findings: move title/URL inside wrapExternalContent (a hostile <title>
must not become trusted header text), prefer the user's selection over the
full Google Docs export, and pass the context-menu selectionText through so
iframe selections and selections cleared during relay reconnect still win.

* fix(browser): bind context-menu shares to the click-time document

Selection shares from the context menu now send the click snapshot directly
(no recapture), so navigations during relay reconnect cannot mislabel the
source and iframe selections are preserved. The Google Docs selection probe
scans all accessible frames before falling back to the full-document export.

* test(browser): expect the page-share handler in relay server args

* fix(browser): probe only the main frame for Google Docs selections

All-frame injection rejects wholesale when one frame is inaccessible and
returns child frames in nondeterministic order, so the probe now reads the
main frame only. Child-frame selections still share correctly through the
context menu's click-time selectionText; toolbar/shortcut entry sends the
full page for that case (named tradeoff in the code comment).

* fix(browser): satisfy page-share CI gates

---------

Co-authored-by: Codex <codex@openai.com>
2026-07-19 01:07:28 -07:00
FMLS
4074e0cae1 fix(browser): close tracked tabs after gateway restart (#110797)
* fix(browser): preserve tab cleanup across restarts

* fix(browser): disambiguate restart tab aliases

* fix(browser): keep untrack selection type private

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-18 22:24:20 -07:00
Peter Steinberger
0a8c485395 fix(browser): make copilot runtime bundle deterministic (#111109) 2026-07-18 18:49:38 -07:00
Peter Steinberger
03c3c4b978 fix(browser): keep copilot runtime bundle in sync (#111089) 2026-07-18 18:15:21 -07:00
Peter Steinberger
e7fe47d63f fix(browser): allow one retry after transient failures (#110607)
* fix(browser): soften transient retry guidance

* style(browser): format retry hint test

* chore: keep release note in PR body
2026-07-18 10:58:22 +01:00
Alix-007
bc77ef3398 fix(browser): time out relay WebSocket opening (#109114)
* fix(browser): time out relay WebSocket opening

* test(browser): resolve worker import at runtime

* test(browser): refresh relay worker mocks

Co-authored-by: Alix-007 <li.long15@xydigit.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
Co-authored-by: Peter Steinberger <peter@steipete.me>
2026-07-18 09:36:46 +01:00
Shakker
3fd448c559 fix: keep browser asset build package-local 2026-07-18 06:42:27 +01:00
Shakker
643476f24d fix: derive generated asset watch exclusions 2026-07-18 06:42:27 +01:00
Shakker
b118e9e913 fix: preserve generated asset mtimes 2026-07-18 06:42:27 +01:00
Peter Steinberger
ec8f6e5e03 feat(browser): add secure per-tab copilot panel (#109817)
* feat(browser): add copilot security contracts

* fix(gateway): expose verified client identity to handlers

* feat(browser): add secure per-tab copilot panel

Co-authored-by: Cameron Beeley <cameron.beeley@gmail.com>

* refactor(browser): separate copilot gateway hint custody

* fix(browser): preserve legacy pairing parse shape

* fix(browser): harden copilot lifecycle custody

Co-authored-by: Cameron Beeley <cameron.beeley@gmail.com>

* fix(browser): enforce copilot lifecycle boundaries

Co-authored-by: Cameron Beeley <cameron.beeley@gmail.com>

* style(browser): format copilot sources

Co-authored-by: Cameron Beeley <cameron.beeley@gmail.com>

* fix(browser): preserve copilot consent revocation

Co-authored-by: Cameron Beeley <cameron.beeley@gmail.com>

* refactor(browser): split copilot custody owners

Co-authored-by: Cameron Beeley <cameron.beeley@gmail.com>

* test(browser): normalize websocket array buffers

Co-authored-by: Cameron Beeley <cameron.beeley@gmail.com>

* chore(protocol): regenerate Swift gateway models

Co-authored-by: Cameron Beeley <cameron.beeley@gmail.com>

* refactor(browser): model copilot runtime entrypoints

Co-authored-by: Cameron Beeley <cameron.beeley@gmail.com>

* fix(browser): honor extension build boundaries

Co-authored-by: Cameron Beeley <cameron.beeley@gmail.com>

* test(gateway): assert targeted chat delivery

Co-authored-by: Cameron Beeley <cameron.beeley@gmail.com>

* test(gateway): cover targeted delivery calls

Co-authored-by: Cameron Beeley <cameron.beeley@gmail.com>

* fix(browser): declare copilot build dependencies

Co-authored-by: Cameron Beeley <cameron.beeley@gmail.com>

* fix(ci): clear browser copilot gate failures

Co-authored-by: Cameron Beeley <cameron.beeley@gmail.com>

* test(ci): cover copilot lint exclusion

Co-authored-by: Cameron Beeley <cameron.beeley@gmail.com>

* fix(browser): gate copilot on relay custody

Co-authored-by: Cameron Beeley <cameron.beeley@gmail.com>

* test(browser): bound copilot relay frames

Co-authored-by: Cameron Beeley <cameron.beeley@gmail.com>

---------

Co-authored-by: Cameron Beeley <cameron.beeley@gmail.com>
2026-07-18 01:00:23 +01:00
Peter Steinberger
9cdb9762a3 style: format tab-discovery-poll-abort test
8243afc062 (#109617) landed unformatted, failing check-lint repo-wide.
2026-07-17 01:50:53 -07:00
wangmiao0668000666
1fe6b6b0f4 fix(browser): honor abortSignal during Playwright click-and-hold delay (#109710)
* fix(browser): honor abortSignal during Playwright click-and-hold delay

The click-and-hold delay in clickViaPlaywright slept with a bare
setTimeout(resolve, delayMs) where delayMs can reach ACT_MAX_CLICK_DELAY_MS
(5s). The interaction AbortSignal was checked before and after the sleep
but the sleep itself was not abortable, so an abort mid-hold left the
action chain orphaned: the ref'd timer kept the event loop busy for the
full remaining delay and postponed the navigation-guard teardown (grace
recheck and, with an SSRF policy, the page.route interception) until the
orphaned sleep ended.

Swap the bare timer for sleepWithAbort(delayMs, signal) from
openclaw/plugin-sdk/runtime-env so the abort clears the timer and unwinds
the chain immediately. The caller-visible rejection is unchanged: the
abort listener registered before the action still wins the race with the
original signal.reason.

Co-Authored-By: Claude <noreply@anthropic.com>

* refactor(browser): reuse click abort helper

Keep the contributor abort-aware hold delay while deleting duplicate promise and listener lifecycle code in favor of the existing interaction helper.

Co-authored-by: wangmiao0668000666 <wang.miao86@xydigit.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-17 01:42:47 -07:00
wangmiao0668000666
8243afc062 fix(browser): honor AbortSignal in tab discovery poll (#109617)
* fix(browser): honor AbortSignal in tab discovery poll

* fix(browser): unify abort-aware tab discovery

Apply the canonical abortable sleep to both discovery loops, reject late backend results after cancellation, and replace timing-sensitive proof with deterministic timer coverage.

Co-authored-by: wangmiao0668000666 <wang.miao86@xydigit.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-17 01:39:05 -07:00
Peter Steinberger
f1cb2a8f20 perf(test): poll Chrome MCP state faster (#109776) 2026-07-17 00:42:52 -07:00
Peter Steinberger
0631252f4b refactor: reduce plugin SDK test import overhead (#109333)
* refactor(test): split plugin SDK test helpers

* fix(test): align plugin SDK test subpaths
2026-07-16 13:55:23 -07:00