Skip to content

feat(sidebar): orchestration actions and copy helpers on agent rows#7940

Open
thiagomsoares wants to merge 8 commits into
stablyai:mainfrom
thiagomsoares:feat/sidebar-agent-orchestration-copy
Open

feat(sidebar): orchestration actions and copy helpers on agent rows#7940
thiagomsoares wants to merge 8 commits into
stablyai:mainfrom
thiagomsoares:feat/sidebar-agent-orchestration-copy

Conversation

@thiagomsoares

@thiagomsoares thiagomsoares commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Summary

When coordinating agents with the Orca CLI / orchestration skill, users often need a terminal handle, a ready-made send/ask command, a @worktree:<id> address, the coordinator handle for worker_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

  • Dispatch to this agent…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.
  • Send message to this agent…orchestration.send with subject/body.
  • Ask this agent…orchestration.ask (waits up to ~2 minutes for a reply).

Busy-worker guard (skill / runtime rule)

  • One active dispatch per assignee: the UI probes orchestration.taskList (status=dispatched, match assignee_handle) before offering Dispatch.
  • Menu item and dialog submit are disabled when the worker already has an active dispatch (tooltip/banner explain which dispatch/task).
  • Submit path still re-checks and rejects if a race lands between open and submit.
  • After a successful Dispatch, the success toast includes a coordinator hint:
    No terminal do coord: orca orchestration check --wait --types worker_done,escalation,decision_gate --timeout-ms 900000 --json

Copy helpers

  • Copy Terminal ID — runtime handle (term_…) for --to / --terminal / --from
  • Copy send command / Copy ask command — ready CLI templates
  • Copy worktree address@worktree:<id>
  • Copy coordinator handle — enabled when the agent already has orchestration coordinator metadata

Implementation notes:

  • Agent rows stamp lightweight data attributes; WorktreeContextMenu reads the event target (including composedPath) so agent-row actions are not swallowed by the card-level capture handler.
  • Handle resolution reuses terminal.resolvePane (same path as the terminal pane “Copy Terminal ID”).
  • Coordinator defaults to the focused terminal when it differs from the worker; otherwise another terminal in the same worktree. The dialog still lets the user pick explicitly.

Screenshots

UI dogfooded in pnpm dev — images attached in the PR conversation (not committed to the branch, per AGENTS.md):

  1. Context menu — Orchestration entry above Workspace
    Orchestration context menu
  2. Orchestration submenu — Dispatch / Send / Ask + copy helpers
    Orchestration submenu
  3. Dispatch dialog — coordinator list with agent icons (Codex/Grok/etc.), task spec, inject checkbox
    Dispatch dialog coordinator picker

Testing

  • pnpm lint (full suite — not run end-to-end in this contribution session)
  • pnpm typecheck
  • pnpm test (full suite)
  • pnpm build
  • Focused unit tests: orchestration clipboard, coordinator picker/actions (including busy-worker reject + wait hint), terminal-handle-copy, DashboardAgentRow, WorktreeCardAgents, WorktreeContextMenu
  • Manual: pnpm dev — right-click agent → Orchestration menu; Dispatch with explicit coordinator + icons; copy actions; inject path exercised

AI Review Report

Self-review with the coding agent covered:

  • Correctness: reuses existing orchestration RPCs (taskCreate, dispatch, send, ask, taskList) and terminal.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”).
  • Event handling: nested agent-only ContextMenu was abandoned after the worktree capture handler won; actions live in WorktreeContextMenu via data attributes + composedPath.
  • Cross-platform: no new keyboard shortcuts; no hard-coded metaKey; no OS path separators; clipboard/CLI strings match existing skill docs. Toasts use existing i18n patterns. Icons reuse shared AgentIcon / agentTypeToIconAgent. macOS / Linux / Windows path and accelerator conventions not newly introduced.
  • SSH / remote: resolve + orchestration RPCs go through the runtime used for terminals (no local-only assumption).
  • Performance: work is on-demand (right-click / dialog submit / open-menu busy probe); no background polling loops.
  • UX iteration: fixed “coordinator == worker” when the worker was focused; made coordinator an explicit picker; added agent icons; disabled Dispatch when worker is busy; surface check --wait after dispatch.

Security Audit

  • Clipboard writes only resolved handles, fixed CLI templates, @worktree:<id>, or coordinator handle from status — no secrets.
  • Dispatch/send/ask call existing runtime RPCs only; no new shell execution from the UI path.
  • No new IPC surface beyond existing runtime.call + ui.writeClipboardText.
  • User-provided task spec / message / question are passed as RPC params the same way the CLI already does.
  • Orchestration still gated by the experimental setup flag (orca.orchestration.enabled).
  • Busy-worker probe is a read-only taskList call; no privilege escalation.

