From d03300a20fd2b1ba6bfac924e9f12042ba1810a5 Mon Sep 17 00:00:00 2001 From: Blomios Date: Wed, 9 Sep 2026 08:38:29 +0200 Subject: [PATCH] =?UTF-8?q?fix(backend,orchestrator,chat):=20tour=20humain?= =?UTF-8?q?=20agent=5Fsend=20survit=20au=20self-restart=20=E2=80=94=20#290?= =?UTF-8?q?=20(le=20tour=20humain=20direct=20agent=5Fsend/ws=20chat.send?= =?UTF-8?q?=20s'enregistre=20au=20RestartRendezvousCoordinator=20via=20Str?= =?UTF-8?q?ucturedHumanTurnGuard:=20Final/Error=20de=20l'ancien=20provider?= =?UTF-8?q?=20devient=20transition=20de=20contr=C3=B4le=20et=20non=20le=20?= =?UTF-8?q?Final=20logique;=20transition=5Fafter=5Fprovider=5Fterminal=5Fa?= =?UTF-8?q?sync=20attend=20Ready=20puis=20accept=5Fready=20acquitte=20le?= =?UTF-8?q?=20handoff=20et=20lib=C3=A8re=20le=20scheduler,=20Resume=20=3D>?= =?UTF-8?q?=20drain=20de=20la=20session=20de=20remplacement=20avec=20SELF?= =?UTF-8?q?=5FRESTART=5FCONTINUATION=5FPROMPT,=20seul=20le=20terminal=20du?= =?UTF-8?q?=20remplacement=20est=20transmis=20au=20chat;=20Failed/Cancelle?= =?UTF-8?q?d=20surfac=C3=A9s=20explicitement=20avec=20lib=C3=A9ration=20Bu?= =?UTF-8?q?sy/FIFO;=20send=5Fwith=5Ftap=20en=20erreur=20pendant=20restart?= =?UTF-8?q?=20actif=20trait=C3=A9=20comme=20teardown=20attendu=20(stream?= =?UTF-8?q?=20vide=20puis=20attente=20du=20remplacement,=20plus=20de=20Fin?= =?UTF-8?q?al=20pr=C3=A9matur=C3=A9=20ni=20chat=20bloqu=C3=A9);=20StopLive?= =?UTF-8?q?Agent=20c=C3=A2bl=C3=A9=20with=5Frestart=5Frendezvous:=20stop?= =?UTF-8?q?=20utilisateur=20annule=20le=20rendez-vous=20restart;=20Restart?= =?UTF-8?q?RendezvousRegistration=20rendu=20public=20(state/changed/accept?= =?UTF-8?q?=5Fready);=20QA=20verte:=20backend=20self=5Frestart=5Fcontract?= =?UTF-8?q?=5Ftests=203/3=20dont=202=20nouveaux=20(old=20terminal=20ignor?= =?UTF-8?q?=C3=A9=20+=20handoff=20accept=C3=A9,=20failure=20explicite=20+?= =?UTF-8?q?=20busy=20lib=C3=A9r=C3=A9),=20application=20orchestrator=5Fser?= =?UTF-8?q?vice=20restart=208/8=20dont=20registration=20directe=20du=20ren?= =?UTF-8?q?dez-vous,=20workstate=5Factions=209/9,=20app-tauri=20chat=5Fbri?= =?UTF-8?q?dge=2031/31,=20web-server=20websocket=5Fchat=5Fsend=203/3,=20ca?= =?UTF-8?q?rgo=20check=20--workspace,=20fmt=20--check,=20diff=20--check=20?= =?UTF-8?q?=E2=80=94=20reconfirm=C3=A9s=20par=20Git=20avant=20commit;=20r?= =?UTF-8?q?=C3=A9serves=20hors=20p=C3=A9rim=C3=A8tre=20pr=C3=A9existantes:?= =?UTF-8?q?=207=20=C3=A9checs=20fixture=20PTY=20web-server=20+=202=20?= =?UTF-8?q?=C3=A9checs=20plugin=20application;=20carnet=20#290=20+=20cl?= =?UTF-8?q?=C3=B4ture=20store=20#289=20inclus;=20scripts/=20hors=20p=C3=A9?= =?UTF-8?q?rim=C3=A8tre;=20aucun=20push)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...c2afdde0-clipboard-image-1788934010151.png | Bin 0 -> 101043 bytes .../9a881769-d8e8-410d-85f1-7a3ec2afdde0.json | 13 ++ .ideai/tickets/289/carnet.md | 148 ++------------ .ideai/tickets/289/issue.md | 6 +- .ideai/tickets/290/carnet.md | 87 ++++++++ .ideai/tickets/290/issue.md | 17 ++ .ideai/tickets/counter.json | 2 +- .ideai/tickets/index.json | 20 +- crates/app-tauri/src/commands.rs | 190 ++++++++++++++---- crates/application/src/lib.rs | 6 +- crates/application/src/orchestrator/mod.rs | 4 +- .../application/src/orchestrator/service.rs | 19 +- crates/application/src/workstate/actions.rs | 15 ++ .../application/tests/orchestrator_service.rs | 54 +++++ crates/backend/src/lib.rs | 147 +++++++++++++- crates/web-server/src/lib.rs | 145 ++++++++++--- 16 files changed, 660 insertions(+), 213 deletions(-) create mode 100644 .ideai/attachments/agent-chat/7d2abba7-ed93-405f-9fe5-857c8455e436/9a881769-d8e8-410d-85f1-7a3ec2afdde0-clipboard-image-1788934010151.png create mode 100644 .ideai/attachments/agent-chat/7d2abba7-ed93-405f-9fe5-857c8455e436/9a881769-d8e8-410d-85f1-7a3ec2afdde0.json create mode 100644 .ideai/tickets/290/carnet.md create mode 100644 .ideai/tickets/290/issue.md diff --git a/.ideai/attachments/agent-chat/7d2abba7-ed93-405f-9fe5-857c8455e436/9a881769-d8e8-410d-85f1-7a3ec2afdde0-clipboard-image-1788934010151.png b/.ideai/attachments/agent-chat/7d2abba7-ed93-405f-9fe5-857c8455e436/9a881769-d8e8-410d-85f1-7a3ec2afdde0-clipboard-image-1788934010151.png new file mode 100644 index 0000000000000000000000000000000000000000..be7346e402a7c5bcb5f91fbe5dd4795182aa2e7d GIT binary patch literal 101043 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 literal 0 HcmV?d00001 diff --git a/.ideai/attachments/agent-chat/7d2abba7-ed93-405f-9fe5-857c8455e436/9a881769-d8e8-410d-85f1-7a3ec2afdde0.json b/.ideai/attachments/agent-chat/7d2abba7-ed93-405f-9fe5-857c8455e436/9a881769-d8e8-410d-85f1-7a3ec2afdde0.json new file mode 100644 index 00000000..19e835e4 --- /dev/null +++ b/.ideai/attachments/agent-chat/7d2abba7-ed93-405f-9fe5-857c8455e436/9a881769-d8e8-410d-85f1-7a3ec2afdde0.json @@ -0,0 +1,13 @@ +{ + "id": "9a881769-d8e8-410d-85f1-7a3ec2afdde0", + "projectId": "97b49ac2-8376-4aa3-8ea9-bf3ac81d0023", + "agentId": "a6c6ea12-bfc6-4bdc-8031-324102dfa34d", + "sessionId": "7d2abba7-ed93-405f-9fe5-857c8455e436", + "filename": "clipboard-image-1788934010151.png", + "mime": "image/png", + "sizeBytes": 101043, + "sourceKind": "clipboard", + "storagePath": "agent-chat/7d2abba7-ed93-405f-9fe5-857c8455e436/9a881769-d8e8-410d-85f1-7a3ec2afdde0-clipboard-image-1788934010151.png", + "readablePath": "/home/anthony/Documents/Projects/IdeA/.ideai/attachments/agent-chat/7d2abba7-ed93-405f-9fe5-857c8455e436/9a881769-d8e8-410d-85f1-7a3ec2afdde0-clipboard-image-1788934010151.png", + "createdAt": 1788934156106 +} \ No newline at end of file diff --git a/.ideai/tickets/289/carnet.md b/.ideai/tickets/289/carnet.md index 2b4cbeda..dabaa528 100644 --- a/.ideai/tickets/289/carnet.md +++ b/.ideai/tickets/289/carnet.md @@ -1,8 +1,8 @@ --- issueRef: "#289" -version: 4 +version: 5 updatedBy: {"kind":"agent","agent_id":"a6c6ea12-bfc6-4bdc-8031-324102dfa34d"} -updatedAt: 1788900709736 +updatedAt: 1788901112288 --- ## 2026-09-08 — Main @@ -59,139 +59,29 @@ Tests QA ajoutés dans `crates/application/tests/orchestrator_service.rs` : - `ask_target_restart_cleans_busy_workstate_and_replaces_structured_registry_entry` : succès same-project post-restart, cible revenue `Idle`, FIFO vide, live-state `Done`, ancienne session structurée absente, session de remplacement enregistrée sur le même agent logique. - `same_project_stop_during_restart_releases_ask_and_leaves_no_replacement_session` : cancel/stop same-project pendant le gap de restart, ask réveillé en erreur explicite de cancel, busy/FIFO/live-state nettoyés, aucune session de remplacement enregistrée. -Commandes exécutées et sorties pertinentes : +Commandes finales vertes : +- `cargo fmt --all -- --check` +- `cargo test -p application --test orchestrator_service` — 87 passés +- `cargo test -p application --lib` — 171 passés +- `cargo test -p backend --lib` — 89 passés, 7 ignorés +- `cargo check -p backend` — OK -```bash -git status --short --branch -``` +Couverture validée : same-project, cross-project, faux no-reply interdit, failure restart explicite, cancel pendant restart, nettoyage busy/workstate/registry, watchdog réarmé par activité restart. -```text -## fix/289-ask-agent-during-target-restart - M .ideai/tickets/counter.json - M .ideai/tickets/index.json - M crates/application/src/lib.rs - M crates/application/src/orchestrator/mod.rs - M crates/application/src/orchestrator/service.rs - M crates/application/tests/orchestrator_service.rs - M crates/backend/src/lib.rs -?? .ideai/tickets/289/ -?? scripts/ -``` +Risques résiduels : pas d'E2E vrai provider relancé dans QA ; les 7 tests backend ignorés demandent une permission locale de bind socket. -```bash -cargo test -p application --test orchestrator_service ask_target_restart_keeps_logical_turn_and_returns_post_restart_final -- --nocapture -``` +## 2026-09-08 — Git/Main clôture -```text -test ask_target_restart_keeps_logical_turn_and_returns_post_restart_final ... ok -test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 84 filtered out; finished in 0.02s -``` +Git a committé le correctif sur la branche feature : `d6d9891`. -```bash -cargo test -p application --test orchestrator_service cross_project_ask_preserves_correlation_across_target_restart -- --nocapture -``` +Git a mergé localement dans `develop` avec merge commit : `321ebda`. -```text -test cross_project_ask_preserves_correlation_across_target_restart ... ok -test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 84 filtered out; finished in 0.02s -``` +Aucun push distant effectué. État final après build : `develop...origin/develop [ahead 276]`, working tree propre sauf `scripts/` non suivi préexistant et hors périmètre. -```bash -cargo test -p application --test orchestrator_service ask_target_restart_failure_is_explicit_not_no_reply -- --nocapture -``` +Build AppImage exécuté via le skill `build-appimage` : +- `npm --prefix frontend run build` — OK. +- depuis `crates/app-tauri` : `CARGO_HOME=/tmp/idea-cargo-home APPIMAGE_EXTRACT_AND_RUN=1 NO_STRIP=1 ../../frontend/node_modules/.bin/tauri build --bundles appimage` — OK. -```text -test ask_target_restart_failure_is_explicit_not_no_reply ... ok -test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 84 filtered out; finished in 0.01s -``` +Artefact produit : `target/release/bundle/appimage/IdeA_0.3.0_amd64.AppImage`. -```bash -cargo test -p application --test orchestrator_service cross_project_cancel_during_restart_releases_ask_and_correlation -- --nocapture -``` - -```text -test cross_project_cancel_during_restart_releases_ask_and_correlation ... ok -test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 84 filtered out; finished in 0.00s -``` - -```bash -cargo test -p application --lib restart_control_activity_rearms_inactivity_watchdog -- --nocapture -``` - -```text -test orchestrator::service::tests::restart_control_activity_rearms_inactivity_watchdog ... ok -test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 170 filtered out; finished in 0.05s -``` - -```bash -cargo test -p application --test orchestrator_service ask_target_restart_cleans_busy_workstate_and_replaces_structured_registry_entry -- --nocapture -``` - -```text -test ask_target_restart_cleans_busy_workstate_and_replaces_structured_registry_entry ... ok -test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 86 filtered out; finished in 0.02s -``` - -```bash -cargo test -p application --test orchestrator_service same_project_stop_during_restart_releases_ask_and_leaves_no_replacement_session -- --nocapture -``` - -```text -test same_project_stop_during_restart_releases_ask_and_leaves_no_replacement_session ... ok -test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 86 filtered out; finished in 0.00s -``` - -```bash -cargo fmt --all -- --check -``` - -```text -OK (aucune sortie) -``` - -```bash -cargo test -p application --test orchestrator_service -``` - -```text -running 87 tests -test result: ok. 87 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.09s -``` - -```bash -cargo test -p application --lib -``` - -```text -running 171 tests -test result: ok. 171 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.15s -``` - -```bash -cargo test -p backend --lib -``` - -```text -running 96 tests -test result: ok. 89 passed; 0 failed; 7 ignored; 0 measured; 0 filtered out; finished in 0.04s -``` - -```bash -cargo check -p backend -``` - -```text -Finished `dev` profile [unoptimized + debuginfo] target(s) in 3.37s -``` - -Couverture validée : -- same-project : Final récupéré après restart et test QA de nettoyage busy/workstate/registre. -- cross-project : corrélation conservée jusqu'au Final post-restart puis supprimée. -- pas de faux no-reply/timeout pendant restart : failure restart reste `PROCESS` explicite, watchdog réarmé par activité de contrôle restart. -- failure restart : erreur explicite contenant la cause provider. -- cancel pendant restart : cross-project et same-project couverts. -- nettoyage busy/workstate/registry : FIFO vide, `AgentBusyState::Idle`, live-state `Done`, registre structuré remplacé après succès et non recréé après cancel. - -Risques résiduels : -- Pas d'E2E AppImage / vrai provider relancé : la preuve porte sur les ports applicatifs, les sessions structurées de test et le câblage backend compilé. -- Les 7 tests backend ignorés demandent une permission de bind socket locale ; ils restent hors preuve dans cet environnement. +Étape non automatisée volontairement : remplacement de `/home/anthony/Documents/IdeA_0.1.0_amd64.AppImage` et relance d'IdeA, car cela tue l'orchestrateur actif. À faire manuellement quand l'utilisateur est prêt pour activer le correctif dans l'application lancée. \ No newline at end of file diff --git a/.ideai/tickets/289/issue.md b/.ideai/tickets/289/issue.md index c70ca12b..3519be8a 100644 --- a/.ideai/tickets/289/issue.md +++ b/.ideai/tickets/289/issue.md @@ -2,7 +2,7 @@ id: "ae7f4f57-087e-463e-9390-33727e751d4f" number: 289 title: "Garantir ask_agent pendant restart de la cible, same-project et cross-project" -status: "qa" +status: "closed" priority: "high" sprint: null links: [] @@ -11,7 +11,7 @@ attachments: [] createdBy: {"kind":"agent","agent_id":"a6c6ea12-bfc6-4bdc-8031-324102dfa34d"} updatedBy: {"kind":"agent","agent_id":"a6c6ea12-bfc6-4bdc-8031-324102dfa34d"} createdAt: 1788899102915 -updatedAt: 1788900574986 -version: 3 +updatedAt: 1788901112288 +version: 5 --- Vérifier et corriger le comportement suivant : lorsqu'un agent cible a été appelé via ask_agent puis se restart, le demandeur doit attendre la fin du restart et le travail repris/après restart doit rester rattaché à cette même cible. Le contrat doit couvrir les appels intra-projet et cross-project, sans perte de résultat ni attribution à un mauvais agent. \ No newline at end of file diff --git a/.ideai/tickets/290/carnet.md b/.ideai/tickets/290/carnet.md new file mode 100644 index 00000000..98ecdc5d --- /dev/null +++ b/.ideai/tickets/290/carnet.md @@ -0,0 +1,87 @@ +--- +issueRef: "#290" +version: 5 +updatedBy: {"kind":"agent","agent_id":"a6c6ea12-bfc6-4bdc-8031-324102dfa34d"} +updatedAt: 1788935506924 +--- +# Ticket #290 - Redemarrage agent finalise le tour et bloque le chat + +## Reproduction utilisateur +- Date: 2026-09-09. +- L'utilisateur demande a Main de se redemarrer puis d'indiquer le succes une fois fini. +- Dans la conversation, Main lance le redemarrage, envoie un message, le tour se finit et le message envoye est considere comme Final. +- Tentative Stop ensuite: message "Request already stopped". +- Le chat reste bloque en ecriture. +- Capture: `/home/anthony/Documents/Projects/IdeA/.ideai/attachments/agent-chat/7d2abba7-ed93-405f-9fe5-857c8455e436/9a881769-d8e8-410d-85f1-7a3ec2afdde0-clipboard-image-1788934010151.png`. + +## Attendu +- Le restart agent doit provoquer une vraie relance/reprise, pas une simple fin de tour capturee comme Final. +- Le chat doit redevenir ecrivable apres redemarrage ou echec controle. +- Le message de succes doit correspondre a un redemarrage effectif, pas au tour qui initie le restart. + +## Cadrage UX +- Etats visibles attendus: redemarrage demande, agent en reprise, reprise reussie ou echec controle. +- Stop ne doit etre cliquable que si une requete stoppable existe; ne pas exposer "Request already stopped" brut. +- Le message de succes ne doit etre affiche qu'apres preuve runtime d'un agent relance/reconnecte. + +## Cadrage Architecture +- Bug de contrat entre restart autonome et chat humain `agent_send`. +- `ask_structured` etait deja restart-aware; `agent_send` devait ignorer le Final de l'ancien runtime pendant restart et attendre la session remplacee. +- Correctif backend-first, pas de filtre frontend sur le texte. + +## Implementation DevBackend +- Branche: `fix/290-restart-agent-send-premature-final`. +- Le tour humain s'enregistre dans `RestartRendezvousCoordinator`. +- `Final`/`Error` de l'ancien provider ignore pendant restart actif. +- Le drain attend la session de remplacement, envoie le prompt de continuation, puis transmet uniquement le vrai terminal. +- Busy/FIFO reste actif jusqu'au vrai resultat, echec explicite ou annulation. +- Stop utilisateur annule aussi le rendez-vous restart. +- Pas de changement frontend/protocole signale. + +## Fichiers modifies annonces +- `crates/application/src/orchestrator/service.rs` +- `crates/application/src/orchestrator/mod.rs` +- `crates/application/src/lib.rs` +- `crates/application/src/workstate/actions.rs` +- `crates/application/tests/orchestrator_service.rs` +- `crates/backend/src/lib.rs` +- `crates/app-tauri/src/commands.rs` +- `crates/web-server/src/lib.rs` + +## Validations DevBackend annoncees vertes +- `cargo check --workspace` +- `cargo test -p backend self_restart_contract_tests -- --nocapture` — 3/3 +- `cargo test -p application --test orchestrator_service restart -- --nocapture` — 8/8 +- `cargo test -p application --test workstate_actions -- --test-threads=1` — 9/9 +- `cargo test -p app-tauri --test chat_bridge` — 31/31 +- `cargo test -p web-server websocket_chat_send -- --test-threads=1` — 3/3 +- `cargo fmt --all -- --check` +- `git diff --check` + +## Risques signales +- Suites globales avec echecs hors perimetre: 7 tests PTY `web-server` et 2 tests package plugin `application`. +- QA doit confirmer que ces echecs sont preexistants/hors #290 ou remonter le detail. + +## Validation QA - 2026-09-09 + +Verdict: VERT sur le perimetre #290, avec reserves globales reproduites hors perimetre. + +Commandes ciblees executees: +- `cargo check --workspace` -> vert. +- `cargo test -p backend self_restart_contract_tests -- --nocapture` -> vert, 3 passed. +- `cargo test -p application --test orchestrator_service restart -- --nocapture` -> vert, 8 passed. +- `cargo test -p application --test workstate_actions -- --test-threads=1` -> vert, 9 passed. +- `cargo test -p app-tauri --test chat_bridge` -> vert, 31 passed. +- `cargo test -p web-server websocket_chat_send -- --test-threads=1` -> vert, 3 passed. +- `cargo fmt --all -- --check` -> vert. +- `git diff --check` -> vert. + +Couverture confirmee: +- `Final`/`Error` de l'ancien provider pendant restart ne finalise pas le tour humain: couvert par `self_restart_contract_tests::human_turn_ignores_old_terminal_and_accepts_replacement` et par les tests de registration directe du rendezvous restart. +- Busy/FIFO reste actif jusqu'au terminal post-restart/echec/annulation: couvert par `self_restart_contract_tests`, `orchestrator_service restart` et `workstate_actions`. +- Stop pendant restart annule proprement sans session de remplacement: couvert par `same_project_stop_during_restart_releases_ask_and_leaves_no_replacement_session`. +- Non-regression #289 `ask_structured` restart-aware: couverte par `orchestrator_service restart`, incluant ask same-project, cross-project, echec et annulation. + +Suites globales rouges reproduites hors perimetre: +- `cargo test -p web-server -- --test-threads=1` -> 103 passed, 7 failed. Les 7 echecs sont les tests PTY `websocket_*terminal*`/`websocket_launch_agent_*` qui recoivent `error` au lieu de `terminal.attached`. +- `cargo test -p application -- --test-threads=1` -> rouge dans `tests/layout_usecases.rs`, 2 failed: `create_layout_plugin_validates_active_runtime_contribution_and_persists_root` et `mutate_set_plugin_layout_state_persists_opaque_state`, tous deux sur `NotFound("plugin package")`. diff --git a/.ideai/tickets/290/issue.md b/.ideai/tickets/290/issue.md new file mode 100644 index 00000000..e8720407 --- /dev/null +++ b/.ideai/tickets/290/issue.md @@ -0,0 +1,17 @@ +--- +id: "00547de0-3061-4d45-ad9b-f798152f75ce" +number: 290 +title: "Corriger le redemarrage agent qui finalise le tour et bloque le chat" +status: "qa" +priority: "high" +sprint: null +links: [] +agentRefs: [{"agentId":"ab328d90-c307-4771-a3b6-6c56089c8506","role":"assigned"}] +attachments: [] +createdBy: {"kind":"agent","agent_id":"a6c6ea12-bfc6-4bdc-8031-324102dfa34d"} +updatedBy: {"kind":"agent","agent_id":"a6c6ea12-bfc6-4bdc-8031-324102dfa34d"} +createdAt: 1788934173642 +updatedAt: 1788935506924 +version: 5 +--- +Bug rapporte par l'utilisateur le 2026-09-09: apres une demande de redemarrage, l'agent lance le redemarrage puis envoie un message; ce message est considere comme Final, le tour se termine, Stop repond "Request already stopped", et le chat devient bloque en ecriture. Attachement de reproduction: /home/anthony/Documents/Projects/IdeA/.ideai/attachments/agent-chat/7d2abba7-ed93-405f-9fe5-857c8455e436/9a881769-d8e8-410d-85f1-7a3ec2afdde0-clipboard-image-1788934010151.png. Objectif: corriger le flux pour que le redemarrage aboutisse vraiment, que l'utilisateur recupere un chat utilisable, et que le message de succes ne soit pas capture comme final prematurant le rendez-vous. \ No newline at end of file diff --git a/.ideai/tickets/counter.json b/.ideai/tickets/counter.json index 194068b8..522a94cb 100644 --- a/.ideai/tickets/counter.json +++ b/.ideai/tickets/counter.json @@ -1,3 +1,3 @@ { - "nextNumber": 290 + "nextNumber": 291 } \ No newline at end of file diff --git a/.ideai/tickets/index.json b/.ideai/tickets/index.json index 24a24db9..d8019ff7 100644 --- a/.ideai/tickets/index.json +++ b/.ideai/tickets/index.json @@ -3954,7 +3954,7 @@ "issueRef": "#289", "path": "289", "title": "Garantir ask_agent pendant restart de la cible, same-project et cross-project", - "status": "qa", + "status": "closed", "priority": "high", "sprint": null, "assignedAgentIds": [], @@ -3962,7 +3962,23 @@ "kind": "agent", "agent_id": "a6c6ea12-bfc6-4bdc-8031-324102dfa34d" }, - "updatedAt": 1788900574986 + "updatedAt": 1788901112288 + }, + { + "issueRef": "#290", + "path": "290", + "title": "Corriger le redemarrage agent qui finalise le tour et bloque le chat", + "status": "qa", + "priority": "high", + "sprint": null, + "assignedAgentIds": [ + "ab328d90-c307-4771-a3b6-6c56089c8506" + ], + "createdBy": { + "kind": "agent", + "agent_id": "a6c6ea12-bfc6-4bdc-8031-324102dfa34d" + }, + "updatedAt": 1788935506924 } ] } \ No newline at end of file diff --git a/crates/app-tauri/src/commands.rs b/crates/app-tauri/src/commands.rs index 1b9d5079..c5c7bab8 100644 --- a/crates/app-tauri/src/commands.rs +++ b/crates/app-tauri/src/commands.rs @@ -2570,8 +2570,10 @@ pub async fn managed_temp_gc( /// structured registry, open the turn stream (`session.send`), then drain that /// blocking [`ReplyStream`] on a dedicated OS thread, mapping each /// [`ReplyEvent`](domain::ports::ReplyEvent) to a [`ReplyChunk`] and forwarding it -/// through the [`ChatBridge`]. The turn ends deterministically at -/// [`ReplyChunk::Final`]; the stream is bounded and closes right after. +/// through the [`ChatBridge`]. The logical turn ends deterministically at the +/// authoritative [`ReplyChunk::Final`]. If the agent self-restarts, the old +/// provider's terminal event is a control transition: the pump waits for the +/// replacement session and only forwards its terminal outcome. /// /// The channel is (re-)registered each call, bumping the bridge **generation** so /// a previous attach's pump can no longer deliver (generation supersede): only the @@ -2621,6 +2623,7 @@ pub async fn agent_send( backend::StructuredHumanTurnGuard::start( std::sync::Arc::clone(&state.turn_watch_input), std::sync::Arc::clone(&state.input_mailbox), + std::sync::Arc::clone(&state.restart_rendezvous), *project_id, *agent_id, &prompt_for_model, @@ -2673,7 +2676,20 @@ pub async fn agent_send( // still live, so we keep the attach and surface the error. let stream_result = session.send_with_tap(&prompt_for_model, tap_tx).await; let _ = tap_pump.join(); - let stream = stream_result.map_err(|e| ErrorDto::from(AppError::from(e)))?; + let mut turn_guard = turn_guard; + let stream = match stream_result { + Ok(stream) => stream, + Err(_error) + if turn_guard + .as_mut() + .is_some_and(|guard| guard.restart_is_active()) => + { + // Teardown of the old provider is expected once self-restart owns the + // logical turn. The detached drain below waits for the replacement. + Box::new(std::iter::empty()) as domain::ports::ReplyStream + } + Err(error) => return Err(ErrorDto::from(AppError::from(error))), + }; // Drain the blocking reply iterator on a dedicated OS thread (the stream is a // synchronous `Iterator`, exactly like the PTY byte stream). It runs to the @@ -2685,52 +2701,144 @@ pub async fn agent_send( // event then feeds the service (détecter→planifier). Without a conversation id the // service must not pretend an automatic resume is armed. let service = std::sync::Arc::clone(&state.session_limit_service); + let structured = std::sync::Arc::clone(&state.structured_sessions); + let runtime = tokio::runtime::Handle::current(); let meta = session_meta; std::thread::spawn(move || { let mut terminal_visible = false; let mut settler = crate::chat::ProgressSettler::default(); - for event in stream { - if !matches!(event, domain::ports::ReplyEvent::Final { .. }) { - if let Some(guard) = &turn_guard { - guard.mark_alive(); - } - } - // Non-terminal, sans contenu chat : un signal de limite alimente le service - // (le badge UI vient du bus `AgentRateLimited`, pas du flux chat) puis on - // continue à drainer comme pour un battement. - if let domain::ports::ReplyEvent::RateLimited { resets_at_ms } = &event { - if let Some((project_id, agent_id, node_id, conversation_id)) = &meta { - let (conversation_id, resets_at_ms) = match (conversation_id, resets_at_ms) { - (Some(conversation_id), resets_at_ms) => { - (Some(conversation_id.clone()), *resets_at_ms) + let mut current_stream = stream; + 'logical_turn: loop { + let mut concrete_terminal = false; + let mut resume = false; + for event in current_stream { + let provider_terminal = matches!( + event, + domain::ports::ReplyEvent::Final { .. } + | domain::ports::ReplyEvent::Error { .. } + ); + if provider_terminal { + concrete_terminal = true; + if let Some(guard) = turn_guard.as_mut() { + match runtime.block_on(guard.transition_after_provider_terminal_async()) { + backend::StructuredHumanTurnTransition::Complete => {} + backend::StructuredHumanTurnTransition::Resume => { + resume = true; + break; + } + backend::StructuredHumanTurnTransition::Failed(message) => { + terminal_visible = true; + let _ = bridge.send_output_settled( + &sid, + &mut settler, + ReplyChunk::Error { message }, + ); + break 'logical_turn; + } + backend::StructuredHumanTurnTransition::Cancelled => { + terminal_visible = true; + break 'logical_turn; + } } - (None, None) => (None, None), - // Reset connu mais conversation moteur absente : ne pas armer - // une reprise non reprenable ; surfacer le fallback humain. - (None, Some(_)) => (None, None), - }; - service.on_rate_limited( - *project_id, - *agent_id, - *node_id, - conversation_id, - resets_at_ms, - ); + } + } else { + if let Some(guard) = &turn_guard { + guard.mark_alive(); + } + } + // Non-terminal, sans contenu chat : un signal de limite alimente le service + // (le badge UI vient du bus `AgentRateLimited`, pas du flux chat) puis on + // continue à drainer comme pour un battement. + if let domain::ports::ReplyEvent::RateLimited { resets_at_ms } = &event { + if let Some((project_id, agent_id, node_id, conversation_id)) = &meta { + let (conversation_id, resets_at_ms) = match (conversation_id, resets_at_ms) + { + (Some(conversation_id), resets_at_ms) => { + (Some(conversation_id.clone()), *resets_at_ms) + } + (None, None) => (None, None), + // Reset connu mais conversation moteur absente : ne pas armer + // une reprise non reprenable ; surfacer le fallback humain. + (None, Some(_)) => (None, None), + }; + service.on_rate_limited( + *project_id, + *agent_id, + *node_id, + conversation_id, + resets_at_ms, + ); + } + } + // Heartbeats carry no chat content (readiness/heartbeat lot 1) ⇒ no wire + // chunk; skip them while still draining so the turn runs to its `Final`. + let Some(chunk) = crate::chat::chunk_from_event(event) else { + continue; + }; + if matches!(chunk, ReplyChunk::Final { .. } | ReplyChunk::Error { .. }) { + terminal_visible = true; + } + // `send_output` always records into the conversation scrollback; the + // boolean only reflects live delivery. If the view navigated away + // (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 !resume && !concrete_terminal { + if let Some(guard) = turn_guard.as_mut() { + match runtime.block_on(guard.transition_after_provider_terminal_async()) { + backend::StructuredHumanTurnTransition::Complete => {} + backend::StructuredHumanTurnTransition::Resume => resume = true, + backend::StructuredHumanTurnTransition::Failed(message) => { + terminal_visible = true; + let _ = bridge.send_output_settled( + &sid, + &mut settler, + ReplyChunk::Error { message }, + ); + } + backend::StructuredHumanTurnTransition::Cancelled => { + terminal_visible = true; + } + } } } - // Heartbeats carry no chat content (readiness/heartbeat lot 1) ⇒ no wire - // chunk; skip them while still draining so the turn runs to its `Final`. - let Some(chunk) = crate::chat::chunk_from_event(event) else { - continue; - }; - if matches!(chunk, ReplyChunk::Final { .. } | ReplyChunk::Error { .. }) { - terminal_visible = true; + + if !resume { + break; + } + let Some((project_id, agent_id, _, _)) = meta.as_ref() else { + break; + }; + let Some(replacement) = structured.session_for_agent_in_project(*project_id, agent_id) + else { + terminal_visible = true; + let _ = bridge.send_output_settled( + &sid, + &mut settler, + ReplyChunk::Error { + message: "Le redémarrage n'a pas fourni de session de remplacement." + .to_owned(), + }, + ); + break; + }; + match runtime.block_on(replacement.send(application::SELF_RESTART_CONTINUATION_PROMPT)) + { + Ok(stream) => current_stream = stream, + Err(error) => { + terminal_visible = true; + let _ = bridge.send_output_settled( + &sid, + &mut settler, + ReplyChunk::Error { + message: error.to_string(), + }, + ); + break; + } } - // `send_output` always records into the conversation scrollback; the - // boolean only reflects live delivery. If the view navigated away - // (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 !terminal_visible { let chunk = crate::chat::unfinished_turn_chunk(); diff --git a/crates/application/src/lib.rs b/crates/application/src/lib.rs index 5cd85674..b4fe9039 100644 --- a/crates/application/src/lib.rs +++ b/crates/application/src/lib.rs @@ -168,9 +168,9 @@ pub use orchestrator::{ AgentRestartScheduled, AgentSelfRestartScheduler, AgentWakeService, AskLivenessProbe, ContextGuardUseCases, LiveStateProvider, LiveStateReadProvider, McpRuntimeProvider, OrchestratorOutcome, OrchestratorService, ProposeContext, ReadContext, ReadMemory, - RecordTurnProvider, RestartRendezvousCoordinator, RestartRendezvousState, WakeSessionProvider, - WatchdogOutcome, WriteMemory, DEFAULT_RENDEZVOUS_CEILING, DEFAULT_RENDEZVOUS_WINDOW, - SELF_RESTART_CONTINUATION_PROMPT, + RecordTurnProvider, RestartRendezvousCoordinator, RestartRendezvousRegistration, + RestartRendezvousState, WakeSessionProvider, WatchdogOutcome, WriteMemory, + DEFAULT_RENDEZVOUS_CEILING, DEFAULT_RENDEZVOUS_WINDOW, SELF_RESTART_CONTINUATION_PROMPT, }; pub use permission::{ GetProjectPermissions, GetProjectPermissionsInput, GetProjectPermissionsOutput, diff --git a/crates/application/src/orchestrator/mod.rs b/crates/application/src/orchestrator/mod.rs index 1b8b9ca0..0f05fb0d 100644 --- a/crates/application/src/orchestrator/mod.rs +++ b/crates/application/src/orchestrator/mod.rs @@ -21,7 +21,7 @@ pub use rendezvous::{ pub use service::{ AgentRestartScheduled, AgentSelfRestartScheduler, AskLivenessProbe, ContextGuardUseCases, LiveStateProvider, LiveStateReadProvider, McpRuntimeProvider, OrchestratorOutcome, - OrchestratorService, RecordTurnProvider, RestartRendezvousCoordinator, RestartRendezvousState, - SELF_RESTART_CONTINUATION_PROMPT, + OrchestratorService, RecordTurnProvider, RestartRendezvousCoordinator, + RestartRendezvousRegistration, RestartRendezvousState, SELF_RESTART_CONTINUATION_PROMPT, }; pub use wake::{AgentWakeService, WakeSessionProvider}; diff --git a/crates/application/src/orchestrator/service.rs b/crates/application/src/orchestrator/service.rs index 3eeb2d27..6ae31136 100644 --- a/crates/application/src/orchestrator/service.rs +++ b/crates/application/src/orchestrator/service.rs @@ -138,7 +138,9 @@ impl RestartRendezvousCoordinator { Self::default() } - fn register(self: &Arc, key: RuntimeAgentKey) -> RestartRendezvousRegistration { + /// Registers one logical turn that must survive replacement of its concrete + /// provider session. + pub fn register(self: &Arc, key: RuntimeAgentKey) -> RestartRendezvousRegistration { let mut entries = self .entries .lock() @@ -359,20 +361,29 @@ impl RestartRendezvousCoordinator { } } -struct RestartRendezvousRegistration { +/// Per-turn subscription to restart control-plane transitions. +pub struct RestartRendezvousRegistration { coordinator: Arc, key: RuntimeAgentKey, rx: watch::Receiver, } impl RestartRendezvousRegistration { - fn state(&mut self) -> RestartRendezvousState { + /// Returns the latest transition, marking it observed by this turn. + pub fn state(&mut self) -> RestartRendezvousState { self.rx.borrow_and_update().clone() } - async fn changed(&mut self) -> Result<(), watch::error::RecvError> { + /// Waits asynchronously for a restart control transition. + pub async fn changed(&mut self) -> Result<(), watch::error::RecvError> { self.rx.changed().await } + + /// Acknowledges ownership of the replacement session and releases the + /// detached restart scheduler. + pub fn accept_ready(&self, restart_id: &str) { + self.coordinator.resume_taken(self.key, restart_id); + } } impl Drop for RestartRendezvousRegistration { diff --git a/crates/application/src/workstate/actions.rs b/crates/application/src/workstate/actions.rs index 39033220..e2f19591 100644 --- a/crates/application/src/workstate/actions.rs +++ b/crates/application/src/workstate/actions.rs @@ -149,6 +149,7 @@ pub struct StopLiveAgent { input: Option>, waits: Option>, cascade: Option>, + restart_rendezvous: Option>, } impl StopLiveAgent { @@ -161,6 +162,7 @@ impl StopLiveAgent { input: None, waits: None, cascade: None, + restart_rendezvous: None, } } @@ -187,6 +189,16 @@ impl StopLiveAgent { self } + /// Wires cancellation of a logical turn currently crossing a self-restart. + #[must_use] + pub fn with_restart_rendezvous( + mut self, + coordinator: Arc, + ) -> Self { + self.restart_rendezvous = Some(coordinator); + self + } + /// Stops the agent's current work. /// /// # Errors @@ -198,6 +210,9 @@ impl StopLiveAgent { ) -> Result { self.stop_dependencies(&input.project, input.agent_id) .await?; + if let Some(coordinator) = &self.restart_rendezvous { + coordinator.cancel(RuntimeAgentKey::new(input.project.id, input.agent_id)); + } if let Some(mediator) = &self.input { mediator.preempt(RuntimeAgentKey::new(input.project.id, input.agent_id)); } diff --git a/crates/application/tests/orchestrator_service.rs b/crates/application/tests/orchestrator_service.rs index df58098e..a1c03c27 100644 --- a/crates/application/tests/orchestrator_service.rs +++ b/crates/application/tests/orchestrator_service.rs @@ -5716,6 +5716,60 @@ async fn same_project_stop_during_restart_releases_ask_and_leaves_no_replacement ); } +#[tokio::test] +async fn direct_human_turn_registration_holds_restart_until_replacement_handoff() { + let coordinator = Arc::new(application::RestartRendezvousCoordinator::new()); + let key = rkey(1); + let restart_id = "restart-human-turn".to_owned(); + let mut turn = coordinator.register(key); + + assert!( + coordinator.begin(key, restart_id.clone()), + "a registered direct human turn owns self-restart continuation" + ); + assert!(matches!( + turn.state(), + application::RestartRendezvousState::Restarting { .. } + )); + + coordinator.ready(key, &restart_id); + turn.changed().await.expect("ready transition remains live"); + assert_eq!( + turn.state(), + application::RestartRendezvousState::Ready { + restart_id: restart_id.clone() + } + ); + + turn.accept_ready(&restart_id); + coordinator + .wait_for_handoff(key, &restart_id) + .await + .expect("scheduler is released only after the replacement is accepted"); + assert_eq!(turn.state(), application::RestartRendezvousState::Idle); +} + +#[tokio::test] +async fn direct_human_turn_registration_surfaces_restart_failure() { + let coordinator = Arc::new(application::RestartRendezvousCoordinator::new()); + let key = rkey(1); + let restart_id = "restart-human-failure".to_owned(); + let mut turn = coordinator.register(key); + + assert!(coordinator.begin(key, restart_id.clone())); + coordinator.fail(key, &restart_id, "replacement launch failed".to_owned()); + turn.changed() + .await + .expect("failure transition remains live"); + assert_eq!( + turn.state(), + application::RestartRendezvousState::Failed { + restart_id, + message: "replacement launch failed".to_owned(), + } + ); +} + /// Régression #287 : la sonde transcript peut être stable (notamment inexistante pour /// un provider non-Claude), tandis que `send_with_tap` publie réellement l'activité du /// tour. Chaque événement non terminal doit réarmer la fenêtre jusqu'au `Final` tardif. diff --git a/crates/backend/src/lib.rs b/crates/backend/src/lib.rs index f39fb79b..a4e0202f 100644 --- a/crates/backend/src/lib.rs +++ b/crates/backend/src/lib.rs @@ -1072,6 +1072,7 @@ fn self_restart_launch_input( #[cfg(test)] mod self_restart_contract_tests { use super::*; + use domain::input::InputMediator; #[test] fn relaunch_preserves_conversation_and_never_requests_fresh_start() { @@ -1104,6 +1105,77 @@ mod self_restart_contract_tests { assert!(input.require_structured); assert!(!input.fresh_start); } + + #[tokio::test] + async fn human_turn_ignores_old_terminal_and_accepts_replacement() { + let mailbox = Arc::new(InMemoryMailbox::new()); + let input = Arc::new(MediatedInbox::new( + Arc::clone(&mailbox), + Arc::new(SystemMillisClock), + )); + let coordinator = Arc::new(application::RestartRendezvousCoordinator::new()); + let project_id = ProjectId::from_uuid(Uuid::from_u128(290)); + let agent_id = AgentId::from_uuid(Uuid::from_u128(1)); + let key = domain::RuntimeAgentKey::new(project_id, agent_id); + let mut turn = StructuredHumanTurnGuard::start( + Arc::clone(&input) as Arc, + Arc::clone(&mailbox) as Arc, + Arc::clone(&coordinator), + project_id, + agent_id, + "restart me", + ); + let restart_id = "restart-human-290".to_owned(); + + assert!(coordinator.begin(key, restart_id.clone())); + coordinator.ready(key, &restart_id); + assert_eq!( + turn.transition_after_provider_terminal_async().await, + StructuredHumanTurnTransition::Resume, + "the old provider terminal is a control transition, not the human Final" + ); + coordinator + .wait_for_handoff(key, &restart_id) + .await + .expect("replacement handoff is acknowledged"); + + turn.finish(); + assert_eq!(input.busy_state(key), domain::input::AgentBusyState::Idle); + assert_eq!(mailbox.pending(&key), 0); + } + + #[tokio::test] + async fn human_turn_surfaces_explicit_restart_failure_and_releases_busy() { + let mailbox = Arc::new(InMemoryMailbox::new()); + let input = Arc::new(MediatedInbox::new( + Arc::clone(&mailbox), + Arc::new(SystemMillisClock), + )); + let coordinator = Arc::new(application::RestartRendezvousCoordinator::new()); + let project_id = ProjectId::from_uuid(Uuid::from_u128(290)); + let agent_id = AgentId::from_uuid(Uuid::from_u128(2)); + let key = domain::RuntimeAgentKey::new(project_id, agent_id); + let mut turn = StructuredHumanTurnGuard::start( + Arc::clone(&input) as Arc, + Arc::clone(&mailbox) as Arc, + Arc::clone(&coordinator), + project_id, + agent_id, + "restart me", + ); + let restart_id = "restart-human-failure-290".to_owned(); + + assert!(coordinator.begin(key, restart_id.clone())); + coordinator.fail(key, &restart_id, "replacement failed".to_owned()); + assert_eq!( + turn.transition_after_provider_terminal_async().await, + StructuredHumanTurnTransition::Failed("replacement failed".to_owned()) + ); + + turn.finish(); + assert_eq!(input.busy_state(key), domain::input::AgentBusyState::Idle); + assert_eq!(mailbox.pending(&key), 0); + } } impl AgentSelfRestartScheduler for AppAgentSelfRestarter { @@ -1306,6 +1378,7 @@ impl AgentSelfRestartScheduler for AppAgentSelfRestarter { let turn = StructuredHumanTurnGuard::start( Arc::clone(&input), Arc::clone(&mailbox), + Arc::clone(&rendezvous), project.id, agent, application::SELF_RESTART_CONTINUATION_PROMPT, @@ -1361,23 +1434,39 @@ impl AgentSelfRestartScheduler for AppAgentSelfRestarter { /// /// Direct chat adapters (`agent_send`, web `chat.send`) still drive the provider /// through `AgentSession::send`, but they must share the same busy/FIFO accounting -/// as structured inter-agent asks. This guard starts a silent human turn and -/// retires it on terminal outcome or drop. +/// as structured inter-agent asks. This guard starts a silent human turn, +/// registers it for restart handoff, and retires it only on the logical terminal +/// outcome or drop. pub struct StructuredHumanTurnGuard { input: Arc, mailbox: Arc, agent: domain::RuntimeAgentKey, ticket_id: domain::mailbox::TicketId, armed: Arc, + restart: Option, finish_on_drop: bool, } +/// Control-plane outcome observed when one concrete provider turn terminates. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum StructuredHumanTurnTransition { + /// No self-restart owns this terminal event; it is authoritative for the UI. + Complete, + /// The old terminal event is superseded; continue on the replacement runtime. + Resume, + /// Replacement failed explicitly. + Failed(String), + /// The user cancelled the logical turn while replacement was in flight. + Cancelled, +} + impl StructuredHumanTurnGuard { /// Starts the mediated busy turn for a direct human structured chat message. #[must_use] pub fn start( input: Arc, mailbox: Arc, + restart_rendezvous: Arc, project_id: ProjectId, agent_id: AgentId, prompt: &str, @@ -1397,6 +1486,7 @@ impl StructuredHumanTurnGuard { agent, ticket_id, armed: Arc::new(std::sync::atomic::AtomicBool::new(owns_turn)), + restart: owns_turn.then(|| restart_rendezvous.register(agent)), finish_on_drop: true, } } @@ -1410,10 +1500,55 @@ impl StructuredHumanTurnGuard { agent: self.agent, ticket_id: self.ticket_id, armed: Arc::clone(&self.armed), + restart: None, finish_on_drop: false, } } + /// Resolves whether a concrete provider terminal event really ends the + /// logical human turn. During self-restart it waits for the replacement + /// session, then acknowledges the handoff so the scheduler can complete. + pub async fn transition_after_provider_terminal_async( + &mut self, + ) -> StructuredHumanTurnTransition { + let Some(restart) = self.restart.as_mut() else { + return StructuredHumanTurnTransition::Complete; + }; + loop { + match restart.state() { + application::RestartRendezvousState::Idle => { + return StructuredHumanTurnTransition::Complete; + } + application::RestartRendezvousState::Restarting { .. } => { + if restart.changed().await.is_err() { + return StructuredHumanTurnTransition::Failed( + "restart coordination closed".to_owned(), + ); + } + } + application::RestartRendezvousState::Ready { restart_id } => { + restart.accept_ready(&restart_id); + return StructuredHumanTurnTransition::Resume; + } + application::RestartRendezvousState::Failed { message, .. } => { + return StructuredHumanTurnTransition::Failed(message); + } + application::RestartRendezvousState::Cancelled { .. } => { + return StructuredHumanTurnTransition::Cancelled; + } + } + } + } + + /// Reports whether a self-restart already owns the current concrete + /// provider failure. The scheduler publishes `Restarting` synchronously + /// before the MCP tool returns, so this check does not inspect provider text. + pub fn restart_is_active(&mut self) -> bool { + self.restart.as_mut().is_some_and(|restart| { + !matches!(restart.state(), application::RestartRendezvousState::Idle) + }) + } + /// Records proof of liveness for the running turn. pub fn mark_alive(&self) { if self.armed.load(std::sync::atomic::Ordering::Acquire) { @@ -2005,6 +2140,8 @@ pub struct BackendCore { /// Mailbox paired with [`Self::turn_watch_input`], used by direct structured /// human turns to retire the synthetic busy/FIFO ticket on terminal outcome. pub input_mailbox: Arc, + /// Stable restart control-plane shared by delegated and direct human turns. + pub restart_rendezvous: Arc, /// Handles des watches de fin-de-tour vivants, par clé runtime. (Re)lancer un agent /// dans un projet **remplace** son handle (l'ancien est droppé ⇒ polling arrêté) ; /// fermer/arrêter l'agent le retire. `Mutex` car launch/stop y accèdent concurremment. @@ -3639,7 +3776,7 @@ impl BackendCore { .with_conversations(Arc::clone(&conversation_registry)) .with_events(Arc::clone(&events_port)) .with_self_restart(self_restart) - .with_restart_rendezvous(restart_rendezvous) + .with_restart_rendezvous(Arc::clone(&restart_rendezvous)) // Faits OS/runtime (exe $APPIMAGE/current_exe + endpoint loopback) pour // que les (re)lancements issus du chemin `ask` écrivent la déclaration MCP // réelle ⇒ le pont MCP de la cible se spawne et `idea_reply` débloque le round-trip. @@ -3757,7 +3894,8 @@ impl BackendCore { Arc::clone(&orchestrator_service), ) .with_execution_cascade(Arc::clone(&orchestrator_service) - as Arc), + as Arc) + .with_restart_rendezvous(Arc::clone(&restart_rendezvous)), ); // --- Windows (L10) --- @@ -3952,6 +4090,7 @@ impl BackendCore { turn_watcher, turn_watch_input: Arc::clone(&input_mediator), input_mailbox: Arc::clone(&mailbox), + restart_rendezvous: Arc::clone(&restart_rendezvous), turn_watch_handles: Mutex::new(HashMap::new()), fs_port: Arc::clone(&fs_port), home_dir, diff --git a/crates/web-server/src/lib.rs b/crates/web-server/src/lib.rs index e5826f00..ae27add8 100644 --- a/crates/web-server/src/lib.rs +++ b/crates/web-server/src/lib.rs @@ -1882,6 +1882,7 @@ async fn ws_send_chat( backend::StructuredHumanTurnGuard::start( Arc::clone(&state.app.turn_watch_input), Arc::clone(&state.app.input_mailbox), + Arc::clone(&state.app.restart_rendezvous), *project_id, *agent_id, &prompt, @@ -1898,6 +1899,9 @@ async fn ws_send_chat( guard.mark_alive(); } } + if matches!(event, ReplyEvent::Final { .. } | ReplyEvent::Error { .. }) { + continue; + } let chunk = reply_event_chunk(event); if tap_out .blocking_send(ServerFrame::chat_output(None, sid, chunk)) @@ -1909,35 +1913,128 @@ async fn ws_send_chat( }); let stream_result = session.send_with_tap(&prompt, tap_tx).await; let _ = tap_pump.join(); - let stream = stream_result.map_err(|err| ErrorDto { - code: "CHAT_SEND_FAILED".to_owned(), - message: err.to_string(), - })?; + let mut turn_guard = turn_guard; + let mut stream = match stream_result { + Ok(stream) => stream, + Err(_err) + if turn_guard + .as_mut() + .is_some_and(|guard| guard.restart_is_active()) => + { + Box::new(std::iter::empty()) as domain::ports::ReplyStream + } + Err(err) => { + return Err(ErrorDto { + code: "CHAT_SEND_FAILED".to_owned(), + message: err.to_string(), + }) + } + }; let mut sent_ack = false; - for event in stream { - if !matches!(event, ReplyEvent::Final { .. }) { - if let Some(guard) = &turn_guard { - guard.mark_alive(); + 'logical_turn: loop { + let mut concrete_terminal = false; + let mut resume = false; + for event in stream { + let provider_terminal = + matches!(event, ReplyEvent::Final { .. } | ReplyEvent::Error { .. }); + if provider_terminal { + concrete_terminal = true; + if let Some(guard) = turn_guard.as_mut() { + match guard.transition_after_provider_terminal_async().await { + backend::StructuredHumanTurnTransition::Complete => {} + backend::StructuredHumanTurnTransition::Resume => { + resume = true; + break; + } + backend::StructuredHumanTurnTransition::Failed(message) => { + let chunk = reply_event_chunk(ReplyEvent::Error { message }); + tx.send(ServerFrame::chat_output( + (!sent_ack).then_some(frame.id.as_str()), + sid, + chunk, + )) + .await + .map_err(|_| ErrorDto { + code: "WS_CLOSED".to_owned(), + message: "websocket output closed".to_owned(), + })?; + sent_ack = true; + break 'logical_turn; + } + backend::StructuredHumanTurnTransition::Cancelled => { + break 'logical_turn; + } + } + } + } else { + if let Some(guard) = &turn_guard { + guard.mark_alive(); + } + } + let chunk = reply_event_chunk(event); + if !sent_ack { + tx.send(ServerFrame::chat_output(Some(&frame.id), sid, chunk)) + .await + .map_err(|_| ErrorDto { + code: "WS_CLOSED".to_owned(), + message: "websocket output closed".to_owned(), + })?; + sent_ack = true; + } else { + tx.send(ServerFrame::chat_output(None, sid, chunk)) + .await + .map_err(|_| ErrorDto { + code: "WS_CLOSED".to_owned(), + message: "websocket output closed".to_owned(), + })?; } } - let chunk = reply_event_chunk(event); - if !sent_ack { - tx.send(ServerFrame::chat_output(Some(&frame.id), sid, chunk)) - .await - .map_err(|_| ErrorDto { - code: "WS_CLOSED".to_owned(), - message: "websocket output closed".to_owned(), - })?; - sent_ack = true; - } else { - tx.send(ServerFrame::chat_output(None, sid, chunk)) - .await - .map_err(|_| ErrorDto { - code: "WS_CLOSED".to_owned(), - message: "websocket output closed".to_owned(), - })?; + + if !resume && !concrete_terminal { + 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::Failed(message) => { + let chunk = reply_event_chunk(ReplyEvent::Error { message }); + tx.send(ServerFrame::chat_output( + (!sent_ack).then_some(frame.id.as_str()), + sid, + chunk, + )) + .await + .map_err(|_| ErrorDto { + code: "WS_CLOSED".to_owned(), + message: "websocket output closed".to_owned(), + })?; + sent_ack = true; + } + backend::StructuredHumanTurnTransition::Cancelled => break, + } + } } + if !resume { + break; + } + let Some((project_id, agent_id, _, _)) = session_meta.as_ref() else { + break; + }; + let replacement = state + .app + .structured_sessions + .session_for_agent_in_project(*project_id, agent_id) + .ok_or_else(|| ErrorDto { + code: "CHAT_RESTART_FAILED".to_owned(), + message: "restart completed without a replacement structured session".to_owned(), + })?; + stream = replacement + .send(application::SELF_RESTART_CONTINUATION_PROMPT) + .await + .map_err(|err| ErrorDto { + code: "CHAT_RESTART_FAILED".to_owned(), + message: err.to_string(), + })?; } if !sent_ack {