Skip to content

Electron Viewer#217

Merged
arul28 merged 4 commits intomainfrom
ade/electron-viewer-0d20e278
Apr 30, 2026
Merged

Electron Viewer#217
arul28 merged 4 commits intomainfrom
ade/electron-viewer-0d20e278

Conversation

@arul28
Copy link
Copy Markdown
Owner

@arul28 arul28 commented Apr 30, 2026

Summary

Describe the change.

What Changed

Key files and behaviors.

Validation

How you tested.

Risks

Anything to watch.

Summary by CodeRabbit

  • New Features

    • Added App Control system for managing Electron applications—launch, screenshot, inspect elements, and interact via clicking/typing within the chat interface.
    • Introduced chat-scoped terminal management with persistent terminal sessions and dedicated terminal drawer in chat.
    • Added new CLI commands for app control and terminal operations.
  • CLI Updates

    • New app-control command suite for status, launch, connect, and interaction.
    • New terminal command for session management.

Greptile Summary

This PR introduces an App Control system that uses Chrome DevTools Protocol (CDP) over WebSocket to launch, screenshot, inspect, and interact with Electron applications from within the chat interface. It also adds chat-scoped terminal management (persistent PTY sessions visible in a new ChatTerminalDrawer) and corresponding CLI commands (app-control, terminal).

The core service (appControlService.ts) is well-structured: the sourceFileCache is correctly scoped per-instance (not module-level), the IPC rate-limit buckets now include a stale-entry sweep, and resolveTerminalId correctly returns null after clearing a stale cache entry — all previous review concerns appear addressed. Two P2-level observations remain around the free-port allocation race and an async window in the launch path.

Confidence Score: 5/5

Safe to merge; only P2-level quality observations, no blocking bugs found.

All findings are P2 (speculative timing issues). The three P1 items from previous review rounds are resolved. Trusted-sender assertion, rate limiting, and input validation are properly implemented on all new IPC handlers.

apps/desktop/src/main/services/appControl/appControlService.ts — findFreePort TOCTOU and concurrent-launch async gap.

Important Files Changed

Filename Overview
apps/desktop/src/main/services/appControl/appControlService.ts New 2050-line service wiring CDP over WebSocket to Electron renderers; well-structured with LRU-scoped source cache, rate-limited IPC guards, and screencast generation counters — two P2 concerns: TOCTOU on findFreePort and an async window where a concurrent launch can orphan a PTY.
apps/desktop/src/main/services/ipc/registerIpc.ts Adds appControl IPC handlers with trusted-sender assertion, per-channel rate-limit buckets with stale-entry sweep, and redaction of sensitive fields (command, env, typed text) from trace logs.
apps/desktop/src/main/services/pty/ptyService.ts Adds chat-scoped terminal management (activeTerminalByChatSession map, resolveTerminalId logic); stale-ID-return bug from previous review appears fixed — resolveTerminalId now returns null after deleting the stale cache entry.
apps/desktop/src/renderer/components/chat/ChatAppControlPanel.tsx New React component for launching, screencasting, and interacting with the controlled Electron app; scroll batching via RAF, non-passive wheel listener, and blank-frame detection are well-handled.
apps/desktop/src/renderer/components/chat/ChatTerminalDrawer.tsx New chat-scoped terminal drawer with per-session tab management and proper cleanup on unmount.
apps/ade-cli/src/bootstrap.ts Wires AppControlService into AdeRuntime using a late-bound agentChatServiceHolder to break the initialization-order cycle between appControlService and agentChatService.
apps/desktop/src/main/services/adeActions/registry.ts Adds app_control to the action domain list and allowlist; scroll/dispatchKey/listTargets/attachToTarget are intentionally omitted from the allowlist (UI-only operations).

Sequence Diagram

