Skip to content

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

Merged
steipete merged 21 commits into
mainfrom
feat/browser-copilot-contracts
Jul 18, 2026
Merged

feat(browser): add secure per-tab copilot panel#109817
steipete merged 21 commits into
mainfrom
feat/browser-copilot-contracts

Conversation

@steipete

@steipete steipete commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Related: #93680

What Problem This Solves

The browser side-panel copilot needs one isolated agent session per tab without letting page content, another tab, or a stale panel acquire a different browser target or receive another session's events. Closing a tab must also end live delivery and run custody without deleting its transcript.

Why This Change Was Made

This rebuild gives the browser copilot a dedicated canonical device-pairing identity with issued-token reuse, Gateway-owned session subscription allowlists with pre-delivery filtering, and typed tab bindings enforced by the browser tool. Chrome receives a capability-bound panel URL through chrome.sidePanel.setOptions({ tabId, path, enabled }) for each shared tab; there is no global default panel.

The extension service worker owns session creation, panel capability custody, debugger attachment, subscription, abort, and archival. Tab close first revokes delivery/debugger access and aborts any active run, then marks the Gateway session archived. The transcript remains in that archived Gateway session until normal user/admin session deletion; the extension stores no parallel transcript or silent live-session fallback. Failed abort/archive RPCs remain durable recovery work and retry without restoring access.

Based on @2CB2's side-panel implementation in #93680, which established the product direction and served as the implementation basis for this secure rebuild.

User Impact

Users can open a copilot panel scoped to the current shared tab. Two tabs receive distinct sessions and histories, unshared tabs get a clear denial/share flow, and closing a tab archives its session while preserving the transcript for later inspection.

Evidence

Before: unshared-tab denial After: isolated active panel
Unshared tab denial Isolated per-tab copilot
  • Real Chromium extension E2E: two tabs/two sessions with no cross-talk, typed binding rejection, denial UX, and archive-on-tab-close all passed.
  • Focused browser copilot and Gateway origin suite: 71 tests passed across Gateway auth/origin policy, background custody, session registry, panel contracts, relay contracts, and CLI pairing.
  • Full hosted proof on the runtime head: protocol generation parity, dead-code scans, changed-file formatting and guards, project typecheck, core/extension lint, runtime import cycles, production build, and Chromium E2E passed on Blacksmith Testbox run 29605834706.
  • Full feature-head proof before the final rebase: 596 focused unit/integration tests, regenerated-bundle parity, and the real Chromium side-panel E2E passed on Blacksmith Testbox run 29609031432.
  • Final package-boundary repair: all 442 extension runtime-dependency contract tests passed locally; the full changed gate, including project typecheck, core/extension lint, and runtime import-cycle checks, passed on Blacksmith Testbox run 29614203527.
  • Final relay/credential repair passed the full hosted changed gate on Blacksmith Testbox run 29619406820: extension and test typechecks, full extension lint, formatting, max-lines, and import-cycle checks.
  • Exact current-head Chromium proof passed on Blacksmith Testbox run 29621066040: two isolated tab sessions, no cross-talk, typed binding rejection, denial UX, archive-on-tab-close, and live relay-loss abort/gating/recovery.
  • Exact current-head OpenGrep passed on run 29620944168, including the bounded relay WebSocket frame policy.
  • Exact current-head CI passed on run 29620944159, including all Linux, macOS, iOS, and Windows required jobs.
  • Stage 1 Gateway/security contract proof passed on Blacksmith Testbox run 29565227421.
  • Mandatory autoreview completed clean after the final ownership split, E2E type repair, and generated-protocol refresh.

@openclaw-barnacle openclaw-barnacle Bot added app: web-ui App: web-ui gateway Gateway runtime agents Agent runtime and tooling size: XL maintainer Maintainer-authored PR labels Jul 17, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3faba78ca2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +207 to +210
(!opts?.sessionKeys?.length ||
!opts.sessionKeys.some((sessionKey) =>
params.sessionMessageSubscribers?.get(sessionKey).has(c.connId),
))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve run-scoped tool event delivery

