Skip to content

Ade Web App Launch Via Cli#381

Merged
arul28 merged 2 commits into
mainfrom
ade/ade-web-app-launch-via-cli-e16fe5c4
May 27, 2026
Merged

Ade Web App Launch Via Cli#381
arul28 merged 2 commits into
mainfrom
ade/ade-web-app-launch-via-cli-e16fe5c4

Conversation

@arul28
Copy link
Copy Markdown
Owner

@arul28 arul28 commented May 27, 2026

Summary

Describe the change.

What Changed

Key files and behaviors.

Validation

How you tested.

Risks

Anything to watch.

ADE   Open in ADE  ·  ade/ade-web-app-launch-via-cli-e16fe5c4 branch  ·  PR #381

Summary by CodeRabbit

Release Notes

New Features

  • Added Codex thread goal card UI with objective editing, status tracking, and token usage display in chat subagents panel.
  • Enhanced subagent transcript rendering with typed timeline cards for reasoning, plans, commands, file changes, and web searches.
  • Improved built-in browser window management with per-window isolation.

Bug Fixes

  • Fixed environment variable handling for runtime build hash and color output preferences.

UI/Styling

  • Refreshed chat control panel, browser panel, and iOS simulator panel styling.
  • Introduced reusable work surface header component for improved consistency.

Refactors

  • Extracted CLI session header as dedicated component for better modularity.

Review Change Stack

Greptile Summary

This PR introduces per-window isolation for the built-in browser service, a new WorkSurfaceHeader shared component consumed by both chat and CLI session headers, and a rich typed-card timeline for Codex/Cursor subagent transcripts with a new CodexGoalCard for tracking thread objectives.

  • Built-in browser per-window isolation: createBuiltInBrowserService now manages a Map<windowId, service> so each Electron window gets its own browser state; IPC handlers pass the resolved BrowserWindow as sourceWindow, and events are sent directly to the originating window's renderer instead of broadcasting.
  • Unified WorkSurfaceHeader: A new shared header component replaces the separate inline implementations in AgentChatPane and the removed WorkCliSessionHeader; both CliSessionWorkSurfaceHeader and AgentChatPane now compose it, with the stop-session action correctly forwarded through CliSurfaceTrailingActions.
  • Codex transcript typed cards + goal card: agentChatService maps Codex thread items to AgentChatEvent-shaped transcript messages with deterministic UUIDs; AgentChatMessageList renders them through a typed timeline; CodexGoalCard is surfaced in ChatSubagentsPanel and replaces the in-chat CodexGoalBanner.

Confidence Score: 4/5

Safe to merge after fixing the CodexGoalCard Escape/blur interaction bug.

The built-in browser refactor and WorkSurfaceHeader unification are well-structured. The one concrete defect is in CodexGoalCard: pressing Escape fires onBlur on the unmounting textarea before React commits the batched setDraft(objective) reset, so submitEdit runs with the stale modified draft and calls onEdit — the cancel does the opposite of what the user intended. The same mechanism causes a double /goal set dispatch on Enter.

apps/desktop/src/renderer/components/chat/codex/CodexGoalCard.tsx — the onBlur/onKeyDown interaction needs a guard to prevent submitEdit from firing after cancelEdit.

Important Files Changed

Filename Overview
apps/desktop/src/renderer/components/chat/codex/CodexGoalCard.tsx New Codex thread goal card with inline editing; the onBlur={submitEdit} pattern causes Escape to accidentally submit edits and Enter to double-submit.
apps/desktop/src/main/services/builtInBrowser/builtInBrowserService.ts Refactored to a per-window service registry; each BrowserWindow gets an isolated browser service, with clean disposal via the closed event listener.
apps/desktop/src/main/services/ipc/registerIpc.ts guardBuiltInBrowserIpc now returns the BrowserWindow and all built-in browser IPC handlers pass it as sourceWindow, enabling per-window isolation.
apps/desktop/src/renderer/components/terminals/WorkViewArea.tsx Replaces WorkCliSessionHeader with CliSessionWorkSurfaceHeader (including lanes prop); stop action props are now correctly forwarded. WorkTab accessibility improved by moving role/tabIndex to the outer div.
apps/desktop/src/renderer/components/terminals/CliSessionWorkSurfaceHeader.tsx New header component replacing WorkCliSessionHeader; CliSurfaceTrailingActions preserves the stop button, info, and kebab actions.
apps/desktop/src/main/services/chat/agentChatService.ts Adds Codex thread transcript rendering (typed cards) and plan-mode sync via system:status messages; UUID fallback is now deterministic.
apps/desktop/src/renderer/components/chat/AgentChatMessageList.tsx Adds rich typed-card timeline for subagent transcripts (reasoning, plan, command, file_change, web_search cards); falls back gracefully to role+text for Claude SDK transcripts.
apps/desktop/src/renderer/components/work/WorkSurfaceHeader.tsx New shared header component used by both AgentChatPane and CLI session surfaces; clean abstraction of title + lane chip + cache badge + git toolbar.
apps/desktop/src/renderer/components/chat/ChatSubagentsPanel.tsx Adds goal, onEditGoal, and onClearGoal props; renders CodexGoalCard at the top of the subagents panel for Codex sessions.
apps/desktop/src/main/main.ts onEvent callback now accepts a targetWindow and sends events directly to that window's renderer rather than broadcasting to all windows.

Sequence Diagram