sequenceDiagram
    participant UI as ChatAppControlPanel
    participant IPC as registerIpc (main)
    participant ACS as AppControlService
    participant PTY as PtyService
    participant CDP as CdpClient (WebSocket)
    participant App as Electron App

    UI->>IPC: appControlLaunch(command, env)
    IPC->>IPC: assertTrustedSender + rateLimit
    IPC->>ACS: launch(args)
    ACS->>ACS: findFreePort()
    ACS->>ACS: resolveLaunch(args, port)
    ACS->>PTY: create({ startupCommand, env })
    PTY-->>ACS: { sessionId, ptyId }
    ACS->>ACS: startCdpPoller(sessionId, port)
    App-->>CDP: binds --remote-debugging-port
    loop CDP poll (500ms)
        ACS->>App: GET /json/list
        App-->>ACS: CdpTarget[]
        ACS->>CDP: CdpClient.connect(wsUrl)
        ACS->>ACS: updateSession(connected)
        ACS->>CDP: Page.startScreencast
    end
    CDP-->>ACS: Page.screencastFrame events
    ACS-->>UI: appControlEvent(frame)
    UI->>IPC: appControlClick(x, y)
    IPC->>ACS: click(args)
    ACS->>CDP: Input.dispatchMouseEvent (press+release)
    UI->>IPC: appControlGetSnapshot()
    IPC->>ACS: getSnapshot()
    ACS->>CDP: Runtime.evaluate(domSnapshotScript)
    CDP-->>ACS: DomSnapshotPayload
    ACS-->>UI: AppControlSnapshot (elements + screenshot)
Loading

Fix All in Claude Code

Prompt To Fix All With AI
Fix the following 2 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 2
apps/desktop/src/main/services/appControl/appControlService.ts:238-252
**TOCTOU race in `findFreePort` can silently bind the wrong port**

`findFreePort` opens a temporary server to claim a port, then closes it and returns the number. Between the server closing and Electron binding `--remote-debugging-port=<port>`, any other process can claim the same port. The CDP poller will wait indefinitely if the port is stolen, giving the user only the long timeout message `"Waiting for CDP on 127.0.0.1:${port}"` with no indication that a port collision occurred. Adding a validation step—e.g., re-probing the port after a short delay to confirm the app owns it—or logging a warning when `listCdpTargets` keeps returning nothing after a reasonable window would make failures easier to diagnose.

### Issue 2 of 2
apps/desktop/src/main/services/appControl/appControlService.ts:1382-1437
**Orphaned PTY if a second `launch` arrives during `ptyService.create()`**

`activeSession` is set to the new (unstarted) session at line 1382, and `ptyService.create()` is then awaited. During that `await`, a concurrent `launch` or `stop` call sees a session with `terminalSessionId: null`, sends no signal (the `if (terminalSessionId && …)` guard is false), and sets `activeSession = null`. When the original `create()` resolves, `updateSession(…)` finds `activeSession` is null and returns null, so the newly spawned terminal is never recorded on any session — it runs silently until the user restarts ADE. Rate-limiting on IPC makes this unlikely in the renderer path, but agent-driven CLI calls share no such throttle. A simple guard (e.g., a per-service `launchInProgress` flag or a monotonic `launchEpoch` check around `updateSession`) would close the window.

Reviews (3): Last reviewed commit: "ship: iteration 1 — address coderabbit r..." | Re-trigger Greptile

@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 30, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
ade Ignored Ignored Preview Apr 30, 2026 3:54am

@arul28
Copy link
Copy Markdown
Owner Author

arul28 commented Apr 30, 2026

@copilot review but do not make fixes

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 30, 2026

📝 Walkthrough

Walkthrough

Introduces a comprehensive app control system enabling desktop Electron application orchestration via Chrome DevTools Protocol, along with chat-scoped terminal support. New appControlService manages session lifecycle, screenshot streaming, DOM inspection, and input dispatch. Adds corresponding CLI surfaces (ade terminal, ade app-control), IPC layer, React UI components, and persists chat-terminal associations to the database.

Changes

