feat(agent): messagerie inter-agents via send_blocking (D6) — §17
Comble le trou historique : un agent qui en interroge un autre reçoit enfin
le CONTENU de la réponse, plus seulement un ACK de cycle de vie.
- domain : OrchestratorCommand::AskAgent { target, task } + action wire
agent.message (target+task requis) ; DomainEvent::AgentReplied
{ agent_id, reply_len } (bus I/O-free, le contenu remonte par l'outcome).
- application : OrchestratorOutcome gagne reply: Option<String>. Méthode
ask_agent (§17.4) — cible structurée vivante ⇒ send_blocking direct ;
cible morte ⇒ LaunchAgent structuré puis send ; agent vivant en PTY ou
profil sans structured_adapter ⇒ Invalid (non adressable, jamais d'ACK
trompeur) ; agent inconnu ⇒ NotFound ; service non câblé ⇒ Invalid.
Timeout (300s) ⇒ erreur typée SANS tuer la session. Succès ⇒ publie
AgentReplied + reply: Some(content). Injection additive via builders
with_structured / with_events (call sites legacy intacts).
- app-tauri : state câble with_structured/with_events ; DTO AgentReplied.
Tests (QA) : +14 verts (11 app + 3 domaine), invariants validés par
mutation test (reply!=None, timeout ne shutdown pas). cargo test
--workspace : 817 passed, 0 failed.
Suivi (D6b) : surfacer reply dans le writer wire .response.json
(infrastructure/orchestrator) pour la délégation par protocole fichier.
Reste D7 : menu restreint Claude/Codex + retrait custom.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -47,6 +47,14 @@ pub enum DomainEventDto {
|
||||
/// Exit code.
|
||||
code: i32,
|
||||
},
|
||||
/// A target agent produced a synchronous reply to an inter-agent `ask` (§17.4).
|
||||
#[serde(rename_all = "camelCase")]
|
||||
AgentReplied {
|
||||
/// Target agent id.
|
||||
agent_id: String,
|
||||
/// Reply length in bytes (metric, not the payload).
|
||||
reply_len: usize,
|
||||
},
|
||||
/// An agent's runtime profile was changed (hot-swap of the AI engine).
|
||||
#[serde(rename_all = "camelCase")]
|
||||
AgentProfileChanged {
|
||||
@ -179,6 +187,13 @@ impl From<&DomainEvent> for DomainEventDto {
|
||||
agent_id: agent_id.to_string(),
|
||||
code: *code,
|
||||
},
|
||||
DomainEvent::AgentReplied {
|
||||
agent_id,
|
||||
reply_len,
|
||||
} => Self::AgentReplied {
|
||||
agent_id: agent_id.to_string(),
|
||||
reply_len: *reply_len,
|
||||
},
|
||||
DomainEvent::AgentProfileChanged {
|
||||
agent_id,
|
||||
profile_id,
|
||||
|
||||
@ -710,16 +710,22 @@ impl AppState {
|
||||
// the UI drives (IdeA stays the single source of truth for the agent/skill
|
||||
// lifecycle). The per-project watcher that feeds it is started lazily when
|
||||
// a project is opened (see `ensure_orchestrator_watch`).
|
||||
let orchestrator_service = Arc::new(OrchestratorService::new(
|
||||
Arc::clone(&create_agent),
|
||||
Arc::clone(&launch_agent),
|
||||
Arc::clone(&list_agents),
|
||||
Arc::clone(&close_terminal),
|
||||
Arc::clone(&update_agent_context),
|
||||
Arc::clone(&create_skill),
|
||||
Arc::clone(&profile_store_port),
|
||||
Arc::clone(&terminal_sessions),
|
||||
));
|
||||
let orchestrator_service = Arc::new(
|
||||
OrchestratorService::new(
|
||||
Arc::clone(&create_agent),
|
||||
Arc::clone(&launch_agent),
|
||||
Arc::clone(&list_agents),
|
||||
Arc::clone(&close_terminal),
|
||||
Arc::clone(&update_agent_context),
|
||||
Arc::clone(&create_skill),
|
||||
Arc::clone(&profile_store_port),
|
||||
Arc::clone(&terminal_sessions),
|
||||
)
|
||||
// Messagerie inter-agents §17.4 : registre structuré + bus pour
|
||||
// `agent.message` (AskAgent) — rendez-vous synchrone via send_blocking.
|
||||
.with_structured(Arc::clone(&structured_sessions))
|
||||
.with_events(Arc::clone(&events_port)),
|
||||
);
|
||||
|
||||
// --- Windows (L10) ---
|
||||
let move_tab = Arc::new(MoveTabToNewWindow::new(
|
||||
|
||||
Reference in New Issue
Block a user