sequenceDiagram
    participant R as Renderer (IPC)
    participant G as guardBuiltInBrowserIpc
    participant M as createBuiltInBrowserService (multi-window)
    participant W as WindowBrowserService (per-window)
    participant E as Electron BrowserWindow

    R->>G: IPC call (e.g. builtInBrowserNavigate)
    G->>G: validate sender URL + rate limit
    G-->>R: return BrowserWindow (win)
    R->>M: navigate(args, win)
    M->>M: serviceFor(win) → windowServices.get(win.id)
    alt window service exists
        M->>W: navigate(args)
    else new window
        M->>W: createBuiltInBrowserWindowService()
        M->>E: win.once("closed", closedListener)
        M->>W: navigate(args)
    end
    W-->>R: BuiltInBrowserStatus
    Note over M,E: on win "closed": delete from map, dispose service
Loading

Comments Outside Diff (3)

  1. apps/desktop/src/renderer/components/terminals/WorkViewArea.tsx, line 606-626 (link)

    P1 Stop button removed for running agent CLI sessions

    SessionSurface still accepts onStopRunningSession and stopping in its prop interface (lines 582–583), and callers at lines 1370–1371 and 1726–1727 pass them in, but neither prop is forwarded to CliSessionWorkSurfaceHeader. The old WorkCliSessionHeader rendered a StopCircle button whenever session.status === "running" && Boolean(session.ptyId) && Boolean(onStopRunningSession) — that affordance is now gone. Users running a Claude Code, Codex CLI, or Cursor CLI session have no header-level way to terminate it; the only stop path left is the context menu (kebab), which is not as discoverable. The two props in SessionSurface are effectively dead code at this point.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: apps/desktop/src/renderer/components/terminals/WorkViewArea.tsx
    Line: 606-626
    
    Comment:
    **Stop button removed for running agent CLI sessions**
    
    `SessionSurface` still accepts `onStopRunningSession` and `stopping` in its prop interface (lines 582–583), and callers at lines 1370–1371 and 1726–1727 pass them in, but neither prop is forwarded to `CliSessionWorkSurfaceHeader`. The old `WorkCliSessionHeader` rendered a `StopCircle` button whenever `session.status === "running" && Boolean(session.ptyId) && Boolean(onStopRunningSession)` — that affordance is now gone. Users running a Claude Code, Codex CLI, or Cursor CLI session have no header-level way to terminate it; the only stop path left is the context menu (kebab), which is not as discoverable. The two props in `SessionSurface` are effectively dead code at this point.
    
    How can I resolve this? If you propose a fix, please make it concise.

    Fix in Claude Code

  2. apps/desktop/src/main/services/chat/agentChatService.ts, line 213-219 (link)

    P2 Non-deterministic UUID fallback for items without an id

    When a Codex thread item has no id (or an empty string), randomUUID() is called to generate a fallback. Because getSubagentTranscript can be called multiple times for the same session (e.g. on reopen or scroll-back), items that lack server-assigned IDs will receive a different UUID on every fetch. Downstream consumers key on uuid to deduplicate or reconcile transcript entries, so two fetches of the same itemless item would appear as two distinct transcript messages. Replacing the fallback with a stable hash or composite key (e.g. codex-thread-item:${threadId}:${itemIndex}) would make the fallback deterministic without requiring the server to provide an id.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: apps/desktop/src/main/services/chat/agentChatService.ts
    Line: 213-219
    
    Comment:
    **Non-deterministic UUID fallback for items without an `id`**
    
    When a Codex thread item has no `id` (or an empty string), `randomUUID()` is called to generate a fallback. Because `getSubagentTranscript` can be called multiple times for the same session (e.g. on reopen or scroll-back), items that lack server-assigned IDs will receive a different UUID on every fetch. Downstream consumers key on `uuid` to deduplicate or reconcile transcript entries, so two fetches of the same itemless item would appear as two distinct transcript messages. Replacing the fallback with a stable hash or composite key (e.g. `codex-thread-item:${threadId}:${itemIndex}`) would make the fallback deterministic without requiring the server to provide an id.
    
    How can I resolve this? If you propose a fix, please make it concise.

    Fix in Claude Code

  3. apps/desktop/src/renderer/components/terminals/WorkViewArea.tsx, line 568-583 (link)

    P1 onStopRunningSession / stopping are dead props in SessionSurface

    Both props are still declared in SessionSurface's interface (lines 582-583), destructured in its body (lines 568-569), and passed by every caller in this file (e.g. lines 1372-1373 and 1729-1730), but the switch to CliSessionWorkSurfaceHeader dropped the forwarding — the new header accepts neither. The old WorkCliSessionHeader rendered a StopCircle button whenever session.status === "running" + onStopRunningSession was provided; that affordance is now gone. Users running a Claude Code, Codex CLI, or Cursor CLI session have no header-level way to stop it. Consider either forwarding the props to CliSessionWorkSurfaceHeader / CliSurfaceTrailingActions, or removing them from SessionSurface's interface if the stop action is intentionally retired.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: apps/desktop/src/renderer/components/terminals/WorkViewArea.tsx
    Line: 568-583
    
    Comment:
    **`onStopRunningSession` / `stopping` are dead props in `SessionSurface`**
    
    Both props are still declared in `SessionSurface`'s interface (lines 582-583), destructured in its body (lines 568-569), and passed by every caller in this file (e.g. lines 1372-1373 and 1729-1730), but the switch to `CliSessionWorkSurfaceHeader` dropped the forwarding — the new header accepts neither. The old `WorkCliSessionHeader` rendered a `StopCircle` button whenever `session.status === "running"` + `onStopRunningSession` was provided; that affordance is now gone. Users running a Claude Code, Codex CLI, or Cursor CLI session have no header-level way to stop it. Consider either forwarding the props to `CliSessionWorkSurfaceHeader` / `CliSurfaceTrailingActions`, or removing them from `SessionSurface`'s interface if the stop action is intentionally retired.
    
    How can I resolve this? If you propose a fix, please make it concise.

    Fix in Claude Code

