feat(diag): instrumente les blocages de délégation idea_ask_agent
Ajoute des logs diag! sans changement sémantique le long du canal de délégation inter-agents (application/orchestrator, infrastructure input, mailbox, mcp server & tools) pour diagnostiquer les blocages récurrents de idea_ask_agent. Couvert par les tests existants étendus (mcp_server, mailbox, input, tools, orchestrator_service). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -147,7 +147,7 @@ impl BusyTracker {
|
||||
/// appel, l'`enqueue` publie la `DelegationReady` immédiatement (chemin chaud,
|
||||
/// zéro régression).
|
||||
fn mark_starting(&self, agent: AgentId) {
|
||||
eprintln!("[input-mediator] cold-start gate armed agent={agent}");
|
||||
application::diag!("[input-mediator] mark_starting cold-start gate armed agent={agent}");
|
||||
self.lock_starting().insert(agent);
|
||||
}
|
||||
|
||||
@ -235,6 +235,10 @@ impl BusyTracker {
|
||||
.collect()
|
||||
};
|
||||
for agent in newly_stalled {
|
||||
// Transition de vivacité Alive→Stalled : le tour de l'agent n'a pas battu
|
||||
// depuis plus que son seuil. Beacon événementiel (une fois par transition,
|
||||
// jamais en boucle) — utile pour repérer une cible silencieuse pendant un ask.
|
||||
application::diag!("[input-mediator] liveness Alive->Stalled agent={agent}");
|
||||
self.publish_liveness(agent, AgentLiveness::Stalled);
|
||||
}
|
||||
}
|
||||
@ -264,10 +268,10 @@ impl BusyTracker {
|
||||
let mut busy = self.lock();
|
||||
let entry = busy.entry(agent).or_insert(AgentBusyState::Idle);
|
||||
if entry.is_busy() {
|
||||
eprintln!("[input-mediator] enqueue queued behind busy agent={agent}");
|
||||
application::diag!("[input-mediator] start_turn queued behind busy agent={agent}");
|
||||
false
|
||||
} else {
|
||||
eprintln!("[input-mediator] turn started agent={agent} state={state:?}");
|
||||
application::diag!("[input-mediator] start_turn started agent={agent} state={state:?}");
|
||||
*entry = state;
|
||||
true
|
||||
}
|
||||
@ -285,15 +289,17 @@ impl BusyTracker {
|
||||
Some(sink) => match sink(agent, d) {
|
||||
Some(d) => d,
|
||||
None => {
|
||||
eprintln!("[input-mediator] delegation handled by headless sink agent={agent}");
|
||||
application::diag!(
|
||||
"[input-mediator] delegationReady handled by headless sink agent={agent}"
|
||||
);
|
||||
return;
|
||||
}
|
||||
},
|
||||
None => d,
|
||||
};
|
||||
if let Some(events) = &self.events {
|
||||
eprintln!(
|
||||
"[input-mediator] publishing DelegationReady agent={agent} ticket={} text_bytes={} submit_sequence={} submit_delay_ms={:?}",
|
||||
application::diag!(
|
||||
"[input-mediator] delegationReady -> write-portal agent={agent} ticket={} text_bytes={} submit_sequence={} submit_delay_ms={:?}",
|
||||
d.ticket,
|
||||
d.text.as_bytes().len(),
|
||||
describe_submit_sequence(d.submit_sequence.as_deref()),
|
||||
@ -323,13 +329,24 @@ impl BusyTracker {
|
||||
let deferred = self.lock_deferred().remove(&agent);
|
||||
match deferred {
|
||||
Some(d) => {
|
||||
eprintln!(
|
||||
"[input-mediator] prompt-ready released deferred delegation agent={agent} ticket={}",
|
||||
application::diag!(
|
||||
"[input-mediator] prompt_ready released deferred delegation agent={agent} ticket={}",
|
||||
d.ticket
|
||||
);
|
||||
self.publish_deferred(agent, d);
|
||||
}
|
||||
None => self.mark_idle(agent),
|
||||
None => {
|
||||
// Pas de tour différé : le prompt-ready ne fait que **libérer** le busy
|
||||
// state / la FIFO (mark_idle). Il ne porte AUCUNE réponse pour réveiller
|
||||
// un appelant en attente — d'où `no_reply_payload`. C'est exactement le
|
||||
// scénario où une cible revient prompt-ready sans `idea_reply` : l'ask
|
||||
// ne se résout pas par ce chemin, seulement par l'`idea_reply` ou le
|
||||
// timeout côté appelant.
|
||||
application::diag!(
|
||||
"[input-mediator] prompt_ready agent={agent} no_reply_payload -> mark_idle"
|
||||
);
|
||||
self.mark_idle(agent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -341,8 +358,8 @@ impl BusyTracker {
|
||||
fn release_cold_start(&self, agent: AgentId) {
|
||||
self.lock_starting().remove(&agent);
|
||||
if let Some(d) = self.lock_deferred().remove(&agent) {
|
||||
eprintln!(
|
||||
"[input-mediator] mcp-ready released deferred delegation agent={agent} ticket={}",
|
||||
application::diag!(
|
||||
"[input-mediator] release_cold_start (mcp-ready) released deferred delegation agent={agent} ticket={}",
|
||||
d.ticket
|
||||
);
|
||||
self.publish_deferred(agent, d);
|
||||
@ -359,7 +376,7 @@ impl BusyTracker {
|
||||
.is_some_and(|s| s.is_busy())
|
||||
};
|
||||
if was_busy {
|
||||
eprintln!("[input-mediator] turn marked idle agent={agent}");
|
||||
application::diag!("[input-mediator] mark_idle turn ended agent={agent}");
|
||||
if let Some(events) = &self.events {
|
||||
events.publish(DomainEvent::AgentBusyChanged {
|
||||
agent_id: agent,
|
||||
@ -837,6 +854,9 @@ impl InputMediator for MediatedInbox {
|
||||
submit_delay_ms: submit.delay_ms,
|
||||
};
|
||||
if self.tracker.lock_starting().contains(&agent) {
|
||||
application::diag!(
|
||||
"[input-mediator] enqueue deferred (cold-start gate) agent={agent} ticket={ticket_id}"
|
||||
);
|
||||
self.tracker.lock_deferred().insert(agent, deferred);
|
||||
} else {
|
||||
self.tracker.publish_deferred(agent, deferred);
|
||||
@ -918,6 +938,7 @@ impl InputMediator for MediatedInbox {
|
||||
// CLI agents honour. A missing handle/port is a no-op (the agent simply has no
|
||||
// live stream to interrupt). The busy state is left untouched: it returns to
|
||||
// Idle through `mark_idle` (prompt-ready / explicit signal), not here.
|
||||
application::diag!("[input-mediator] preempt agent={agent} (interrupt byte, no ticket)");
|
||||
if let Some(pty) = &self.pty {
|
||||
if let Some(handle) = self.handles().get(&agent).cloned() {
|
||||
let _ = pty.write(&handle, b"\x1b");
|
||||
|
||||
Reference in New Issue
Block a user