For a browser copilot or other session-scoped-events client that also requests tool-events, chat.ts registers the initiating connection in toolEventRecipients and later sends tool lifecycle agent frames with broadcastToConnIds(..., runToolRecipients) without sessionKeys. This new gate drops those targeted frames before sending, so copilot-originated runs cannot receive live tool start/end updates even though the run-recipient registry already authorized that connection; pass the delivery session key on that path or recognize run-tool recipients as an authorized scoped source.

Useful? React with 👍 / 👎.

@steipete steipete self-assigned this Jul 17, 2026
@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. merge-risk: 🚨 security-boundary 🚨 May affect sandboxing, authorization, credentials, or sensitive data. labels Jul 17, 2026
@clawsweeper

clawsweeper Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Codex review: found issues before merge. Reviewed July 17, 2026, 7:40 PM ET / 23:40 UTC.

Summary
The PR adds a Chrome tab side-panel copilot with tab-bound Gateway sessions, paired-device/origin checks, typed browser bindings, scoped event delivery, recovery custody, documentation, and tests.

Reproducibility: yes. from source: a scoped client can enter the broad session subscriber path, while the new allowlist does not cover all session-bearing events; the related follow-up also reports a real-Gateway reproduction.

Review metrics: 2 noteworthy metrics.

  • Paired-device persistence: 2 added persistent columns. The paired and pending device records gain browser_origin, which requires upgrade-safe compatibility review.
  • Run-context API: 1 added toolBindings context field. The new generic plugin run binding becomes a compatibility-sensitive core contract for browser tool authorization.

Stored data model
Persistent data-model change detected: durable storage schema: extensions/browser/chrome-extension/modules/copilot-gateway-lifecycle.js, durable storage schema: extensions/browser/chrome-extension/modules/copilot-session-registry.js, serialized state: src/gateway/session-message-events.test.ts, serialized state: src/state/openclaw-state-db.generated.d.ts, unknown-data-model-change: src/gateway/session-message-events.test.ts, unknown-truncated-pull-files, and 2 more. Confirm migration or upgrade compatibility proof before merge.

Root-cause cluster
Relationship: canonical
Canonical: #109817
Summary: This PR introduces the browser-copilot scoped-session model; the related follow-up is a candidate repair for its remaining broad-subscription isolation defect but is not yet a viable replacement.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

Merge readiness
Overall: 🦐 gold shrimp
Proof: 🦞 diamond lobster ✨ media proof bonus
Patch quality: 🦐 gold shrimp
Result: needs maintainer review before merge.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • [P2] Reject broad session subscriptions for scoped clients with a real-Gateway regression test.
  • Obtain owner approval for the permanent generic run-binding and paired-browser-origin contract.

Risk before merge

  • [P1] A scoped browser-copilot connection can still use broad session subscription paths and receive sibling-session transcript, operation, or metadata events, defeating the per-tab isolation boundary.
  • [P1] The PR adds a durable paired-device origin field and a new run-level tool-binding contract; maintainers need explicit upgrade and permanent API-boundary acceptance before merge.

Maintainer options:

  1. Close the broad-subscription path (recommended)
    Reject sessions.subscribe for scoped clients and add a real-Gateway regression proving foreign session events cannot reach a browser-copilot connection before merge.
  2. Accept a broader client authority
    Maintain the broad subscription path only if maintainers intentionally redefine the browser copilot as able to observe all Gateway sessions.
  3. Pause the panel contract
    Do not merge the feature if the desired long-term session-subscription authority remains undecided.

Next step before merge

  • [P2] A narrow security repair is identifiable, but this protected maintainer-labeled feature also needs human approval of its permanent browser, persistence, and plugin-contract boundary.