Fix All in Claude Code

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

---

### Issue 1 of 1
apps/desktop/src/renderer/components/chat/codex/CodexGoalCard.tsx:162-190
**`onBlur={submitEdit}` fires after Escape, ignoring the cancellation**

`cancelEdit` queues `setEditing(false)` + `setDraft(objective)` via React's batched state updates. Those updates are not committed until *after* the keydown handler returns. When React commits and removes the textarea from the DOM, the browser fires `blur`, which calls `submitEdit`. At that point, the closure's `draft` still holds the user's modified text (the `setDraft(objective)` batch hasn't been applied yet), so `next !== objective` passes and `onEdit` is called — sending `/goal set <modified text>` even though the user pressed Escape to cancel. The same mechanism also causes a double `/goal set` when Enter is pressed (once from `onKeyDown`, once from the blur on unmount).

The simplest fix is to track intent with a `useRef` that is flipped synchronously before the async state update, and guard `onBlur` against calling `submitEdit` after a cancel.

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

Greptile also left 1 inline comment on this PR.

@vercel
Copy link
Copy Markdown

vercel Bot commented May 27, 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 May 27, 2026 9:40pm

@arul28
Copy link
Copy Markdown
Owner Author

arul28 commented May 27, 2026

@copilot review but do not make fixes

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 27, 2026

Warning

Review limit reached

@arul28, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 27 minutes and 5 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 3fa83701-85ab-4b8d-8597-bd775b9ab061

📥 Commits

Reviewing files that changed from the base of the PR and between d7dac0b and e7c81a3.

📒 Files selected for processing (5)
  • .agents/skills/ade-web/SKILL.md
  • apps/desktop/src/main/services/chat/agentChatService.ts
  • apps/desktop/src/renderer/components/chat/AgentChatMessageList.tsx
  • apps/desktop/src/renderer/components/terminals/CliSessionWorkSurfaceHeader.tsx
  • apps/desktop/src/renderer/components/terminals/WorkViewArea.tsx
📝 Walkthrough

Walkthrough

This PR refactors the built-in browser service for multi-window support, enhances chat with Codex transcript fetching and plan-mode transitions, consolidates terminal headers into a shared component, and updates environment handling across daemon spawning and PTY configuration.

Changes

Built-in browser multi-window support

Layer / File(s) Summary
Browser service window-scoped architecture
apps/desktop/src/main/services/builtInBrowser/builtInBrowserService.ts, apps/desktop/src/main/services/builtInBrowser/builtInBrowserService.test.ts
createBuiltInBrowserService now manages per-window browser services via a windowServices map with activeWindowId tracking and a lazily-created fallbackService; all operations route through serviceFor(sourceWindow?) to select the correct instance; window lifecycle/disposal handle per-window closed listeners and service cleanup.
IPC handler validation and window passing
apps/desktop/src/main/services/ipc/registerIpc.ts, apps/desktop/src/main/main.ts
guardBuiltInBrowserIpc updated to return the validated BrowserWindow instead of void; all IPC.builtInBrowser* handlers refactored to pass the validated window into ensureBuiltInBrowser() service methods; onEvent handler accepts optional targetWindow and sends directly to that window's webContents when present, otherwise broadcasts to all windows.

Chat agent Codex transcript and plan-mode updates

Layer / File(s) Summary
Codex app-server transcript and plan-mode handling
apps/desktop/src/main/services/chat/agentChatService.ts, apps/desktop/src/main/services/chat/agentChatService.test.ts
agentChatService adds codexThreadItemToTranscriptMessages and fetchCodexSubagentTranscriptFromAppServer for paginated app-server fetch via thread/turns/list; system:status handler extracts permissionMode and emits plan-mode transition notices; transcript fetch logic prefers live app-server pull for Codex runtimes with fallback to event-history filtering.
CodexGoalCard component for goal display
apps/desktop/src/renderer/components/chat/codex/CodexGoalCard.tsx, apps/desktop/src/renderer/components/chat/codex/CodexGoalCard.test.tsx
New component displays Codex thread goal with status tone, token usage/budget progress bar, elapsed time; supports edit/clear callbacks with textarea and keyboard handling; includes helpers for formatting tokens and elapsed seconds, mapping goal status to UI classes.
Chat pane integrates Codex goal in subagent panel
apps/desktop/src/renderer/components/chat/ChatSubagentsPanel.tsx, apps/desktop/src/renderer/components/chat/AgentChatPane.tsx
ChatSubagentsPanel extended to accept goal/onEditGoal/onClearGoal props and render CodexGoalCard when objective exists; AgentChatPane removes CodexGoalBanner and passes goal wiring to ChatSubagentsPanel; selectedSubagentPaneAvailable updated to show panel when goal exists.
AgentChatMessageList rich timeline cards for subagent events
apps/desktop/src/renderer/components/chat/AgentChatMessageList.tsx
Partitions transcript entries into richEntries (typed via readSubagentEvent) and legacyEntries; renders rich timeline cards for reasoning/text/plan/command/file_change/web_search/codex_image_generation/subagent events via SubagentTimelineCard switch.

Terminal work-surface header consolidation

