Skip to content

feat(pip): add computer use snapshot preview - #2050

Merged
zerob13 merged 3 commits into
devfrom
codex/computer-use-snapshot-pip
Jul 28, 2026
Merged

feat(pip): add computer use snapshot preview#2050
zerob13 merged 3 commits into
devfrom
codex/computer-use-snapshot-pip

Conversation

@zerob13

@zerob13 zerob13 commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • add a read-only Computer Use snapshot PiP with a single Close control
  • share one process-global NativeKit overlay coordinator between Browser and Computer Use while preserving Browser open-panel behavior
  • consume trusted get_window_state images through a bounded main-process pipeline with a renderer Canvas fallback
  • silently request a PiP-only get_window_state({ pid, window_id }) after an eligible successful click; the private result is never returned to the model, conversation, cache, or generic MCP result event
  • add typed routes/events, lifecycle and ownership isolation, regression coverage, and durable SDD documentation

Why

Browser PiP owns a continuously capturable WebContentsView, while Computer Use only exposes discrete window snapshots. Users still need to supervise desktop automation without switching away from DeepChat. This implementation presents the latest honest snapshot and adds one event-driven post-click refresh instead of background polling.

UX

BEFORE
┌──────────────────────┐
│ Agent tool calls     │    target application state is not visible
└──────────────────────┘