Maintainer decision needed

  • Question: Should OpenClaw permanently accept the new generic toolBindings run context and paired-browser-origin persistence as the core contract for the bundled browser copilot after the subscription isolation repair?
  • Rationale: The patch is a large feature that crosses browser-plugin ownership, Gateway authentication, event delivery, plugin runtime context, and persisted pairing records; source review can identify the defect but cannot choose the permanent public ownership and compatibility boundary.
  • Likely owner: steipete — This person authored the proposed cross-surface contract and is the best available routing candidate from the reviewed history.
  • Options:
    • Sponsor the bounded core contract (recommended): Approve the generic binding and origin-record seams after broad subscriptions are denied and upgrade behavior is explicitly proven.
    • Narrow the contract before merge: Require the browser plugin to own more of the feature so core exposes only the minimal generic authorization and run-binding seam.
    • Pause the feature direction: Defer the panel until owners choose a different browser/session architecture.

Security
Needs attention: The remaining broad-session subscription path is a concrete confidentiality failure in the feature’s advertised per-tab isolation boundary.

Review findings

  • [P1] Reject broad subscriptions for scoped clients — src/gateway/server-broadcast.ts:81
Review details

Best possible solution:

Deny broad sessions.subscribe for session-scoped-events clients at the subscription boundary, retain only explicit per-session subscriptions for the copilot, add real-Gateway regression coverage for foreign-session non-delivery, then have the browser/Gateway owners approve the permanent tool-binding and pairing-record contract.

Do we have a high-confidence way to reproduce the issue?

Yes, from source: a scoped client can enter the broad session subscriber path, while the new allowlist does not cover all session-bearing events; the related follow-up also reports a real-Gateway reproduction.

Is this the best way to solve the issue?

No: the tab-bound design is plausible, but merging before broad session subscription is denied leaves the advertised per-tab isolation incomplete; the narrow root-boundary denial is safer than expanding event-by-event filtering.

Full review comments:

  • [P1] Reject broad subscriptions for scoped clients — src/gateway/server-broadcast.ts:81
    SESSION_SUBSCRIPTION_EVENTS only guards three event names, while a client advertising session-scoped-events can still join the legacy broad session subscriber set. Session transcript, operation, and session-metadata broadcasts outside this allowlist can therefore reach a tab-bound copilot for a different session. Reject broad sessions.subscribe for scoped clients and retain only per-session subscriptions; the related follow-up demonstrates the intended narrow fix.
    Confidence: 0.93

Overall correctness: patch is incorrect
Overall confidence: 0.9

AGENTS.md: unclear because the file could not be read completely.

Codex review notes: model internal, reasoning high; reviewed against 03d5d88a14e4.

Label changes

Label justifications:

  • P1: A browser-copilot client can receive data from sessions outside its admitted tab, undermining a core user-facing isolation guarantee.
  • merge-risk: 🚨 compatibility: The feature adds paired-device origin persistence and a generic run-context binding contract that affect upgrades and plugin behavior.
  • merge-risk: 🚨 message-delivery: The patch changes Gateway session-event and chat-event routing, and the remaining broad path can misdeliver sibling-session data.
  • merge-risk: 🚨 security-boundary: The tab-isolation boundary relies on subscription filtering, origin binding, and browser tool binding enforcement.
  • rating: 🦐 gold shrimp: Overall readiness is 🦐 gold shrimp; proof is 🦞 diamond lobster and patch quality is 🦐 gold shrimp.
  • status: ⏳ waiting on author: ClawSweeper has contributor-facing work open and is waiting for author action. Sufficient (screenshot): The PR body includes directly visible before/after panel proof and reports exact-current-head real Chromium E2E covering tab isolation, denial UX, archive-on-close, and relay-loss recovery.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body includes directly visible before/after panel proof and reports exact-current-head real Chromium E2E covering tab isolation, denial UX, archive-on-close, and relay-loss recovery.
  • proof: 📸 screenshot: Contributor real behavior proof includes screenshot evidence. The PR body includes directly visible before/after panel proof and reports exact-current-head real Chromium E2E covering tab isolation, denial UX, archive-on-close, and relay-loss recovery.
