merge(custom-cli): streaming progress/outils restauré sur la seconde requête au même agent — #252 (QA verte)
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 441 KiB |
@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"id": "20e73001-de0e-4905-9c89-8051721f66ef",
|
||||||
|
"projectId": "97b49ac2-8376-4aa3-8ea9-bf3ac81d0023",
|
||||||
|
"agentId": "a6c6ea12-bfc6-4bdc-8031-324102dfa34d",
|
||||||
|
"sessionId": "7a7a2723-6659-4962-b610-98187ff94327",
|
||||||
|
"filename": "clipboard-image-1787499890595.png",
|
||||||
|
"mime": "image/png",
|
||||||
|
"sizeBytes": 451914,
|
||||||
|
"sourceKind": "clipboard",
|
||||||
|
"storagePath": "agent-chat/7a7a2723-6659-4962-b610-98187ff94327/20e73001-de0e-4905-9c89-8051721f66ef-clipboard-image-1787499890595.png",
|
||||||
|
"readablePath": "/home/anthony/Documents/Projects/IdeA/.ideai/attachments/agent-chat/7a7a2723-6659-4962-b610-98187ff94327/20e73001-de0e-4905-9c89-8051721f66ef-clipboard-image-1787499890595.png",
|
||||||
|
"createdAt": 1787499910141
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 48 KiB |
@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"id": "2695b989-6f90-4bb6-ad0e-b19fe51784a7",
|
||||||
|
"projectId": "97b49ac2-8376-4aa3-8ea9-bf3ac81d0023",
|
||||||
|
"agentId": "a6c6ea12-bfc6-4bdc-8031-324102dfa34d",
|
||||||
|
"sessionId": "7a7a2723-6659-4962-b610-98187ff94327",
|
||||||
|
"filename": "clipboard-image-1787496765661.png",
|
||||||
|
"mime": "image/png",
|
||||||
|
"sizeBytes": 48599,
|
||||||
|
"sourceKind": "clipboard",
|
||||||
|
"storagePath": "agent-chat/7a7a2723-6659-4962-b610-98187ff94327/2695b989-6f90-4bb6-ad0e-b19fe51784a7-clipboard-image-1787496765661.png",
|
||||||
|
"readablePath": "/home/anthony/Documents/Projects/IdeA/.ideai/attachments/agent-chat/7a7a2723-6659-4962-b610-98187ff94327/2695b989-6f90-4bb6-ad0e-b19fe51784a7-clipboard-image-1787496765661.png",
|
||||||
|
"createdAt": 1787496806513
|
||||||
|
}
|
||||||
@ -5,7 +5,7 @@
|
|||||||
"probableAppModule": null,
|
"probableAppModule": null,
|
||||||
"projectId": "97b49ac2-8376-4aa3-8ea9-bf3ac81d0023",
|
"projectId": "97b49ac2-8376-4aa3-8ea9-bf3ac81d0023",
|
||||||
"projectRoot": "/home/anthony/Documents/Projects/IdeA",
|
"projectRoot": "/home/anthony/Documents/Projects/IdeA",
|
||||||
"updatedAt": "2026-08-23T14:26:45.510Z"
|
"updatedAt": "2026-08-23T14:50:50.473Z"
|
||||||
},
|
},
|
||||||
"ownerAgentId": null,
|
"ownerAgentId": null,
|
||||||
"ownerAgentMapping": {},
|
"ownerAgentMapping": {},
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
---
|
---
|
||||||
issueRef: "#247"
|
issueRef: "#247"
|
||||||
version: 6
|
version: 8
|
||||||
updatedBy: {"kind":"agent","agent_id":"a6c6ea12-bfc6-4bdc-8031-324102dfa34d"}
|
updatedBy: {"kind":"agent","agent_id":"a6c6ea12-bfc6-4bdc-8031-324102dfa34d"}
|
||||||
updatedAt: 1787495640501
|
updatedAt: 1787495761555
|
||||||
---
|
---
|
||||||
2026-08-23 Main
|
2026-08-23 Main
|
||||||
- Demande utilisateur clarifiée: en custom CLI, l’agent doit rester `busy` dans le workstate, mais sans overlay plein panneau.
|
- Demande utilisateur clarifiée: en custom CLI, l’agent doit rester `busy` dans le workstate, mais sans overlay plein panneau.
|
||||||
@ -13,4 +13,7 @@ updatedAt: 1787495640501
|
|||||||
- DevFrontend: correctif implémenté; overlay retiré en custom CLI, badge busy conservé, chat toujours bloqué, TUI natif inchangé.
|
- DevFrontend: correctif implémenté; overlay retiré en custom CLI, badge busy conservé, chat toujours bloqué, TUI natif inchangé.
|
||||||
- Tests dev verts: `npm test -- src/features/layout/LayoutGrid.chat.test.tsx src/features/agents/CustomAgentChatView.test.tsx` (74 pass), `npm test -- src/features/workstate/workstate.test.tsx` (23 pass).
|
- Tests dev verts: `npm test -- src/features/layout/LayoutGrid.chat.test.tsx src/features/agents/CustomAgentChatView.test.tsx` (74 pass), `npm test -- src/features/workstate/workstate.test.tsx` (23 pass).
|
||||||
- QA: verdict OK sur l’état courant du dépôt (`develop`, worktree sale). Validation indépendante via `npm run test -- src/features/layout/LayoutGrid.chat.test.tsx src/features/agents/CustomAgentChatView.test.tsx` => 74/74 verts.
|
- QA: verdict OK sur l’état courant du dépôt (`develop`, worktree sale). Validation indépendante via `npm run test -- src/features/layout/LayoutGrid.chat.test.tsx src/features/agents/CustomAgentChatView.test.tsx` => 74/74 verts.
|
||||||
- QA a confirmé le test dédié `keeps custom CLI visible while busy and locks only the composer (#247)` et l’absence d’overlay en custom CLI.
|
- QA a confirmé le test dédié `keeps custom CLI visible while busy and locks only the composer (#247)` et l’absence d’overlay en custom CLI.
|
||||||
|
- Git a créé `fc923b7` sur `fix/247-custom-cli-busy-overlay`, puis merge local `--no-ff` dans `develop` via `09df7bf`.
|
||||||
|
- Git a isolé la synchro du store local dans `38f5572` pour ne pas polluer le commit de fix.
|
||||||
|
- État final: branche courante `develop`, aucun changement suivi restant pour #247; seul `scripts/restart-idea-over-ssh.sh` non suivi subsiste hors périmètre.
|
||||||
@ -2,7 +2,7 @@
|
|||||||
id: "6b8e3a3f-ed83-43e4-a764-5e465ac95345"
|
id: "6b8e3a3f-ed83-43e4-a764-5e465ac95345"
|
||||||
number: 247
|
number: 247
|
||||||
title: "Retirer l’overlay busy de la custom CLI et conserver le blocage du chat seul"
|
title: "Retirer l’overlay busy de la custom CLI et conserver le blocage du chat seul"
|
||||||
status: "qa"
|
status: "closed"
|
||||||
priority: "high"
|
priority: "high"
|
||||||
sprint: null
|
sprint: null
|
||||||
links: []
|
links: []
|
||||||
@ -11,7 +11,7 @@ attachments: []
|
|||||||
createdBy: {"kind":"agent","agent_id":"a6c6ea12-bfc6-4bdc-8031-324102dfa34d"}
|
createdBy: {"kind":"agent","agent_id":"a6c6ea12-bfc6-4bdc-8031-324102dfa34d"}
|
||||||
updatedBy: {"kind":"agent","agent_id":"a6c6ea12-bfc6-4bdc-8031-324102dfa34d"}
|
updatedBy: {"kind":"agent","agent_id":"a6c6ea12-bfc6-4bdc-8031-324102dfa34d"}
|
||||||
createdAt: 1787495368894
|
createdAt: 1787495368894
|
||||||
updatedAt: 1787495640501
|
updatedAt: 1787495761555
|
||||||
version: 6
|
version: 8
|
||||||
---
|
---
|
||||||
Quand un agent reçoit une demande, il doit apparaître busy dans le workstate, mais la custom CLI ne doit pas afficher un overlay plein panneau qui masque la sortie et bloque toute interaction visuelle. Le comportement attendu est celui du chat déjà bloqué après envoi d’un message: saisie désactivée/contrôles pertinents bloqués, sans voile global sur la vue terminal/chat.
|
Quand un agent reçoit une demande, il doit apparaître busy dans le workstate, mais la custom CLI ne doit pas afficher un overlay plein panneau qui masque la sortie et bloque toute interaction visuelle. Le comportement attendu est celui du chat déjà bloqué après envoi d’un message: saisie désactivée/contrôles pertinents bloqués, sans voile global sur la vue terminal/chat.
|
||||||
15
.ideai/tickets/248/carnet.md
Normal file
15
.ideai/tickets/248/carnet.md
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
---
|
||||||
|
issueRef: "#248"
|
||||||
|
version: 5
|
||||||
|
updatedBy: {"kind":"agent","agent_id":"a6c6ea12-bfc6-4bdc-8031-324102dfa34d"}
|
||||||
|
updatedAt: 1787496151533
|
||||||
|
---
|
||||||
|
2026-08-23 Main
|
||||||
|
- Demande utilisateur: rebuild AppImage.
|
||||||
|
- Skill `build-appimage` chargé et suivi.
|
||||||
|
- Git a identifié le commit de build `38f5572ce5fbe7dc57d72f24e2193c092cb5b98d` sur `develop`.
|
||||||
|
- Git a créé le tag local annoté `v0.30.0` sur ce commit exact selon la règle develop => incrément de `y` depuis `v0.29.0`.
|
||||||
|
- Point d’attention Git: worktree sale sur `.ideai` et `scripts/`, non inclus dans le commit taggé.
|
||||||
|
- Build frontend exécuté avec succès via `npm --prefix frontend run build`.
|
||||||
|
- Bundling Tauri AppImage exécuté depuis `crates/app-tauri` avec `CARGO_HOME` en tmp managé + `APPIMAGE_EXTRACT_AND_RUN=1 NO_STRIP=1`.
|
||||||
|
- L’artefact a été produit sous `target/release/bundle/appimage/IdeA_0.3.0_amd64.AppImage`.
|
||||||
17
.ideai/tickets/248/issue.md
Normal file
17
.ideai/tickets/248/issue.md
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
---
|
||||||
|
id: "4b7abab6-31d1-49e9-b2f9-e8b42afed1b5"
|
||||||
|
number: 248
|
||||||
|
title: "Rebuild AppImage IdeA Linux"
|
||||||
|
status: "closed"
|
||||||
|
priority: "medium"
|
||||||
|
sprint: null
|
||||||
|
links: []
|
||||||
|
agentRefs: []
|
||||||
|
attachments: []
|
||||||
|
createdBy: {"kind":"agent","agent_id":"a6c6ea12-bfc6-4bdc-8031-324102dfa34d"}
|
||||||
|
updatedBy: {"kind":"agent","agent_id":"a6c6ea12-bfc6-4bdc-8031-324102dfa34d"}
|
||||||
|
createdAt: 1787495811958
|
||||||
|
updatedAt: 1787496151533
|
||||||
|
version: 5
|
||||||
|
---
|
||||||
|
Reconstruction de l’AppImage Linux d’IdeA à la demande utilisateur, avec incrément de tag/version Git sur le commit exact du build selon la règle de branche (x sur main, y sur develop, z sur autre branche).
|
||||||
16
.ideai/tickets/249/carnet.md
Normal file
16
.ideai/tickets/249/carnet.md
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
issueRef: "#249"
|
||||||
|
version: 7
|
||||||
|
updatedBy: {"kind":"agent","agent_id":"a6c6ea12-bfc6-4bdc-8031-324102dfa34d"}
|
||||||
|
updatedAt: 1787497292539
|
||||||
|
---
|
||||||
|
2026-08-23 Main
|
||||||
|
- Signalement utilisateur avec screenshot: des boutons/controles d’overlay restent visibles dans la partie superieure de la cellule au-dessus de la custom CLI pendant une demande agent.
|
||||||
|
- Ticket cree puis passe en inProgress.
|
||||||
|
- Cadrage UX: en mode custom CLI, seule la surface CLI et son header minimal doivent rester visibles pendant l’execution; pas de chrome generique de cellule superpose.
|
||||||
|
- Cadrage Architect: correction a porter dans frontend/src/features/layout/LayoutGrid.tsx via composition conditionnelle explicite pour cellMode=custom, sans casser le mode TUI ni le contrat du fix #247.
|
||||||
|
- Git a cree la branche locale fix/249-custom-cli-overlay-controls depuis develop.
|
||||||
|
- DevFrontend a implemente le fix sur cette branche: suppression du chrome generique de cellule pendant custom CLI + busy; ajustement des tests LayoutGrid.*.
|
||||||
|
- QA a valide en vert via tests reels: LayoutGrid.cellControlsLayering.test.tsx, LayoutGrid.chat.test.tsx, overlayExclusion.test.ts.
|
||||||
|
- Git a commit le fix seul dans 55dc7c9 puis merge localement avec --no-ff dans develop via d5fea39. Aucun push distant. Les changements non lies .ideai/ et scripts/ ont ete exclus du commit.
|
||||||
|
- Ticket cloture.
|
||||||
17
.ideai/tickets/249/issue.md
Normal file
17
.ideai/tickets/249/issue.md
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
---
|
||||||
|
id: "47aa211b-5b56-405e-b23b-bd6f6468e924"
|
||||||
|
number: 249
|
||||||
|
title: "Bug UI: les boutons d’overlay restent visibles sur la custom CLI pendant une demande agent"
|
||||||
|
status: "closed"
|
||||||
|
priority: "high"
|
||||||
|
sprint: null
|
||||||
|
links: []
|
||||||
|
agentRefs: []
|
||||||
|
attachments: []
|
||||||
|
createdBy: {"kind":"agent","agent_id":"a6c6ea12-bfc6-4bdc-8031-324102dfa34d"}
|
||||||
|
updatedBy: {"kind":"agent","agent_id":"a6c6ea12-bfc6-4bdc-8031-324102dfa34d"}
|
||||||
|
createdAt: 1787496831265
|
||||||
|
updatedAt: 1787497292539
|
||||||
|
version: 7
|
||||||
|
---
|
||||||
|
Dans la cellule agent, lors d’une demande lancée sur la custom CLI, des boutons/contrôles d’overlay restent affichés dans la partie supérieure au-dessus de la surface CLI. Le screenshot utilisateur montre notamment les contrôles de mode/annulation qui se superposent à la zone terminale. Attendu: la custom CLI ne doit pas être polluée visuellement par des contrôles d’overlay non pertinents pendant l’exécution.
|
||||||
24
.ideai/tickets/250/carnet.md
Normal file
24
.ideai/tickets/250/carnet.md
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
issueRef: "#250"
|
||||||
|
version: 9
|
||||||
|
updatedBy: {"kind":"agent","agent_id":"a6c6ea12-bfc6-4bdc-8031-324102dfa34d"}
|
||||||
|
updatedAt: 1787502038344
|
||||||
|
---
|
||||||
|
2026-08-23 Main
|
||||||
|
- Demande utilisateur: rebuild de l’AppImage.
|
||||||
|
- Skill build-appimage charge et suivi.
|
||||||
|
- Git a determine la base de build: branche develop, commit d5fea391, tag local cree v0.31.0 selon la regle projet (increment y sur develop).
|
||||||
|
- Build frontend execute via `npm --prefix frontend run build`.
|
||||||
|
- Bundle Tauri AppImage execute avec les variables du workflow: `CARGO_HOME=/tmp/idea-cargo-home APPIMAGE_EXTRACT_AND_RUN=1 NO_STRIP=1 ../../frontend/node_modules/.bin/tauri build --bundles appimage` depuis `crates/app-tauri/`.
|
||||||
|
- Particularite de session: bruit Python initial lie a l’environnement AppImage monte (`PYTHONHOME/PYTHONPATH` sous `/tmp/.mount_IdeA_*`), sans empecher l’execution effective du build dans cette session.
|
||||||
|
- Resultat: build termine avec code 0, artefact produit dans `target/release/bundle/appimage/IdeA_0.3.0_amd64.AppImage`.
|
||||||
|
- Etape manuelle restante non automatisee: remplacer l’AppImage installee puis relancer IdeA quand l’utilisateur est pret.
|
||||||
|
|
||||||
|
2026-08-23 Main
|
||||||
|
- Nouvelle demande utilisateur de rebuild AppImage apres merge local du ticket #251.
|
||||||
|
- Git a determine la nouvelle base de build: branche develop, commit 15c1c29, tag local cree v0.32.0 selon la regle projet (increment y sur develop).
|
||||||
|
- Build frontend relance via `npm --prefix frontend run build`.
|
||||||
|
- Bundle Tauri AppImage relance avec les variables du workflow: `CARGO_HOME=/tmp/idea-cargo-home APPIMAGE_EXTRACT_AND_RUN=1 NO_STRIP=1 ../../frontend/node_modules/.bin/tauri build --bundles appimage` depuis `crates/app-tauri/`.
|
||||||
|
- Particularite de session identique: bruit Python initial lie a l’environnement AppImage monte (`PYTHONHOME/PYTHONPATH` sous `/tmp/.mount_IdeA_*`), sans empecher l’execution effective du build.
|
||||||
|
- Resultat: rebuild termine avec code 0, artefact produit dans `target/release/bundle/appimage/IdeA_0.3.0_amd64.AppImage` sur la base taggee v0.32.0.
|
||||||
|
- Etape manuelle restante non automatisee: remplacer l’AppImage installee puis relancer IdeA quand l’utilisateur est pret.
|
||||||
17
.ideai/tickets/250/issue.md
Normal file
17
.ideai/tickets/250/issue.md
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
---
|
||||||
|
id: "88afe91e-f8a3-4de2-80f3-10ab9ee27b38"
|
||||||
|
number: 250
|
||||||
|
title: "Rebuild AppImage Linux"
|
||||||
|
status: "closed"
|
||||||
|
priority: "medium"
|
||||||
|
sprint: null
|
||||||
|
links: []
|
||||||
|
agentRefs: []
|
||||||
|
attachments: []
|
||||||
|
createdBy: {"kind":"agent","agent_id":"a6c6ea12-bfc6-4bdc-8031-324102dfa34d"}
|
||||||
|
updatedBy: {"kind":"agent","agent_id":"a6c6ea12-bfc6-4bdc-8031-324102dfa34d"}
|
||||||
|
createdAt: 1787498213514
|
||||||
|
updatedAt: 1787502038344
|
||||||
|
version: 9
|
||||||
|
---
|
||||||
|
Reconstruction de l’AppImage IdeA Linux selon le workflow projet valide, avec tagging Git du commit buildé conformement a la regle vx.y.z.
|
||||||
16
.ideai/tickets/251/carnet.md
Normal file
16
.ideai/tickets/251/carnet.md
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
issueRef: "#251"
|
||||||
|
version: 7
|
||||||
|
updatedBy: {"kind":"agent","agent_id":"a6c6ea12-bfc6-4bdc-8031-324102dfa34d"}
|
||||||
|
updatedAt: 1787500329426
|
||||||
|
---
|
||||||
|
2026-08-23 Main
|
||||||
|
- Signalement utilisateur avec screenshot: a partir de la seconde demande sur le meme agent, la vue custom CLI melange des reliquats du tour precedent (termine, carte de progression, separateurs d’etat) avec le nouveau tour.
|
||||||
|
- Ticket cree puis passe en inProgress.
|
||||||
|
- Cadrage UX: un seul ensemble d’UI dynamique a la fois, attache exclusivement au tour actif; les tours termines deviennent des blocs figes d’historique; ordre strictement chronologique; scroll ancre sur le tour actif.
|
||||||
|
- Cadrage Architect: bug probable dans la projection locale multi-tour de CustomAgentChatView.tsx, autour de send(), foldChunk(), completeChatViewProgress() et de la logique progressScope/progressIndex. Hypothese principale: double autorite d’ouverture de tour (optimiste local + userPrompt backend) ou progress tardif apres final.
|
||||||
|
- Git a cree la branche locale fix/251-custom-cli-conversation-turn-display depuis develop.
|
||||||
|
- DevFrontend a implemente le fix sur cette branche: idempotence de l’echo backend userPrompt sur le tour ouvert et neutralisation des progress tardifs apres final/error; tests renforces dans CustomAgentChatView.test.tsx.
|
||||||
|
- QA a valide en vert via tests reels: CustomAgentChatView.test.tsx et LayoutGrid.chat.test.tsx.
|
||||||
|
- Git a commit le fix seul dans 7b7ab5b puis merge localement avec --no-ff dans develop via 15c1c29. Aucun push distant. Les changements non lies .ideai/ et scripts/ ont ete exclus du commit.
|
||||||
|
- Ticket cloture.
|
||||||
17
.ideai/tickets/251/issue.md
Normal file
17
.ideai/tickets/251/issue.md
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
---
|
||||||
|
id: "40f32d57-3853-441a-ac2a-78a4d664aaff"
|
||||||
|
number: 251
|
||||||
|
title: "Bug UI: affichage incohérent de la conversation agent à partir du second tour"
|
||||||
|
status: "closed"
|
||||||
|
priority: "high"
|
||||||
|
sprint: null
|
||||||
|
links: []
|
||||||
|
agentRefs: []
|
||||||
|
attachments: []
|
||||||
|
createdBy: {"kind":"agent","agent_id":"a6c6ea12-bfc6-4bdc-8031-324102dfa34d"}
|
||||||
|
updatedBy: {"kind":"agent","agent_id":"a6c6ea12-bfc6-4bdc-8031-324102dfa34d"}
|
||||||
|
createdAt: 1787499941511
|
||||||
|
updatedAt: 1787500329426
|
||||||
|
version: 7
|
||||||
|
---
|
||||||
|
Dans la vue custom CLI d’un agent, à partir de la seconde demande sur le même agent, l’affichage de la conversation devient incohérent. Le screenshot montre la coexistence d’un ancien état terminé / carte de progression du tour précédent avec le nouveau tour utilisateur et un nouvel état en cours, ce qui pollue la lecture chronologique de la conversation. Attendu: chaque tour doit s’afficher de façon stable, ordonnée et sans reliquats visuels du tour précédent.
|
||||||
32
.ideai/tickets/252/carnet.md
Normal file
32
.ideai/tickets/252/carnet.md
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
---
|
||||||
|
issueRef: "#252"
|
||||||
|
version: 4
|
||||||
|
updatedBy: {"kind":"agent","agent_id":"a6c6ea12-bfc6-4bdc-8031-324102dfa34d"}
|
||||||
|
updatedAt: 1787551198700
|
||||||
|
---
|
||||||
|
# Git — décision de branche (2026-08-24)
|
||||||
|
|
||||||
|
- Branche de travail : `fix/252-custom-cli-second-request-streaming` (créée et checkout, base = tip develop `1f14217`, lignage custom CLI `#249/#251` QA verte inclus).
|
||||||
|
- Dev : implémente le fix sur cette branche. Commits : `fix(custom-cli): … — #252`.
|
||||||
|
- QA : tests fonctionnels réels sur la MÊME branche (deux requêtes successives au même agent custom CLI ; échec si progress/messages d'outils disparaissent au second tour). Commits : `test(custom-cli): … — #252`.
|
||||||
|
- Interdits : aucun push distant ; pas de merge/rebase vers `develop` sans l'agent Git. Fusion locale décidée par Git après QA verte.
|
||||||
|
- `scripts/restart-idea-over-ssh.sh` reste non suivi — ne pas le committer sur cette branche.
|
||||||
|
|
||||||
|
# Architect — cadrage root cause (2026-08-24)
|
||||||
|
|
||||||
|
- Suspect principal : `crates/infrastructure/src/session/opencode.rs`, `LiveTurnProjector` passe en `hold_until_new_message` dès qu'un `conversation_id` existe, donc typiquement au 2e tour.
|
||||||
|
- Effet probable : si le 2e tour n'expose qu'un seul `message_id` courant, le projecteur bufferise tous les `step_start` / `tool_use` / `text` dans `pending` et n'émet rien en live.
|
||||||
|
- Le pipeline backend `send_with_tap` rend le tap live critique : quand `has_tap=true`, le flux autoritaire ne garde plus que `Final|Error`; si le tap n'émet rien, l'UI ne verra jamais les `progress` / messages d'outils du 2e tour.
|
||||||
|
- Le frontend n'est pas la cause racine principale.
|
||||||
|
- Stratégie de fix minimale : corriger le seam OpenCode pour identifier le tour courant sans exiger un second basculement de `message_id`, puis ajouter une régression backend dédiée au cas « session existante + un seul `message_id` sur le nouveau tour ».
|
||||||
|
|
||||||
|
# QA — audit de couverture (2026-08-24)
|
||||||
|
|
||||||
|
- Les tests existants ne permettaient pas d'annoncer honnêtement vert sur ce bug.
|
||||||
|
- Gap principal : pas de test fonctionnel réaliste avec deux callbacks de stream distincts et un vrai réarmement du 2e tour après le `final` du 1er.
|
||||||
|
- Tests à exiger après fix :
|
||||||
|
- 2 requêtes successives avec nouveau callback de stream au 2e tour.
|
||||||
|
- Même outil entre les deux tours.
|
||||||
|
- Vérification d'un `progress started` puis `completed` sur le 2e tour.
|
||||||
|
- Variante négative : `late progress` du 1er tour ignoré, vrai `progress` du 2e tour affiché.
|
||||||
|
- Ne pas revalider ce ticket sans preuve de commandes réelles et, si possible, scénario fonctionnel via harness custom CLI.
|
||||||
17
.ideai/tickets/252/issue.md
Normal file
17
.ideai/tickets/252/issue.md
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
---
|
||||||
|
id: "efe8d000-3dc5-4d4e-bdae-3c3a066abd1c"
|
||||||
|
number: 252
|
||||||
|
title: "Custom CLI: la seconde requête au même agent perd les progress et messages d’outils"
|
||||||
|
status: "qa"
|
||||||
|
priority: "critical"
|
||||||
|
sprint: null
|
||||||
|
links: []
|
||||||
|
agentRefs: []
|
||||||
|
attachments: []
|
||||||
|
createdBy: {"kind":"agent","agent_id":"a6c6ea12-bfc6-4bdc-8031-324102dfa34d"}
|
||||||
|
updatedBy: {"kind":"agent","agent_id":"a6c6ea12-bfc6-4bdc-8031-324102dfa34d"}
|
||||||
|
createdAt: 1787550761371
|
||||||
|
updatedAt: 1787551198700
|
||||||
|
version: 4
|
||||||
|
---
|
||||||
|
Régression fonctionnelle sur la custom CLI. Scénario utilisateur à couvrir et à reproduire: 1) lancer un agent avec un profil custom CLI, 2) envoyer une première requête qui émet des progress et/ou messages d’outils, 3) attendre la fin, 4) envoyer une seconde requête au même agent, 5) vérifier que les progress et messages d’outils arrivent encore pendant cette seconde requête. Actuel: sur la seconde requête, plus aucun progress ni message d’outil n’est affiché. Attendu: streaming inchangé sur chaque tour. Exigence QA: ajouter de vrais tests fonctionnels qui pilotent la custom CLI sur deux requêtes successives et échouent si les progress/outils disparaissent.
|
||||||
@ -1,3 +1,3 @@
|
|||||||
{
|
{
|
||||||
"nextNumber": 248
|
"nextNumber": 253
|
||||||
}
|
}
|
||||||
@ -3327,7 +3327,7 @@
|
|||||||
"issueRef": "#247",
|
"issueRef": "#247",
|
||||||
"path": "247",
|
"path": "247",
|
||||||
"title": "Retirer l’overlay busy de la custom CLI et conserver le blocage du chat seul",
|
"title": "Retirer l’overlay busy de la custom CLI et conserver le blocage du chat seul",
|
||||||
"status": "qa",
|
"status": "closed",
|
||||||
"priority": "high",
|
"priority": "high",
|
||||||
"sprint": null,
|
"sprint": null,
|
||||||
"assignedAgentIds": [],
|
"assignedAgentIds": [],
|
||||||
@ -3335,7 +3335,77 @@
|
|||||||
"kind": "agent",
|
"kind": "agent",
|
||||||
"agent_id": "a6c6ea12-bfc6-4bdc-8031-324102dfa34d"
|
"agent_id": "a6c6ea12-bfc6-4bdc-8031-324102dfa34d"
|
||||||
},
|
},
|
||||||
"updatedAt": 1787495640501
|
"updatedAt": 1787495761555
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"issueRef": "#248",
|
||||||
|
"path": "248",
|
||||||
|
"title": "Rebuild AppImage IdeA Linux",
|
||||||
|
"status": "closed",
|
||||||
|
"priority": "medium",
|
||||||
|
"sprint": null,
|
||||||
|
"assignedAgentIds": [],
|
||||||
|
"createdBy": {
|
||||||
|
"kind": "agent",
|
||||||
|
"agent_id": "a6c6ea12-bfc6-4bdc-8031-324102dfa34d"
|
||||||
|
},
|
||||||
|
"updatedAt": 1787496151533
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"issueRef": "#249",
|
||||||
|
"path": "249",
|
||||||
|
"title": "Bug UI: les boutons d’overlay restent visibles sur la custom CLI pendant une demande agent",
|
||||||
|
"status": "closed",
|
||||||
|
"priority": "high",
|
||||||
|
"sprint": null,
|
||||||
|
"assignedAgentIds": [],
|
||||||
|
"createdBy": {
|
||||||
|
"kind": "agent",
|
||||||
|
"agent_id": "a6c6ea12-bfc6-4bdc-8031-324102dfa34d"
|
||||||
|
},
|
||||||
|
"updatedAt": 1787497292539
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"issueRef": "#250",
|
||||||
|
"path": "250",
|
||||||
|
"title": "Rebuild AppImage Linux",
|
||||||
|
"status": "closed",
|
||||||
|
"priority": "medium",
|
||||||
|
"sprint": null,
|
||||||
|
"assignedAgentIds": [],
|
||||||
|
"createdBy": {
|
||||||
|
"kind": "agent",
|
||||||
|
"agent_id": "a6c6ea12-bfc6-4bdc-8031-324102dfa34d"
|
||||||
|
},
|
||||||
|
"updatedAt": 1787502038344
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"issueRef": "#251",
|
||||||
|
"path": "251",
|
||||||
|
"title": "Bug UI: affichage incohérent de la conversation agent à partir du second tour",
|
||||||
|
"status": "closed",
|
||||||
|
"priority": "high",
|
||||||
|
"sprint": null,
|
||||||
|
"assignedAgentIds": [],
|
||||||
|
"createdBy": {
|
||||||
|
"kind": "agent",
|
||||||
|
"agent_id": "a6c6ea12-bfc6-4bdc-8031-324102dfa34d"
|
||||||
|
},
|
||||||
|
"updatedAt": 1787500329426
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"issueRef": "#252",
|
||||||
|
"path": "252",
|
||||||
|
"title": "Custom CLI: la seconde requête au même agent perd les progress et messages d’outils",
|
||||||
|
"status": "qa",
|
||||||
|
"priority": "critical",
|
||||||
|
"sprint": null,
|
||||||
|
"assignedAgentIds": [],
|
||||||
|
"createdBy": {
|
||||||
|
"kind": "agent",
|
||||||
|
"agent_id": "a6c6ea12-bfc6-4bdc-8031-324102dfa34d"
|
||||||
|
},
|
||||||
|
"updatedAt": 1787551198700
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -390,8 +390,11 @@ impl LiveTurnProjector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if self.hold_until_new_message && self.seen_message_id {
|
if self.hold_until_new_message && self.seen_message_id {
|
||||||
self.pending.push(record);
|
if matches!(record.event, ParsedEvent::Text(_) | ParsedEvent::Error(_)) {
|
||||||
return Vec::new();
|
self.pending.push(record);
|
||||||
|
return Vec::new();
|
||||||
|
}
|
||||||
|
return self.emit_record(record);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.emit_record(record)
|
self.emit_record(record)
|
||||||
@ -1226,6 +1229,84 @@ printf '%s\n' '{"type":"step_finish","sessionID":"seeded-engine","part":{"messag
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
#[tokio::test]
|
||||||
|
async fn send_with_tap_streams_progress_for_seeded_session_with_single_current_message_id() {
|
||||||
|
let tmp = TempDir::new("tap-seeded-single-message");
|
||||||
|
let script = tmp.path().join("opencode-fixture.sh");
|
||||||
|
write_executable_script(
|
||||||
|
&script,
|
||||||
|
r#"#!/bin/sh
|
||||||
|
printf '%s\n' '{"type":"step_start","sessionID":"seeded-engine","part":{"messageID":"current","type":"step-start"}}'
|
||||||
|
sleep 1
|
||||||
|
printf '%s\n' '{"type":"tool_use","sessionID":"seeded-engine","part":{"messageID":"current","type":"tool","tool":"idea_ask_agent"}}'
|
||||||
|
printf '%s\n' '{"type":"text","sessionID":"seeded-engine","part":{"messageID":"current","type":"text","text":"réponse courante"}}'
|
||||||
|
printf '%s\n' '{"type":"step_finish","sessionID":"seeded-engine","part":{"messageID":"current","type":"step-finish"}}'
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
let session = Arc::new(
|
||||||
|
OpenCodeSession::new(
|
||||||
|
SessionId::new_random(),
|
||||||
|
script.to_string_lossy(),
|
||||||
|
Vec::new(),
|
||||||
|
tmp.path().to_string_lossy(),
|
||||||
|
Some("seeded-engine".to_owned()),
|
||||||
|
Vec::new(),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
let (tx, rx) = std::sync::mpsc::channel();
|
||||||
|
let worker = {
|
||||||
|
let session = Arc::clone(&session);
|
||||||
|
tokio::spawn(async move {
|
||||||
|
session
|
||||||
|
.send_with_tap("prompt", tx)
|
||||||
|
.await
|
||||||
|
.map(|stream| stream.collect::<Vec<_>>())
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
let (first_live, rx) = tokio::task::spawn_blocking(move || {
|
||||||
|
let first_live = rx
|
||||||
|
.recv_timeout(Duration::from_millis(500))
|
||||||
|
.expect("la session existante doit streamer le progress du tour courant");
|
||||||
|
(first_live, rx)
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert!(
|
||||||
|
matches!(
|
||||||
|
first_live,
|
||||||
|
ReplyEvent::Progress { ref progress }
|
||||||
|
if progress.native_event.as_deref() == Some("step_start")
|
||||||
|
),
|
||||||
|
"le premier événement live doit être le step_start du message courant: {first_live:?}"
|
||||||
|
);
|
||||||
|
|
||||||
|
let returned = worker.await.unwrap().unwrap();
|
||||||
|
let live: Vec<_> = std::iter::once(first_live).chain(rx.into_iter()).collect();
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
live.iter().any(|event| matches!(
|
||||||
|
event,
|
||||||
|
ReplyEvent::Progress { progress }
|
||||||
|
if progress.source == ReplyProgressSource::IdeaLocal
|
||||||
|
&& progress.kind == ReplyProgressKind::Mcp
|
||||||
|
&& progress.stage == ReplyProgressStage::Completed
|
||||||
|
&& progress.tool_name.as_deref() == Some("idea_ask_agent")
|
||||||
|
)),
|
||||||
|
"le tool_use du seul message courant doit atteindre le tap live: {live:?}"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
returned,
|
||||||
|
vec![ReplyEvent::Final {
|
||||||
|
content: "réponse courante".to_owned()
|
||||||
|
}]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn extract_session_id_accepts_opencode_jsonl_session_id_variants() {
|
fn extract_session_id_accepts_opencode_jsonl_session_id_variants() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|||||||
@ -72,6 +72,45 @@ function deferred<T>() {
|
|||||||
return { promise, resolve };
|
return { promise, resolve };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createStreamingAgentHarness() {
|
||||||
|
const emits: Array<((chunk: ReplyChunk) => void) | null> = [];
|
||||||
|
const sendDone = new Map<number, () => void>();
|
||||||
|
const sendAgentChat = vi.fn(
|
||||||
|
(_sessionId: string, _prompt: string, onChunk: (chunk: ReplyChunk) => void) =>
|
||||||
|
new Promise<void>((resolve) => {
|
||||||
|
const index = emits.length;
|
||||||
|
emits.push((chunk: ReplyChunk) => {
|
||||||
|
onChunk(chunk);
|
||||||
|
if (chunk.kind === "final" || chunk.kind === "error") {
|
||||||
|
sendDone.get(index)?.();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
sendDone.set(index, resolve);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
agent: {
|
||||||
|
launchAgentChat: vi.fn(),
|
||||||
|
reattachAgentChat: vi.fn(async (sessionId: string) => ({
|
||||||
|
sessionId,
|
||||||
|
scrollback: [],
|
||||||
|
})),
|
||||||
|
sendAgentChat,
|
||||||
|
cancelAgentChat: vi.fn(async () => {}),
|
||||||
|
closeAgentChat: vi.fn(async () => {}),
|
||||||
|
},
|
||||||
|
sendAgentChat,
|
||||||
|
emit(index: number, chunk: ReplyChunk) {
|
||||||
|
const emit = emits[index];
|
||||||
|
if (!emit) throw new Error(`Missing emitter for send call #${index + 1}`);
|
||||||
|
emit(chunk);
|
||||||
|
},
|
||||||
|
emitterCount() {
|
||||||
|
return emits.length;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function expectedChatTime(ms: number): string {
|
function expectedChatTime(ms: number): string {
|
||||||
const parts = new Intl.DateTimeFormat(undefined, {
|
const parts = new Intl.DateTimeFormat(undefined, {
|
||||||
hour: "2-digit",
|
hour: "2-digit",
|
||||||
@ -1489,6 +1528,352 @@ describe("CustomAgentChatView", () => {
|
|||||||
expect(screen.getAllByText("idea_context_read")).toHaveLength(2);
|
expect(screen.getAllByText("idea_context_read")).toHaveLength(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("surfaces second-turn streaming progress through a distinct callback after the first turn completed", async () => {
|
||||||
|
const harness = createStreamingAgentHarness();
|
||||||
|
|
||||||
|
render(
|
||||||
|
<DIProvider
|
||||||
|
gateways={{
|
||||||
|
agent: harness.agent,
|
||||||
|
system: { pickFile: vi.fn(async () => null) },
|
||||||
|
} as unknown as Gateways}
|
||||||
|
>
|
||||||
|
<CustomAgentChatView
|
||||||
|
projectId="project-1"
|
||||||
|
agentId="agent-1"
|
||||||
|
agentName="Worker"
|
||||||
|
profile={profile}
|
||||||
|
cwd="/repo"
|
||||||
|
nodeId="node-1"
|
||||||
|
sessionId="chat-session-1"
|
||||||
|
conversationId="conversation-1"
|
||||||
|
onSessionId={vi.fn()}
|
||||||
|
onConversationId={vi.fn()}
|
||||||
|
/>
|
||||||
|
</DIProvider>,
|
||||||
|
);
|
||||||
|
|
||||||
|
await waitFor(() =>
|
||||||
|
expect(harness.agent.reattachAgentChat).toHaveBeenCalledWith(
|
||||||
|
"chat-session-1",
|
||||||
|
expect.any(Function),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
fireEvent.change(screen.getByLabelText(/message CLI custom/), {
|
||||||
|
target: { value: "first request" },
|
||||||
|
});
|
||||||
|
fireEvent.click(screen.getByRole("button", { name: "Envoyer" }));
|
||||||
|
await waitFor(() => expect(harness.emitterCount()).toBe(1));
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
harness.emit(0, {
|
||||||
|
kind: "progress",
|
||||||
|
progress: {
|
||||||
|
source: "providerNative",
|
||||||
|
kind: "tool",
|
||||||
|
stage: "started",
|
||||||
|
label: "shell",
|
||||||
|
toolName: "shell",
|
||||||
|
text: "Running first request",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
expect(screen.getByText("Running first request")).toBeTruthy();
|
||||||
|
expect(screen.getByText("Tool").parentElement?.textContent).toContain("En cours");
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
harness.emit(0, {
|
||||||
|
kind: "progress",
|
||||||
|
progress: {
|
||||||
|
source: "providerNative",
|
||||||
|
kind: "tool",
|
||||||
|
stage: "completed",
|
||||||
|
label: "shell",
|
||||||
|
toolName: "shell",
|
||||||
|
text: "Done first request",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
harness.emit(0, { kind: "final", content: "final first request" });
|
||||||
|
});
|
||||||
|
await screen.findByText("final first request");
|
||||||
|
|
||||||
|
fireEvent.change(screen.getByLabelText(/message CLI custom/), {
|
||||||
|
target: { value: "second request" },
|
||||||
|
});
|
||||||
|
fireEvent.click(screen.getByRole("button", { name: "Envoyer" }));
|
||||||
|
await waitFor(() => expect(harness.emitterCount()).toBe(2));
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
harness.emit(1, {
|
||||||
|
kind: "progress",
|
||||||
|
progress: {
|
||||||
|
source: "providerNative",
|
||||||
|
kind: "message",
|
||||||
|
stage: "delta",
|
||||||
|
label: "Analyse",
|
||||||
|
text: "Second request context loaded.",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
expect(screen.getByText("Second request context loaded.")).toBeTruthy();
|
||||||
|
expect(screen.getByText("Progress").parentElement?.textContent).toContain("En cours");
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
harness.emit(1, {
|
||||||
|
kind: "progress",
|
||||||
|
progress: {
|
||||||
|
source: "ideaLocal",
|
||||||
|
kind: "mcp",
|
||||||
|
stage: "started",
|
||||||
|
label: "idea_ticket_read",
|
||||||
|
toolName: "idea_ticket_read",
|
||||||
|
text: "Reading ticket for second request",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
expect(screen.getByText("Reading ticket for second request")).toBeTruthy();
|
||||||
|
expect(screen.getAllByTestId("chat-tool-progress")).toHaveLength(2);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
harness.emit(1, {
|
||||||
|
kind: "progress",
|
||||||
|
progress: {
|
||||||
|
source: "ideaLocal",
|
||||||
|
kind: "mcp",
|
||||||
|
stage: "completed",
|
||||||
|
label: "idea_ticket_read",
|
||||||
|
toolName: "idea_ticket_read",
|
||||||
|
text: "Ticket read complete",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
harness.emit(1, { kind: "final", content: "final second request" });
|
||||||
|
});
|
||||||
|
|
||||||
|
await screen.findByText("final second request");
|
||||||
|
expect(screen.getByText("Ticket read complete")).toBeTruthy();
|
||||||
|
expect(screen.getByText("Second request context loaded.")).toBeTruthy();
|
||||||
|
expect(screen.getAllByText("Terminé")).toHaveLength(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("treats same-tool progress on the second turn as fresh streaming activity with a distinct callback", async () => {
|
||||||
|
const harness = createStreamingAgentHarness();
|
||||||
|
|
||||||
|
render(
|
||||||
|
<DIProvider
|
||||||
|
gateways={{
|
||||||
|
agent: harness.agent,
|
||||||
|
system: { pickFile: vi.fn(async () => null) },
|
||||||
|
} as unknown as Gateways}
|
||||||
|
>
|
||||||
|
<CustomAgentChatView
|
||||||
|
projectId="project-1"
|
||||||
|
agentId="agent-1"
|
||||||
|
agentName="Worker"
|
||||||
|
profile={profile}
|
||||||
|
cwd="/repo"
|
||||||
|
nodeId="node-1"
|
||||||
|
sessionId="chat-session-1"
|
||||||
|
conversationId="conversation-1"
|
||||||
|
onSessionId={vi.fn()}
|
||||||
|
onConversationId={vi.fn()}
|
||||||
|
/>
|
||||||
|
</DIProvider>,
|
||||||
|
);
|
||||||
|
|
||||||
|
await waitFor(() =>
|
||||||
|
expect(harness.agent.reattachAgentChat).toHaveBeenCalledWith(
|
||||||
|
"chat-session-1",
|
||||||
|
expect.any(Function),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
fireEvent.change(screen.getByLabelText(/message CLI custom/), {
|
||||||
|
target: { value: "first request" },
|
||||||
|
});
|
||||||
|
fireEvent.click(screen.getByRole("button", { name: "Envoyer" }));
|
||||||
|
await waitFor(() => expect(harness.emitterCount()).toBe(1));
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
harness.emit(0, {
|
||||||
|
kind: "progress",
|
||||||
|
progress: {
|
||||||
|
source: "ideaLocal",
|
||||||
|
kind: "mcp",
|
||||||
|
stage: "started",
|
||||||
|
label: "idea_context_read",
|
||||||
|
toolName: "idea_context_read",
|
||||||
|
text: "Reading shared context for first request",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
harness.emit(0, {
|
||||||
|
kind: "progress",
|
||||||
|
progress: {
|
||||||
|
source: "ideaLocal",
|
||||||
|
kind: "mcp",
|
||||||
|
stage: "completed",
|
||||||
|
label: "idea_context_read",
|
||||||
|
toolName: "idea_context_read",
|
||||||
|
text: "Context read complete for first request",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
harness.emit(0, { kind: "final", content: "done first request" });
|
||||||
|
});
|
||||||
|
await screen.findByText("done first request");
|
||||||
|
|
||||||
|
fireEvent.change(screen.getByLabelText(/message CLI custom/), {
|
||||||
|
target: { value: "second request" },
|
||||||
|
});
|
||||||
|
fireEvent.click(screen.getByRole("button", { name: "Envoyer" }));
|
||||||
|
await waitFor(() => expect(harness.emitterCount()).toBe(2));
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
harness.emit(1, {
|
||||||
|
kind: "progress",
|
||||||
|
progress: {
|
||||||
|
source: "ideaLocal",
|
||||||
|
kind: "mcp",
|
||||||
|
stage: "started",
|
||||||
|
label: "idea_context_read",
|
||||||
|
toolName: "idea_context_read",
|
||||||
|
text: "Reading shared context for second request",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
expect(screen.getByText("Reading shared context for second request")).toBeTruthy();
|
||||||
|
expect(screen.getAllByText("idea_context_read")).toHaveLength(2);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
harness.emit(1, {
|
||||||
|
kind: "progress",
|
||||||
|
progress: {
|
||||||
|
source: "ideaLocal",
|
||||||
|
kind: "mcp",
|
||||||
|
stage: "completed",
|
||||||
|
label: "idea_context_read",
|
||||||
|
toolName: "idea_context_read",
|
||||||
|
text: "Context read complete for second request",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
harness.emit(1, { kind: "final", content: "done second request" });
|
||||||
|
});
|
||||||
|
|
||||||
|
await screen.findByText("done second request");
|
||||||
|
expect(screen.getByText("Context read complete for second request")).toBeTruthy();
|
||||||
|
expect(screen.getByText("Context read complete for first request")).toBeTruthy();
|
||||||
|
expect(screen.getAllByTestId("chat-tool-progress")).toHaveLength(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("ignores a late first-turn progress chunk but still shows the real second-turn progress from the next callback", async () => {
|
||||||
|
const harness = createStreamingAgentHarness();
|
||||||
|
|
||||||
|
render(
|
||||||
|
<DIProvider
|
||||||
|
gateways={{
|
||||||
|
agent: harness.agent,
|
||||||
|
system: { pickFile: vi.fn(async () => null) },
|
||||||
|
} as unknown as Gateways}
|
||||||
|
>
|
||||||
|
<CustomAgentChatView
|
||||||
|
projectId="project-1"
|
||||||
|
agentId="agent-1"
|
||||||
|
agentName="Worker"
|
||||||
|
profile={profile}
|
||||||
|
cwd="/repo"
|
||||||
|
nodeId="node-1"
|
||||||
|
sessionId="chat-session-1"
|
||||||
|
conversationId="conversation-1"
|
||||||
|
onSessionId={vi.fn()}
|
||||||
|
onConversationId={vi.fn()}
|
||||||
|
/>
|
||||||
|
</DIProvider>,
|
||||||
|
);
|
||||||
|
|
||||||
|
await waitFor(() =>
|
||||||
|
expect(harness.agent.reattachAgentChat).toHaveBeenCalledWith(
|
||||||
|
"chat-session-1",
|
||||||
|
expect.any(Function),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
fireEvent.change(screen.getByLabelText(/message CLI custom/), {
|
||||||
|
target: { value: "first request" },
|
||||||
|
});
|
||||||
|
fireEvent.click(screen.getByRole("button", { name: "Envoyer" }));
|
||||||
|
await waitFor(() => expect(harness.emitterCount()).toBe(1));
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
harness.emit(0, {
|
||||||
|
kind: "progress",
|
||||||
|
progress: {
|
||||||
|
source: "providerNative",
|
||||||
|
kind: "tool",
|
||||||
|
stage: "started",
|
||||||
|
label: "shell",
|
||||||
|
toolName: "shell",
|
||||||
|
text: "Running first request",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
harness.emit(0, { kind: "final", content: "done first request" });
|
||||||
|
});
|
||||||
|
await screen.findByText("done first request");
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
harness.emit(0, {
|
||||||
|
kind: "progress",
|
||||||
|
progress: {
|
||||||
|
source: "providerNative",
|
||||||
|
kind: "tool",
|
||||||
|
stage: "completed",
|
||||||
|
label: "shell",
|
||||||
|
toolName: "shell",
|
||||||
|
text: "Late first request completion",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
expect(screen.queryByText("Late first request completion")).toBeNull();
|
||||||
|
|
||||||
|
fireEvent.change(screen.getByLabelText(/message CLI custom/), {
|
||||||
|
target: { value: "second request" },
|
||||||
|
});
|
||||||
|
fireEvent.click(screen.getByRole("button", { name: "Envoyer" }));
|
||||||
|
await waitFor(() => expect(harness.emitterCount()).toBe(2));
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
harness.emit(1, {
|
||||||
|
kind: "progress",
|
||||||
|
progress: {
|
||||||
|
source: "providerNative",
|
||||||
|
kind: "tool",
|
||||||
|
stage: "started",
|
||||||
|
label: "shell",
|
||||||
|
toolName: "shell",
|
||||||
|
text: "Running second request",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
expect(screen.getByText("Running second request")).toBeTruthy();
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
harness.emit(1, {
|
||||||
|
kind: "progress",
|
||||||
|
progress: {
|
||||||
|
source: "providerNative",
|
||||||
|
kind: "tool",
|
||||||
|
stage: "completed",
|
||||||
|
label: "shell",
|
||||||
|
toolName: "shell",
|
||||||
|
text: "Done second request",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
harness.emit(1, { kind: "final", content: "done second request" });
|
||||||
|
});
|
||||||
|
|
||||||
|
await screen.findByText("done second request");
|
||||||
|
expect(screen.getByText("Done second request")).toBeTruthy();
|
||||||
|
expect(screen.queryByText("Late first request completion")).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
it("renders available pre-MCP progress before the first MCP card", async () => {
|
it("renders available pre-MCP progress before the first MCP card", async () => {
|
||||||
let emitChunk: ((chunk: unknown) => void) | null = null;
|
let emitChunk: ((chunk: unknown) => void) | null = null;
|
||||||
const agent = {
|
const agent = {
|
||||||
|
|||||||
Reference in New Issue
Block a user