Skip to content

Feat: Coordinating Agent#31

Open
cledoux95 wants to merge 3 commits intojohannesjo:mainfrom
cledoux95:task/coordinating-agent
Open

Feat: Coordinating Agent#31
cledoux95 wants to merge 3 commits intojohannesjo:mainfrom
cledoux95:task/coordinating-agent

Conversation

@cledoux95
Copy link
Copy Markdown
Contributor

Summary

  • Add an MCP server (electron/mcp/) with tools (create_task, send_prompt, wait_for_idle, get_task_diff, merge_task, etc.) enabling a Claude Code agent to programmatically orchestrate tasks in Parallel Code
  • Add main-process orchestrator, REST API endpoints, and MCP client infrastructure
  • Add UI support: coordinator mode toggle in new task dialog, sub-task status strip, "via Coordinator" sidebar labels, and consolidated TaskPanel component
  • Refactor: merge TaskAITerminal, TaskBranchInfoBar, TaskClosingOverlay, TaskNotesPanel, TaskShellSection, and TaskTitleBar into a single TaskPanel
  • Remove Docker isolation mode, SegmentedButtons, system fonts IPC, and theme utilities
  • Simplify NewTaskDialog, SettingsDialog, ScrollingDiffView, and multiple store modules

Test plan

  • Verify MCP server starts and responds to tool calls
  • Create a task via the coordinating agent and confirm it appears in the UI
  • Test send_prompt, wait_for_idle, get_task_diff, and merge_task end-to-end
  • Confirm sub-task strip renders correctly with multiple coordinated tasks
  • Verify standard (non-coordinated) task creation still works
  • Confirm removed components (Docker mode, system fonts, segmented buttons) have no lingering references

Croissant Le Doux and others added 3 commits March 19, 2026 13:17
Enable a Claude Code agent to programmatically create and orchestrate
other tasks in Parallel Code. Adds an MCP server with 9 tools
(create_task, send_prompt, wait_for_idle, get_task_diff, merge_task,
etc.), a main-process orchestrator, REST API endpoints, and UI
support including coordinator mode toggle, sub-task status strip,
and "via Coordinator" sidebar labels.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@cledoux95
Copy link
Copy Markdown
Contributor Author

@johannesjo I tried to bring it up to the same commit as Main, looks like I still ran into the npm version issue and a few more dangling conflicts.

You should be able to run this branch - let me know if you want me to update it to fix these last conflicts.

@cledoux95
Copy link
Copy Markdown
Contributor Author

#25

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces a “coordinating agent” mode that can orchestrate sub-tasks via an MCP server backed by new remote REST endpoints and a main-process orchestrator, alongside UI/store refactors to support coordinator tasks, nested sidebar rendering, and additional isolation/runtime options.

Changes:

  • Add MCP server + main-process orchestrator + remote REST endpoints to programmatically create/monitor/merge/close tasks.
  • Update task model/persistence/UI to support coordinator tasks (sub-task strip, sidebar nesting/labels) and new git isolation/base-branch plumbing.
  • Refactor and modernize multiple UI/store utilities (task panel components consolidation, diff viewer improvements, notifications, font loading).

Reviewed changes