Evidence reviewed

Security concerns:

  • [high] Scoped client can join broad session delivery — src/gateway/server-broadcast.ts:81
    The PR gives browser-copilot clients session-scoped-events but does not prevent the legacy broad subscription path; event families outside the new allowlist can expose sibling session data to that client.
    Confidence: 0.93

What I checked:

  • Remaining scoped-subscription gap: The PR’s scoped-delivery allowlist covers only agent, chat, and chat.side_result; the reviewed discussion and the related follow-up identify that a session-scoped-events client can still call broad sessions.subscribe and receive session-bearing broadcast families outside that allowlist. (src/gateway/server-broadcast.ts:81, 4f630fd3281a)
  • Existing follow-up is not a safe replacement: The related follow-up proposes denying broad subscriptions, but it is open, based on this PR, dirty against its base, and lacks sufficient positive proof; it does not supersede this PR or remove the need to fix the defect here before merge. (src/gateway/server-broadcast.ts:81, c2b8ccdc2fd9)
  • Real behavior evidence: The PR body reports exact-current-head Chromium proof for isolated tab sessions, denial UX, archive-on-close, relay-loss recovery, and bounded relay frames, with linked hosted runs and directly visible before/after panel screenshots. (extensions/browser/chrome-extension/sidepanel.e2e.test.ts:1, 4f630fd3281a)
  • Persistent upgrade surface: The patch adds browser_origin to both pending and paired device records and uses additive runtime columns, so upgrade behavior and paired-device compatibility remain merge-critical even though the schema change is additive. (src/state/openclaw-state-db.ts:1367, 4f630fd3281a)

Likely related people:

  • steipete: The PR’s 21 commits introduce the reviewed browser copilot, Gateway delivery, pairing, and persistence contract surfaces. (role: proposed Gateway/browser contract author; confidence: medium; commits: 4e29cba3278b, 2e82a1a2bb4b, 4f630fd3281a; files: src/gateway/server-broadcast.ts, src/gateway/server/ws-connection/connect-admission.ts, extensions/browser/chrome-extension/modules/copilot-background.js)
  • anagnorisis2peripeteia: The prior panel proposal established the product direction cited by this PR, and this person opened the related narrow subscription-hardening follow-up. (role: adjacent feature author and isolation follow-up author; confidence: medium; commits: c2b8ccdc2fd9; files: src/gateway/server-broadcast.ts, src/gateway/session-message-events.test.ts)
  • Cameron Beeley: Multiple feature and lifecycle commits list this person as co-author for the browser copilot ownership split and lifecycle hardening. (role: co-author of reviewed browser custody work; confidence: medium; commits: 7b8293187758, 2fddcd108750, 7c1aa447fd00; files: extensions/browser/chrome-extension/modules/copilot-background.js, extensions/browser/chrome-extension/modules/copilot-recovery.js)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.
Review history (12 earlier review cycles; latest 8 shown)
  • reviewed 2026-07-17T19:50:39.487Z sha f136288 :: needs real behavior proof before merge. :: [P1] Define the new plugin run-binding contract
  • reviewed 2026-07-17T20:03:05.349Z sha f136288 :: needs real behavior proof before merge. :: [P1] Filter session operation events by subscribed session | [P1] Define the plugin run-binding contract before exposing it
  • reviewed 2026-07-17T20:45:26.835Z sha f136288 :: needs real behavior proof before merge. :: [P1] Filter session operation events by subscribed session | [P1] Define and isolate the plugin run-binding contract
  • reviewed 2026-07-17T21:41:24.199Z sha fdc8df4 :: needs real behavior proof before merge. :: [P1] Deny broad subscriptions for scoped clients | [P1] Preserve tab bindings through queued runs
  • reviewed 2026-07-17T22:02:54.634Z sha d4af120 :: needs real behavior proof before merge. :: [P1] Deny broad subscriptions for scoped clients
  • reviewed 2026-07-17T22:54:32.365Z sha 933db40 :: found issues before merge. :: [P1] Deny broad subscriptions for scoped clients
  • reviewed 2026-07-17T23:19:53.934Z sha 78559af :: found issues before merge. :: [P1] Reject broad subscriptions for scoped clients
  • reviewed 2026-07-17T23:35:32.119Z sha 4f630fd :: found issues before merge. :: [P1] Deny broad subscriptions for scoped clients