Cohort / File(s) Summary
CLI & Bootstrap
apps/ade-cli/src/bootstrap.ts, apps/ade-cli/src/cli.ts, apps/ade-cli/src/cli.test.ts
Adds appControlService to AdeRuntime with resolveLaneId wiring; introduces terminal and app-control CLI command surfaces covering launch, connect, screenshot, DOM snapshot, element inspection/selection, click/type input, and terminal management; includes comprehensive plan-construction unit tests for new CLI actions and aliases.
App Control Service
apps/desktop/src/main/services/appControl/appControlService.ts, apps/desktop/src/main/services/appControl/appControlService.test.ts
Implements core app control orchestration via CDP and WebSocket: session launch/connect/stop, live JPEG screencast polling, element detection via DOM evaluation, source-code snippet matching, and input dispatch (click, type, key, scroll). Includes full test suite mocking HTTP/WebSocket with concurrent attachment and fallback interaction flows.
Main Process Integration
apps/desktop/src/main/main.ts, apps/desktop/src/main/services/adeActions/registry.ts, apps/desktop/src/main/services/ipc/registerIpc.ts
Wires appControlService into project initialization and ADE runtime; exposes terminal and app_control ADE action domains with per-domain allowlisting; adds strict IPC handlers for app-control launch/connect/screenshot/snapshot/interactions and chat-terminal operations with trusted-renderer verification and rate limiting.
PTY & Session Services
apps/desktop/src/main/services/pty/ptyService.ts, apps/desktop/src/main/services/pty/ptyService.test.ts, apps/desktop/src/main/services/sessions/sessionService.ts
Adds chat-scoped terminal tracking: PTY entries store chatSessionId, active-terminal mappings per chat session with automatic reassignment on close; new APIs listTerminals/activeForChat/readTerminal/writeTerminal/signalTerminal resolve targets by explicit ID or chatSessionId. Session service persists/surfaces chatSessionId with new setChatSessionId API and emits "created" event.
Database & Persistence
apps/desktop/src/main/services/state/kvDb.ts, apps/ios/ADE/Resources/DatabaseBootstrap.sql, apps/ios/ADE/Services/Database.swift
Adds chat_session_id column and index to terminal_sessions with migration support; extends iOS models/persistence to encode/decode and store chatSessionId field with backward compatibility for legacy payloads.
Preload & Global Types
apps/desktop/src/preload/global.d.ts, apps/desktop/src/preload/preload.ts, apps/desktop/src/shared/types/appControl.ts
Defines comprehensive app-control type contracts (status, session, screenshot, snapshot, elements, events) and terminal types; exposes window.ade.appControl and window.ade.terminal namespaces via preload with all control/query/subscription methods.
Renderer Components
apps/desktop/src/renderer/components/chat/AgentChatPane.tsx, apps/desktop/src/renderer/components/chat/AgentChatComposer.tsx, apps/desktop/src/renderer/components/chat/ChatAppControlPanel.tsx, apps/desktop/src/renderer/components/chat/ChatTerminalDrawer.tsx, apps/desktop/src/renderer/components/terminals/SessionListPane.tsx, apps/desktop/src/renderer/components/terminals/SessionCard.tsx
Adds ChatAppControlPanel component for full app-control UI (launch, connect, screenshot viewing, Control/Inspect modes with click/type/select); integrates app-control context into AgentChatPane/AgentChatComposer alongside iOS context; enhances ChatTerminalDrawer with chatSessionId-scoped tab restoration and app-control status awareness; refactors SessionListPane for parent-child terminal grouping by chat session; adds compact mode to SessionCard.
Shared Types & Guidance
apps/desktop/src/shared/types/sessions.ts, apps/desktop/src/shared/types/index.ts, apps/desktop/src/shared/ipc.ts, apps/desktop/src/shared/adeCliGuidance.ts, apps/desktop/scripts/dev.cjs
Extends TerminalSessionSummary and PtyCreateArgs with chatSessionId; adds chat-terminal API types; expands IPC channel definitions; updates CLI guidance with app-control workflow/examples; enhances developer script with CDP port precedence and TCP probing helper.
iOS Support
apps/ios/ADE/Models/RemoteModels.swift, apps/ios/ADETests/ADETests.swift
Extends TerminalSessionSummary model and adds schema migration for chat_session_id persistence; adds test validating Codable encode/decode and backward compatibility with legacy payloads.
Test Mocks
apps/desktop/src/renderer/components/chat/AgentChatPane.submit.test.tsx, apps/desktop/src/renderer/components/terminals/WorkStartSurface.tsx
Adds window.ade.terminal mock API to test harness; minor layout change (removes mx-auto centering).

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

