Skip to content

fix(orchestration): wire Heavy Brain mode, fix project grouping and relay display#134

Open
FridayLiu wants to merge 25 commits intomainfrom
feat/new-orchestrator
Open

fix(orchestration): wire Heavy Brain mode, fix project grouping and relay display#134
FridayLiu wants to merge 25 commits intomainfrom
feat/new-orchestrator

Conversation

@FridayLiu
Copy link
Copy Markdown
Collaborator

Summary

Fix multiple issues with the team orchestration feature:

Heavy Brain mode wiring

  • Wire the Light/Heavy Brain selection from PromptInput team menu through handleOrchestrationSend to gateway.createOrchestration
  • Show team menu (forced Light/Heavy choice) only on team parent sessions before first run; revert to normal send button afterward
  • Add plan confirmation support to HeavyBrainOrchestrator — pauses on first dispatch when requirePlanConfirmation is set, same pattern as Light Brain

Project grouping after refresh

  • Pass worktreeId and parentDirectory from the team session to OrchestrationCreateRequest so the orchestration run inherits correct project association
  • Fix DAG executor to always pass the parent project directory (not the resolved worktree dir) to createSession — the worktree dir is resolved internally from worktreeId, preventing resolveProjectId from deriving the wrong project name

Relay message display

  • Add internal flag to ConversationMessage and UnifiedMessage types
  • Mark orchestration relay user messages as internal: true so SessionTurn hides the user bubble while preserving turn structure for the assistant response
  • Suppress engine-emitted message.updated events for internal sends via internalSendSessions tracking set — prevents the relay prompt from appearing as user input in real-time
  • Skip applyTitleFallback for internal messages to prevent the session title from being overwritten by the relay prompt

User prompt persistence

  • Persist the user's orchestration prompt to the parent session conversation file in OrchestrationService.createRun so it survives page refresh

Files changed

  • electron/main/gateway/engine-manager.tsinternal option on sendMessage, suppress user events, persist internal flag
  • electron/main/services/orchestration/index.ts — persist user prompt, pass awaitPlanConfirmation to Heavy Brain, mark relay as internal
  • electron/main/services/orchestration/heavy-brain.ts — plan confirmation support, accept awaitPlanConfirmation
  • electron/main/services/orchestration/dag-executor.ts — always use parent dir for task execution
  • src/pages/Chat.tsx — wire onTeamSend, pass worktreeId/parentDirectory, mode parameter
  • src/components/PromptInput.tsx — hide send button when team menu is active
  • src/components/SessionTurn.tsx — hide user bubble for internal messages
  • src/types/unified.ts — add internal field to message types

realDuang and others added 25 commits April 16, 2026 19:28
Add complete multi-engine task orchestration infrastructure:
- Backend orchestrator service with DAG-based parallel execution, team worktree isolation, and result aggregation via sendMessage
- Role-based engine assignment (explorer/researcher/reviewer/designer/coder) inspired by oh-my-opencode-slim
- Frontend orchestration store, gateway API integration, and WS event handling
- UI components: plan confirmation, DAG execution visualization, result cards, subtask editor with role selection
- Sidebar team grouping with parent/child visual distinction and team worktree filtering
- Chat.tsx integration: team task creation, orchestration flow interception, state restoration on refresh

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Upgrade @github/copilot-sdk from 0.2.0 to 0.2.2
- Switch default theme from "system" to "dark" for a more consistent
  first-launch experience with the upcoming team orchestration UI

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…llapse

- Streamline TeamPlanCard layout and spacing
- Fix SubtaskEditor collapse/expand behavior

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ection

- Persist orchestration runs to disk so team state survives restart
- Move team worktree creation to frontend; backend accepts worktreeInfo
  in OrchestrationCreateRequest and reuses it for all subtask sessions
- Dispatch subtasks via worktreeId (team worktree name) so all child
  sessions share the same working tree