@anagnorisis2peripeteia

anagnorisis2peripeteia commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

@steipete this is awesome to see. May I assist further?

Happy to take the Stage 2 you flagged — bringing the #93680 side panel onto these contracts once Stage 1 lands, as the UI client rather than a parallel path. Concretely: rebase the panel to connect as the openclaw-browser-copilot identity (ui mode + run-tool-bindings + session-scoped-events), emit the per-tab toolBindings.browser binding instead of the in-prompt tab hint, consume the scoped delivery, and move device auth onto GatewayBrowserDeviceAuthLifecycle — keeping the panel's streaming render, per-tab sessions, and untrusted-page fencing on top.

Would you prefer that as a follow-up PR stacked on this once it merges, or folded in some other way? Happy to work to whatever shape suits you.

@steipete
steipete force-pushed the feat/browser-copilot-contracts branch from 3faba78 to 1c39716 Compare July 17, 2026 18:21
@steipete
steipete requested a review from a team as a code owner July 17, 2026 18:21
@github-actions

Copy link
Copy Markdown
Contributor

Dependency Guard

This PR changes dependency-related files. Maintainers should confirm these changes are intentional.

Changed files:

  • extensions/browser/package.json
  • pnpm-lock.yaml

Maintainer follow-up:

  • Review whether the dependency changes are intentional.
  • Inspect resolved package deltas when lockfile, shrinkwrap, or workspace dependency policy changes are present.
  • Treat package-lock.json and npm-shrinkwrap.json diffs as security-review surfaces.
  • Run pnpm deps:changes:report -- --base-ref origin/main --markdown /tmp/dependency-changes.md --json /tmp/dependency-changes.json locally for detailed release-style evidence.

@github-actions github-actions Bot added the dependencies-changed PR changes dependency-related files label Jul 17, 2026
@openclaw-barnacle openclaw-barnacle Bot added docs Improvements or additions to documentation and removed dependencies-changed PR changes dependency-related files labels Jul 17, 2026
@github-actions

github-actions Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Dependency graph changes noted

This PR includes dependency graph changes. The dependency guard is informational because the PR author is a repository admin or a member of @openclaw/openclaw-secops.

  • Current SHA: 4f630fd3281abdb0b23003d35550bf9aa97cf0aa
  • Trusted actor: @steipete
  • Trusted role: pull request author; openclaw-secops

Security review is still recommended before merge when the dependency graph change is intentional.