Layer / File(s) Summary
Shared WorkSurfaceHeader component
apps/desktop/src/renderer/components/work/WorkSurfaceHeader.tsx, apps/desktop/src/renderer/components/work/WorkSurfaceHeader.test.tsx
New reusable header layout exports constants and WorkSurfaceHeaderProps interface supporting optional lane chip (with click handler), Claude cache badge, git toolbar, and trailing actions; includes comprehensive test suite for conditional rendering and interactions.
CliSessionWorkSurfaceHeader replaces WorkCliSessionHeader
apps/desktop/src/renderer/components/terminals/CliSessionWorkSurfaceHeader.tsx, apps/desktop/src/renderer/components/terminals/CliSessionWorkSurfaceHeader.test.tsx
New component adapts TerminalSessionSummary into WorkSurfaceHeader with Claude cache badge gating, lane navigation, QuickRunMenu for lane-scoped actions, and Info/context-menu buttons via CliSurfaceTrailingActions; old WorkCliSessionHeader removed.
WorkViewArea lanes propagation and tab semantics
apps/desktop/src/renderer/components/terminals/WorkViewArea.tsx, apps/desktop/src/renderer/components/terminals/WorkViewArea.test.tsx
ClosedCliSessionSurface and SessionSurface extended with lanes prop and pass it to new CLI header; WorkTab changed to role="tab" with keyboard handler; leftTrailing wrapped in AnimatePresence; tabStripCenter consolidates tab/grouped rendering; test mocks updated.
CSS layout updates for work glass header and lane band
apps/desktop/src/renderer/index.css, apps/desktop/src/renderer/components/terminals/TerminalsPage.tsx
TerminalsPage adds ResizeObserver to measure Sessions pane width and write --ade-sessions-pane-w CSS variable; .ade-work-glass-header-left uses variable for width/min-width; .ade-work-lane-band refines spacing/background/radius; .ade-work-lane-band-collapse and .ade-work-lane-band-tabs adjusted.

Environment variable and build hash management

Layer / File(s) Summary
Runtime build hash explicit removal in daemon spawning
apps/ade-cli/src/cli.ts, apps/ade-cli/src/tuiClient/connection.ts, apps/ade-cli/src/stdioRpcDaemon.test.ts
ADE_RUNTIME_BUILD_HASH explicitly deleted from daemon environment when runtimeBuildHash/buildHash unavailable, ensuring environment never carries stale values.
CLI env snapshot and macOS VM env fixes
apps/desktop/src/main/services/cli/adeCliService.ts, apps/desktop/src/main/services/macosVm/macosVmService.ts
adeCliService snapshots env once instead of per-variable fallbacks; macosVmService removes redundant process.env.ADE_HOME fallback and normalizes path comparison in leaseMatchesCurrentProjectLane.
PTY terminal and remote bootstrap updates
apps/desktop/src/main/services/pty/ptyService.ts, apps/desktop/src/main/services/remoteRuntime/remoteBootstrap.test.ts
ptyService conditionally removes FORCE_COLOR when NO_COLOR explicitly provided but FORCE_COLOR not; remoteBootstrap.test.ts passes explicit layout to buildRemoteRuntimeEnvironmentPrefix calls.

Chat UI panel styling and development tooling

Layer / File(s) Summary
Chat control panel and simulator panel styling
apps/desktop/src/renderer/components/chat/ChatAppControlPanel.tsx, apps/desktop/src/renderer/components/chat/ChatBuiltInBrowserPanel.tsx, apps/desktop/src/renderer/components/chat/ChatIosSimulatorPanel.tsx
Tailwind class updates across control panel buttons/inputs, browser panel tab bar/toolbars (loading spinner indicator, removed bottom context bar), and iOS simulator layout/controls; no functional logic changes.
ADE web skill and browser snapshot export
.agents/skills/ade-web/SKILL.md, apps/desktop/scripts/export-browser-mock-ade-snapshot.mjs
New skill documentation for running ADE as Vite-only web preview on port 5173; export-browser-mock-ade-snapshot.mjs adds resolveWorktreeParentRoot helper for database snapshot resolution.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes


Possibly related PRs

  • arul28/ADE#243: Both PRs modify the built-in browser integration at the main-service/IPC boundaries, particularly createBuiltInBrowserService behavior and ensureBuiltInBrowser() service method signatures.
  • arul28/ADE#301: Both PRs modify apps/desktop/src/main/services/chat/agentChatService.ts around Codex turn-scoped behavior and plan-mode/permission-mode logic.
  • arul28/ADE#120: Both PRs touch chat and terminal UI surfaces including AgentChatPane.tsx, AgentChatMessageList.tsx, and work-surface/session-related components.

Suggested labels

desktop

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ade/ade-web-app-launch-via-cli-e16fe5c4

@capy-ai
Copy link
Copy Markdown

capy-ai Bot commented May 27, 2026

Capy auto-review is paused for this organization because the monthly auto-review limit has been reached. Increase the limit or turn it off in billing settings to resume automatic reviews.

Copy link
Copy Markdown
Contributor

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

PR Review

Scope: 39 file(s), +2780 / −849
Verdict: Minor issues

This PR refactors Work/CLI/chat headers, scopes the built-in browser per Electron window, adds Codex goal + subagent transcript plumbing, and syncs Claude plan-mode from SDK status messages. The multi-window browser work and IPC routing look sound; the main gaps are incomplete subagent timeline rendering and heavy Codex transcript polling.


🐛 Functionality

[High] Codex subagent drill-in drops tool and user messages

File: apps/desktop/src/renderer/components/chat/AgentChatMessageList.tsx:L3804-L3871
Issue: codexThreadItemToTranscriptMessages maps Codex mcpToolCall / dynamicToolCall and userMessage items into tool_call and user_message events, but SubagentTimelineCard has no cases for those types and returns null in the default branch. Drill-in transcripts can look empty or incomplete even when the app-server returned data.
Repro: Open a Codex subagent whose thread is mostly tool calls (typical delegation). Events land in richEntries, but nothing renders.
Fix: Add tool_call and user_message cases (reuse CommandEventCard / existing user row patterns), or route unhandled types through the legacy role+text path instead of silently skipping.