Suggested labels

desktop, ios, chat, cli, feature

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 1.72% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Electron Viewer' is vague and does not clearly convey the primary changes in this changeset, which include app control service, terminal support, CLI commands, and multiple cross-layer integrations. Replace the vague title with a specific summary of the main feature. Consider something like 'Add Electron app control service with terminal integration' or 'Introduce app-control CLI domain and desktop service'.
✅ Passed checks (3 passed)
Check name Status Explanation
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 ade/electron-viewer-0d20e278

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 and usage tips.

Comment thread apps/desktop/src/main/services/pty/ptyService.ts
Comment thread apps/desktop/src/main/services/appControl/appControlService.ts Outdated
Comment thread apps/desktop/src/main/services/ipc/registerIpc.ts
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 13

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/desktop/src/renderer/components/terminals/SessionListPane.tsx (1)

239-319: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Keep child nesting bucket-local in time/status views.

childrenByParentId and excludedTopLevelIds are derived from allSessions, then reused for every grouping mode. If a parent is in running but its child is in ended/older, the child gets removed from its own bucket and either shows under the wrong header or disappears entirely when the parent bucket is collapsed. Build the child map from the current rendered list, or limit this nesting behavior to by-lane.

Also applies to: 320-360, 427-440

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src/renderer/components/terminals/SessionListPane.tsx` around
lines 239 - 319, childrenByParentId and excludedTopLevelIds are built from
allSessions and reused for every grouping mode which causes children to be moved
or hidden when parent and child fall into different time/status buckets; change
the logic so the child map is built from the currently rendered session list for
the active grouping mode (e.g. for time/status views use sessions derived from
timeBuckets or the current bucket list, while preserving full nesting only for
by-lane views using sessionsGroupedByLane). Update the computation sites for
childrenByParentId and excludedTopLevelIds to accept a source array (or create
two variants) so expandSessionWithChildren and collectVisibleIds operate against
the grouping-local child map, and ensure their use in the time/status render
paths uses the grouping-local map rather than the global allSessions-derived
map.
🧹 Nitpick comments (2)
apps/ade-cli/src/cli.test.ts (1)

1314-1439: ⚡ Quick win

Add one bootstrap-level App Control smoke test.

These assertions stop at buildCliPlan(), but the new app-control / terminal surface only really pays off once bootstrap.ts picks the desktop socket transport and dispatches the RPC. One small end-to-end CLI test there would catch routing regressions this suite cannot see.

Based on learnings: "For ADE CLI changes, verify both headless mode and the desktop socket-backed ADE RPC path."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/ade-cli/src/cli.test.ts` around lines 1314 - 1439, Add a bootstrap-level
smoke test that goes beyond buildCliPlan by invoking the application's bootstrap
entry (bootstrap.ts) to exercise the desktop-socket ADE RPC path: start the
bootstrap process (the module/function that selects transports in bootstrap.ts),
point it at a test desktop socket transport or a mocked desktop socket, then run
a real CLI invocation equivalent to buildCliPlan(["app-control","status"]) and
assert the RPC reached App Control (e.g., that the dispatched RPC action/domain
is "app_control"/"getStatus" and returned/caused the expected execute step).
Ensure the test also runs the headless path for parity; reference buildCliPlan
and the bootstrap startup code in bootstrap.ts to wire the transport selection
and teardown the bootstrap process after the assertion.
apps/desktop/src/main/services/appControl/appControlService.test.ts (1)