- Extract result summary directly from engine response text parts
- Add autoDetectTeams() to recover team grouping from worktreeId pattern
  ("team-*") when a run is not yet in the orchestration store
- Allow team-* worktree creation even when global worktree toggle is off
- Add updateRoleMappings/getRoleMappings helpers backed by settings.json

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Settings
- Add Role → Engine mapping editor with per-role engine selection,
  persisted to settings.json via updateRoleMappings()
- Split Experimental section: stable Features (workspace, scheduled
  tasks, worktree) vs Experimental (team orchestration)
- Fix SolidJS <select value> not syncing when <For> options load
  asynchronously by applying the ref+createEffect pattern already
  used by the model selector in the same file

Chat page
- Separate orchestrator Dashboard vs Chat views via a pill-style tab
  bar (macOS segmented control look); default to Dashboard
- Auto-switch to Dashboard when a new team run is created
- Reset to Dashboard on session switch
- New team task flow creates the team worktree on the frontend and
  passes worktreeInfo to the backend
- Refresh path runs autoDetectTeams to recover stale team sessions

Sidebar
- Add delete button on team worktree header that removes all sessions
  in the team (clears the "stale Team mo2* entries" use case)

i18n / styles
- en/zh/ru strings for team orchestration, role labels, and settings
- CSS-based tooltip workaround for Electron + macOS hiddenInset title
  bar (native title tooltips don't fire reliably on hover-revealed
  buttons)
- Session store exposes teamOrchestrationEnabled flag

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implement cross-engine agent team orchestration with two modes:
- Light Brain: deterministic DAG planning + parallel execution
- Heavy Brain: LLM orchestrator loop with dynamic task dispatch