Copilot reviewed 65 out of 67 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
src/store/ui.ts Store setters updated (terminal font as string) and docker-related UI state setters added.
src/store/types.ts Expand task/project/persisted types (git isolation, base branch, coordinator metadata, docker fields, MCP status).
src/store/terminals.ts Remove window title updates from terminal lifecycle code paths.
src/store/tasks.ts Unify task creation for worktree/direct, coordinator MCP integration, plan-watcher cleanup, and child-task unparenting.
src/store/store.ts Export updated store APIs (remove direct task APIs, add MCP + docker setters/status).
src/store/sidebar-order.ts Add coordinator child grouping and filter children from flat sidebar list.
src/store/projects.ts Add git-repo validation on project add/relink; add default isolation/base-branch project settings.
src/store/persistence.ts Persist new task fields (git isolation/base branch/coordinator/docker) and migrate legacy direct-mode state.
src/store/navigation.ts Remove title updates; re-focus moved tasks after reordering.
src/store/mcpStatus.ts Add polling for MCP/remote connectivity status.
src/store/focus.ts Refactor adjacent-task focus navigation logic.
src/store/desktopNotifications.ts Improve debouncing/deduplication and route notifications through IPC helper.
src/store/core.ts Add docker/MCP defaults; adjust cleanup of per-task persisted UI state.
src/store/coordinator-preamble.ts Add coordinator-mode system preamble injected into initial prompt.
src/store/autosave.ts Update autosave snapshot schema for git isolation/base branch and notification setting.
src/lib/theme.ts Add shared banner/section label styles.
src/lib/terminalFitManager.ts Simplify terminal registration (no longer stores Terminal instance).
src/lib/ipc.ts Add Channel.dispose(); tighten renderer IPC surface.
src/lib/fonts.ts Switch to string font names and add async system font enumeration (IPC + canvas fallback).
src/lib/focus-registration.ts Add helper hook to register/unregister focus functions automatically.
src/components/TilingLayout.tsx Layout polish + git isolation-aware close messaging.
src/components/TerminalView.tsx Pass docker flags to backend spawn and update terminal fit registration.
src/components/TaskTitleBar.tsx New consolidated title bar UI with badges (direct branch, docker) and actions.
src/components/TaskShellSection.tsx New consolidated shell toolbar + multi-shell rendering and focus management.
src/components/TaskNotesPanel.tsx New notes/plan + changed-files split panel and keyboard navigation.
src/components/TaskClosingOverlay.tsx New overlay shown during close/error states.
src/components/TaskBranchInfoBar.tsx New branch/project/path bar with editor/reveal and GitHub link support.
src/components/TaskAITerminal.tsx New consolidated AI terminal panel with restart/switch-agent UI.
src/components/SubTaskStrip.tsx Add coordinator sub-task status chips (clickable navigation).
src/components/SidebarFooter.tsx Display MCP connectivity when coordinator tasks exist; start/stop polling.
src/components/SettingsDialog.tsx Async system font fetching; add docker image setting section; use shared section label style.
src/components/SegmentedButtons.tsx (Re)introduce segmented button component used by project isolation settings.
src/components/ScrollingDiffView.tsx Add base-branch support, improve gaps/expansion, and search highlighting styles.
src/components/PushDialog.tsx Use shared banner styling for errors.
src/components/PromptInput.tsx Improve auto-send stability checks; handle agent exit; reduce marker scan magic numbers.
src/components/MergeDialog.tsx Thread base-branch through merge/rebase/status flows; use shared banners; simplify cancel reset.
src/components/EditProjectDialog.tsx Add default git isolation + base branch settings and update styling.
src/components/DiffViewerDialog.tsx Thread base-branch through diff fetching and rendering.
src/components/ConnectPhoneModal.tsx Migrate modal to shared Dialog component and make QR import more robust.
src/components/CloseTaskDialog.tsx Git isolation-aware messaging + coordinator orphan-warning; use shared banners for warnings/errors.
src/components/ChangedFilesList.tsx Add base-branch support and keyboard/selection scroll behavior improvements.
src/App.tsx Initialize MCP listeners and docker availability; add defensive IPC payload check; adjust shortcut scope.
package.json Add MCP SDK + esbuild bundle step; package MCP server/docker resources; adjust build identity.
electron/remote/server.ts Add orchestrator REST API routes + MCP log ring buffer.
electron/preload.cjs Expand allowed IPC channels; remove ipcRenderer.send exposure.
electron/mcp/types.ts Define shared orchestrator/MCP tool and API types.
electron/mcp/server.ts Add standalone MCP stdio server that delegates to Electron via HTTP.
electron/mcp/prompt-detect.ts Shared prompt detection helpers for renderer/main process.
electron/mcp/orchestrator.ts Implement main-process task lifecycle/orchestration (PTY + git integration).
electron/mcp/client.ts HTTP client used by MCP server process to call remote API.
electron/ipc/tasks.ts Add base-branch support and change deleteTask signature; improve branch naming uniqueness.
electron/ipc/system-fonts.ts Add fc-list based monospace font discovery with caching.
electron/ipc/register.ts Register new IPC handlers (docker, MCP, system fonts, git checks) and refactor window event throttling.
electron/ipc/pty.ts Add Docker mode execution path, container lifecycle management, and docker image build helpers.
electron/ipc/plans.ts Ensure watcher registration order is correct.
electron/ipc/persistence.ts Make persistence write atomic with error cleanup; validate JSON and fall back to backup.
electron/ipc/channels.ts Add new IPC channel definitions (docker/system/MCP/git checks).
docker/Dockerfile Add bundled Docker agent image definition used for docker-isolated tasks.
COORDINATING-AGENT.md Document coordinating agent architecture, tools, and manual test flows.
.prettierignore Ignore .letta/ artifacts.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

