fix(web-desktop): mirror a web-started Auto Run into the desktop app - #1520
Conversation
Auto Run is renderer-owned state, so a run lives entirely in the client that started it. #1508 taught a web-desktop browser tab to RENDER a run owned by the desktop, but only in that direction: `autorun_state` is a WebSocket packet and the Electron app is not a WebSocket client, so a run started IN the browser had no path back. The desktop drew the agent as idle for the whole run. Main now forwards a bridge-originated `web:broadcastAutoRunState` straight to the desktop windows on `remote:autoRunStateMirror`, the channel the mirror hook already consumes, and the hook drops its web-desktop-only gate so the desktop has a consumer for it. Forwarding is narrowed to frames that arrived over the WebSocket bridge. A desktop-owned run must never be echoed back to its owner: the owning window would receive its own state and could stamp its live run `mirrored: true`, disabling Stop, Skip, Resume and Abort on the window actually driving it. It also deliberately bypasses `safeSend`, whose bridge fanout would deliver the same frame to web clients a second time on a second channel. This fixes visibility only. A run still dies with the client that owns it (#1470), which needs the renderer ownership primitive that issue is blocked on.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Team Run ID: 📒 Files selected for processing (4)
Included review availability: Your plan provides up to 8 included reviews per hour; 7 remain after this review. 📝 WalkthroughWalkthroughAuto Run state from web-desktop bridge invokes is now forwarded to Electron desktop windows. The renderer mirror hook consumes forwarded state in all builds while preserving local-run ownership behavior. Tests cover forwarding, filtering, clearing, destroyed windows, send failures, and absent web servers. ChangesAuto Run state mirroring
Estimated code review effort: 3 (Moderate) | ~20 minutes Merge Risk: ⚪ Minimal · up to Browser-originated Auto Run state is now reflected in Desktop windows without echoing Desktop-owned runs. The change is ready to merge with no identified merge-blocking risk. Sequence Diagram(s)sequenceDiagram
participant WebDesktop as Web-desktop bridge
participant MainProcess as Main process
participant DesktopWindow as Electron desktop window
participant MirrorHook as useAutoRunStateMirror
WebDesktop->>MainProcess: Invoke web:broadcastAutoRunState
MainProcess->>DesktopWindow: Send remote:autoRunStateMirror
DesktopWindow->>MirrorHook: Deliver session ID and state
MirrorHook-->>DesktopWindow: Update or reap mirrored state
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Full details: Linked Issues checkExplanation The PR implements the visibility synchronization objective from issue
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
Greptile SummaryThis PR makes web-desktop-owned Auto Runs visible in Electron by forwarding bridge-originated state frames directly to desktop renderers and enabling the existing mirror consumer in both builds.
Confidence Score: 5/5The PR appears safe to merge, with the new reverse mirror path preserving owner controls and existing read-only behavior. No actionable failures remain: bridge-originated frames reach desktop consumers, desktop-originated frames are excluded, per-window failures are isolated, and mirror consumption does not create a broadcast feedback loop. Important Files Changed
Sequence DiagramsequenceDiagram
participant Web as Web-desktop owner
participant Bridge as WebSocket bridge
participant Main as Electron main
participant Desktop as Desktop renderer
participant Peers as Web-desktop peers
Web->>Bridge: bridge.invoke(web:broadcastAutoRunState)
Bridge->>Main: handler(FAKE_EVENT, sessionId, state)
Main->>Main: update Auto Run tracker
Main->>Desktop: remote:autoRunStateMirror
Main->>Peers: autorun_state
Desktop->>Desktop: apply read-only mirrored state
Reviews (1): Last reviewed commit: "fix(web-desktop): mirror a web-started A..." | Re-trigger Greptile |
closes #1519
The report
Start an Auto Run from the web-desktop browser client and the desktop app never shows the agent as Auto Run-active, though it is running. Close the browser and the next queued task may never start. Starting from the desktop shows correctly in the browser.
Two symptoms, two different bugs
The reporter's instinct that these "likely share one root cause" is right about the root cause and wrong about the fix boundary, so this PR deliberately only takes the first.
Symptom (a), the desktop showing nothing, is a real gap and is fixed here.
Symptom (b), the run dying when the web client closes, is #1470 and is not fixed here. The run loop is a live async closure with
letcursors inside it; when the client holding it goes away, the loop goes with it. Re-claiming an orphaned run needs a renderer liveness/ownership primitive that does not exist yet, which is exactly what #1470 is open on. Shipping a guess there can have two clients spawning tasks into the same working tree.Root cause of (a)
Auto Run is renderer-owned state, so a run lives entirely in whichever client pressed Go. #1508 taught a web-desktop tab to RENDER a run owned by the desktop, but only in that direction:
web:broadcastAutoRunState.autorun_stateWebSocket packet.So a run started in a browser tab had no path back to the desktop at all.
useAutoRunStateMirroreven encoded the assumption, gating both its effects onisWebDesktop(), and the preload comment said "In the Electron desktop app nothing sends it". True of the channel, wrong about the need.The change
main/ipc/handlers/web.ts-forwardAutoRunStateToDesktopWindows()sends a bridge-originated broadcast straight to the desktop windows onremote:autoRunStateMirror, the channel the mirror hook already consumes. One consumer, two producers.useAutoRunStateMirrordrops itsisWebDesktop()gate so the desktop has a consumer.The echo, which is the part worth reviewing
The forward is narrowed to frames that arrived over the WebSocket bridge, identified by the synthetic
FAKE_EVENT(type: 'bridge') thathandleBridgeInvokecalls ipcMain handlers with.A desktop-owned run must never be echoed back to its owner. The owning window would receive its own state and
applyAutoRunMirrorFramecould stamp its own live runmirrored: true- and every Auto Run mutator refuses a mirrored entry, so Stop, Skip, Resume and Abort would go dead on the window actually driving the run. The renderer has its own ownership guard (existing?.isRunning === true && !isMirror), but the cheapest way to be certain is not to send the echo at all. Both layers are now in place.It also deliberately bypasses
safeSend, which fans every push out to the bridge as well - that would deliver this frame to web clients a second time, on a second channel, when they already receive it asautorun_state.Deliberately unchanged
startBatchRun's existingisMirroredBatchRunbackstop also stops the desktop launching a second loop against an agent the browser is already running. That is the intended interaction, not a side effect.Targeting
rc, notmainsrc/web-desktop/anduseAutoRunStateMirror.tsdo not exist onmain- the whole feature is rc-only.Validation
npm run lintclean (all three tsconfigs).npx eslintclean on every changed file.npm run test- 42,091 passed, 0 failures.nullteardown reaching the desktop, destroyed-window skip, one window throwing not costing the others, and the forward surviving Live Mode having no server attached.Local runs are single-OS; both CI matrix legs still need to be green.
Summary by CodeRabbit
New Features
Bug Fixes