fix(backend,orchestrator,chat): l'objectif suspendu guide le premier tour post-restart — #290 regression partielle (register_suspended_turn capture la demande utilisateur exacte au point d'entrée chat agent_send/chat.send avec source diagnostique, RestartRendezvousCoordinator conserve tour/objectif/restart_attempt_id, take_continuation consommable exactement une fois et seulement par la tentative courante: suspended_restart_continuation_prompt place l'objectif utilisateur avant toute consigne generique et StructuredHumanTurnTransition::Resume porte le prompt host-provided injecte au runtime de remplacement; nettoyage du tour suspendu sur succes, echec, annulation et abandon/expiration; diagnostics projet/agent/turn_id/restart_attempt_id/source; rendez-vous inter-agents #289 inchanges; QA verte reconfirmee par Git: cargo check --workspace, backend self_restart_contract_tests 3/3, orchestrator_service restart 10/10, workstate_actions 9/9, app-tauri chat_bridge 31/31, web-server websocket_chat_send 3/3, fmt --check, diff --check; restants: rebuild AppImage + validation live reprise objectif apres restart; carnet #290 iteration regression + passage store qa inclus; scripts/ hors périmètre; aucun push)
This commit is contained in:
@ -1879,13 +1879,14 @@ async fn ws_send_chat(
|
||||
})?;
|
||||
let session_meta = state.app.structured_sessions.meta_for_session(&sid);
|
||||
let turn_guard = session_meta.as_ref().map(|(project_id, agent_id, _, _)| {
|
||||
backend::StructuredHumanTurnGuard::start(
|
||||
backend::StructuredHumanTurnGuard::start_with_source(
|
||||
Arc::clone(&state.app.turn_watch_input),
|
||||
Arc::clone(&state.app.input_mailbox),
|
||||
Arc::clone(&state.app.restart_rendezvous),
|
||||
*project_id,
|
||||
*agent_id,
|
||||
&prompt,
|
||||
"websocket_chat_send",
|
||||
)
|
||||
});
|
||||
|
||||
@ -1935,6 +1936,7 @@ async fn ws_send_chat(
|
||||
'logical_turn: loop {
|
||||
let mut concrete_terminal = false;
|
||||
let mut resume = false;
|
||||
let mut resume_prompt = None;
|
||||
for event in stream {
|
||||
let provider_terminal =
|
||||
matches!(event, ReplyEvent::Final { .. } | ReplyEvent::Error { .. });
|
||||
@ -1943,8 +1945,9 @@ async fn ws_send_chat(
|
||||
if let Some(guard) = turn_guard.as_mut() {
|
||||
match guard.transition_after_provider_terminal_async().await {
|
||||
backend::StructuredHumanTurnTransition::Complete => {}
|
||||
backend::StructuredHumanTurnTransition::Resume => {
|
||||
backend::StructuredHumanTurnTransition::Resume { prompt } => {
|
||||
resume = true;
|
||||
resume_prompt = Some(prompt);
|
||||
break;
|
||||
}
|
||||
backend::StructuredHumanTurnTransition::Failed(message) => {
|
||||
@ -1995,7 +1998,10 @@ async fn ws_send_chat(
|
||||
if let Some(guard) = turn_guard.as_mut() {
|
||||
match guard.transition_after_provider_terminal_async().await {
|
||||
backend::StructuredHumanTurnTransition::Complete => {}
|
||||
backend::StructuredHumanTurnTransition::Resume => resume = true,
|
||||
backend::StructuredHumanTurnTransition::Resume { prompt } => {
|
||||
resume = true;
|
||||
resume_prompt = Some(prompt);
|
||||
}
|
||||
backend::StructuredHumanTurnTransition::Failed(message) => {
|
||||
let chunk = reply_event_chunk(ReplyEvent::Error { message });
|
||||
tx.send(ServerFrame::chat_output(
|
||||
@ -2028,8 +2034,10 @@ async fn ws_send_chat(
|
||||
code: "CHAT_RESTART_FAILED".to_owned(),
|
||||
message: "restart completed without a replacement structured session".to_owned(),
|
||||
})?;
|
||||
let resume_prompt = resume_prompt
|
||||
.unwrap_or_else(|| application::SELF_RESTART_CONTINUATION_PROMPT.to_owned());
|
||||
stream = replacement
|
||||
.send(application::SELF_RESTART_CONTINUATION_PROMPT)
|
||||
.send(&resume_prompt)
|
||||
.await
|
||||
.map_err(|err| ErrorDto {
|
||||
code: "CHAT_RESTART_FAILED".to_owned(),
|
||||
|
||||
Reference in New Issue
Block a user