Notes

  • Requires Orchestration enabled (Settings → Experimental / setup markers).
  • Copy coordinator handle stays disabled until the agent has live orchestration coordinator metadata (typical after a real dispatch).
  • Same-worktree workers may still appear as peer rows in the sidebar (existing lineage rules); this PR does not change parent/child tree presentation.
  • Follow-ups (optional): show worker identity with icon in the dialog header; full CI suite green; attach screenshots in the GitHub UI.

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.
@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 964396da-f726-4eea-a0a8-b634e56497c7

📥 Commits

Reviewing files that changed from the base of the PR and between c6752e3 and 6e7fbf1.

📒 Files selected for processing (2)
  • src/renderer/src/components/sidebar/agent-row-orchestration-actions.test.ts
  • src/renderer/src/components/sidebar/agent-row-orchestration-actions.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/renderer/src/components/sidebar/agent-row-orchestration-actions.ts
  • src/renderer/src/components/sidebar/agent-row-orchestration-actions.test.ts

📝 Walkthrough

Walkthrough

This 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)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.81% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main sidebar orchestration and copy-helper changes.
Description check ✅ Passed The description covers the required template sections, testing, screenshots, review, security, and notes with relevant detail.
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.

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (3)
src/renderer/src/components/sidebar/agent-row-orchestration-coordinator-picker.tsx (1)

30-68: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

Consider adding arrow-key navigation to the listbox.

The role="listbox" pattern expects arrow-key navigation between role="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 value

Use a static import for listCoordinatorCandidates.

listCoordinatorCandidates is dynamically imported via await 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 win

The type cast on callRuntime appears unnecessary.

The actions' CallRuntime accepts { method: string; params?: Record<string, unknown> }, while resolveTerminalHandleForPaneKey expects { method: 'terminal.resolvePane'; params: { paneKey: string } }. By function-parameter contravariance (with strictFunctionTypes), 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 --noEmit after applying the change.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: f0dc446e-7848-47a3-b24f-60d2bc568c3d

📥 Commits

Reviewing files that changed from the base of the PR and between 6fb3036 and 122f5f9.

📒 Files selected for processing (14)
  • src/renderer/src/components/dashboard/DashboardAgentRow.tsx
  • src/renderer/src/components/dashboard/agent-row-orchestration-clipboard.test.ts
  • src/renderer/src/components/dashboard/agent-row-orchestration-clipboard.ts
  • src/renderer/src/components/sidebar/WorktreeContextMenu.tsx
  • src/renderer/src/components/sidebar/agent-row-orchestration-action-dialog.tsx
  • src/renderer/src/components/sidebar/agent-row-orchestration-actions.test.ts
  • src/renderer/src/components/sidebar/agent-row-orchestration-actions.ts
  • src/renderer/src/components/sidebar/agent-row-orchestration-coordinator-picker.tsx
  • src/renderer/src/components/sidebar/agent-row-orchestration-coordinator.ts
  • src/renderer/src/components/sidebar/worktree-agent-orchestration-menu.test.ts
  • src/renderer/src/components/sidebar/worktree-agent-orchestration-menu.tsx
  • src/renderer/src/components/sidebar/worktree-card-compact-agent-row.tsx
  • src/renderer/src/components/terminal-pane/terminal-handle-copy.test.ts
  • src/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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/renderer/src/components/sidebar/agent-row-orchestration-actions.ts (1)

151-160: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Avoid redundant worker handle resolution in dispatchTaskToAgent.

resolveCoordinatorAndWorkerHandles (Line 151) already resolves the worker terminal handle via an IPC call to terminal.resolvePane. Then findActiveDispatchForWorker (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, so dispatchTaskToAgent can reuse the handle from Line 151 without a second terminal.resolvePane call. findActiveDispatchForWorker can 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

📥 Commits

Reviewing files that changed from the base of the PR and between 1180e90 and c6752e3.

📒 Files selected for processing (5)
  • src/renderer/src/components/sidebar/agent-row-orchestration-action-copy.ts
  • src/renderer/src/components/sidebar/agent-row-orchestration-action-dialog.tsx
  • src/renderer/src/components/sidebar/agent-row-orchestration-actions.test.ts
  • src/renderer/src/components/sidebar/agent-row-orchestration-actions.ts
  • src/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

@thiagomsoares

Copy link
Copy Markdown
Contributor Author

UI screenshots (dogfood)

Attached here per project norms (not committed to the branch).

1. Context menu — Orchestration entry

Orchestration context menu

2. Orchestration submenu — Dispatch / Send / Ask + copy helpers

Orchestration submenu

3. Dispatch dialog — coordinator picker with agent icons

Dispatch dialog coordinator picker

Note: hosted on litterbox (72h) because GitHub has no public image-upload API for issue/PR comments and AGENTS.md forbids committing evidence images / gh-attach. Re-host via drag-and-drop in this thread if you want permanent user-attachments URLs.

Avoid a second terminal.resolvePane when dispatch already resolved the
worker handle; extract handle-based taskList scanning for shared use.
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.

2 participants