const root = this.projectRoot;
if (root) {
try {
await deleteTask([task.agentId], task.branchName, true, root);
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

deleteTask was refactored to take a single options object (see electron/ipc/tasks.ts / electron/ipc/register.ts), but the orchestrator still calls it with the old positional signature. This will fail to typecheck/compile and prevents orchestrated tasks from cleaning up their worktrees. Update the call to pass the new { agentIds, branchName, deleteBranch, projectRoot, taskId? } shape (and consider passing taskId so the plan watcher is stopped as well).

Suggested change
await deleteTask([task.agentId], task.branchName, true, root);
await deleteTask({
agentIds: [task.agentId],
branchName: task.branchName,
deleteBranch: true,
projectRoot: root,
taskId,
});

Copilot uses AI. Check for mistakes.
// Scroll selected item into view reactively
createEffect(() => {
const idx = selectedIndex();
if (idx >= 0) rowRefs[idx]?.scrollIntoView({ block: 'nearest', behavior: 'instant' });
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

scrollIntoView's behavior option is typed as 'auto' | 'smooth' in the DOM lib; 'instant' will cause a TypeScript error and may not be supported consistently at runtime. Use behavior: 'auto' (or omit behavior) if you want immediate scrolling.

Suggested change
if (idx >= 0) rowRefs[idx]?.scrollIntoView({ block: 'nearest', behavior: 'instant' });
if (idx >= 0) rowRefs[idx]?.scrollIntoView({ block: 'nearest', behavior: 'auto' });

Copilot uses AI. Check for mistakes.
Comment on lines 508 to +520
@@ -483,14 +516,12 @@ export async function collapseTask(taskId: string): Promise<void> {
const agentIds = [...task.agentIds];
const shellAgentIds = [...task.shellAgentIds];

for (const agentId of agentIds) {
await invoke(IPC.KillAgent, { agentId }).catch(console.error);
clearAgentActivity(agentId);
}
for (const shellId of shellAgentIds) {
await invoke(IPC.KillAgent, { agentId: shellId }).catch(console.error);
clearAgentActivity(shellId);
}
invoke(IPC.StopPlanWatcher, { taskId }).catch(console.error);
const allIds = [...agentIds, ...shellAgentIds];
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

collapseTask invokes IPC.StopPlanWatcher twice back-to-back for the same taskId. This is redundant work (and makes the intent harder to follow). Keep a single call (either before killing agents or just before), relying on backend idempotency if needed.

Copilot uses AI. Check for mistakes.
Comment on lines +226 to +242
if (url.pathname === '/api/tasks' && req.method === 'POST') {
readBody()
.then(async (body) => {
mcpLog('info', `create_task name=${body.name as string}`);
const result = await orch.createTask({
name: body.name as string,
prompt: body.prompt as string | undefined,
coordinatorTaskId: (body.coordinatorTaskId as string) ?? 'api',
projectId: body.projectId as string | undefined,
});
mcpLog('info', `create_task OK id=${result.id}`);
jsonReply(201, orch.getTaskStatus(result.id));
})
.catch((err) => {
mcpLog('error', `create_task FAIL: ${String(err)}`);
jsonReply(500, { error: String(err) });
});
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

These orchestrator API routes trust body.name, body.prompt, etc. via direct casts. If a client sends invalid JSON (or missing/incorrect fields), this can propagate undefined into orchestrator calls and result in confusing 500s or runtime errors. Consider validating request bodies (type checks + required fields) and returning a 400 with a clear error for bad input.

Copilot uses AI. Check for mistakes.
Comment on lines 73 to 81
const prefix = id + ':';
for (const key of Object.keys(s.fontScales)) {
if (key === id || key.startsWith(prefix)) delete s.fontScales[key];
}
for (const key of Object.keys(s.panelSizes)) {
if (key.includes(id)) delete s.panelSizes[key];
if (key === id || key.startsWith(prefix)) delete s.panelSizes[key];
}
s.taskOrder = s.taskOrder.filter((x) => x !== id);
s.collapsedTaskOrder = s.collapsedTaskOrder.filter((x) => x !== id);
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

cleanupPanelEntries now only deletes panelSizes keys that equal id or start with ${id}:, but panel sizes are also persisted under keys like task:${id}:... (e.g. ResizablePanel uses persistKey={"task:${taskId}"}). With the current predicate, those entries will never be cleaned up when a task is removed, causing persisted-state growth over time. Consider deleting keys that contain the task ID as a delimited segment (e.g. key.startsWith(task:${id}) / key.includes(:${id}:)), while still avoiding accidental substring matches.

Copilot uses AI. Check for mistakes.
Comment on lines +30 to +41
<div
style={{
'margin-bottom': '12px',
'font-size': '12px',
color: theme.warning,
background: `color-mix(in srgb, ${theme.warning} 8%, transparent)`,
padding: '8px 12px',
'border-radius': '8px',
border: `1px solid color-mix(in srgb, ${theme.warning} 20%, transparent)`,
'font-weight': '600',
}}
>
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

The coordinator warning banner duplicates the styling that bannerStyle(theme.warning) already centralizes (and bannerStyle is already imported in this file). Using bannerStyle here would keep banner visuals consistent and reduce future style drift.

Suggested change
<div
style={{
'margin-bottom': '12px',
'font-size': '12px',
color: theme.warning,
background: `color-mix(in srgb, ${theme.warning} 8%, transparent)`,
padding: '8px 12px',
'border-radius': '8px',
border: `1px solid color-mix(in srgb, ${theme.warning} 20%, transparent)`,
'font-weight': '600',
}}
>
<div style={bannerStyle(theme.warning)}>

Copilot uses AI. Check for mistakes.
Comment on lines 72 to 100
@@ -82,12 +85,17 @@
"electron/preload.cjs"
],
"asarUnpack": [
"**/node-pty/**"
"**/node-pty/**",
"dist-electron/mcp-server.cjs"
],
"extraResources": [
{
"from": "build/icon.png",
"to": "icon.png"
},
{
"from": "docker/",
"to": "docker/"
}
],
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

PR description says Docker isolation mode is removed, but this PR adds/expands Docker isolation support (e.g. Dockerfile under docker/, docker image settings in persisted state, and bundling docker/ as extraResources). Please reconcile the description with the implemented changes (or clarify that an older Docker mode was removed and replaced with the new one).

Copilot uses AI. Check for mistakes.
@johannesjo
Copy link
Copy Markdown
Owner

Thank you very much! <3

@johannesjo
Copy link
Copy Markdown
Owner

Thanks for this — it's a genuinely interesting feature and a well-structured implementation. Here's an honest assessment to help move it forward.

State of the branch

The branch does not appear to rebase cleanly on current main (the author confirmed lingering npm-version and merge conflicts in their last comment from March 24). The PR diff shows no conflict markers, but the branch is not up to date with the commits that have landed on main since then (e.g., the steps/indicator refactors). A rebase is required before this can be meaningfully reviewed or merged.

Architecture & Security

The good:

  • The new /api/tasks/* REST routes are correctly placed inside the existing if (url.pathname.startsWith('/api/')) guard in electron/remote/server.ts, which checks Authorization: Bearer <token> using timingSafeEqual before any route handler runs. Auth is not bypassed.
  • The MCP client hardcodes http://127.0.0.1 (not the network IP), so the bearer token only travels over loopback.
  • The ephemeral bearer token is written to .mcp.json in the worktree and immediately appended to .git/info/exclude — a thoughtful touch to prevent accidental commits.

Issues to address:

  1. Hardcoded port 7777IPC.StartMCPServer always starts the remote server on port 7777. If that port is in use the server silently fails to start. The port should either be configurable or the server should try a fallback.
  2. Network exposure — The remote server still binds to 0.0.0.0 (existing behaviour, not introduced here). Now that the API surface includes task creation, arbitrary prompt injection, and branch merging, the risk of the bearer token being intercepted on a shared network is higher. Consider binding to 127.0.0.1 for the MCP use-case, or at least documenting the risk.
  3. body.name as string without validation — The POST /api/tasks handler casts body.name directly to string and passes it to createBackendTask. A missing or non-string name would propagate as undefined. The existing IPC layer uses assertString helpers — the REST layer should do the same.
  4. Docker isolation removed then re-added — The PR summary says Docker isolation mode is removed, but docker/Dockerfile, docker IPC handlers (IPC.CheckDockerAvailable, IPC.BuildDockerImage, etc.), and TerminalView docker spawn paths are all present in the diff. This seems contradictory; please clarify what was actually removed vs added.

Scope

This is a very large PR (67 files, ~13 000 diff lines). The coordinator feature itself is the clearly novel piece. The refactor of TaskPanel (merging six components into one ~1300-line file), the base-branch plumbing through every git command, Docker isolation, and system-font enumeration are each independent concerns. Splitting into smaller PRs would make review much more tractable — but if that's not practical, at minimum please rebase first.

Summary

Rebase on main, fix the hardcoded port and the missing body validation on the REST endpoint, and this will be in good shape for a proper review.

Copy link
Copy Markdown
Owner

@johannesjo johannesjo left a comment

Choose a reason for hiding this comment

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

Please rebase onto main before anything else — the branch has known merge conflicts that block review.

After rebase, a few items to address:

  1. Hardcoded port 7777 — no fallback if the port is in use; port conflicts will silently break the MCP server at runtime.
  2. Missing REST input validation — the orchestrator REST endpoints lack the assertString-style validation that IPC handlers enforce; user-controlled inputs should be validated on the main-process side.
  3. Bundled scope — Docker isolation, base-branch plumbing, TaskPanel consolidation, and the coordinator feature are all in one PR, making it very difficult to review. Splitting would make each concern reviewable on its own merits.

The core MCP/orchestration concept is solid. Details in the review comment.

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.

3 participants