AFTER
┌──────────────────────┐    ┌────────────────── × ┐
│ Agent is working     │    │ latest target app  │
│ click result returns │    │ snapshot           │
└──────────────────────┘    └────────────────────┘
             `-> private get_window_state -> refresh PiP only

Computer Use exposes Close only. It has no expand/open-panel action and forwards no input to the target application.

Safety and performance

  • trust the resolved official CUA plugin identity, not a display name
  • bind frames to session, run, target, tool call, claim sequence, and epoch
  • keep native-path pixels in the main process and all frames memory-only
  • cap input, dimensions, transform concurrency, output size, and pending work
  • skip private capture when PiP is suspended, stopped, dismissed, stale, or missing a valid target
  • preserve the original click response and suppress private capture failures
  • add no timer, polling loop, CUA runtime change, plugin-policy change, or new capture dependency

Validation

  • pnpm run format:check
  • pnpm run i18n
  • pnpm run lint
  • pnpm run typecheck
  • pnpm exec electron-vite build
  • focused main regression suite: 161 passed, 1 skipped
  • full renderer suite: 1,625 passed
  • full main suite: 5,383 passed; one unrelated provider-metadata expectation fails identically in isolation and its code is unchanged

Remaining QA

  • packaged NativeKit native-overlay and Canvas-fallback runs
  • cross-platform interaction checks
  • p95 valid-result-to-visible measurement against the 150 ms budget

Summary by CodeRabbit

  • New Features
    • Added read-only Computer Use Picture-in-Picture showing the latest eligible snapshot, with native-to-Canvas fallback.
    • Added preview mode and close-only dismissal controls for both Browser and Computer Use, including safer run-scoped refresh after clicks.
  • Documentation
    • Added Computer Use preview specifications, implementation plan, and task/acceptance criteria, plus rollout guidance.
  • Tests
    • Expanded coverage for preview lifecycle, event/route contracts, stale-frame rejection, dismissal privacy rules, and shutdown/unmount behavior.

@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 1c5caae7-53b4-42a5-be9a-ab59eb08164b

📥 Commits

Reviewing files that changed from the base of the PR and between 861fd89 and 7119547.

📒 Files selected for processing (10)
  • docs/features/computer-use-snapshot-pip/plan.md
  • docs/features/computer-use-snapshot-pip/spec.md
  • src/main/desktop/computerUse/ComputerUsePreviewPresenter.ts
  • src/main/mcp/toolManager.ts
  • src/renderer/src/components/browser/AgentBrowserPiP.vue
  • src/renderer/src/components/computerUse/AgentComputerUsePiP.vue
  • test/main/desktop/computerUse/ComputerUsePreviewPresenter.test.ts
  • test/main/mcp/toolManager.test.ts
  • test/renderer/components/AgentBrowserPiP.test.ts
  • test/renderer/components/AgentComputerUsePiP.test.ts
🚧 Files skipped from review as they are similar to previous changes (10)
  • docs/features/computer-use-snapshot-pip/spec.md
  • src/renderer/src/components/browser/AgentBrowserPiP.vue
  • test/main/mcp/toolManager.test.ts
  • test/renderer/components/AgentBrowserPiP.test.ts
  • docs/features/computer-use-snapshot-pip/plan.md
  • src/renderer/src/components/computerUse/AgentComputerUsePiP.vue
  • test/main/desktop/computerUse/ComputerUsePreviewPresenter.test.ts
  • src/main/desktop/computerUse/ComputerUsePreviewPresenter.ts
  • test/renderer/components/AgentComputerUsePiP.test.ts
  • src/main/mcp/toolManager.ts

📝 Walkthrough

Walkthrough

Adds Computer Use latest-snapshot PiP with shared NativeKit coordination, trusted MCP snapshot observation, bounded JPEG processing, typed desktop contracts, Canvas fallback rendering, dismissal handling, and lifecycle/session staleness protections.

Changes

Computer Use Snapshot PiP

Layer / File(s) Summary
Feature contracts and rollout plan
docs/features/computer-use-snapshot-pip/*, docs/features/agent-browser-pip/spec.md, docs/architecture/nativekit-agent-browser-pip/spec.md
Documents define snapshot eligibility, identity propagation, shared ownership, image limits, fallback behavior, verification, and rollout rules.
Shared preview ownership and Browser integration
src/main/desktop/preview/AgentPreviewCoordinator.ts, src/main/desktop/browser/YoBrowserPresenter.ts, src/main/app/composition.ts
Browser and Computer Use previews share claim arbitration, NativeKit lifecycle, toolbar profiles, dismissal state, surface selection, and shutdown handling.
Trusted MCP snapshots and bounded presentation
src/main/mcp/*, src/main/tool/index.ts, src/main/desktop/computerUse/ComputerUsePreviewPresenter.ts
Trusted Computer Use calls propagate runId, schedule private post-click snapshots, validate and transform images, reject stale results, and present native or renderer frames.
Typed routes, events, and desktop wiring
src/shared/contracts/*, src/shared/types/*, src/main/desktop/routes.ts
Adds preview modes, surfaces, routes, events, presenter interfaces, payload validation, and active-session route gating.
Renderer clients and Canvas fallback
src/renderer/api/*, src/renderer/src/components/*, src/renderer/src/apps/chat-main/ChatTabView.vue
Adds typed preview clients and Browser/Computer Use PiP components with surface synchronization, Canvas frame decoding, dismissal, focus handling, dragging, and teardown.

Estimated code review effort: 5 (Critical) | ~90 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ToolManager
  participant ComputerUsePreviewPresenter
  participant AgentPreviewCoordinator
  participant AgentComputerUsePiP
  ToolManager->>ComputerUsePreviewPresenter: notify trusted snapshot started/completed
  ComputerUsePreviewPresenter->>ComputerUsePreviewPresenter: validate and transform latest image
  ComputerUsePreviewPresenter->>AgentPreviewCoordinator: prepare and present target
  AgentPreviewCoordinator-->>ComputerUsePreviewPresenter: native-overlay or renderer-canvas
  ComputerUsePreviewPresenter->>AgentComputerUsePiP: publish surface and Canvas frame events
  AgentComputerUsePiP->>ComputerUsePreviewPresenter: set mode or dismiss preview
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding a Computer Use snapshot preview PiP feature.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/computer-use-snapshot-pip

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@zerob13
zerob13 marked this pull request as ready for review July 28, 2026 07:09

@coderabbitai coderabbitai 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.

Actionable comments posted: 5

🧹 Nitpick comments (3)
src/main/desktop/preview/AgentPreviewCoordinator.ts (2)

157-167: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

releaseClaim leaves dismissedRuns entries behind.

dismissedRuns is only pruned inside claim() when a different runId claims the same session, so sessions that are dismissed and then torn down (e.g. destroySessionBrowserreleaseClaim) keep an entry until shutdown(). Consider deleting the dismissal when the matching claim is released.

♻️ Suggested cleanup
     if (
       claim &&
       (target.source == null || claim.source === target.source) &&
       (target.runId == null || claim.runId === target.runId)
     ) {
       this.claims.delete(target.sessionId)
+      if (this.dismissedRuns.get(target.sessionId) === claim.runId) {
+        this.dismissedRuns.delete(target.sessionId)
+      }
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/desktop/preview/AgentPreviewCoordinator.ts` around lines 157 - 167,
Update releaseClaim in AgentPreviewCoordinator to remove the session’s
dismissedRuns entry when the released target matches the current claim, using
the same source and runId matching conditions as claim deletion. Preserve
removeTarget behavior and avoid clearing dismissals for non-matching claims.