127-133: ⚡ Quick win

Tear down the polling service between tests.

Each case leaves a connected service running under fake timers. That background health poll can consume mockState.httpResponses or emit WebSocket traffic during a later test, which makes the suite order-dependent. Track created services and dispose them in teardown, or at least clear timers after each test.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src/main/services/appControl/appControlService.test.ts` around
lines 127 - 133, Tests are leaving the background health polling service running
under fake timers; track any services created by appControlService in the tests
and ensure they are torn down in afterEach by calling their stop/dispose method
(e.g., service.dispose() or service.stopPolling()) and by clearing fake timers
with vi.clearAllTimers() and restoring with vi.useRealTimers(); additionally
reset mockState arrays as already done to avoid cross-test interference.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/ade-cli/src/bootstrap.ts`:
- Around line 483-497: In resolveLaneId, the chatSession lookup currently uses
sessionService.get (which reads terminal_sessions) and attachedRootPath uses
only equality; change the chat lookup to use the chat-session store/service
(e.g., chatSessionService.get or chatSessionStore.get) instead of
sessionService.get so chatSessionId resolves actual ADE chat sessions, and
update the lane match logic in the lanes.find loop to mirror the worktreePath
descendant check for attachedRootPath (use path.resolve and
startsWith(`${attachedRootPath}${path.sep}`) in addition to exact equality) so
--cwd inside attached lane subdirectories matches correctly; keep using
laneService.list to load lanes and preserve the existing fallback behavior.

In `@apps/ade-cli/src/cli.ts`:
- Around line 2359-2364: readTrailingCommand currently rebuilds the post---
command by joining tokens which destroys quoted boundaries; change
readTrailingCommand to return string[] | null (preserve original tokens) by
doing const trailing = args.slice(index + 1); args.splice(index); return
trailing.length ? trailing : null, and update all callers of readTrailingCommand
(e.g., launch code that expects a single string) to accept the array and either
pass it directly as argv to child_process.spawn/execFile or only join with
spaces when intentionally invoking a shell.

In `@apps/desktop/scripts/dev.cjs`:
- Around line 204-207: The current validation of remoteDebugPortRaw using
Number.parseInt allows malformed values like "9222abc" and doesn't enforce the
TCP port range; update the check around remoteDebugPortRaw/remoteDebugPort to
first ensure remoteDebugPortRaw is a pure decimal string (e.g. /^\d+$/) before
parsing, then parse with base 10 and assert Number.isFinite(remoteDebugPort) and
that remoteDebugPort is between 1 and 65535 inclusive, otherwise throw the same
Error with the original raw value; apply this to the block that defines
remoteDebugPort and the related error creation.

In `@apps/desktop/src/main/main.ts`:
- Around line 2728-2733: The lane matching predicate in matchingLane currently
only checks exact equality for attachedRootPath, which misses cases where
targetRoot is a subdirectory of the attached root; update the predicate inside
lanes.find (the arrow function using worktreePath, attachedRootPath, and
targetRoot) to also treat targetRoot as belonging to the attached root when it
starts with `${attachedRootPath}${path.sep}` (and guard for attachedRootPath
being null), mirroring the existing startsWith logic used for worktreePath so
subdirectories of attachedRootPath resolve to the correct lane.