⚡ Performance

[Medium] Subagent transcript polling re-fetches full Codex thread every 1.5s

File: apps/desktop/src/main/services/chat/agentChatService.ts:L22826-L22874 and apps/desktop/src/renderer/components/chat/AgentChatPane.tsx:L2465-L2494
Issue: While a subagent is running, the pane polls getSubagentTranscript every 1.5s without a limit. For live Codex runtimes, fetchCodexSubagentTranscriptFromAppServer walks up to 10 pages × 50 turns (itemsView: "full"), converts every item, then slices—so each poll can issue many app-server round-trips and parse large payloads even when the UI only needs the tail.
Impact: Noticeable main-process + Codex load during drill-in on long subagent threads (~6–7 full paginated fetches per 10s of viewing).
Fix: Pass a small limit from the renderer (e.g. last 100 messages), stop early once enough items are collected, and/or only paginate on first open then append deltas from eventHistoryBySession on subsequent polls.


🎨 UI/UX

[Low] Browser panel no longer shows what element is selected

File: apps/desktop/src/renderer/components/chat/ChatBuiltInBrowserPanel.tsx:L1735-L1806 (removed block)
Issue: The footer that showed selected element label, selector/URL, attachment ack, Clear, and Insert draft was removed. Inspect/Attach still work, but users cannot see which element is selected or confirm attachment without inferring from toolbar state.
Fix: Restore a compact status row (or show selection summary next to the Attach control) when selectedItem is set.

[Low] Browser tab controls are below comfortable touch size

File: apps/desktop/src/renderer/components/chat/ChatBuiltInBrowserPanel.tsx:L1493-L1533
Issue: Tab rows are h-[18px] with 8–11px icons and a 16px new-tab control—well under the 40px touch target guideline used elsewhere in Work surfaces.
Fix: Increase tab hit area (padding/min-height) while keeping the visual chrome compact, or add a touch-friendly density mode.

[Low] Codex goal edits commit on blur

File: apps/desktop/src/renderer/components/chat/codex/CodexGoalCard.tsx:L149-L155
Issue: The goal textarea calls submitEdit on onBlur, so tabbing away or clicking elsewhere sends /goal set … even when the user did not intend to save.
Fix: Commit only on explicit Enter (without Shift) or a Save control; treat blur as cancel unless the draft changed and was confirmed.


Notes

  • Per-window built-in browser routing (builtInBrowserService.ts + registerIpc.ts passing BrowserWindow) and targeted builtInBrowserEvent delivery in main.ts are solid improvements for multi-window ADE.
  • Removing the in-chat CodexGoalBanner in favor of ChatSubagentsPanel + CodexGoalCard is coherent; Agents tab gating on selectedCodexGoal is updated accordingly.
  • CLI header stop control removal is documented in tests as intentional (sidebar/context menu path); not flagged as a regression.
  • Could not fetch PR body via gh (401); review is based on the provided SHAs and local diff 880c47790e83a6a8cc561f0d324c29826cbcaa56...8a177ecd24ab27f2853c96b85220d60f2af0e7ae.
Open in Web View Automation 

Sent by Cursor Automation: BUGBOT in Versic

@arul28 arul28 force-pushed the ade/ade-web-app-launch-via-cli-e16fe5c4 branch from 8a177ec to 182ec15 Compare May 27, 2026 20:39
@arul28
Copy link
Copy Markdown
Owner Author

arul28 commented May 27, 2026

@copilot review but do not make fixes

Refactor Work tab surfaces to support web app launching from CLI sessions.
Consolidate chat panel components, add Codex goal card rendering, fix CLI
service command resolution, and sync plan-mode transitions from SDK status
messages.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
@arul28 arul28 force-pushed the ade/ade-web-app-launch-via-cli-e16fe5c4 branch from 182ec15 to d7dac0b Compare May 27, 2026 21:08
@arul28
Copy link
Copy Markdown
Owner Author

arul28 commented May 27, 2026

@copilot review but do not make fixes

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: 6

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/chat/AgentChatMessageList.tsx (1)

3910-3993: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Keep rich and legacy transcript entries in their original order.

This partition renders all rich cards first and all legacy rows second. If a transcript mixes ADE events with legacy SDK messages, the drill-in loses chronology and becomes misleading.

Suggested fix
-  const richEntries: Array<{
-    key: string;
-    event: AgentChatEvent;
-  }> = [];
-  const legacyEntries: Array<{
-    key: string;
-    message: import("../../../shared/types").AgentChatSubagentTranscriptMessage;
-  }> = [];
-  for (let i = 0; i < messages.length; i += 1) {
-    const m = messages[i];
-    const key = m.uuid ?? `idx-${i}`;
-    const event = readSubagentEvent(m);
-    if (event) {
-      richEntries.push({ key, event });
-    } else {
-      legacyEntries.push({ key, message: m });
-    }
-  }
+  const entries = messages.map((message, index) => ({
+    key: message.uuid ?? `idx-${index}`,
+    event: readSubagentEvent(message),
+    message,
+    index,
+  }));
 