@github-actions github-actions Bot added the dependencies-changed PR changes dependency-related files label Jul 17, 2026
@steipete steipete changed the title feat(browser): establish secure copilot contracts feat(browser): add secure per-tab copilot panel Jul 17, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1c397169d1

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

},
onConnectFailure: (error, { plan }) => {
const details = error.details && typeof error.details === "object" ? error.details : {};
if (details.code === "AUTH_DEVICE_TOKEN_MISMATCH") {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Clear stale copilot tokens on identity mismatch

If a stored browser-copilot device token is rejected by the new dedicated identity/origin check (for example after the Chrome extension ID/origin changes, or when an existing pairing lacks browserOrigin), connect-device-pairing.ts returns NOT_PAIRED without a details.code or request id. This handler only clears the token for AUTH_DEVICE_TOKEN_MISMATCH, so resolveCopilotClose keeps retrying with the same rejected token and never creates a fresh pairing request, leaving the side panel stuck until extension storage is manually wiped. Treat that identity/origin NOT_PAIRED rejection as token recovery, or have the server return a pairing-required detail so the next connect can re-pair.

Useful? React with 👍 / 👎.

@steipete
steipete force-pushed the feat/browser-copilot-contracts branch from 1c39716 to 322d163 Compare July 17, 2026 18:36
@clawsweeper clawsweeper Bot added proof: 📸 screenshot Contributor real behavior proof includes screenshot evidence. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. and removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. labels Jul 17, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 322d1631e5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

return clientId === GATEWAY_CLIENT_NAMES.CONTROL_UI;
return (
clientId === GATEWAY_CLIENT_NAMES.CONTROL_UI ||
clientId === GATEWAY_CLIENT_NAMES.BROWSER_COPILOT

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Exclude tab-bound copilot sends from restart recovery

When this classifies openclaw-browser-copilot as a browser operator UI, a normal side-panel chat.send with toolBindings.browser now satisfies the restart-safe eligibility in src/gateway/server-methods/chat-send-session.ts:132-147, but the restart-safe fingerprint in src/gateway/server-methods/chat-restart-recovery.ts:75-87 only covers message and sender identity. After a gateway crash/setup failure, retry/adoption with the same idempotency key and text can therefore keep the durable claim while changing the browser tab binding, so the recovered run may act on a different tab than the one admitted. Exclude tool-bound copilot sends from restart-safe recovery or bind the fingerprint to the tool binding.

Useful? React with 👍 / 👎.

@steipete
steipete force-pushed the feat/browser-copilot-contracts branch from d4af120 to 933db40 Compare July 17, 2026 22:09

@anagnorisis2peripeteia anagnorisis2peripeteia left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One more, lower-priority than the subscribe gap — same shape on the tool side, flagging for defense-in-depth.

The per-run browser tab binding's targetId is client-supplied and not server-side ownership-validated. The gateway does admission only (src/gateway/server-methods/chat-send-request.ts:160-161: "Gateway owns only paired-client admission"), delegating binding validation to the plugin. But the plugin validates shape (extensions/browser/src/browser-tool-binding.ts parseBrowserTabToolBinding) and agent-override (applyBrowserTabToolBinding pins the agent to the run binding) — neither checks that toolBindings.browser.targetId belongs to this connection's / session's shared tab.

relay-bridge.ts tabByTargetId does constrain dispatch to currently-shared, attached tabs, so this isn't reachable by a web page or the agent (both are pinned to the binding). The residual is a compromised or malicious paired copilot connection sending chat.send with toolBindings.browser.targetId set to another shared tab's target, driving a tab outside its own binding. Low-moderate / defense-in-depth (single-operator, and it needs the other tab's targetId) — but it's the same "client-supplied identifier not ownership-checked" pattern as the subscribe gap.

Belt-and-suspenders fix: validate binding.targetId / tabId against the connection's own shared-tab set at admission (or in the plugin binding validation), so a copilot run can only bind to a tab it actually shares. Happy to fold this into #110184 if useful.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 933db40cf0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

}
case "unpair": {
await chrome.storage.local.remove(["relayUrl", "token"]);
await chrome.storage.local.remove(["relayUrl", "gatewayUrl", "token"]);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Clear copilot device credentials on unpair

When a user clicks Unpair after the new side-panel flow has connected once, this only removes the relay pairing fields; the copilot identity and issued Gateway token stored under copilotDeviceIdentitiesV1/copilotDeviceTokensV1 remain in chrome.storage.local. If the same browser later pastes a pairing string for the same Gateway, CopilotGatewayClient reloads that old operator device token and reconnects without a fresh device approval, so Unpair no longer actually clears this browser's Gateway credential. Clear the copilot identity/token state for the gateway (or all copilot gateways) as part of this path.

Useful? React with 👍 / 👎.

@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. and removed proof: 📸 screenshot Contributor real behavior proof includes screenshot evidence. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jul 17, 2026
Comment thread extensions/browser/chrome-extension/sidepanel.e2e.test.ts Fixed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 78559af68a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +154 to +158
isBrowserCopilotClient(clientInfo) &&
!stopCommand &&
(!p.toolBindings || !Object.hasOwn(p.toolBindings, "browser"))
) {
return { ok: false, error: "browser copilot runs require an explicit browser tool binding" };

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject copilot sends when browser tool is unavailable

When the Browser plugin/tool is disabled or filtered out for the target agent (for example browser.enabled=false, the browser plugin disabled, or a restrictive tool allow/deny policy), this gate still admits openclaw-browser-copilot sends as long as a toolBindings.browser object is present. The binding is only validated/applied later when the Browser tool is actually constructed in extensions/browser/plugin-registration.ts, so with no Browser tool the panel can start a tab-bound run that cannot act on the admitted tab and may complete using unrelated tools/text. Please fail the send/preparation when the browser tool will not be available for this run, or ensure the copilot-owned tool grant is added before dispatch.

Useful? React with 👍 / 👎.

@clawsweeper clawsweeper Bot added the proof: 📸 screenshot Contributor real behavior proof includes screenshot evidence. label Jul 17, 2026
@steipete
steipete merged commit ec8f6e5 into main Jul 18, 2026
134 of 136 checks passed
@steipete
steipete deleted the feat/browser-copilot-contracts branch July 18, 2026 00:00
@steipete

Copy link
Copy Markdown
Contributor Author

Merged via squash.

@anagnorisis2peripeteia

Copy link
Copy Markdown
Contributor

@steipete thanks so much for this. could you change the @ reference to my gh and not my discord, @anagnorisis2peripeteia please? <3

github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jul 18, 2026
* 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 <[email protected]>

* 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 <[email protected]>

* fix(browser): enforce copilot lifecycle boundaries

Co-authored-by: Cameron Beeley <[email protected]>

* style(browser): format copilot sources

Co-authored-by: Cameron Beeley <[email protected]>

* fix(browser): preserve copilot consent revocation

Co-authored-by: Cameron Beeley <[email protected]>

* refactor(browser): split copilot custody owners

Co-authored-by: Cameron Beeley <[email protected]>

* test(browser): normalize websocket array buffers

Co-authored-by: Cameron Beeley <[email protected]>

* chore(protocol): regenerate Swift gateway models

Co-authored-by: Cameron Beeley <[email protected]>

* refactor(browser): model copilot runtime entrypoints

Co-authored-by: Cameron Beeley <[email protected]>

* fix(browser): honor extension build boundaries

Co-authored-by: Cameron Beeley <[email protected]>

* test(gateway): assert targeted chat delivery

Co-authored-by: Cameron Beeley <[email protected]>

* test(gateway): cover targeted delivery calls

Co-authored-by: Cameron Beeley <[email protected]>

* fix(browser): declare copilot build dependencies

Co-authored-by: Cameron Beeley <[email protected]>

* fix(ci): clear browser copilot gate failures

Co-authored-by: Cameron Beeley <[email protected]>

* test(ci): cover copilot lint exclusion

Co-authored-by: Cameron Beeley <[email protected]>

* fix(browser): gate copilot on relay custody

Co-authored-by: Cameron Beeley <[email protected]>

* test(browser): bound copilot relay frames

Co-authored-by: Cameron Beeley <[email protected]>

---------

Co-authored-by: Cameron Beeley <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling app: web-ui App: web-ui dependencies-changed PR changes dependency-related files docs Improvements or additions to documentation gateway Gateway runtime maintainer Maintainer-authored PR merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. merge-risk: 🚨 security-boundary 🚨 May affect sandboxing, authorization, credentials, or sensitive data. P1 High-priority user-facing bug, regression, or broken workflow. proof: 📸 screenshot Contributor real behavior proof includes screenshot evidence. proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. size: XL status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants