Work-tab UI overhaul + ADE Code subagent/render parity#534
Conversation
Major overhaul of the Work-tab chat surface across all runtimes. Layout & navigation - Removed the entire tabs/grid top bar and the viewMode concept app-wide; WorkViewArea shrank ~2278→1207 lines (legacy preset grid, tab strip, WorkGlassHeader, ArrangeMenu, ViewModeToggle all deleted). - Per-surface header now owns the sessions toggle (far left) + Tools toggle (far right, purple glyph) for both chat and CLI. - New Cursor-style drag grid: drag a session card from the sidebar onto a chat/CLI to split at the hovered edge; resize, rearrange, drag-out to single view, right-click remove-from-grid; multiple grid sets; sidebar grid badge (active set highlighted). New WorkGridView + lib/workGrid + grid-set state. Panes - Chat-actions + PR floating panes: neutral sidebar-colored background (no purple), compact, smaller width, chat recenters on normal screens. - Chat-actions tabs restyled neutral with an animated sliding violet underline (GlowMenu neutral mode); Run moved to a 4th tab rendered inline. - Subagent cards redesigned: compact rows with per-agent geometric glyphs (no childish purple boxes). Subagent takeover only when there's content. - New inline PR creator in the PR pane (no leaving the Work tab). Transcript & composer - Unbubbled assistant text, flat lighter canvas (#0f0f11), 14px prose, no scrollbar, clean turn dividers (time · worked-for), interrupted text-only. - Removed composer BorderBeam + white border; relocated clipboard notice. - Nuked the Codex "Open in CLI" button + its entire cross-process plumbing. Animations - Smooth lane-collapse, floating-pane fades, Tools-sidebar fade, iMessage-style message send-up, and a blur-dissolve when the new-chat pane becomes the chat. Gates: typecheck PASS, lint PASS (warnings only), desktop build PASS. Known: 11 renderer tests assert pre-overhaul UI text (turn-divider rollup, draft-launch banner wording) — assertion drift to reconcile in a follow-up. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Chat UI (Work tab): - CLI ended-session view: chat-canvas bg, one-line green Resume header, model pill, round send button, thinner auto-grow continuation composer - PR floating pane: header/X removed, 2-line locked lane+branch source + LaneCombobox target dropdown, bold accent, "Open in PRs tab"; AI draft now runs on the chat's model via the pr_descriptions background job and surfaces real errors (requireAi) - Context-usage circular dial across all runtimes (registry context-window fallback) with force-enabled hover breakdown; replaces the Codex-only strip - Floating panes fade in place; chat reserves to make room (no overlap) - User-message minimap bounded to the chat column; chat column widened to 52rem Also includes in-progress Codex subagent transcript metadata work (agentChatService, adeActions registry, ade-cli bootstrap, docs). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Subagent pane parity for the 4 shared runtimes (Codex/Cursor/Droid/OpenCode): - Capability-gated subagent rows (resolveSubagentCapability): Codex/OpenCode take over with the real daemon transcript (getSubagentTranscript, fallback to local reconstruction); Cursor/Droid show inline detail (no takeover). - Per-runtime stat chips (tokens/tool-uses/cost), Droid Missions section + ^k kill-worker, once-per-session auto-open on first subagent. - Backend: allowlist killDroidWorker; widen SubagentSnapshot (toolUses/costUsd). Render fixes: - Identity-aware coalesceLines + shared assistantTextIdentity predicate so interleaved concurrent messages don't scramble; filter codex-subagent: text from the parent transcript (mirror desktop). - Coalesce streamed text deltas before React state + flush interval 24->48ms to cut the per-token re-aggregate/re-wrap flood (flicker/layout-shift). - Distinct itemId per synthetic takeover-transcript line so they don't fuse into one separator-less blob. - Chat right gutter so prose doesn't hug the right pane. - Desktop terminal: integer fontSize (12.5->13) + minimumContrastRatio so the xterm.js WebGL renderer stops crowding glyphs / dashing box-drawing borders. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… remote-runtime hardening - Old work-tab grid (WorkViewMode/viewMode) dropped — replaced by this lane's work-tab overhaul (WorkGridView/workGrid). - Kept main's remote-runtime hardening: remote session polling intervals (useWorkSessions), remote-PR handling (ChatGitToolbar), remote delta-skip (SessionCard), remote browser-sidebar guard (TerminalsPage), projectBinding/ selectActiveProjectRoot. - Deduped getSubagentTranscript in the chat action allowlist; restored selectActiveProjectRoot imports the auto-merged bodies needed; restored lane test versions where they assert the overhaul's API. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ruth) The overhaul intentionally changed UI copy/structure; these tests still asserted pre-overhaul behavior. Aligned the tests to the overhaul (kept the new impl): - Draft-launch notice: 'Message sent: "<prompt>"', 'View' button, 'Dismiss launch status' (AgentChatPane.test, 7 tests). - Composer clipboard hint moved + reworded to 'Prompt copies to clipboard on send.' (AgentChatComposer.test, 2 tests). - End-of-turn divider restructured: 'Task list' rollup (was 'Response'), calmer completed footer (model attribution now on interrupted/failed turns), agents moved to the subagent pane (AgentChatMessageList.test, 4 tests). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- TerminalView: fontSize now rounded to integer (12.5→13) for the WebGL box-drawing/glyph fix. - App.workKeepAlive: revealWorkBrowser no longer sets the removed work-tab viewMode; just opens the browser sidebar. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
|
Warning Review limit reached
More reviews will be available in 20 minutes and 27 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 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 configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (3)
📒 Files selected for processing (105)
✨ Finishing Touches🧪 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 |
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
| } | ||
| } | ||
|
|
||
| // Terminate a single AGI mission worker. killWorkerSession lives only on the | ||
| // low-level DroidClient — DroidSession (what createSession/resumeSession return) | ||
| // exposes no public getter at @factory/droid-sdk 0.2.0 — so reach the underlying | ||
| // client via its (TS-private, runtime-present) `_client` field. | ||
| async function killWorker(workerSessionId: string): Promise<void> { | ||
| if (!session) throw new Error("Droid SDK worker is not initialized."); | ||
| const id = workerSessionId?.trim(); | ||
| if (!id) return; | ||
| const client = (session as unknown as { | ||
| _client?: { killWorkerSession?: (params: { workerSessionId: string }) => Promise<unknown> }; | ||
| })._client; | ||
| if (!client || typeof client.killWorkerSession !== "function") { | ||
| throw new Error("This Droid SDK build does not expose killWorkerSession."); | ||
| } |
There was a problem hiding this comment.
Fragile
_client private-field bypass
session._client is a TypeScript-private implementation detail of DroidSession. At @factory/droid-sdk 0.2.0 the field is present at runtime, but nothing prevents a future release from renaming it, hoisting it, or removing it. When that happens, client is undefined and every ^k kill-worker attempt throws "This Droid SDK build does not expose killWorkerSession" — an error message that implies an SDK-version problem rather than a structural one, making it hard to triage.
If the SDK team can expose killWorkerSession publicly (or expose the underlying client), that would remove the coupling entirely. If not, consider adding a version guard or a clearly-marked adapter module so the fragile cast is a single-source-of-truth shim rather than inline in the worker dispatch.
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src/main/services/chat/droidSdkWorker.ts
Line: 341-357
Comment:
**Fragile `_client` private-field bypass**
`session._client` is a TypeScript-private implementation detail of `DroidSession`. At `@factory/droid-sdk` 0.2.0 the field is present at runtime, but nothing prevents a future release from renaming it, hoisting it, or removing it. When that happens, `client` is `undefined` and every `^k` kill-worker attempt throws "This Droid SDK build does not expose killWorkerSession" — an error message that implies an SDK-version problem rather than a structural one, making it hard to triage.
If the SDK team can expose `killWorkerSession` publicly (or expose the underlying client), that would remove the coupling entirely. If not, consider adding a version guard or a clearly-marked adapter module so the fragile cast is a single-source-of-truth shim rather than inline in the worker dispatch.
How can I resolve this? If you propose a fix, please make it concise.| const timestampFor = (index: number): string => | ||
| new Date(Date.UTC(2026, 0, 1, 0, 0, 0, Math.min(index, 999))).toISOString(); |
There was a problem hiding this comment.
Hardcoded
2026-01-01 timestamp base is already in the past
Today is 2026-06-06, so every synthetic envelope generated by buildSubagentEventHistory carries a January 2026 timestamp. Any relative-time display ("5 months ago") in the subagent takeover view will be visibly wrong. The parallel subagentTranscriptMessagesToEvents in chatSubagents.ts uses snapshot.startedAt — this function should do the same. Since startedAt isn't currently threaded into buildSubagentEventHistory's args, the easiest safe fallback is Date.now().
| const timestampFor = (index: number): string => | |
| new Date(Date.UTC(2026, 0, 1, 0, 0, 0, Math.min(index, 999))).toISOString(); | |
| const baseTs = Date.now(); | |
| const timestampFor = (index: number): string => | |
| new Date(baseTs + index).toISOString(); |
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src/renderer/components/chat/AgentChatPane.tsx
Line: 531-532
Comment:
**Hardcoded `2026-01-01` timestamp base is already in the past**
Today is 2026-06-06, so every synthetic envelope generated by `buildSubagentEventHistory` carries a January 2026 timestamp. Any relative-time display ("5 months ago") in the subagent takeover view will be visibly wrong. The parallel `subagentTranscriptMessagesToEvents` in `chatSubagents.ts` uses `snapshot.startedAt` — this function should do the same. Since `startedAt` isn't currently threaded into `buildSubagentEventHistory`'s args, the easiest safe fallback is `Date.now()`.
```suggestion
const baseTs = Date.now();
const timestampFor = (index: number): string =>
new Date(baseTs + index).toISOString();
```
How can I resolve this? If you propose a fix, please make it concise.subagentEventsForDisplay useMemo (+ its view-model consts) ran after the `if (!laneId) return` guard, making the hook conditional. Relocated the block up to the subagent state cluster (identical logic + deps) so the hook is unconditional. Lint 0 errors, typecheck clean, AgentChatPane.test 127/127. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
What
WorkViewModetabs/grid.^kkill-worker, once-per-session auto-open;killDroidWorkerallowlisted.codex-subagent:filtered from the parent transcript, per-message flood coalescing (flicker), chat right gutter, distinct synthetic-transcript line ids, desktop terminal integerfontSize+minimumContrastRatio(fixes WebGL dashed box-drawing / crowded glyphs).Merge with main
Reconciled
origin/main(PRs-tab overhaul, remote-runtime hardening, agent bootstrap, iOS sim): the overhaul wins the work-tab grid; main's remote-runtime hardening is preserved (remote poll intervals, remote-PR handling, remote browser-sidebar guard,projectBinding/selectActiveProjectRoot).Tests
Lane-blast-radius green locally: ade-cli 571, desktop chat 515 + work-tab/lanes/settings/files/app/main-services 1272; both packages typecheck clean. Chat-test suites aligned to the overhaul's new copy/structure.
🤖 Generated with Claude Code
Greptile Summary
This PR delivers a Cursor-style work-tab overhaul (single-focused session with DnD grid formation), ADE Code TUI subagent/render parity (identity-aware delta coalescing, real child-transcript takeover, per-runtime stat chips,
^kkill-worker), and Droid AGI mission support. It also cleans up dead code (codexCliLauncher,codexOpenInCli,WorkViewMode), reconciles remote-runtime hardening frommain, and fixes TUI render artefacts (integerfontSize, WebGL glyph crowding, chat right gutter).viewMode: "tabs"|"grid"toggle with aWorkGridSet[]model; sessions join grids via DnD and are rendered inWorkGridView/PaneTilingLayout; grid membership normalisation drops sub-2-member sets and deduplicates cross-set membership on load.subagentCapabilities.tssingle source of truth gates takeover vs. inline drawer;buildSubagentEventHistoryexpands Anthropic message blocks into typed events;coalesceTextDeltaEnvelopesinassistantTextIdentity.tsis shared by both the buffer-level and render-line coalescers so concurrent parent+child deltas no longer scramble.toDroidInteractionModemaps the new"agi"permission mode;killWorkerexposes^kworker termination (via the_clientshim already noted);droidSdkEventMappermaps mission lifecycle events tomission_state/features/progressenvelopes consumed bychatMission.tsandMissionControlPanel.Confidence Score: 5/5
mergeAdjacentSubagentEvents(only reachable for streaming command/file-change events in a subagent view) and a missing try/catch around a JSON.stringify fallback key — both produce visible artefacts only in specific, low-frequency scenarios and neither causes data loss or session corruption.Important Files Changed
killDroidWorker,mergeSubagentTranscriptMessages, identity-aware delta coalescing, and parent-visible event filtering. Minor:JSON.stringifywithout try/catch in fallback dedup key.buildSubagentEventHistory. Template literals on optionaloutput/difffields may emit "undefined" prefix for streaming command/file_change events.killWorkervia private_client._clientbypass (already noted in a prior review thread), AGI interaction mode mapping, and mission-proposal permission rendering. The_clientcoupling concern stands but is unchanged by this review cycle.addSessionBesideTargetreturns a fabricatedgridSetIdnot present in the output, but all callers guard againstsessionId === targetSessionIdbefore calling.viewModewithWorkGridSet[]/activeGridSetId, addsuserOverrodeChatFontSizeflag, and integer terminalfontSize. Normalisation is thorough: deduplicates grid membership, drops sub-2-member sets.AgentChatKillDroidWorkerArgs,AgentChatDroidPermissionMode"agi" variant,reasoningTokens/contextWindowon done events, andSubagentCapabilityon session capabilities.MissionSnapshotfrom the event stream by taking the latest of each full-replacement mission event type. Clean design; correctly handles full-replace semantics.detectDropEdge; overlay rendering with live edge preview looks correct.Sequence Diagram
sequenceDiagram participant SDK as Codex/Droid/Cursor SDK participant SVC as agentChatService participant IPC as registerIpc / preload participant REND as AgentChatPane (renderer) participant TUI as AdeCodeApp (TUI) SDK->>SVC: subagent_started / turn events SVC->>SVC: registerCodexSubagentThread() SVC->>SVC: "recordCodexSubagentTranscriptMessages()<br/>(dedup via transcriptKeys Set)" SVC->>SVC: "writeTranscript() → persisted JSONL<br/>provenance.targetKind=codex_subagent" REND->>IPC: "getSubagentTranscript({ sessionId, agentId })" IPC->>SVC: getSubagentTranscript() SVC->>SVC: "merge captured + live transcripts<br/>mergeSubagentTranscriptMessages()" SVC-->>REND: AgentChatSubagentTranscriptMessage[] REND->>REND: "eventsFromSubagentTranscriptMessage()<br/>expand Anthropic blocks → typed events" REND->>REND: "buildSubagentEventHistory()<br/>completedKeys filter + coalesceSubagentEventEnvelopes()" REND->>REND: "AgentChatMessageList renders<br/>subagent transcript as takeover view" TUI->>IPC: "getSubagentTranscript({ sessionId, agentId })" IPC-->>TUI: AgentChatSubagentTranscriptMessage[] TUI->>TUI: "subagentTranscriptMessagesToEvents()<br/>→ setRealSubagentTranscript" SDK->>SVC: Droid mission_state_changed / features / progress SVC->>SVC: droidSdkEventMapper → mission_state/features/progress envelopes REND->>REND: "deriveMissionSnapshot(events)<br/>→ MissionControlPanel" REND->>IPC: "killDroidWorker({ sessionId, workerSessionId })" IPC->>SVC: killDroidWorker() SVC->>SVC: "runtime.sdk.killWorker(workerSessionId)<br/>(via _client shim)"Comments Outside Diff (1)
apps/desktop/src/main/services/chat/agentChatService.ts, line 6629-6655 (link)truncated: truefalse-positive when subagent envelopes dominate the response capmergedLengthBeforeResponseCap > CHAT_EVENT_HISTORY_RESPONSE_MAX_PER_SESSIONcounts ALL envelopes (parent + codex-subagent). A session with 1 000 parent events and 8 000 subagent events would havemergedLengthBeforeResponseCap = 9000 > cap, sowindowTruncated = true. ButwindowedisparentVisibleMerged— which contains all 1 000 parent events. The caller receives a complete parent-visible set yet seestruncated: true, so any "load older history" prompt or truncation warning fires spuriously.Prompt To Fix With AI
Prompt To Fix All With AI
Reviews (2): Last reviewed commit: "ship: fix react-hooks/rules-of-hooks lin..." | Re-trigger Greptile