-  if (richEntries.length === 0 && legacyEntries.length === 0) {
+  if (entries.length === 0) {
     return (
       <div className={cn("flex min-h-0 flex-1 flex-col px-8 pt-12 font-sans", className)}>
         <p className="text-[13.5px] leading-6 text-fg/55">
           {loading ? "Listening for the first message…" : "No messages from this subagent yet."}
@@
-      {richEntries.length ? (
-        <ol className="mx-auto w-full max-w-3xl px-6 pb-6 pt-5">
-          {richEntries.map((entry) => (
-            <SubagentTimelineCard key={entry.key} event={entry.event} />
-          ))}
-        </ol>
-      ) : null}
-      {legacyEntries.length ? (
-        <ol className="mx-auto w-full max-w-3xl px-6 pb-10 pt-2">
-          {legacyEntries.map((entry, index) => {
+      <ol className="mx-auto w-full max-w-3xl px-6 pb-10 pt-5">
+        {entries.map((entry) => {
+          if (entry.event) {
+            return <SubagentTimelineCard key={entry.key} event={entry.event} />;
+          }
+
             const text = extractSubagentMessageText(entry.message);
             const role: string = entry.message.type;
             const isUser = role === "user";
             const isSystem = role === "system";
@@
             return (
               <li
-                key={entry.key ?? `${index}-${role}`}
+                key={entry.key ?? `${entry.index}-${role}`}
                 className="grid grid-cols-[88px_minmax(0,1fr)] gap-x-5 border-b border-white/[0.03] py-3 last:border-b-0"
               >
@@
               </li>
             );
-          })}
-        </ol>
-      ) : null}
+        })}
+      </ol>
🤖 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 `@apps/desktop/src/renderer/components/chat/AgentChatMessageList.tsx` around
lines 3910 - 3993, The current code splits messages into richEntries and
legacyEntries which loses original chronology; instead build a single ordered
entries array while iterating messages (e.g. push {key, index: i, kind: "rich",
event} or {key, index: i, kind: "legacy", message}) using readSubagentEvent to
classify, then render by mapping that unified entries array in order and for
each entry conditionally render SubagentTimelineCard(entry.event) for kind ===
"rich" or the legacy row (using extractSubagentMessageText, role/type handling
and the same markup) for kind === "legacy"; remove the separate
richEntries/legacyEntries rendering blocks so messages keep their original
ordering.
🤖 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 @.agents/skills/ade-web/SKILL.md:
- Around line 100-101: Update the guidance that currently reads “Never touch the
ADE socket” to a precise constraint: state that consumers must not start,
replace, or modify the runtime/bridge socket, but may use an existing socket for
browser actions (e.g., when running `ade actions ... --socket`); mention
`browserMock.ts` and `window.ade` as the Vite-only preview stub and clarify it
does not open a runtime connection so operators won’t block legitimate
`--socket` flows. Ensure the new wording replaces the old sentence and
explicitly communicates what changed, what is blocked (starting/replacing the
socket/bridge), and the allowed next action (use existing socket for browser
actions).

In `@apps/desktop/src/main/services/builtInBrowser/builtInBrowserService.ts`:
- Around line 119-123: serviceForWindow() currently disposes fallbackService
before creating a new per-window service which loses any tabs/state that were
created via navigate/showPanel/createTab without a sourceWindow; modify
serviceForWindow/createServiceForWindow/attachToWindow flow to detect an
existing fallbackService and migrate its state into the first created per-window
service instead of disposing it (e.g., transfer tabs, active index, history,
etc.), or alternatively make navigate/showPanel/createTab reject/mutate calls
until a window is bound; specifically update serviceForWindow() to check
fallbackService and call a new transferFallbackState(fallbackService, service)
before setting fallbackService = null, and ensure windowServices.set(win.id,
service) and closedListener logic remain intact so the migrated state persists
for that window (same change also apply to the similar block around lines
160-163).

In `@apps/desktop/src/main/services/chat/agentChatService.ts`:
- Around line 22863-22866: The loop that pages through transcript items uses a
hard MAX_PAGES = 10 and silently returns a partial transcript (variables:
cursor, pages, MAX_PAGES, collected, and the surrounding fetch loop), so change
the logic to avoid silent truncation by making the cap configurable and by
returning an explicit truncation indicator: replace the fixed MAX_PAGES constant
with a configurable limit or remove it, and if the loop must stop because pages
reached the limit set a boolean (e.g. truncated = true) and include it in the
function's return value or throw a clear error; ensure the caller no longer
infers completeness from collected.length and instead checks the new
truncated/hasMore flag (update all code paths that read collected and the
function signature/return type accordingly).
- Around line 22651-22658: The fallback item id should be deterministic instead
of randomUUID() so repeated fetches keep stable IDs; in
codexThreadItemToTranscriptMessages replace the randomUUID() fallback with a
deterministic id derived from stable inputs (for example, serialize stable
fields of item plus threadId and turnId and compute a hash like SHA-256 or a
stable base64 encoding) and use that result as itemId; apply the same
deterministic-fallback change to the other similar occurrence around the codex
handling at the second block mentioned (the lines around the other occurrence)
so both places generate identical stable ids for items missing an id.

In `@apps/desktop/src/renderer/components/chat/AgentChatMessageList.tsx`:
- Around line 3785-3806: SubagentResultRow currently ignores event.status
(subagent_result.status) and always renders the green "Final result" success
card; update SubagentResultRow to read event.status and branch rendering: for
successful status keep the existing emerald card and text, but for "failed" or
"stopped" render a neutral/error style (e.g., red/gray border/background and an
appropriate label/icon and message) and surface the same summary/description if
present; ensure you reference the same event variable (and its
summary/description fallback logic) so the content stays identical while the
visual outcome matches event.status — apply the same status-aware rendering for
the similar block around the other occurrence noted (the block at the other
SubagentResultRow usage).

In
`@apps/desktop/src/renderer/components/terminals/CliSessionWorkSurfaceHeader.tsx`:
- Around line 48-99: The CLI header removed the stop control — restore a stop
affordance in CliSurfaceTrailingActions by adding an optional onStopClick prop
(e.g., onStopClick?: (session: TerminalSessionSummary, event:
ReactMouseEvent<HTMLElement>) => void) and rendering a Stop button (inside a
SmartTooltip with label like "Stop session") when the session is running (check
a session flag such as session.laneId or session.state === 'running' /
session.isRunning). Wire the button’s onClick to call onStopClick?.(session,
event), set aria-label="Stop session", and disable the button when onStopClick
is not provided; apply the same change to the other header component instance
mentioned (lines 107-173) so running agent CLI sessions regain a header stop
path.

---

Outside diff comments:
In `@apps/desktop/src/renderer/components/chat/AgentChatMessageList.tsx`:
- Around line 3910-3993: The current code splits messages into richEntries and
legacyEntries which loses original chronology; instead build a single ordered
entries array while iterating messages (e.g. push {key, index: i, kind: "rich",
event} or {key, index: i, kind: "legacy", message}) using readSubagentEvent to
classify, then render by mapping that unified entries array in order and for
each entry conditionally render SubagentTimelineCard(entry.event) for kind ===
"rich" or the legacy row (using extractSubagentMessageText, role/type handling
and the same markup) for kind === "legacy"; remove the separate
richEntries/legacyEntries rendering blocks so messages keep their original
ordering.
🪄 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: 75b85fc5-45ee-4107-ad69-15f663f47c47

📥 Commits

Reviewing files that changed from the base of the PR and between 1c3cedb and d7dac0b.

⛔ Files ignored due to path filters (5)
  • AGENTS.md is excluded by !*.md
  • docs/ARCHITECTURE.md is excluded by !docs/**
  • docs/features/chat/README.md is excluded by !docs/**
  • docs/features/terminals-and-sessions/README.md is excluded by !docs/**
  • docs/features/terminals-and-sessions/ui-surfaces.md is excluded by !docs/**
📒 Files selected for processing (34)
  • .agents/skills/ade-web/SKILL.md
  • apps/ade-cli/src/cli.ts
  • apps/ade-cli/src/stdioRpcDaemon.test.ts
  • apps/ade-cli/src/tuiClient/connection.ts
  • apps/desktop/scripts/export-browser-mock-ade-snapshot.mjs
  • apps/desktop/src/main/main.ts
  • apps/desktop/src/main/services/builtInBrowser/builtInBrowserService.test.ts
  • apps/desktop/src/main/services/builtInBrowser/builtInBrowserService.ts
  • apps/desktop/src/main/services/chat/agentChatService.test.ts
  • apps/desktop/src/main/services/chat/agentChatService.ts
  • apps/desktop/src/main/services/cli/adeCliService.ts
  • apps/desktop/src/main/services/ipc/registerIpc.ts
  • apps/desktop/src/main/services/macosVm/macosVmService.ts
  • apps/desktop/src/main/services/pty/ptyService.ts
  • apps/desktop/src/main/services/remoteRuntime/remoteBootstrap.test.ts
  • apps/desktop/src/renderer/components/chat/AgentChatMessageList.tsx
  • apps/desktop/src/renderer/components/chat/AgentChatPane.tsx
  • apps/desktop/src/renderer/components/chat/ChatAppControlPanel.tsx
  • apps/desktop/src/renderer/components/chat/ChatBuiltInBrowserPanel.test.tsx
  • apps/desktop/src/renderer/components/chat/ChatBuiltInBrowserPanel.tsx
  • apps/desktop/src/renderer/components/chat/ChatIosSimulatorPanel.tsx
  • apps/desktop/src/renderer/components/chat/ChatSubagentsPanel.tsx
  • apps/desktop/src/renderer/components/chat/codex/CodexGoalCard.test.tsx
  • apps/desktop/src/renderer/components/chat/codex/CodexGoalCard.tsx
  • apps/desktop/src/renderer/components/terminals/CliSessionWorkSurfaceHeader.test.tsx
  • apps/desktop/src/renderer/components/terminals/CliSessionWorkSurfaceHeader.tsx
  • apps/desktop/src/renderer/components/terminals/TerminalsPage.tsx
  • apps/desktop/src/renderer/components/terminals/WorkCliSessionHeader.test.tsx
  • apps/desktop/src/renderer/components/terminals/WorkCliSessionHeader.tsx
  • apps/desktop/src/renderer/components/terminals/WorkViewArea.test.tsx
  • apps/desktop/src/renderer/components/terminals/WorkViewArea.tsx
  • apps/desktop/src/renderer/components/work/WorkSurfaceHeader.test.tsx
  • apps/desktop/src/renderer/components/work/WorkSurfaceHeader.tsx
  • apps/desktop/src/renderer/index.css
💤 Files with no reviewable changes (2)
  • apps/desktop/src/renderer/components/terminals/WorkCliSessionHeader.test.tsx
  • apps/desktop/src/renderer/components/terminals/WorkCliSessionHeader.tsx

Comment thread .agents/skills/ade-web/SKILL.md Outdated
Comment on lines +119 to +123
fallbackService?.dispose();
fallbackService = null;
const service = createServiceForWindow(win);
const closedListener = () => {
windowServices.delete(win.id);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Preserve fallback browser state when the first window attaches.

serviceForWindow() disposes fallbackService before any state is transferred. Because the public API still allows navigate/showPanel/createTab without a sourceWindow, those calls can create real tabs in the fallback instance; the first attachToWindow() then swaps in a fresh empty per-window service and drops that state. Either migrate the fallback instance into the first window entry or reject mutating calls until a window is bound.

Also applies to: 160-163

🤖 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 `@apps/desktop/src/main/services/builtInBrowser/builtInBrowserService.ts`
around lines 119 - 123, serviceForWindow() currently disposes fallbackService
before creating a new per-window service which loses any tabs/state that were
created via navigate/showPanel/createTab without a sourceWindow; modify
serviceForWindow/createServiceForWindow/attachToWindow flow to detect an
existing fallbackService and migrate its state into the first created per-window
service instead of disposing it (e.g., transfer tabs, active index, history,
etc.), or alternatively make navigate/showPanel/createTab reject/mutate calls
until a window is bound; specifically update serviceForWindow() to check
fallbackService and call a new transferFallbackState(fallbackService, service)
before setting fallbackService = null, and ensure windowServices.set(win.id,
service) and closedListener logic remain intact so the migrated state persists
for that window (same change also apply to the similar block around lines
160-163).

Comment thread apps/desktop/src/main/services/chat/agentChatService.ts
Comment on lines +22863 to +22866
let cursor: string | null = null;
let pages = 0;
const MAX_PAGES = 10;
try {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don't return a silently truncated transcript.

The hard MAX_PAGES = 10 cap means any subagent thread with more than 500 items is returned as a partial transcript, but the caller treats it as complete because collected.length > 0. That drops older turns with no fallback path.

Suggested fix
       do {
         const params: Record<string, unknown> = {
@@
         cursor = typeof response?.nextCursor === "string" ? response.nextCursor : null;
         pages += 1;
       } while (cursor && pages < MAX_PAGES);
+      if (cursor) return null;
     } catch {

Also applies to: 22867-22899

🤖 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 `@apps/desktop/src/main/services/chat/agentChatService.ts` around lines 22863 -
22866, The loop that pages through transcript items uses a hard MAX_PAGES = 10
and silently returns a partial transcript (variables: cursor, pages, MAX_PAGES,
collected, and the surrounding fetch loop), so change the logic to avoid silent
truncation by making the cap configurable and by returning an explicit
truncation indicator: replace the fixed MAX_PAGES constant with a configurable
limit or remove it, and if the loop must stop because pages reached the limit
set a boolean (e.g. truncated = true) and include it in the function's return
value or throw a clear error; ensure the caller no longer infers completeness
from collected.length and instead checks the new truncated/hasMore flag (update
all code paths that read collected and the function signature/return type
accordingly).

Comment thread apps/desktop/src/renderer/components/chat/AgentChatMessageList.tsx
- Clarify SKILL.md socket constraint (no longer contradicts --socket usage)
- Use deterministic fallback IDs for Codex thread items without IDs
- Log warning when transcript fetch hits MAX_PAGES cap
- Render failed/stopped subagent results with appropriate colors
- Restore stop-session button in CLI work surface header

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
@arul28 arul28 merged commit 9005c1a into main May 27, 2026
9 of 10 checks passed
Comment on lines +162 to +190
}
}}
rows={2}
className={cn(
"mt-1.5 block w-full resize-none rounded border border-amber-400/30 bg-amber-950/30",
"px-2 py-1 font-sans text-[13px] leading-snug text-amber-50",
"outline-none focus:border-amber-300/60",
)}
aria-label="Edit goal objective"
/>
) : (
<button
type="button"
onClick={() => onEdit && setEditing(true)}
title={onEdit ? "Click to edit" : objective}
disabled={!onEdit}
className={cn(
"mt-1.5 block w-full text-left font-sans text-[13px] leading-snug text-amber-50",
onEdit && "cursor-text rounded hover:text-amber-100",
!onEdit && "cursor-default",
)}
>
{objective}
</button>
)}

<div className="mt-2 flex items-center gap-2">
{tokenBudget ? (
<div
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 onBlur={submitEdit} fires after Escape, ignoring the cancellation

cancelEdit queues setEditing(false) + setDraft(objective) via React's batched state updates. Those updates are not committed until after the keydown handler returns. When React commits and removes the textarea from the DOM, the browser fires blur, which calls submitEdit. At that point, the closure's draft still holds the user's modified text (the setDraft(objective) batch hasn't been applied yet), so next !== objective passes and onEdit is called — sending /goal set <modified text> even though the user pressed Escape to cancel. The same mechanism also causes a double /goal set when Enter is pressed (once from onKeyDown, once from the blur on unmount).

The simplest fix is to track intent with a useRef that is flipped synchronously before the async state update, and guard onBlur against calling submitEdit after a cancel.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src/renderer/components/chat/codex/CodexGoalCard.tsx
Line: 162-190

Comment:
**`onBlur={submitEdit}` fires after Escape, ignoring the cancellation**

`cancelEdit` queues `setEditing(false)` + `setDraft(objective)` via React's batched state updates. Those updates are not committed until *after* the keydown handler returns. When React commits and removes the textarea from the DOM, the browser fires `blur`, which calls `submitEdit`. At that point, the closure's `draft` still holds the user's modified text (the `setDraft(objective)` batch hasn't been applied yet), so `next !== objective` passes and `onEdit` is called — sending `/goal set <modified text>` even though the user pressed Escape to cancel. The same mechanism also causes a double `/goal set` when Enter is pressed (once from `onKeyDown`, once from the blur on unmount).

The simplest fix is to track intent with a `useRef` that is flipped synchronously before the async state update, and guard `onBlur` against calling `submitEdit` after a cancel.

How can I resolve this? If you propose a fix, please make it concise.

Fix in Claude Code

@arul28 arul28 deleted the ade/ade-web-app-launch-via-cli-e16fe5c4 branch May 28, 2026 04:44
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