fix(application,backend,app-tauri): libère l'état busy après Final et draine la queue des messages agents — Final/Error terminent immédiatement le pump structuré via reply_event_ends_turn (le provider n'est pas tenu de fermer son itérateur), release busy/FIFO idempotente bornée au ticket observé (release_turn_accounting: cancel_head + mark_idle seulement si le ticket appartient toujours au tour), cleanup tardif Final/close/timeout/cancel sans libérer un tour successeur, factoring CancelAgentChat sur la même primitive, régression #357 préservée — #362 (QA verte: cargo fmt --check, chat_bridge 36 tests, cancel_agent_chat 4 tests, backend 106 tests, drain_with_readiness_lot1 7 tests)
This commit is contained in:
@ -3122,11 +3122,7 @@ pub async fn agent_send(
|
||||
terminal_visible = true;
|
||||
break 'logical_turn;
|
||||
}
|
||||
let provider_terminal = matches!(
|
||||
event,
|
||||
domain::ports::ReplyEvent::Final { .. }
|
||||
| domain::ports::ReplyEvent::Error { .. }
|
||||
);
|
||||
let provider_terminal = crate::chat::reply_event_ends_turn(&event);
|
||||
if provider_terminal {
|
||||
concrete_terminal = true;
|
||||
if let Some(guard) = turn_guard.as_mut() {
|
||||
@ -3199,6 +3195,13 @@ pub async fn agent_send(
|
||||
// (no channel at this generation) we keep draining so the turn still
|
||||
// completes and the scrollback stays whole for the next re-attach.
|
||||
let _ = bridge.send_output_settled(&sid, &mut settler, chunk);
|
||||
if provider_terminal {
|
||||
// Final/Error is the authoritative structured boundary. Some
|
||||
// providers keep their iterator/channel alive after publishing it;
|
||||
// waiting for EOF would leave the mediated ticket Busy forever even
|
||||
// though the UI already displayed the terminal chunk.
|
||||
break 'logical_turn;
|
||||
}
|
||||
}
|
||||
|
||||
if !resume && !concrete_terminal {
|
||||
|
||||
Reference in New Issue
Block a user