In `@apps/desktop/src/main/services/appControl/appControlService.ts`:
- Around line 1246-1276: stop() is marking terminal-backed sessions as "stopped"
immediately after signaling the PTY; instead, when terminalSessionId is present
and args.ptyService is used, keep the session in "stopping" and do not set
activeSession to "stopped" or emit the final "session-stopped" here — let the
PTY exit callback (the existing PTY/terminal exit handler) set the final
"stopped" | "exited" | "failed" state and emit session-updated/session-stopped.
Concretely: in stop() (reference symbols: stop, activeSession,
terminalSessionId, args.ptyService.signalTerminal, stopScreencast, emit) avoid
creating stoppedSession/setting activeSession to stopped and avoid emitting
"session-stopped" when terminalSessionId is truthy; instead preserve "stopping",
return the previous session in its stopping state, and ensure the PTY exit
handler is responsible for publishing final session status.

In `@apps/desktop/src/main/services/ipc/registerIpc.ts`:
- Around line 5815-5864: Handlers ipcMain.handle for IPC.appControlScroll,
IPC.appControlAttachToTarget and IPC.appControlDispatchKey are currently
coercing bad payloads instead of rejecting them; update each handler to validate
inputs and fail closed: for appControlScroll use Number.isFinite on numeric
fields returned by numberOr and reject if any required numeric is NaN/Infinity,
for appControlAttachToTarget treat rawTargetId as valid only when it's a
non-empty string and reject otherwise (do not convert missing to ""), and for
appControlDispatchKey check rawType against the allowed set
("keyDown","keyUp","rawKeyDown","char") and reject unknown values rather than
defaulting to "keyDown"; in all three handlers call the existing App Control
validators (via ensureAppControl() validation API) or throw a bad-payload error
so the request is rejected instead of synthesizing inputs.

In `@apps/desktop/src/main/services/pty/ptyService.test.ts`:
- Around line 1811-1821: The test currently checks exclusion of the other-chat
terminal by matching a string pattern; instead capture the terminal returned by
service.create for the "C" chat (e.g., const c = await service.create(...)) and
assert the list of terminal IDs from service.listTerminals({ chatSessionId:
"chat-1" }) does not contain that actual terminal/session id (compare
c.sessionId to the mapped terminalId values), and keep the final assertion that
list[0]?.terminalId equals b.sessionId to verify sorting.

In `@apps/desktop/src/main/services/pty/ptyService.ts`:
- Around line 1265-1284: resolveTerminalId can return a stale dead
activeTerminalId after clearing activeTerminalByChatSession; update the final
branch so that when no live replacement is found you delete the stale map entry
and return null (not the old activeTerminalId). In practice, keep the existing
checks (cleanOptionalId, liveEntryBySessionId(activeTerminalId), replacement
logic using ptys and createdAt) but change the last line in resolveTerminalId to
return null after activeTerminalByChatSession.delete(chatSessionId) so callers
like readTerminal({ chatSessionId }) won't receive a closed session id.

In `@apps/desktop/src/renderer/components/chat/AgentChatComposer.tsx`:
- Around line 2619-2636: The badge for the App Control count is absolutely
positioned but its toggle button is not a positioned ancestor, causing the pill
to drift; update the AgentChatComposer button (the element using
onToggleAppControl, appControlOpen and rendering the Desktop icon and
appControlContextItems) to be a positioned ancestor by adding a positioning
class (e.g., Tailwind "relative") to the button's className so the absolutely
positioned badge anchors to the button.

In `@apps/desktop/src/renderer/components/chat/AgentChatPane.tsx`:
- Around line 1562-1582: When the companionStateKey changes the UI is rehydrated
but the App Control context (appControlContextItems) remains global and can leak
between chats; update the companion-state-change logic (around
companionHydrationKeyRef, companionStateKey, readChatCompanionUiState,
writeChatCompanionUiState) to also reset or re-key the App Control context:
either include companionStateKey when storing/reading appControlContextItems or
explicitly clear/reset appControlContextItems (via whatever setter e.g.
setAppControlContextItems or equivalent) in the same effect that reads the saved
UI state so the new chat starts with an empty App Control context.

