feat(sidebar): orchestration actions and copy helpers on agent rows#7940
feat(sidebar): orchestration actions and copy helpers on agent rows#7940thiagomsoares wants to merge 8 commits into
Conversation
Right-clicking a worktree agent row now exposes an Orchestration submenu with Terminal ID, send/ask command templates, worktree address, and coordinator handle so agents can be targeted without hunting CLI handles.
Adds real orchestration actions on agent-row context menus: create a task and dispatch (optional inject) to the clicked agent, send a status message, or ask and wait for a reply. The focused terminal is the coordinator; workers resolve via terminal handles through existing RPCs.
…worker Right-clicking an agent often focuses that same pane, which made dispatch fail. Prefer the focused terminal only when it differs from the worker; otherwise fall back to another terminal in the same worktree.
Dispatch/Send/Ask now show a Coordinator dropdown listing other terminals in the worktree (defaulting to focused when different from the worker), so users can see and pick who owns the task without guessing focus rules.
Replace the plain coordinator <select> with a list of options that include Claude/Codex/Grok (etc.) icons and clearer labels so users can identify which terminal is the coordinator at a glance.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (2)
📝 WalkthroughWalkthroughThis PR adds orchestration support for agent rows in the dashboard and worktree context menu. It introduces pane-key-based terminal handle resolution, coordinator selection helpers, orchestration action helpers for dispatch/send/ask, and a dialog for submitting those actions. It also adds a worktree menu section for orchestration commands and clipboard actions, plus DOM data attributes on agent rows so right-clicked rows can supply orchestration target data. 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
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 |
There was a problem hiding this comment.
🧹 Nitpick comments (3)
src/renderer/src/components/sidebar/agent-row-orchestration-coordinator-picker.tsx (1)
30-68: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low valueConsider adding arrow-key navigation to the listbox.
The
role="listbox"pattern expects arrow-key navigation betweenrole="option"items. Currently, users can only Tab between options, which works but doesn't match the expected ARIA interaction model. This is a minor accessibility gap that won't block task completion but could confuse assistive technology users expecting standard listbox behavior.src/renderer/src/components/sidebar/agent-row-orchestration-actions.test.ts (1)
113-115: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueUse a static import for
listCoordinatorCandidates.
listCoordinatorCandidatesis dynamically imported viaawait import(...)while all other functions from the same module use static imports. Adding it to the existing import statement at lines 2-8 would be more consistent.♻️ Suggested change
import { askAgent, dispatchTaskToAgent, getActiveTerminalPaneKey, + listCoordinatorCandidates, resolveCoordinatorPaneKey, sendMessageToAgent } from './agent-row-orchestration-actions'And at line 115:
- const { listCoordinatorCandidates } = await import('./agent-row-orchestration-actions') + const candidates = listCoordinatorCandidates({src/renderer/src/components/sidebar/agent-row-orchestration-actions.ts (1)
63-65: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winThe type cast on
callRuntimeappears unnecessary.The actions'
CallRuntimeaccepts{ method: string; params?: Record<string, unknown> }, whileresolveTerminalHandleForPaneKeyexpects{ method: 'terminal.resolvePane'; params: { paneKey: string } }. By function-parameter contravariance (withstrictFunctionTypes), the wider type is assignable to the narrower one without a cast. The cast bypasses the compiler's own assignability check and could hide future type mismatches.♻️ Proposed refactor
- const callRuntimeForPane = args.callRuntime as Parameters< - typeof resolveTerminalHandleForPaneKey - >[0]['callRuntime'] const [coordinatorHandle, workerHandle] = await Promise.all([ resolveTerminalHandleForPaneKey({ paneKey: args.coordinatorPaneKey, - callRuntime: callRuntimeForPane + callRuntime: args.callRuntime }), resolveTerminalHandleForPaneKey({ paneKey: args.workerPaneKey, - callRuntime: callRuntimeForPane + callRuntime: args.callRuntime }) ])Verify the project compiles without the cast by running
tsc --noEmitafter applying the change.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: f0dc446e-7848-47a3-b24f-60d2bc568c3d
📒 Files selected for processing (14)
src/renderer/src/components/dashboard/DashboardAgentRow.tsxsrc/renderer/src/components/dashboard/agent-row-orchestration-clipboard.test.tssrc/renderer/src/components/dashboard/agent-row-orchestration-clipboard.tssrc/renderer/src/components/sidebar/WorktreeContextMenu.tsxsrc/renderer/src/components/sidebar/agent-row-orchestration-action-dialog.tsxsrc/renderer/src/components/sidebar/agent-row-orchestration-actions.test.tssrc/renderer/src/components/sidebar/agent-row-orchestration-actions.tssrc/renderer/src/components/sidebar/agent-row-orchestration-coordinator-picker.tsxsrc/renderer/src/components/sidebar/agent-row-orchestration-coordinator.tssrc/renderer/src/components/sidebar/worktree-agent-orchestration-menu.test.tssrc/renderer/src/components/sidebar/worktree-agent-orchestration-menu.tsxsrc/renderer/src/components/sidebar/worktree-card-compact-agent-row.tsxsrc/renderer/src/components/terminal-pane/terminal-handle-copy.test.tssrc/renderer/src/components/terminal-pane/terminal-handle-copy.ts
Add arrow-key navigation for the coordinator listbox, use a static import in tests, and drop the unnecessary callRuntime cast when resolving pane handles.
Probe orchestration.taskList for an active assignee dispatch so the Dispatch menu item and dialog stay disabled, and surface the skill's coordinator wait command after a successful dispatch.
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/renderer/src/components/sidebar/agent-row-orchestration-actions.ts (1)
151-160: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winAvoid redundant worker handle resolution in
dispatchTaskToAgent.
resolveCoordinatorAndWorkerHandles(Line 151) already resolves the worker terminal handle via an IPC call toterminal.resolvePane. ThenfindActiveDispatchForWorker(Line 157) resolves the same worker pane key again internally, adding a redundant IPC round-trip on every dispatch action.Consider extracting the taskList-scanning logic into a helper that accepts a pre-resolved
workerHandle, sodispatchTaskToAgentcan reuse the handle from Line 151 without a secondterminal.resolvePanecall.findActiveDispatchForWorkercan still resolve independently for its standalone callers.♻️ Suggested refactor
export async function findActiveDispatchForWorker(args: { workerPaneKey: string callRuntime: CallRuntime }): Promise<ActiveWorkerDispatch | null> { const workerHandle = await resolveTerminalHandleForPaneKey({ paneKey: args.workerPaneKey, callRuntime: args.callRuntime }) + return findActiveDispatchForWorkerHandle({ workerHandle, callRuntime: args.callRuntime }) +} + +async function findActiveDispatchForWorkerHandle(args: { + workerHandle: string + callRuntime: CallRuntime +}): Promise<ActiveWorkerDispatch | null> { const listResult = assertOk( - await args.callRuntime({ + await args.callRuntime({ method: 'orchestration.taskList', params: { status: 'dispatched' } }) )Then in
dispatchTaskToAgent:const { coordinatorHandle, workerHandle } = await resolveCoordinatorAndWorkerHandles({ workerPaneKey: args.workerPaneKey, coordinatorPaneKey: args.coordinatorPaneKey, callRuntime: args.callRuntime }) - const active = await findActiveDispatchForWorker({ - workerPaneKey: args.workerPaneKey, - callRuntime: args.callRuntime - }) + const active = await findActiveDispatchForWorkerHandle({ + workerHandle, + callRuntime: args.callRuntime + })
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: c6ad6d91-bb52-4739-8f84-ac74af6e3dde
📒 Files selected for processing (5)
src/renderer/src/components/sidebar/agent-row-orchestration-action-copy.tssrc/renderer/src/components/sidebar/agent-row-orchestration-action-dialog.tsxsrc/renderer/src/components/sidebar/agent-row-orchestration-actions.test.tssrc/renderer/src/components/sidebar/agent-row-orchestration-actions.tssrc/renderer/src/components/sidebar/worktree-agent-orchestration-menu.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
- src/renderer/src/components/sidebar/agent-row-orchestration-action-dialog.tsx
- src/renderer/src/components/sidebar/worktree-agent-orchestration-menu.tsx
UI screenshots (dogfood)Attached here per project norms (not committed to the branch). 1. Context menu — Orchestration entry2. Orchestration submenu — Dispatch / Send / Ask + copy helpers3. Dispatch dialog — coordinator picker with agent iconsNote: hosted on litterbox (72h) because GitHub has no public image-upload API for issue/PR comments and AGENTS.md forbids committing evidence images / |
Avoid a second terminal.resolvePane when dispatch already resolved the worker handle; extract handle-based taskList scanning for shared use.



Summary
When coordinating agents with the Orca CLI / orchestration skill, users often need a terminal handle, a ready-made
send/askcommand, a@worktree:<id>address, the coordinator handle forworker_done, or a way to actually dispatch / send / ask without leaving the sidebar.This PR adds an Orchestration submenu to the existing worktree context menu when the right-click lands on an agent row (compact or full list):
Actions
orchestration.taskCreate+orchestration.dispatch(optional inject). Explicit Coordinator picker (other terminals in the same worktree) with agent icons (Claude/Codex/Grok/…). Worker = the right-clicked agent.orchestration.sendwith subject/body.orchestration.ask(waits up to ~2 minutes for a reply).Busy-worker guard (skill / runtime rule)
orchestration.taskList(status=dispatched, matchassignee_handle) before offering Dispatch.No terminal do coord: orca orchestration check --wait --types worker_done,escalation,decision_gate --timeout-ms 900000 --jsonCopy helpers
term_…) for--to/--terminal/--from@worktree:<id>Implementation notes:
WorktreeContextMenureads the event target (includingcomposedPath) so agent-row actions are not swallowed by the card-level capture handler.terminal.resolvePane(same path as the terminal pane “Copy Terminal ID”).Screenshots
UI dogfooded in
pnpm dev— images attached in the PR conversation (not committed to the branch, per AGENTS.md):Testing
pnpm lint(full suite — not run end-to-end in this contribution session)pnpm typecheckpnpm test(full suite)pnpm buildpnpm dev— right-click agent → Orchestration menu; Dispatch with explicit coordinator + icons; copy actions; inject path exercisedAI Review Report
Self-review with the coding agent covered:
taskCreate,dispatch,send,ask,taskList) andterminal.resolvePane; coordinator must differ from worker; inject remains opt-in and relies on runtime agent detection; second active dispatch is blocked in UI and re-checked on submit (matches DB “one active dispatch per assignee”).WorktreeContextMenuvia data attributes +composedPath.metaKey; no OS path separators; clipboard/CLI strings match existing skill docs. Toasts use existing i18n patterns. Icons reuse sharedAgentIcon/agentTypeToIconAgent. macOS / Linux / Windows path and accelerator conventions not newly introduced.check --waitafter dispatch.Security Audit
@worktree:<id>, or coordinator handle from status — no secrets.runtime.call+ui.writeClipboardText.orca.orchestration.enabled).taskListcall; no privilege escalation.Notes