Includes StructuredOutputSkill framework for format-safe LLM output,
DAG executor with topological scheduling, gateway integration,
minimal frontend with team trigger button and status bar.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Implement the agent-team hardening roadmap: DAG-aware heavy execution, real cancellation, planner engine selection, persisted run recovery, inactivity guardrails, minimal observability UI, and protocol-level test coverage.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add a managed server restart flow that preserves the existing quick tunnel when possible, expose it via package scripts, document the behavior, and cover the shell workflow with unit tests.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Deduplicate team runs by id in the team store, cover the create-plus-notification race with tests, remove the duplicate relay notice, and move long final results out of the status card into a separate result panel.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Phase 0 of the Agent Team consolidation plan: bring fridayliu's
Light/Heavy Brain orchestration (electron/main/services/agent-team/*)
onto PR #117's team orchestration base while preserving authorship
on both sides via a merge commit.

Conflict resolution strategy:
- ws-server.ts: keep both orchestratorService (PR #117) and
  agentTeamService wired in parallel; orchestration handlers will be
  absorbed in later phases.
- gateway-api.ts / gateway-client.ts: take union of Orchestration* and
  Team* method groups, types, and notification bindings.
- types/unified.ts: keep both ORCHESTRATION_* and TEAM_* request/
  notification constants.
- Chat.tsx: run PR #117's isTeamParentSession interception first, then
  fridayliu's activeHeavyRelayRun relay; rename duplicate handleTeamSend
  to handleOrchestrationSend to disambiguate.

No behavioral change expected beyond the sum of both branches. Typecheck
passes, all 2312 unit tests pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ng gateway

Extend unified types and gateway protocol to support concepts absorbed
from PR #117 (orchestrator-service) into the agent-team module:

Types (src/types/unified.ts)
- TaskNode: add role?, modelId?, needsWorktree?, duration?
- TeamRun: add teamWorktreeName?, teamWorktreeDir?, roleMappings?
- TeamRunStatus: add 'awaiting-confirmation'
- TeamCreateRequest: add roleMappings?, teamWorktreeInfo?, requirePlanConfirmation?
- New requests: TeamConfirmPlanRequest, TeamUpdateRoleMappingsRequest,
  TeamGetRoleMappingsResponse
- New gateway request keys: TEAM_CONFIRM_PLAN, TEAM_GET_ROLE_MAPPINGS,
  TEAM_UPDATE_ROLE_MAPPINGS

AgentTeamService (electron/main/services/agent-team/index.ts)
- DEFAULT_ROLE_MAPPINGS (5 roles adapted from PR #117)
- awaitPlanConfirmation(runId): promise-based gate for Light Brain to pause
  after DAG generation
- confirmPlan(runId, tasks): resolve gate from gateway handler
- getRoleMappings / updateRoleMappings backed by settings.json via
  loadSettings/saveSettings (team.roleMappings key)
- resolveRole(role, fallback): role → {engineType, modelId}
- Pending confirmations rejected on cleanup to avoid hangs

Gateway (ws-server, gateway-api, gateway-client)
- Wire TEAM_CONFIRM_PLAN / TEAM_GET_ROLE_MAPPINGS / TEAM_UPDATE_ROLE_MAPPINGS
  end-to-end (ws handler + client request + api facade)

The LightBrain/HeavyBrain orchestrators do not yet call
awaitPlanConfirmation or resolveRole — that wiring lands in Phase 2
(t2-role, t2-confirm). The gate / resolver methods are in place so the
protocol surface is stable for frontend work in Phase 3.

Verified: npm run typecheck clean; bun run test:unit 70/70 files,
2312/2312 tests passing.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Wire the Phase 1 types/gateway plumbing into the orchestrator runtime so
the concepts that used to live in orchestrator-service.ts (PR #117) are
now executed by the unified Light/Heavy brain pipeline.

TaskExecutor
- new optional RoleResolver type + constructor param
- executeAttempt() resolves task.role → {engineType, modelId} before
  session creation, only when task.engineType is not already set (explicit
  engineType from the planner still wins)

LightBrainOrchestrator
- two new optional constructor params: resolveRole, awaitPlanConfirmation
- new 'Phase 1.5' between planning and execution:
  if teamRun.requirePlanConfirmation is true and awaitPlanConfirmation is
  provided, set status to 'awaiting-confirmation' and block until the
  gateway resolves the gate via TEAM_CONFIRM_PLAN. User-edited tasks
  replace the auto-generated DAG before execution.
- If the gate rejects (run cancelled mid-confirmation), mark run failed
  cleanly instead of crashing.
- TaskExecutor is now constructed with resolveRole.

HeavyBrainOrchestrator
- new optional resolveRole constructor param (threaded into TaskExecutor).
- Concurrency limit arg moves from 3rd to 4th position; updated call site
  in the one test that used it.

AgentTeamService (createRun / executeRun)
- TeamRun snapshot now carries roleMappings, teamWorktreeName/Dir (from
  teamWorktreeInfo), and requirePlanConfirmation (default: light=on,
  heavy=off).
- executeRun injects resolveRole + awaitPlanConfirmation into the
  orchestrators from Phase 1 helpers.

Types
- TeamRun gains requirePlanConfirmation? to mirror TeamCreateRequest so
  the run snapshot is self-describing (needed by LightBrain runtime and
  by frontend to know whether to render the plan-confirm card).

Verified: npm run typecheck clean; bun run test:unit 70/70 files,
2312/2312 tests passing (1 test updated for HeavyBrain constructor
signature change).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Extract the inline team run card from Chat.tsx (~200 lines) into a
dedicated component in src/components/team/, shrinking Chat.tsx and
adding the user-facing plan confirmation action for the new
'awaiting-confirmation' status.

New files
- src/components/team/TeamRunCard.tsx — renders a TeamRun with its
  tasks, final result, cancel button, and (when status is
  'awaiting-confirmation') a 'Confirm & execute' button that calls
  gateway.confirmTeamPlan.
- src/components/team/team-run-helpers.ts — status label / color /
  icon helpers extracted from Chat.tsx, bound to the shared LocaleDict.

i18n
- New keys teamRunAwaitingConfirmation / teamRunConfirmPlan /
  teamRunEditPlan added to en / zh / ru (keep all locales in sync).

Chat.tsx
- Import TeamRunCard and use it instead of the inline JSX block.
- New handleConfirmTeamPlan handler that forwards to
  gateway.confirmTeamPlan.
- Remove now-duplicate helper functions (isTerminalTeamRun,
  getTeamRunStatusColor, getTeamRunStatusLabel, getTeamRunModeLabel,
  getTeamTaskStatusIcon, getTeamTaskStatusColor).

Net effect: Chat.tsx goes from 3099 → 2931 lines; the user can now
actually act on the Phase 2 'awaiting-confirmation' gate.

The PR #117 OrchestrationCards components are left untouched — they
will be removed in a later step together with orchestrator-service.ts
once the new team flow fully replaces them.

Verified: npm run typecheck clean; bun run test:unit 70/70 files,
2312/2312 tests passing.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add 10 unit tests covering the Phase 2 behaviors wired in 9e1c163.

TaskExecutor (4 new cases)
- Resolves task.role to engineType + modelId via injected RoleResolver
- Preserves explicit task.engineType when both role and engineType set
- Falls back to defaultEngineType when RoleResolver returns null

LightBrainOrchestrator (2 new cases)
- Pauses at 'awaiting-confirmation' when requirePlanConfirmation is true;
  resumes with user-edited tasks; replaces auto-generated DAG
- Marks run 'failed' cleanly when awaitPlanConfirmation rejects (e.g.
  cancelled run) and never spawns worker sessions

AgentTeamService (4 new cases, two new describe blocks)
- role mappings:
  - getRoleMappings() returns DEFAULT_ROLE_MAPPINGS when nothing persisted
  - updateRoleMappings() persists to settings.json under team.roleMappings
    and reloads identically
  - resolveRole() maps known roles and falls back on unknown
- plan confirmation gate:
  - awaitPlanConfirmation() resolves when confirmPlan() fires
  - confirmPlan() throws for unknown runId

Verified: npm run typecheck clean; bun run test:unit 70/70 files,
2322/2322 tests passing (was 2312, +10).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…esign

Adds docs/agent-team.md covering the architecture after merging PR #117 and
fridayliu/feat/agentteam:

- Two-brain split (Light one-shot planner vs Heavy persistent orchestrator)
  with shared DAG executor, TaskExecutor, and guardrails.
- Plan-confirmation flow: awaiting-confirmation gate, TEAM_CONFIRM_PLAN
  gateway call, user-edited DAG resumption.
- Role resolution: DEFAULT_ROLE_MAPPINGS, persistence via settings.json
  team.roleMappings, resolution order (explicit engineType > role > default).
- Team worktree: current type coverage and planned DAGExecutor enforcement;
  read-only roles opt out; team-* worktree name whitelist.
- Service coexistence: PR #117 orchestrator-service and fridayliu agent-team
  ship side-by-side.
- Gateway key reference table.
- Pointer to unit test layout.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…tion

Ports PR #117's aggregateResults pattern into the merged Light/Heavy
brain pipeline. After a run reaches a terminal state (completed or
failed), AgentTeamService.relayResultsToParentSession() sends the
aggregated finalResult + failed-task list as a user message to the
parent session, letting the parent engine summarize for the user.

Changes
- src/types/unified.ts: TeamRun and TeamCreateRequest both gain
  'aggregateToParent?: boolean' (default true when parent exists).
- electron/main/services/agent-team/index.ts:
  - createRun() populates aggregateToParent from request (default true).
  - executeRun() calls relayResultsToParentSession() after orchestrator
    completes but before cleanup so the emitRunUpdated finalizes state.
  - New private relayResultsToParentSession():
    - No-ops when no parentSessionId / no engineManager / no finalResult
    - No-ops when aggregateToParent === false
    - Only relays for 'completed' or 'failed' status
    - Builds a prompt with header + aggregated task results + failed
      tasks section, asks parent engine for a concise summary.
    - Try/catch swallows failures with a warn log so a broken parent
      session never aborts the team run state.

Tests (+3)
- tests/unit/electron/services/agent-team/index.test.ts: three new
  cases under 'result aggregation to parent session':
  - Relays to parentSessionId after light run completes; prompt
    contains task descriptions and results.
  - Skips when aggregateToParent=false.
  - Skips when parentSessionId is missing.

Verified: npm run typecheck clean; bun run test:unit 2325/2325 (was 2322).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…cutor

Completes t2-worktree: DAGExecutor.runSingleTask now routes
write-capable tasks into the run's shared team worktree so sibling
tasks see each other's edits, while keeping read-only tasks on the
primary directory to avoid worktree contention.

Routing rules in DAGExecutor.runSingleTask():
- Read-only detection = task.needsWorktree === false, OR
  run.roleMappings entry for task.role has readOnly === true.
- If run.teamWorktreeDir is set AND task is write-capable:
    directory         = run.teamWorktreeDir
    defaultWorktreeId = run.teamWorktreeName (fallback run.worktreeId)
- Otherwise (read-only or no team worktree):
    directory         = this.directory (run's primary dir)
    defaultWorktreeId = run.worktreeId

This is a pure routing change — TaskExecutor already respects the
directory + defaultWorktreeId it is handed; no changes there.

Tests (+4, new 'team worktree routing' describe):
- Routes write-capable tasks into teamWorktreeDir.
- Keeps read-only (needsWorktree=false) tasks in primary directory.
- Treats read-only role mapping (roleMappings[].readOnly) as read-only.
- Falls back to run.directory / worktreeId when teamWorktreeDir unset.

Verified: npm run typecheck clean; bun run test:unit 2329/2329 (was 2325).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Completes t3-settings-ui by wiring the existing PR #117 role-engine
mapping editor (Settings → Team Orchestration) so every edit mirrors
to the Light/Heavy brain's AgentTeamService in addition to the legacy
orchestrator-service.

Change
- src/stores/orchestration.ts: updateRoleMappings() now calls
  gateway.updateTeamRoleMappings(mappings) in addition to updating
  orchestrationStore and saveSetting(). The gateway call is fire-and-
  forget with a warn log on failure so UI responsiveness is unaffected.

Result: users editing role→engine mappings in Settings now affect BOTH
orchestration flows. The AgentTeamService persists the update to
settings.json under 'team.roleMappings' and resolveRole() picks it up
on the next task dispatch.

No UI changes were needed — the existing Settings section already
renders roleMappings from orchestrationStore; we simply added the
second write target in the shared update function.

Verified: npm run typecheck clean; bun run test:unit 2329/2329.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ridge

Completes t3-sidebar: bridges AgentTeamService team runs into the
orchestration sidebar-grouping registry (originally added by PR #117)
so child sessions spawned by Light/Heavy brain tasks collapse under
their parent session in SessionSidebar, matching the UX of PR #117's
orchestrator-service flow.

Changes
- src/stores/orchestration.ts: new associateChildSession(teamId,
  sessionId) helper that maps a child session into an existing team.
- src/pages/Chat.tsx: wraps connectTeamHandlers() so every TeamRun
  update triggers bridgeToSidebar():
    registerTeam(teamId, parentSessionId, worktreeInfo?)
    associateRunWithTeam(teamId, runId)
    associateChildSession(teamId, task.sessionId) for every task.sessionId
  teamId reuses any existing one for the parent session, falling back to
  the run.id. Idempotent; safe to call on every update so tasks tagged
  late in the run still get grouped.
- Not placed inside stores/team.ts to avoid coupling its module imports
  with the orchestration store; the team test mock uses a single
  createStore container shared across modules, which would break if
  orchestration were imported at module load.

No new tests — this is pure UI glue over existing registry helpers;
orchestration + team store behavior is already covered.

Verified: npm run typecheck clean; bun run test:unit 2329/2329 (no regressions).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…grouping

Updates docs/agent-team.md now that the previously-planned behaviors
have landed:

- Team Worktree: replaced the 'planned' section with the actual
  routing rules used by DAGExecutor.runSingleTask() (write tasks vs
  read-only tasks, role.readOnly detection, fallback when
  teamWorktreeDir is unset).
- New 'Result Aggregation to Parent' section describing
  relayResultsToParentSession() and the aggregateToParent gate.
- New 'Sidebar Grouping' section describing the Chat.tsx bridge that
  reuses PR #117's orchestration registry for AgentTeamService runs.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Unify on a single Orchestration naming convention across backend, gateway,
frontend, tests, and docs, while keeping PR #117's role-based UI and
fridayliu's Light/Heavy Brain DAG executor backend.

Backend:
- Move electron/main/services/agent-team/ -> orchestration/
- Delete orchestrator-service.ts (concepts already absorbed by LightBrain,
  role resolution, plan confirmation, and team-worktree routing)
- Rename identifiers: AgentTeamService -> OrchestrationService, TeamRun ->
  OrchestrationRun, TaskNode -> OrchestrationSubtask, run.tasks ->
  run.subtasks, originalPrompt -> prompt, result/finalResult ->
  resultSummary, status 'planning' -> 'decomposing',
  'awaiting-confirmation' -> 'confirming'
- Persistence file agent-team-runs.json -> orchestration-runs.json
- Log scope agent-team -> orchestration

Gateway protocol:
- Collapse all TEAM_* keys under ORCHESTRATION_* namespace and add GET,
  SEND_MESSAGE, GET_ROLE_MAPPINGS, UPDATE_ROLE_MAPPINGS
- Replace team.run.updated / team.task.updated notifications with
  orchestration.updated / orchestration.subtask.updated

Frontend:
- Delete src/stores/team.ts, src/components/team/ (TeamRunCard etc.)
- Keep PR #117 UI components under src/components/orchestration/
  (OrchestrationCards, TeamPlanCard, TeamResultCard, TeamExecutionCard,
  SubtaskEditor) rebound to unified Orchestration* types
- Strip ~150 lines of inline team* flow from Chat.tsx and route through
  orchestrationStore + gateway.sendOrchestrationMessage / confirmOrchestrationPlan
- Rename settings key team.roleMappings -> orchestration.roleMappings
- Rename i18n keys teamRun* -> orchestrationRun*, teamRelay* ->
  orchestrationRelay*, teamMessageRelayFailed -> orchestrationMessageRelayFailed
- Update user-facing English/Chinese/Russian strings

Tests / docs:
- Move tests/unit/electron/services/agent-team/ -> orchestration/
- Update ws-server.test.ts + gateway-api.test.ts mocks and assertions
- Rename docs/agent-team.md -> docs/orchestration.md and update prose

All 2320 unit tests pass; typecheck clean.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…elay display

- Wire Heavy Brain UI: pass mode from PromptInput team menu through
  handleOrchestrationSend to gateway.createOrchestration. Show team
  menu (Light/Heavy choice) only on team parent sessions before first
  run; revert to normal send button after.

- Fix project grouping after refresh: pass worktreeId and parentDirectory
  from the team session to OrchestrationCreateRequest so child sessions
  inherit correct project association. Fix DAG executor to always pass
  parent project dir (not resolved worktree dir) to createSession —
  prevents resolveProjectId from deriving wrong project name.

- Fix relay message display: add internal flag to ConversationMessage
  and UnifiedMessage. Mark orchestration relay user messages as internal
  so SessionTurn hides the user bubble while preserving turn structure.
  Suppress engine-emitted user message.updated events for internal sends
  via internalSendSessions tracking set.

- Add Heavy Brain plan confirmation: pass awaitPlanConfirmation to
  HeavyBrainOrchestrator, pause on first dispatch when
  requirePlanConfirmation is set. Emit run update in
  awaitPlanConfirmation so frontend shows the confirmation UI.

- Persist user prompt: save the user's orchestration prompt to the
  parent session conversation file in createRun so it survives refresh.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings April 27, 2026 15:45
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 wires the unified Light/Heavy Brain “team orchestration” flow end-to-end (gateway ↔ engine manager ↔ orchestrators ↔ UI), improves project/worktree grouping reliability after refresh, and adjusts relay-message rendering so orchestration relays don’t appear as normal user input.

Changes:

  • Add orchestration run APIs/events to the gateway protocol + client bindings, and implement orchestration services (DAG/task execution, skills/parsers, prompts, user relay channel, plan confirmation).
  • Update UI/stores/types to support orchestration runs, plan confirmation cards, role→engine mappings, internal (hidden) relay messages, and team session grouping.
  • Enhance headless dev server scripts with a restart command that preserves the managed Cloudflare tunnel where possible.

Reviewed changes

Copilot reviewed 57 out of 58 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
tests/unit/src/lib/gateway-api.test.ts Adds unit coverage for orchestration methods/events in GatewayAPI.
tests/unit/shared/server-dev-script.test.ts Adds integration-style tests for scripts/server-dev.sh restart behavior.
tests/unit/electron/services/orchestration/task-executor.test.ts Tests TaskExecutor inactivity timeout, retries, worktreeId propagation, role→engine resolution.
tests/unit/electron/services/orchestration/skills.test.ts Tests JSON extraction/parsing + planning/dispatch skill validators and retry flow.
tests/unit/electron/services/orchestration/light-brain.test.ts Tests Light Brain worktree context, planner worktree propagation, and plan-confirmation gating.
tests/unit/electron/services/orchestration/index.test.ts Tests orchestration service persistence, relay contract, role mappings, and plan confirmation gate behavior.
tests/unit/electron/services/orchestration/dag-executor.test.ts Tests DAG execution ordering, concurrency limits, and team worktree routing logic.
tests/unit/electron/gateway/ws-server.test.ts Adds gateway WS route + broadcast tests for orchestration requests/notifications.
src/types/unified.ts Introduces orchestration types + gateway request/notification constants; adds internal?: boolean on messages.
src/stores/session.ts Adds teamId grouping + teamOrchestrationEnabled feature flag in session store.
src/stores/orchestration.ts New frontend orchestration store for runs/teams/session grouping + role mappings persistence/sync.
src/pages/Settings.tsx Adds team orchestration toggle and role→engine mapping editor UI.
src/locales/zh.ts Adds i18n strings for orchestration UI + settings.
src/locales/ru.ts Adds i18n strings for orchestration UI + settings.
src/locales/en.ts Adds i18n strings for orchestration UI + settings.
src/lib/theme.ts Changes default theme fallback behavior.
src/lib/gateway-client.ts Adds orchestration APIs + event typings to gateway client.
src/lib/gateway-api.ts Adds orchestration methods + binds orchestration notifications to handlers.
src/index.css Adds CSS tooltip workaround for sidebar buttons with title.
src/components/orchestration/TeamResultCard.tsx UI card for completed/failed run summary + per-subtask results.
src/components/orchestration/TeamPlanCard.tsx UI card for plan confirmation/editing before execution.
src/components/orchestration/TeamExecutionCard.tsx UI card for live DAG execution progress + visualization.
src/components/orchestration/SubtaskEditor.tsx Editor for modifying subtasks (role/engine/deps/description).
src/components/orchestration/OrchestrationCards.tsx Switchboard component for orchestration status → appropriate card.
src/components/SessionTurn.tsx Hides user bubble for internal user messages while preserving turn structure.
src/components/PromptInput.tsx Adds Team Run menu (Light/Heavy) and relay-to-orchestrator UX behavior.
scripts/server-init.sh Documents new server:restart command.
scripts/server-dev.sh Implements restart that preserves managed tunnel when possible.
package.json Adds server:restart npm script; bumps @github/copilot-sdk version.
electron/main/services/orchestration/user-channel.ts Adds a buffered human-in-the-loop channel for orchestrator relays.
electron/main/services/orchestration/task-executor.ts Implements per-subtask session execution, retries, inactivity timeout handling.
electron/main/services/orchestration/skills.ts Adds structured-output “skills” for planning/dispatch JSON formats + retry/correction loop.
electron/main/services/orchestration/prompts.ts Adds engine-agnostic prompt templates and task-result formatting.
electron/main/services/orchestration/logger.ts Re-exports scoped logger for orchestration.
electron/main/services/orchestration/light-brain.ts Implements Light Brain DAG planning + optional plan confirmation + execution.
electron/main/services/orchestration/guardrails.ts Adds orchestration guardrail constants.
electron/main/services/orchestration/dag-executor.ts Implements deterministic DAG scheduler + team worktree routing policy.
electron/main/services/orchestration/index.ts OrchestrationService CRUD, persistence, role mappings, plan-confirmation gate, and run execution wiring.
electron/main/services/orchestration/heavy-brain.ts Heavy Brain orchestrator loop support (incl. relays / dispatch protocol).
electron/main/services/logger.ts Adds orchestrationLog scope.
electron/main/index.ts Initializes orchestrationService at app startup.
electron/main/gateway/ws-server.ts Adds gateway routes + broadcasts for orchestration APIs and whitelists team-* worktrees.
electron/main/gateway/engine-manager.ts Adds internal-send suppression + internal message persistence + buffered-part merge into returned message.
electron/main/engines/opencode/index.ts Adds per-session system prompt composition for orchestration instructions.
electron/main/engines/copilot/index.ts Adds per-session system prompt composition for orchestration instructions (create/resume).
electron/main/engines/codex/index.ts Adds per-session system prompt propagation into thread start/resume.
electron/main/engines/claude/index.ts Adds per-session system prompt append support.
docs/orchestration.md Adds orchestration architecture/behavior documentation.
bun.lock Updates lockfile for @github/copilot-sdk bump.
README.zh-CN.md Updates quick tunnel rotation wording.
README.ru.md Updates quick tunnel rotation wording.
README.md Documents server:restart and updates quick tunnel rotation wording.
README.ko.md Updates quick tunnel rotation wording.
README.ja.md Updates quick tunnel rotation wording.

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

Comment on lines 888 to +895
const result = await adapter.sendMessage(engineSessionId, content, {
...options,
directory: conv.directory,
});

// Clear the internal send suppression flag after the send completes
this.internalSendSessions.delete(engineSessionId);

Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

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

internalSendSessions is only cleared after adapter.sendMessage resolves. If adapter.sendMessage throws/rejects, the session will remain in internalSendSessions, causing future real user message.updated events to be suppressed indefinitely for that engine session. Move the internalSendSessions.delete(engineSessionId) into a finally block that always runs after the send attempt (and only add/delete when options.internal is true).

Suggested change
const result = await adapter.sendMessage(engineSessionId, content, {
...options,
directory: conv.directory,
});
// Clear the internal send suppression flag after the send completes
this.internalSendSessions.delete(engineSessionId);
let result;
try {
result = await adapter.sendMessage(engineSessionId, content, {
...options,
directory: conv.directory,
});
} finally {
// Clear the internal send suppression flag after the send attempt completes
if (options?.internal) {
this.internalSendSessions.delete(engineSessionId);
}
}

Copilot uses AI. Check for mistakes.
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.

4 participants