In `@apps/desktop/src/renderer/components/chat/ChatAppControlPanel.tsx`:
- Around line 1192-1257: The inspect overlay math currently always uses
snapshot.screenshot.width/height causing drift when the <img> shows the live
screencast; create a small helper (e.g., getDisplayedImageDims or
computeDisplayedDims) that returns the displayed image width/height and scale
based on liveFrameActive and liveFrameDimsRef (fall back to
screenshot.width/height when not live), then update the onMouseMove hit-test
(the anonymous handler that sets hoveredElementId), and all overlay style
computations for selectedElement, hoverElement and selectedPoint to use that
helper's width/height/scale instead of snapshot.screenshot.width/height so
hover/selection coordinates match the visible image (also ensure
handleImageClick uses the same helper or liveFrameDimsRef as it already does).

In `@apps/desktop/src/renderer/components/chat/ChatTerminalDrawer.tsx`:
- Around line 262-301: The effects treat revealRequest as sticky truthy state
causing stale requests to block auto-create; add a handledRevealNonceRef (or
lastHandledChatKey) to record the nonce/chat-key from revealRequest when you
consume it in the reveal-handling useEffect (the block that builds nextEntry and
setsActiveTabId) and update both useEffect checks to ignore revealRequest whose
nonce equals handledRevealNonceRef.current so revealRequest is edge-triggered:
when you handle a revealRequest set handledRevealNonceRef to its nonce (or chat
key) and in the auto-create useEffect only bail out for revealRequest if its
nonce is different from handledRevealNonceRef.current; ensure this ref is
consulted alongside pendingAutoCreateRef, creatingTab, restoringTabs and when
creating a tab clear/consume the nonce so subsequent opens can auto-create
normally.

---

Outside diff comments:
In `@apps/desktop/src/renderer/components/terminals/SessionListPane.tsx`:
- Around line 239-319: childrenByParentId and excludedTopLevelIds are built from
allSessions and reused for every grouping mode which causes children to be moved
or hidden when parent and child fall into different time/status buckets; change
the logic so the child map is built from the currently rendered session list for
the active grouping mode (e.g. for time/status views use sessions derived from
timeBuckets or the current bucket list, while preserving full nesting only for
by-lane views using sessionsGroupedByLane). Update the computation sites for
childrenByParentId and excludedTopLevelIds to accept a source array (or create
two variants) so expandSessionWithChildren and collectVisibleIds operate against
the grouping-local child map, and ensure their use in the time/status render
paths uses the grouping-local map rather than the global allSessions-derived
map.

---

Nitpick comments:
In `@apps/ade-cli/src/cli.test.ts`:
- Around line 1314-1439: Add a bootstrap-level smoke test that goes beyond
buildCliPlan by invoking the application's bootstrap entry (bootstrap.ts) to
exercise the desktop-socket ADE RPC path: start the bootstrap process (the
module/function that selects transports in bootstrap.ts), point it at a test
desktop socket transport or a mocked desktop socket, then run a real CLI
invocation equivalent to buildCliPlan(["app-control","status"]) and assert the
RPC reached App Control (e.g., that the dispatched RPC action/domain is
"app_control"/"getStatus" and returned/caused the expected execute step). Ensure
the test also runs the headless path for parity; reference buildCliPlan and the
bootstrap startup code in bootstrap.ts to wire the transport selection and
teardown the bootstrap process after the assertion.

In `@apps/desktop/src/main/services/appControl/appControlService.test.ts`:
- Around line 127-133: Tests are leaving the background health polling service
running under fake timers; track any services created by appControlService in
the tests and ensure they are torn down in afterEach by calling their
stop/dispose method (e.g., service.dispose() or service.stopPolling()) and by
clearing fake timers with vi.clearAllTimers() and restoring with
vi.useRealTimers(); additionally reset mockState arrays as already done to avoid
cross-test interference.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2a016dd1-a6f0-4f12-8e8e-d3656826e10b

📥 Commits

Reviewing files that changed from the base of the PR and between ae6b7cb and 8241f6e.

