* 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>
* 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>
* 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>
* fix(extensions): make indexed access explicit across channel plugins
Transport-payload-safe burn-down: malformed Telegram/Discord/QQ/LINE
and sibling channel input keeps existing skip paths; no synthesized
fields, no new throws in delivery loops. Zalo escape sentinels preserve
literal matches instead of undefined replacements.
* fix(extensions): make indexed access explicit across provider and memory plugins
Stream and model iteration, tool-block guards, capture guards, and
sparse accumulators; singleton model reads carry named invariants.
* fix(extensions): make indexed access explicit across tooling plugins, flip the extensions lane
Remaining plugins (oc-path, qa-lab, browser, logbook, and siblings) plus
the tsconfig.extensions.json flag flip. Cleanup: logbook sampleFrames
NaN index at max=1, QA retry clamp at non-positive attempts, dead Canvas
probe and OpenShell no-op slice removed, twitch test setup leak excluded
from the prod lane.
* refactor(plugin-sdk): expose expectDefined via a focused SDK subpath
Extensions imported @openclaw/normalization-core directly, crossing the
external-plugin packaging boundary (it only worked because the runtime
builder bundles undeclared workspace helpers). expect-runtime joins the
canonical entrypoints JSON, generated exports, API baseline, docs, and
subpath contract test; all 78 extension imports now use the SDK seam.
Two scanner-shaped locals renamed for review-bundle hygiene.
* chore(plugin-sdk): raise surface budgets for the expect-runtime subpath
One new entrypoint with one callable export, added intentionally as the
packaging-honest seam for extension invariant helpers.
* fix(browser): reject non-page json new targets
* fix(browser): adopt only validated raw CDP targets
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>