257-268: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

A single pushImage failure permanently disables the native overlay.

disableNative('frame_push_failed') sets unavailable = true and stops the overlay, so one transient push failure downgrades every subsequent preview (Browser and Computer Use) to renderer-canvas for the remaining process lifetime, with no recovery path. Consider tolerating a bounded number of consecutive failures (or a failure window) before disabling native permanently.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/desktop/preview/AgentPreviewCoordinator.ts` around lines 257 - 268,
The native overlay path currently permanently disables itself after one failed
pushImage call. Update the failure handling around pushImage and disableNative
to track consecutive failures or failures within a bounded window, reset that
tracking after a successful push, and call disableNative('frame_push_failed')
only after the configured threshold is reached.
src/renderer/src/components/computerUse/AgentComputerUsePiP.vue (1)

132-213: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift

Significant duplication with AgentBrowserPiP.vue (drag/positioning/preview-mode queue).

updateHostSize, clampPosition, onPointerDown/onPointerMove/onPointerUp/cancelDrag, and the pendingPreviewRequest/drainPreviewRequests single-flight queue are near line-for-line copies of the equivalent logic in AgentBrowserPiP.vue. Given both files already show subtle divergence (e.g. the unmount-queue gap above), extracting a shared composable (e.g. usePipDragging, usePreviewModeQueue) would reduce future drift risk. Not blocking for this PR.

Also applies to: 270-312

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/renderer/src/components/computerUse/AgentComputerUsePiP.vue` around lines
132 - 213, Extract the duplicated PiP dragging/positioning logic and
preview-mode request queue from AgentComputerUsePiP.vue and AgentBrowserPiP.vue
into shared composables, such as usePipDragging and usePreviewModeQueue. Update
both components to consume the shared implementations while preserving their
existing behavior, including queue draining and unmount cleanup.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/features/computer-use-snapshot-pip/plan.md`:
- Around line 308-319: Update the “Verification Commands” section to include the
required Electron build and complete main-suite validation commands, alongside
the existing checks. If those commands are maintained elsewhere, replace or
supplement the list with an explicit link to the authoritative commands in
tasks.md.

In `@src/main/desktop/computerUse/ComputerUsePreviewPresenter.ts`:
- Around line 180-234: Add cleanup for states whose host window is missing or
destroyed, including releasing any coordinator claim associated with the state.
Update the host validation/lookup flow around setPreviewMode and related state
access, or register a closed-window cleanup hook, so entries are removed even
when stopped is not sent. Preserve existing session and host-id checks while
ensuring stale state cannot block future claims.

In `@src/main/mcp/toolManager.ts`:
- Around line 903-951: Gate the implicit snapshot in
captureComputerUsePreviewSnapshot with the same checkToolPermission and
plugin-policy evaluation used for model-driven calls. Treat get_window_state as
read permission, and skip capture unless that permission is already granted for
the server/session; do not prompt or bypass policy. Keep
scheduleComputerUsePreviewAfterClick responsible only for eligibility and
scheduling.

In `@src/renderer/src/components/browser/AgentBrowserPiP.vue`:
- Around line 278-285: Update dismiss() in
src/renderer/src/components/browser/AgentBrowserPiP.vue#L278-L285 and
src/renderer/src/components/computerUse/AgentComputerUsePiP.vue#L172-L178 to
catch failures from the awaited dismissPreview client calls and log or report
the errors, preventing unhandled rejections while preserving the existing
early-return and UI state behavior.

In `@src/renderer/src/components/computerUse/AgentComputerUsePiP.vue`:
- Around line 393-402: Update the onBeforeUnmount teardown in
AgentComputerUsePiP.vue to submit the final 'stopped' preview-mode transition
through the existing pendingPreviewRequest/drainPreviewRequests single-flight
queue, using the same mechanism as syncPreviewMode and the session-switch
watcher. Preserve the current cleanup ordering and only enqueue the request when
currentSessionId.value exists.

---

Nitpick comments:
In `@src/main/desktop/preview/AgentPreviewCoordinator.ts`:
- Around line 157-167: Update releaseClaim in AgentPreviewCoordinator to remove
the session’s dismissedRuns entry when the released target matches the current
claim, using the same source and runId matching conditions as claim deletion.
Preserve removeTarget behavior and avoid clearing dismissals for non-matching
claims.
- Around line 257-268: The native overlay path currently permanently disables
itself after one failed pushImage call. Update the failure handling around
pushImage and disableNative to track consecutive failures or failures within a
bounded window, reset that tracking after a successful push, and call
disableNative('frame_push_failed') only after the configured threshold is
reached.

In `@src/renderer/src/components/computerUse/AgentComputerUsePiP.vue`:
- Around line 132-213: Extract the duplicated PiP dragging/positioning logic and
preview-mode request queue from AgentComputerUsePiP.vue and AgentBrowserPiP.vue
into shared composables, such as usePipDragging and usePreviewModeQueue. Update
both components to consume the shared implementations while preserving their
existing behavior, including queue draining and unmount cleanup.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 6aa2de99-92ce-44a4-a5f2-fef6d1ff2efd

📥 Commits

Reviewing files that changed from the base of the PR and between 082edea and 861fd89.

📒 Files selected for processing (40)
  • docs/architecture/nativekit-agent-browser-pip/spec.md
  • docs/features/agent-browser-pip/spec.md
  • docs/features/computer-use-snapshot-pip/plan.md
  • docs/features/computer-use-snapshot-pip/spec.md
  • docs/features/computer-use-snapshot-pip/tasks.md
  • src/main/app/composition.ts
  • src/main/desktop/browser/YoBrowserPresenter.ts
  • src/main/desktop/computerUse/ComputerUsePreviewPresenter.ts
  • src/main/desktop/preview/AgentPreviewCoordinator.ts
  • src/main/desktop/routes.ts
  • src/main/mcp/index.ts
  • src/main/mcp/toolManager.ts
  • src/main/tool/index.ts
  • src/renderer/api/BrowserClient.ts
  • src/renderer/api/ComputerUseClient.ts
  • src/renderer/src/apps/chat-main/ChatTabView.vue
  • src/renderer/src/components/browser/AgentBrowserPiP.vue
  • src/renderer/src/components/computerUse/AgentComputerUsePiP.vue
  • src/shared/contracts/events.ts
  • src/shared/contracts/events/browser.events.ts
  • src/shared/contracts/events/computerUse.events.ts
  • src/shared/contracts/routes.ts
  • src/shared/contracts/routes/browser.routes.ts
  • src/shared/contracts/routes/computerUse.routes.ts
  • src/shared/types/computerUse.ts
  • src/shared/types/desktop.ts
  • src/shared/types/mcp.ts
  • test/main/desktop/browser/AgentBrowserNativeOverlay.test.ts
  • test/main/desktop/browser/YoBrowserPresenter.test.ts
  • test/main/desktop/computerUse/ComputerUsePreviewPresenter.test.ts
  • test/main/desktop/preview/AgentPreviewCoordinator.test.ts
  • test/main/mcp/toolManager.test.ts
  • test/main/routes/contracts.test.ts
  • test/main/routes/dispatcher.test.ts
  • test/main/tool/toolService.test.ts
  • test/renderer/api/clients.test.ts
  • test/renderer/components/AgentBrowserPiP.test.ts
  • test/renderer/components/AgentComputerUsePiP.test.ts
  • test/renderer/components/ChatTabView.test.ts
  • test/setup.ts
💤 Files with no reviewable changes (1)
  • test/main/desktop/browser/AgentBrowserNativeOverlay.test.ts

Comment thread docs/features/computer-use-snapshot-pip/plan.md
Comment thread src/main/desktop/computerUse/ComputerUsePreviewPresenter.ts
Comment thread src/main/mcp/toolManager.ts
Comment thread src/renderer/src/components/browser/AgentBrowserPiP.vue Outdated
Comment thread src/renderer/src/components/computerUse/AgentComputerUsePiP.vue
@zerob13
zerob13 merged commit ae42069 into dev Jul 28, 2026
12 checks passed
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.

1 participant