⛔ Files ignored due to path filters (6)
  • docs/ARCHITECTURE.md is excluded by !docs/**
  • docs/features/chat/README.md is excluded by !docs/**
  • docs/features/chat/composer-and-ui.md is excluded by !docs/**
  • docs/features/computer-use/README.md is excluded by !docs/**
  • docs/features/computer-use/app-control.md is excluded by !docs/**
  • docs/features/terminals-and-sessions/README.md is excluded by !docs/**
📒 Files selected for processing (33)
  • apps/ade-cli/src/bootstrap.ts
  • apps/ade-cli/src/cli.test.ts
  • apps/ade-cli/src/cli.ts
  • apps/desktop/scripts/dev.cjs
  • apps/desktop/src/main/main.ts
  • apps/desktop/src/main/services/adeActions/registry.ts
  • apps/desktop/src/main/services/appControl/appControlService.test.ts
  • apps/desktop/src/main/services/appControl/appControlService.ts
  • apps/desktop/src/main/services/chat/agentChatService.ts
  • apps/desktop/src/main/services/ipc/registerIpc.ts
  • apps/desktop/src/main/services/pty/ptyService.test.ts
  • apps/desktop/src/main/services/pty/ptyService.ts
  • apps/desktop/src/main/services/sessions/sessionService.ts
  • apps/desktop/src/main/services/state/kvDb.ts
  • apps/desktop/src/preload/global.d.ts
  • apps/desktop/src/preload/preload.ts
  • apps/desktop/src/renderer/components/chat/AgentChatComposer.tsx
  • apps/desktop/src/renderer/components/chat/AgentChatPane.submit.test.tsx
  • apps/desktop/src/renderer/components/chat/AgentChatPane.tsx
  • apps/desktop/src/renderer/components/chat/ChatAppControlPanel.tsx
  • apps/desktop/src/renderer/components/chat/ChatTerminalDrawer.tsx
  • apps/desktop/src/renderer/components/terminals/SessionCard.tsx
  • apps/desktop/src/renderer/components/terminals/SessionListPane.tsx
  • apps/desktop/src/renderer/components/terminals/WorkStartSurface.tsx
  • apps/desktop/src/shared/adeCliGuidance.ts
  • apps/desktop/src/shared/ipc.ts
  • apps/desktop/src/shared/types/appControl.ts
  • apps/desktop/src/shared/types/index.ts
  • apps/desktop/src/shared/types/sessions.ts
  • apps/ios/ADE/Models/RemoteModels.swift
  • apps/ios/ADE/Resources/DatabaseBootstrap.sql
  • apps/ios/ADE/Services/Database.swift
  • apps/ios/ADETests/ADETests.swift

Comment thread apps/ade-cli/src/bootstrap.ts
Comment thread apps/ade-cli/src/cli.ts
Comment thread apps/desktop/scripts/dev.cjs
Comment thread apps/desktop/src/main/main.ts
Comment thread apps/desktop/src/main/services/appControl/appControlService.ts Outdated
Comment thread apps/desktop/src/main/services/pty/ptyService.ts
Comment thread apps/desktop/src/renderer/components/chat/AgentChatComposer.tsx
Comment thread apps/desktop/src/renderer/components/chat/AgentChatPane.tsx
Comment thread apps/desktop/src/renderer/components/chat/ChatAppControlPanel.tsx Outdated
Comment thread apps/desktop/src/renderer/components/chat/ChatTerminalDrawer.tsx Outdated
@arul28 arul28 force-pushed the ade/electron-viewer-0d20e278 branch from 8241f6e to 8976756 Compare April 30, 2026 03:34
@arul28
Copy link
Copy Markdown
Owner Author

arul28 commented Apr 30, 2026

@copilot review but do not make fixes

…edaction/validation, app-control coord system, terminal session lifecycle)
@arul28
Copy link
Copy Markdown
Owner Author

arul28 commented Apr 30, 2026

@copilot review but do not make fixes

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