feat(agent): orchestration v3 — surface MCP model-agnostic (M0→M4) — §14.3

Expose l'orchestration IdeA comme serveur MCP par-dessus le même
OrchestratorService::dispatch, avec repli fichier .ideai/requests pour les
CLI sans MCP. v3 réduite à la surface MCP : la messagerie inter-agents et la
corrélation requête↔réponse étaient déjà résolues par §17 (send_blocking).

- M0 capacité MCP sur le profil (McpCapability/McpConfigStrategy/McpTransport)
- M1 injection conf MCP au LaunchAgent + prose adaptée selon la surface
- M2 serveur/adapter MCP (JSON-RPC 2.0 maison ; outils idea_*) + ListAgents
- M3 câblage par projet (registre mcp_servers jumeau du watcher)
- M4 observabilité UI : OrchestratorRequestProcessed.source = file|mcp + badge

Trois portes d'entrée (fichier, MCP, UI) → un seul dispatch ; aucun nouveau
port applicatif ; MCP confiné à l'adapter infra. Tous lots verts (cycle §3).
Cadrage : .ideai/briefs/orchestration-v3-cadrage.md ; ARCHITECTURE.md §14.3.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-10 12:53:31 +02:00
parent 97daf3fae5
commit 37e72747d3
30 changed files with 3646 additions and 27 deletions

View File

@ -323,6 +323,9 @@ export function AgentsPanel({ projectId, projectRoot = "" }: AgentsPanelProps) {
vm.profiles.find((p) => p.id === a.profileId)?.name ??
a.profileId;
const agentDrift = drift.driftByAgentId.get(a.id);
// Source of this agent's last orchestration delegation (mcp vs
// file), if any has been observed. Absent ⇒ no badge.
const delegationSource = vm.delegationSourceByRequester[a.id];
return (
<li
key={a.id}
@ -347,6 +350,19 @@ export function AgentsPanel({ projectId, projectRoot = "" }: AgentsPanelProps) {
update available
</span>
)}
{delegationSource && (
<span
aria-label={`delegation source ${delegationSource}`}
title={
delegationSource === "mcp"
? "Last delegation arrived via the MCP server"
: "Last delegation arrived via .ideai/requests"
}
className="rounded-full bg-raised px-2 py-0.5 text-xs font-medium uppercase tracking-wide text-muted"
>
{delegationSource}
</span>
)}
</span>
<span className="text-xs text-muted">{profileName}</span>
{live && (