From efbd56a1499265f9c02049b300e04e3e209a7828 Mon Sep 17 00:00:00 2001 From: Blomios Date: Tue, 4 Aug 2026 23:57:30 +0200 Subject: [PATCH 1/2] chore(tickets): sync carnets/issues #102/#141-#147 + agent glmopencode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rebase de l'état ticket (réouverture #102, clôtures #141-146, ouverture #147) et ajout du contexte agent glmopencode avant démarrage du cycle #147. Co-Authored-By: Claude Sonnet 5 --- .ideai/agents/glmopencode.md | 0 .ideai/idea-android-plugin.json | 13 ++++- .ideai/tickets/102/carnet.md | 38 +++++++++++++-- .ideai/tickets/102/issue.md | 9 ++-- .ideai/tickets/141/carnet.md | 6 +-- .ideai/tickets/141/issue.md | 8 ++-- .ideai/tickets/142/carnet.md | 4 +- .ideai/tickets/142/issue.md | 6 +-- .ideai/tickets/143/carnet.md | 4 +- .ideai/tickets/143/issue.md | 6 +-- .ideai/tickets/144/carnet.md | 4 +- .ideai/tickets/144/issue.md | 6 +-- .ideai/tickets/145/carnet.md | 4 +- .ideai/tickets/145/issue.md | 6 +-- .ideai/tickets/146/carnet.md | 4 +- .ideai/tickets/146/issue.md | 6 +-- ...-4190-9c53-186585602006-task_completed.jpg | Bin 0 -> 19288 bytes ...49aa-c5d5-44d5-a7d7-35ef929efd19-Cline.png | Bin 0 -> 137940 bytes .ideai/tickets/147/carnet.md | 45 ++++++++++++++++++ .ideai/tickets/147/issue.md | 23 +++++++++ .ideai/tickets/counter.json | 2 +- .ideai/tickets/index.json | 41 ++++++++++------ 22 files changed, 180 insertions(+), 55 deletions(-) create mode 100644 .ideai/agents/glmopencode.md create mode 100644 .ideai/tickets/147/attachments/06f4e827-d693-4190-9c53-186585602006-task_completed.jpg create mode 100644 .ideai/tickets/147/attachments/93c049aa-c5d5-44d5-a7d7-35ef929efd19-Cline.png create mode 100644 .ideai/tickets/147/carnet.md create mode 100644 .ideai/tickets/147/issue.md diff --git a/.ideai/agents/glmopencode.md b/.ideai/agents/glmopencode.md new file mode 100644 index 0000000..e69de29 diff --git a/.ideai/idea-android-plugin.json b/.ideai/idea-android-plugin.json index 0b2298a..26a0019 100644 --- a/.ideai/idea-android-plugin.json +++ b/.ideai/idea-android-plugin.json @@ -5,14 +5,25 @@ "probableAppModule": null, "projectId": "97b49ac2-8376-4aa3-8ea9-bf3ac81d0023", "projectRoot": "/home/anthony/Documents/Projects/IdeA", - "updatedAt": "2026-08-03T20:37:53.046Z" + "updatedAt": "2026-08-04T21:31:12.564Z" }, "ownerAgentId": null, + "ownerAgentMapping": {}, "refresh": { "onActivate": true, "watchWorkspace": true }, "schemaVersion": 1, + "selection": { + "activeModulePath": null, + "debugApkPathByModule": {}, + "selectedDeviceSerial": null + }, + "toolchain": { + "adbPath": null, + "androidSdkPath": null, + "emulatorPath": null + }, "ui": { "showDebugActions": true } diff --git a/.ideai/tickets/102/carnet.md b/.ideai/tickets/102/carnet.md index 45a35f8..8381c2d 100644 --- a/.ideai/tickets/102/carnet.md +++ b/.ideai/tickets/102/carnet.md @@ -1,6 +1,38 @@ --- issueRef: "#102" -version: 4 -updatedBy: {"kind":"user"} -updatedAt: 1784993980505 +version: 11 +updatedBy: {"kind":"agent","agent_id":"a6c6ea12-bfc6-4bdc-8031-324102dfa34d"} +updatedAt: 1785878554632 --- +## 2026-08-04 — Réouverture + +- Repro utilisateur confirmée: en changeant de projet, si une cellule contient un terminal d'agent IA et que du contenu est arrivé pendant que le projet n'était pas affiché, le terminal revient partiellement/blanc/tronqué. +- Symptôme visuel observé: le contenu complet réapparaît immédiatement après un resize de la cellule ou de la fenêtre. +- Portée repro indiquée historiquement: switch de projet, switch de layout, ajout/suppression/redécoupage de cellules; le cas prioritaire confirmé aujourd'hui est le retour sur projet. +- Hypothèse de travail à valider: problème de refresh/fit/reflow frontend du terminal lors de la ré-attache ou ré-activation de la vue, pas un manque de données côté agent. +- Action en cours: recadrage Architect, décision de branche Git, implémentation dev ciblée, puis validation QA sur reproduction réelle. + +## 2026-08-04 — Cadrage Architecture (analyse statique, avant implémentation) + +- Frontière validée: frontend pur, ciblée sur `frontend/src/features/terminals/TerminalView.tsx`. +- Le backend/PTY n'est pas suspecté à ce stade: le tail de scrollback est bien restitué au reattach; le défaut semble être de rendu/reflow au retour sur la vue. +- Point d'attention principal: `term.write(scrollback)` pourrait arriver avant le premier `fit()` réellement utile, ce qui laisserait xterm wrapper le contenu sur une géométrie transitoire et n'obtiendrait un repaint correct qu'au resize manuel ultérieur. +- Les correctifs précédents ont déjà renforcé la boucle de fit; le trou probable est l'absence de test sur le rendu/repaint effectif et sur l'ordre `reattach/write` vs `first useful fit`. +- Stratégie recommandée: corriger dans `TerminalView` l'ordre de réécriture du scrollback et/ou forcer un repaint plein-buffer après le premier fit utile, puis valider par test ciblé et repro réelle. + +## 2026-08-04 — Implémentation DevFrontend + +- Correctif appliqué dans `frontend/src/features/terminals/TerminalView.tsx`. +- Changement clé: en cas de `reattach`, le scrollback et les chunks live reçus pendant la fenêtre de réattache ne sont plus écrits immédiatement dans xterm; ils sont tamponnés jusqu'au premier `fit` utile, puis rejoués dans l'ordre `scrollback` puis `live output`. +- Objectif: éviter un rendu initial du buffer sur une géométrie transitoire et supprimer la dépendance à un resize manuel ultérieur pour déclencher l'affichage correct. +- Test ajouté/ajusté dans `frontend/src/features/terminals/TerminalView.test.tsx` pour verrouiller qu'aucun `term.write` ne précède le premier `fit` utile et que l'ordre de replay est conservé. + +## 2026-08-04 — Verdict QA + +- Validation automatisée verte: + - `cd frontend && npx vitest run src/features/terminals/TerminalView.test.tsx` -> 22 tests passés. + - `cd frontend && npx vitest run src/features/terminals/TerminalView.scrollback.test.tsx src/features/terminals/TerminalView.portal.test.tsx` -> 5 tests passés. + - `cd frontend && npx vitest run src/features/terminals` -> 39 tests passés. + - `cd frontend && npx vitest run` -> 115 fichiers, 1083 tests passés. +- Verdict QA: `vert avec réserve de preuve manuelle`. +- Réserve restante: absence de repro visuelle réelle rejouée dans l'application avec un vrai xterm, une session vivante, puis un reattach après switch projet/layout. Le ticket reste donc ouvert tant que cette validation de terrain n'est pas obtenue. diff --git a/.ideai/tickets/102/issue.md b/.ideai/tickets/102/issue.md index a13e871..2894068 100644 --- a/.ideai/tickets/102/issue.md +++ b/.ideai/tickets/102/issue.md @@ -2,15 +2,16 @@ id: "e91fd358-da94-4382-aa70-6a3fe5a63840" number: 102 title: "[Bug] Devoir resize les cellule pour afficher la TUI d'un agent" -status: "closed" +status: "inProgress" priority: "medium" sprint: "e28a4d53-8bd2-446a-b0ac-2a017373b8b2" links: [] agentRefs: [{"agentId":"a6ced819-b893-4213-b003-9e9dc79b9641","role":"assigned"}] +attachments: [] createdBy: {"kind":"user"} -updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"} +updatedBy: {"kind":"agent","agent_id":"a6c6ea12-bfc6-4bdc-8031-324102dfa34d"} createdAt: 1784993319700 -updatedAt: 1785083912470 -version: 5 +updatedAt: 1785878554632 +version: 11 --- J'ai toujours un soucis qui fait que quand je switch de projet IdeA ou de layout ou que j'ajoute des cellules ou autres, je suis obligé de resize un coup la cellule pour que son constenu s'affiche correctement diff --git a/.ideai/tickets/141/carnet.md b/.ideai/tickets/141/carnet.md index dd292fd..1589858 100644 --- a/.ideai/tickets/141/carnet.md +++ b/.ideai/tickets/141/carnet.md @@ -1,6 +1,6 @@ --- issueRef: "#141" -version: 1 -updatedBy: {"kind":"agent","agent_id":"a6c6ea12-bfc6-4bdc-8031-324102dfa34d"} -updatedAt: 1785766766212 +version: 2 +updatedBy: {"kind":"user"} +updatedAt: 1785838243826 --- diff --git a/.ideai/tickets/141/issue.md b/.ideai/tickets/141/issue.md index 6ff28e3..d3b38dd 100644 --- a/.ideai/tickets/141/issue.md +++ b/.ideai/tickets/141/issue.md @@ -2,17 +2,17 @@ id: "5ed1fc9b-1eef-4236-be89-e5d351ece549" number: 141 title: "Supporter l’ouverture des layouts plugins comme Android Health" -status: "open" +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"} +updatedBy: {"kind":"user"} createdAt: 1785766766212 -updatedAt: 1785766766212 -version: 1 +updatedAt: 1785838243826 +version: 2 --- Le plugin Android `dev.idea.android-plugin` déclare un layout `idea-android.health` et l’enregistre correctement à l’activation, mais l’ouverture depuis l’UI échoue avec le message : « la création de layouts plugins nécessite une extension backend pas encore livrée ». diff --git a/.ideai/tickets/142/carnet.md b/.ideai/tickets/142/carnet.md index bbcfd3f..518d0a2 100644 --- a/.ideai/tickets/142/carnet.md +++ b/.ideai/tickets/142/carnet.md @@ -1,6 +1,6 @@ --- issueRef: "#142" -version: 3 +version: 4 updatedBy: {"kind":"agent","agent_id":"a6c6ea12-bfc6-4bdc-8031-324102dfa34d"} -updatedAt: 1785796721082 +updatedAt: 1785796835464 --- diff --git a/.ideai/tickets/142/issue.md b/.ideai/tickets/142/issue.md index 8cf6229..e5f8720 100644 --- a/.ideai/tickets/142/issue.md +++ b/.ideai/tickets/142/issue.md @@ -2,7 +2,7 @@ id: "73c2a22d-86bb-4bf5-912b-7766a528d5e7" number: 142 title: "Plugin SDK: backend support for plugin-hosted windows" -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: 1785794926619 -updatedAt: 1785796721082 -version: 3 +updatedAt: 1785796835464 +version: 4 --- Implement the backend/domain/application changes needed so plugin commands can open a new OS window that hosts a plugin-contributed layout, reusing the existing window pipeline and anti-duplication rules instead of inventing a parallel window system. Scope: extend the accepted view/window surface contract for plugin layout ids, preserve native panel behavior, and keep the window lifecycle compatible with the existing layout/window stores and commands. \ No newline at end of file diff --git a/.ideai/tickets/143/carnet.md b/.ideai/tickets/143/carnet.md index 837f836..ef18b09 100644 --- a/.ideai/tickets/143/carnet.md +++ b/.ideai/tickets/143/carnet.md @@ -1,6 +1,6 @@ --- issueRef: "#143" -version: 3 +version: 4 updatedBy: {"kind":"agent","agent_id":"a6c6ea12-bfc6-4bdc-8031-324102dfa34d"} -updatedAt: 1785796721101 +updatedAt: 1785796835483 --- diff --git a/.ideai/tickets/143/issue.md b/.ideai/tickets/143/issue.md index 2a58ac3..64bea51 100644 --- a/.ideai/tickets/143/issue.md +++ b/.ideai/tickets/143/issue.md @@ -2,7 +2,7 @@ id: "9e684778-62df-491f-b824-d74960bd6b9d" number: 143 title: "Plugin SDK: frontend host for plugin windows and window-open API" -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: 1785794926635 -updatedAt: 1785796721101 -version: 3 +updatedAt: 1785796835483 +version: 4 --- Implement the frontend runtime and SDK service surface so a plugin menu command can open a new window rendering one of its declared layout contributions. Scope: route plugin window surfaces through the existing view-window host, reuse plugin layout rendering/fallback behavior, and expose a public `services.windows.open(...)` API validated against declared layout ids. \ No newline at end of file diff --git a/.ideai/tickets/144/carnet.md b/.ideai/tickets/144/carnet.md index 750f2b6..f288798 100644 --- a/.ideai/tickets/144/carnet.md +++ b/.ideai/tickets/144/carnet.md @@ -1,6 +1,6 @@ --- issueRef: "#144" -version: 3 +version: 4 updatedBy: {"kind":"agent","agent_id":"a6c6ea12-bfc6-4bdc-8031-324102dfa34d"} -updatedAt: 1785796721119 +updatedAt: 1785796835501 --- diff --git a/.ideai/tickets/144/issue.md b/.ideai/tickets/144/issue.md index 6482577..5c57ccb 100644 --- a/.ideai/tickets/144/issue.md +++ b/.ideai/tickets/144/issue.md @@ -2,7 +2,7 @@ id: "7ed6eb7f-713e-41c6-a0b4-9783acf68268" number: 144 title: "Plugin SDK: shared React runtime for plugin layouts" -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: 1785794926654 -updatedAt: 1785796721119 -version: 3 +updatedAt: 1785796835501 +version: 4 --- Upgrade the plugin SDK/runtime so plugin-contributed layouts can be authored as real React components with JSX and hooks. Scope: resolve `react`/`react-dom` imports to the host instance, update SDK public types/tsconfig/package metadata accordingly, and refresh the hello-plugin example to demonstrate the supported React authoring model. \ No newline at end of file diff --git a/.ideai/tickets/145/carnet.md b/.ideai/tickets/145/carnet.md index 7bf1085..c5d1bc2 100644 --- a/.ideai/tickets/145/carnet.md +++ b/.ideai/tickets/145/carnet.md @@ -1,6 +1,6 @@ --- issueRef: "#145" -version: 3 +version: 4 updatedBy: {"kind":"agent","agent_id":"a6c6ea12-bfc6-4bdc-8031-324102dfa34d"} -updatedAt: 1785796721137 +updatedAt: 1785796835521 --- diff --git a/.ideai/tickets/145/issue.md b/.ideai/tickets/145/issue.md index 4675e6b..3f52bb9 100644 --- a/.ideai/tickets/145/issue.md +++ b/.ideai/tickets/145/issue.md @@ -2,7 +2,7 @@ id: "00df9acb-29ec-498b-b0dd-56af00b3d630" number: 145 title: "Plugin SDK: expand and restructure SDK documentation" -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: 1785794926667 -updatedAt: 1785796721137 -version: 3 +updatedAt: 1785796835521 +version: 4 --- Produce a much more complete SDK documentation set under `sdk/IdeaSDK/docs/` with explicit file names and focused topics. Scope: turn the root README into a concise entrypoint/summary, add dedicated docs for manifest, activation/context, menus, layouts with React, windows, services, packaging/distribution, and keep the content aligned with the real runtime contracts and example plugin. \ No newline at end of file diff --git a/.ideai/tickets/146/carnet.md b/.ideai/tickets/146/carnet.md index 9a8b7ca..7f62fec 100644 --- a/.ideai/tickets/146/carnet.md +++ b/.ideai/tickets/146/carnet.md @@ -1,6 +1,6 @@ --- issueRef: "#146" -version: 2 +version: 3 updatedBy: {"kind":"agent","agent_id":"a6c6ea12-bfc6-4bdc-8031-324102dfa34d"} -updatedAt: 1785796721013 +updatedAt: 1785796835539 --- diff --git a/.ideai/tickets/146/issue.md b/.ideai/tickets/146/issue.md index 3259f3a..41f8793 100644 --- a/.ideai/tickets/146/issue.md +++ b/.ideai/tickets/146/issue.md @@ -2,7 +2,7 @@ id: "1b3f9aa1-c16a-4fb7-aab0-a3c5b56b200b" number: 146 title: "QA: validate plugin window opening, React layouts, and SDK docs/examples" -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: 1785794926679 -updatedAt: 1785796721013 -version: 2 +updatedAt: 1785796835539 +version: 3 --- Validate the plugin SDK feature set end to end after implementation. Scope: real test evidence that a plugin submenu click can open a new window, the opened window renders a React-based plugin layout correctly, layout state still round-trips, existing plugin layout cells still work, and the refreshed SDK docs/example match the shipped behavior. \ No newline at end of file diff --git a/.ideai/tickets/147/attachments/06f4e827-d693-4190-9c53-186585602006-task_completed.jpg b/.ideai/tickets/147/attachments/06f4e827-d693-4190-9c53-186585602006-task_completed.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f3d8d68407b20c909ee5ae60a77ca55662e002a0 GIT binary patch literal 19288 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 literal 0 HcmV?d00001 diff --git a/.ideai/tickets/147/attachments/93c049aa-c5d5-44d5-a7d7-35ef929efd19-Cline.png b/.ideai/tickets/147/attachments/93c049aa-c5d5-44d5-a7d7-35ef929efd19-Cline.png new file mode 100644 index 0000000000000000000000000000000000000000..7bbff6fd4691d411aa134b32ba1508e726c6b3e2 GIT binary patch literal 137940 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

uIInon*+h<74iI-^~{6rlc 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 wHQvkP%l+OP@zsbn9_j^w&ez*dR^o@5=a_ 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 literal 0 HcmV?d00001 diff --git a/.ideai/tickets/147/carnet.md b/.ideai/tickets/147/carnet.md new file mode 100644 index 0000000..c6f86b7 --- /dev/null +++ b/.ideai/tickets/147/carnet.md @@ -0,0 +1,45 @@ +--- +issueRef: "#147" +version: 6 +updatedBy: {"kind":"agent","agent_id":"a6c6ea12-bfc6-4bdc-8031-324102dfa34d"} +updatedAt: 1785880207496 +--- +## Cadrage consolidé + +### Intention produit +Créer une CLI custom alternative à la TUI native actuelle pour les cellules agent. Cette vue doit communiquer avec l'agent en headless et afficher une retranscription conversationnelle vivante de ce que fait l'agent, sans limiter ce qui fait la spécificité d'IdeA. + +Références visuelles jointes : `task_completed.jpg`, `Cline.png`. + +### Décisions validées avec l'utilisateur +- Tous les agents IdeA sont dans le périmètre, car le mode headless est considéré comme un socle du produit. +- Le choix `TUI native` / `CLI custom` est `par cellule`, uniquement quand la cellule est sur un agent. En mode `Plain`, le terminal reste inchangé. +- La TUI native reste le mode par défaut pour le moment. +- Le switch de mode est autorisé en cours de session, mais avec warning explicite et arrêt de la session courante pour éviter toute confusion utilisateur. +- Même logique quand on repasse de `CLI custom` à `TUI native`. +- Le bouton `cancel` doit se comporter comme une interruption du tour courant (analogue à `Esc` dans Claude/Codex), sans tuer la session elle-même. +- La vue custom est une retranscription live pendant la durée de vie de la session, comme la TUI actuelle ; ce n'est pas un transcript persistant indépendant. +- Si la session est toujours vivante quand on rouvre la cellule, on doit revoir l'historique live associé ; si la session est morte, non. +- On veut afficher un maximum de ce qui est exposé proprement par le headless : messages intermédiaires, progression, appels d'outils, édition de fichiers, final, etc. La règle est d'utiliser au maximum les features fournies par le headless, sans bricolage fragile. +- Si certains rendus avancés (ex. code avec syntax highlighting, détails riches de tool calls, etc.) ne peuvent pas être faits de manière propre et solide, ils ne doivent pas être forcés. + +### Arbitrage prioritaire +Ordre de priorité explicitement demandé par l'utilisateur : +1. Robustesse +2. User experience +3. Beauté de la CLI + +### Position de cadrage +Ce ticket ne doit pas être traité comme un simple ticket UI : il implique un vrai contrat runtime/headless pour piloter et afficher une session agent structurée. Le rendu en bulles est secondaire par rapport à la solidité des événements exposés et de la bascule de mode. + +### Hypothèses de travail à privilégier +- S'appuyer sur le mode headless des agents et normaliser uniquement les événements réellement fiables. +- Prévoir une dégradation contrôlée quand un agent expose moins de richesse événementielle qu'un autre. +- Pour les fichiers joints, privilégier une solution robuste de staging temporaire par session si nécessaire, plutôt qu'un mécanisme dépendant du provider. +- Garder la CLI custom comme vue de session vivante, pas comme nouvelle source de vérité persistante. + +### Questions résiduelles à arbitrer techniquement pendant le cycle +- Contrat précis des événements normalisés côté IdeA (`message`, `tool_call`, `tool_result`, `file_edit`, `status`, `final`, etc.). +- Comportement exact du staging temporaire des fichiers joints : durée de vie, nettoyage, taille max, comportement si le fichier source change. +- UX précise du warning de bascule de mode et du redémarrage de session. +- Stratégie de dégradation contrôlée selon la richesse réellement exposée par chaque agent headless. diff --git a/.ideai/tickets/147/issue.md b/.ideai/tickets/147/issue.md new file mode 100644 index 0000000..7176a44 --- /dev/null +++ b/.ideai/tickets/147/issue.md @@ -0,0 +1,23 @@ +--- +id: "64a8f704-df3e-40f4-9148-d39ae4f86af1" +number: 147 +title: "[UI] créer une CLI custom alternative" +status: "inProgress" +priority: "medium" +sprint: null +links: [] +agentRefs: [] +attachments: [{"id":"06f4e827-d693-4190-9c53-186585602006","filename":"task_completed.jpg","path":"attachments/06f4e827-d693-4190-9c53-186585602006-task_completed.jpg","mime":"image/jpeg","sizeBytes":19288,"addedBy":{"kind":"user"},"addedAt":1785878707537,"summarizedInCarnet":false,"summarizedBy":null,"summarizedAt":null},{"id":"93c049aa-c5d5-44d5-a7d7-35ef929efd19","filename":"Cline.png","path":"attachments/93c049aa-c5d5-44d5-a7d7-35ef929efd19-Cline.png","mime":"image/png","sizeBytes":137940,"addedBy":{"kind":"user"},"addedAt":1785878711232,"summarizedInCarnet":false,"summarizedBy":null,"summarizedAt":null}] +createdBy: {"kind":"user"} +updatedBy: {"kind":"agent","agent_id":"a6c6ea12-bfc6-4bdc-8031-324102dfa34d"} +createdAt: 1785877963139 +updatedAt: 1785880207496 +version: 6 +--- +J'aimerais une CLI alternative pour mes agents. Cette CLI doit contenir bine entendu la barre de chat, ainsi que pour le reste de l'écran les bulle de conversation de l'agent et dde l'utilisateur. Cette CLI doit etre de la meme forme que ce qu'on peut voir dans les CLI des agents IA dans les IDE de code. Je veux voir s'afficher les reflexions de l'IA etc. La CLI communiquera en headless avec l'agent. + +Cette CLI est alternative, il faudra simplement proposé un bouton en haut à côté du choix de l'agent pour proposer d'utiliser la CLI custom ou la CLI (fin la TUI de l'agent, celle qu'on utilise actuellement). Pour le moment, par défaut on utilisera la TUI comme actuellement. + +Pour c equie st un peu du design, j'aimerais que les bulles de conversation de l'utilisateur soient allignée à gauche et que celle de l'agent soient alignées à droite. La couleur de sbulles de l'agents devront etre légèrement différente de celle de l'utilisateur. On devra pouvoir du coup envoyer un message ainsi que joindre un fichier. Une fois envoyé, on devra avoir un bouton pour cancel l'agent, encore une fois comme dans les CLI multi agent qu'on trouve ailleurs. + +Je dirais que notre référence serait Cline, j'aime beaucoup le fait que le final soit mis en avent avec le Task Complete. Je mets des exemple dans les fichiers joint a ce ticket. Ce ne sont que des exemples, il faut que la CLI custom propose un maximum de ce qui fait d'IdeA une experience unique, il faut donc que la CLI ne limite pas IdeA \ No newline at end of file diff --git a/.ideai/tickets/counter.json b/.ideai/tickets/counter.json index 022886c..5c6092f 100644 --- a/.ideai/tickets/counter.json +++ b/.ideai/tickets/counter.json @@ -1,3 +1,3 @@ { - "nextNumber": 147 + "nextNumber": 148 } \ No newline at end of file diff --git a/.ideai/tickets/index.json b/.ideai/tickets/index.json index baccbc2..d837f78 100644 --- a/.ideai/tickets/index.json +++ b/.ideai/tickets/index.json @@ -1342,7 +1342,7 @@ "issueRef": "#102", "path": "102", "title": "[Bug] Devoir resize les cellule pour afficher la TUI d'un agent", - "status": "closed", + "status": "inProgress", "priority": "medium", "sprint": "e28a4d53-8bd2-446a-b0ac-2a017373b8b2", "assignedAgentIds": [ @@ -1351,7 +1351,7 @@ "createdBy": { "kind": "user" }, - "updatedAt": 1785083912470 + "updatedAt": 1785878554632 }, { "issueRef": "#103", @@ -1827,7 +1827,7 @@ "issueRef": "#141", "path": "141", "title": "Supporter l’ouverture des layouts plugins comme Android Health", - "status": "open", + "status": "closed", "priority": "high", "sprint": null, "assignedAgentIds": [], @@ -1835,13 +1835,13 @@ "kind": "agent", "agent_id": "a6c6ea12-bfc6-4bdc-8031-324102dfa34d" }, - "updatedAt": 1785766766212 + "updatedAt": 1785838243826 }, { "issueRef": "#142", "path": "142", "title": "Plugin SDK: backend support for plugin-hosted windows", - "status": "qa", + "status": "closed", "priority": "high", "sprint": null, "assignedAgentIds": [ @@ -1851,13 +1851,13 @@ "kind": "agent", "agent_id": "a6c6ea12-bfc6-4bdc-8031-324102dfa34d" }, - "updatedAt": 1785796721082 + "updatedAt": 1785796835464 }, { "issueRef": "#143", "path": "143", "title": "Plugin SDK: frontend host for plugin windows and window-open API", - "status": "qa", + "status": "closed", "priority": "high", "sprint": null, "assignedAgentIds": [ @@ -1867,13 +1867,13 @@ "kind": "agent", "agent_id": "a6c6ea12-bfc6-4bdc-8031-324102dfa34d" }, - "updatedAt": 1785796721101 + "updatedAt": 1785796835483 }, { "issueRef": "#144", "path": "144", "title": "Plugin SDK: shared React runtime for plugin layouts", - "status": "qa", + "status": "closed", "priority": "high", "sprint": null, "assignedAgentIds": [ @@ -1883,13 +1883,13 @@ "kind": "agent", "agent_id": "a6c6ea12-bfc6-4bdc-8031-324102dfa34d" }, - "updatedAt": 1785796721119 + "updatedAt": 1785796835501 }, { "issueRef": "#145", "path": "145", "title": "Plugin SDK: expand and restructure SDK documentation", - "status": "qa", + "status": "closed", "priority": "high", "sprint": null, "assignedAgentIds": [ @@ -1899,13 +1899,13 @@ "kind": "agent", "agent_id": "a6c6ea12-bfc6-4bdc-8031-324102dfa34d" }, - "updatedAt": 1785796721137 + "updatedAt": 1785796835521 }, { "issueRef": "#146", "path": "146", "title": "QA: validate plugin window opening, React layouts, and SDK docs/examples", - "status": "qa", + "status": "closed", "priority": "high", "sprint": null, "assignedAgentIds": [ @@ -1915,7 +1915,20 @@ "kind": "agent", "agent_id": "a6c6ea12-bfc6-4bdc-8031-324102dfa34d" }, - "updatedAt": 1785796721013 + "updatedAt": 1785796835539 + }, + { + "issueRef": "#147", + "path": "147", + "title": "[UI] créer une CLI custom alternative", + "status": "inProgress", + "priority": "medium", + "sprint": null, + "assignedAgentIds": [], + "createdBy": { + "kind": "user" + }, + "updatedAt": 1785880207496 } ] } \ No newline at end of file From dcba76b87129c41176a146d85859249c58d60118 Mon Sep 17 00:00:00 2001 From: Blomios Date: Wed, 5 Aug 2026 11:59:39 +0200 Subject: [PATCH 2/2] feat(chat): livre la CLI custom de chat agent (#147) et corrige Cancel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implémente la vue chat structurée par cellule agent (toggle TUI/CLI custom, préférence persistée `preferred_view`, reattach live, composer + pièces jointes) avec le socle backend AgentSession/ChatBridge (UserPrompt, cancel_current_turn, routage interrupt_agent, commande cancel_agent_chat). Corrige le bug bloquant relevé par QA : le bouton Cancel de CustomAgentChatView interrompait tout le tour via closeAgentChat au lieu de n'annuler que le tour courant via cancelAgentChat, ce qui tuait la session contrairement au contrat produit validé. Co-Authored-By: Claude Opus 4.8 --- .ideai/tickets/147/carnet.md | 53 ++- .ideai/tickets/147/issue.md | 4 +- crates/app-tauri/src/chat.rs | 1 + crates/app-tauri/src/commands.rs | 198 ++++++++- crates/app-tauri/src/lib.rs | 1 + crates/app-tauri/tests/chat_bridge.rs | 1 + crates/app-tauri/tests/dto.rs | 25 +- crates/app-tauri/tests/dto_chat.rs | 16 +- crates/application/src/layout/usecases.rs | 13 +- .../application/tests/change_agent_profile.rs | 1 + crates/application/tests/layout_usecases.rs | 48 ++- .../tests/list_resumable_agents.rs | 1 + crates/application/tests/reconcile_layouts.rs | 1 + .../tests/snapshot_running_agents.rs | 1 + crates/application/tests/window_usecases.rs | 1 + crates/backend/src/dto.rs | 25 +- crates/domain/src/layout.rs | 60 +++ crates/domain/src/lib.rs | 5 +- crates/domain/src/ports.rs | 13 + crates/domain/tests/agent_profile_a0.rs | 1 + crates/domain/tests/layout.rs | 12 + crates/domain/tests/serde_roundtrip.rs | 4 + crates/domain/tests/window.rs | 1 + crates/infrastructure/src/session/opencode.rs | 122 ++++-- frontend/src/adapters/agent.test.ts | 9 + frontend/src/adapters/agent.ts | 66 +++ frontend/src/adapters/mock/index.ts | 124 +++++- .../agents/CustomAgentChatView.test.tsx | 97 +++++ .../features/agents/CustomAgentChatView.tsx | 388 ++++++++++++++++++ frontend/src/features/agents/index.ts | 2 + .../features/layout/LayoutGrid.chat.test.tsx | 187 +++++---- frontend/src/features/layout/LayoutGrid.tsx | 300 +++++++++++++- frontend/src/ports/index.ts | 44 ++ 33 files changed, 1681 insertions(+), 144 deletions(-) create mode 100644 frontend/src/features/agents/CustomAgentChatView.test.tsx create mode 100644 frontend/src/features/agents/CustomAgentChatView.tsx diff --git a/.ideai/tickets/147/carnet.md b/.ideai/tickets/147/carnet.md index c6f86b7..874d551 100644 --- a/.ideai/tickets/147/carnet.md +++ b/.ideai/tickets/147/carnet.md @@ -1,8 +1,8 @@ --- issueRef: "#147" -version: 6 +version: 7 updatedBy: {"kind":"agent","agent_id":"a6c6ea12-bfc6-4bdc-8031-324102dfa34d"} -updatedAt: 1785880207496 +updatedAt: 1785882543204 --- ## Cadrage consolidé @@ -43,3 +43,52 @@ Ce ticket ne doit pas être traité comme un simple ticket UI : il implique un v - Comportement exact du staging temporaire des fichiers joints : durée de vie, nettoyage, taille max, comportement si le fichier source change. - UX précise du warning de bascule de mode et du redémarrage de session. - Stratégie de dégradation contrôlée selon la richesse réellement exposée par chaque agent headless. + +## Exécution du cycle au 4 août 2026 + +### Architect +- Architect a revu le code réel et a conclu qu'il existe déjà un socle backend de chat structuré (`AgentSession`, `ChatBridge`, `ReplyChunk`, `reattach_agent_chat`) ; le ticket est donc une réintégration de la vue chat avec quelques compléments ciblés, pas une reconstruction complète. +- Contrats proposés : `preferred_view` persistant par cellule, `cancel_current_turn()` côté `AgentSession`, `ReplyChunk::UserPrompt`, toggle par cellule agent, vue live seulement, pas de transcript persistant secondaire. + +### Git +- Branche de travail locale décidée par Git : `feature/ticket147-custom-chat-cli`. +- Base choisie : `develop`. +- Commit de bookkeeping déjà posé par Git : `efbd56a1 chore(tickets): sync carnets/issues #102/#141-#147 + agent glmopencode`. + +### DevBackend — état réel livré +- Livré : + - `LeafCell.preferred_view` avec migration douce (`Tui` par défaut). + - mutation layout pour persister cette préférence. + - `ReplyChunk::UserPrompt` + ajout du prompt user dans le scrollback live. + - `AgentSession::cancel_current_turn()` avec défaut no-op. + - implémentation concrète best-effort du cancel pour `OpenCodeSession`. + - routage `interrupt_agent` selon `preferred_view`. + - commande Tauri `cancel_agent_chat(session_id)`. +- Limites explicitement laissées : + - pas de staging backend structuré des pièces jointes ; le frontend injecte actuellement le chemin dans le prompt. + - cancel concret non généralisé à tous les adapters. + - pas de nouvelle persistance de transcript hors session vivante. +- Tests annoncés verts par DevBackend : `cargo test -p domain`, tests `application` ciblés layout, tests `infrastructure opencode`, tests `app-tauri` ciblés `dto_chat` / `chat_bridge`, `cargo fmt`. + +### DevFrontend — état réel livré +- Livré : + - toggle `TUI native` / `CLI custom` par cellule agent seulement, selon compatibilité structured/headless. + - modale de confirmation de switch avec arrêt + relance et wording dynamique. + - vue `CustomAgentChatView` live en bulles user/agent, rendu défensif, mise en avant du `Final` via `Task Complete`. + - composer texte + pièce jointe via `pickFile()` + bouton `Cancel`. + - reattach live de session structurée si elle est encore vivante. + - câblage TS des méthodes `launchAgentChat`, `reattachAgentChat`, `sendAgentChat`, `closeAgentChat` côté ports/adapters/mock. +- Limites explicitement laissées : + - pas de payload structuré pour les attachments ; chemin injecté dans le prompt. + - rendu limité aux chunks réellement exposés (`textDelta`, `toolActivity`, `final`, `error`, `userPrompt`). + +### QA — verdict actuel +- Verdict QA au 4 août 2026 : **ROUGE** pour le MVP réellement livré. +- Finding bloquant principal : dans `CustomAgentChatView`, le bouton `Cancel` ferme la session structurée via `closeAgentChat` au lieu d'interrompre seulement le tour courant. Cela viole explicitement le contrat produit validé avec l'utilisateur. +- Finding secondaire : absence de test de non-régression couvrant ce comportement `Cancel`. +- Côté frontend, `npx vitest run` a été exécuté par QA et est vert (`115` fichiers / `1083` tests). +- Côté Rust, QA n'a pas obtenu de preuve globale exploitable dans le sandbox à cause de contraintes d'environnement (`Read-only file system`, verrous `cargo`, saturation temporaire `/tmp`). + +### État d'avancement +- Le cycle a été lancé et exécuté jusqu'à QA. +- Le ticket n'est pas encore validé parce qu'il reste un correctif frontend ciblé à livrer sur `Cancel`, suivi d'une revalidation QA. diff --git a/.ideai/tickets/147/issue.md b/.ideai/tickets/147/issue.md index 7176a44..a9e2df0 100644 --- a/.ideai/tickets/147/issue.md +++ b/.ideai/tickets/147/issue.md @@ -11,8 +11,8 @@ attachments: [{"id":"06f4e827-d693-4190-9c53-186585602006","filename":"task_comp createdBy: {"kind":"user"} updatedBy: {"kind":"agent","agent_id":"a6c6ea12-bfc6-4bdc-8031-324102dfa34d"} createdAt: 1785877963139 -updatedAt: 1785880207496 -version: 6 +updatedAt: 1785882543204 +version: 7 --- J'aimerais une CLI alternative pour mes agents. Cette CLI doit contenir bine entendu la barre de chat, ainsi que pour le reste de l'écran les bulle de conversation de l'agent et dde l'utilisateur. Cette CLI doit etre de la meme forme que ce qu'on peut voir dans les CLI des agents IA dans les IDE de code. Je veux voir s'afficher les reflexions de l'IA etc. La CLI communiquera en headless avec l'agent. diff --git a/crates/app-tauri/src/chat.rs b/crates/app-tauri/src/chat.rs index 1cf82da..070b3fa 100644 --- a/crates/app-tauri/src/chat.rs +++ b/crates/app-tauri/src/chat.rs @@ -133,6 +133,7 @@ impl ChatBridge { fn reply_chunk_bytes(chunk: &ReplyChunk) -> usize { match chunk { + ReplyChunk::UserPrompt { text } => text.len(), ReplyChunk::TextDelta { text } => text.len(), ReplyChunk::ToolActivity { label } => label.len(), ReplyChunk::Final { content } => content.len(), diff --git a/crates/app-tauri/src/commands.rs b/crates/app-tauri/src/commands.rs index a122ca8..b11518f 100644 --- a/crates/app-tauri/src/commands.rs +++ b/crates/app-tauri/src/commands.rs @@ -7,6 +7,7 @@ use base64::engine::general_purpose::URL_SAFE_NO_PAD; use base64::Engine; use serde::{Deserialize, Serialize}; +use std::path::{Path, PathBuf}; use tauri::ipc::Channel; use tauri::{AppHandle, Emitter, Manager, State, WebviewUrl, WebviewWindowBuilder, WindowEvent}; @@ -30,12 +31,12 @@ use application::{ UpdateAgentContextInput, UpdateAgentEffortInput, UpdateAgentMcpToolPermissionsInput, UpdateAgentPermissionsInput, UpdateAgentSystemPermissionsInput, UpdateMemoryInput, UpdateProjectContextInput, UpdateProjectMcpToolPermissionsInput, UpdateProjectPermissionsInput, - UpdateProjectSystemPermissionsInput, UpdateSkillInput, + UpdateProjectSystemPermissionsInput, UpdateSkillInput, TICKET_ATTACHMENT_MAX_BYTES, }; use backend::stream::OutputSink; use domain::ports::ModelServerRuntime; use domain::ports::PtyHandle; -use domain::{PersistedPluginLayoutWindow, PluginId, PluginLayoutType}; +use domain::{LayoutNode, PersistedPluginLayoutWindow, PluginId, PluginLayoutType, PreferredView}; use crate::dto::{ model_server_config_domain, parse_agent_id, parse_close_terminal, parse_delete_profile, @@ -2156,6 +2157,7 @@ pub async fn change_agent_profile( pub async fn agent_send( session_id: String, prompt: String, + attachment_paths: Option>, on_reply: Channel, state: State<'_, AppState>, ) -> Result<(), ErrorDto> { @@ -2172,12 +2174,25 @@ pub async fn agent_send( // pump (if any) is superseded and stops delivering to its stale channel. let gen = state.chat_bridge.register(sid, on_reply); + let staged_attachments = + stage_chat_attachments(&state, &sid, attachment_paths.unwrap_or_default()).await?; + let prompt_for_model = prompt_with_staged_attachments(&prompt, &staged_attachments); + + // Retain the human submit in the same live scrollback as model chunks so + // `reattach_agent_chat` can repaint the whole in-flight conversation. + let _ = state.chat_bridge.send_output( + &sid, + ReplyChunk::UserPrompt { + text: prompt.clone(), + }, + ); + // Open the turn stream. A start failure leaves the just-registered channel in // place (the cell stays attached, ready for a retry) — mirrors the PTY pump, // which only unregisters on a hard subscribe failure; here the session is // still live, so we keep the attach and surface the error. let stream = session - .send(&prompt) + .send(&prompt_for_model) .await .map_err(|e| ErrorDto::from(AppError::from(e)))?; @@ -2246,6 +2261,112 @@ pub async fn agent_send( Ok(()) } +async fn stage_chat_attachments( + state: &AppState, + session_id: &domain::SessionId, + paths: Vec, +) -> Result, ErrorDto> { + if paths.is_empty() { + return Ok(Vec::new()); + } + let (project_id, agent_id, _, _) = state + .structured_sessions + .meta_for_session(session_id) + .ok_or_else(|| { + ErrorDto::from(AppError::NotFound(format!( + "structured session {session_id}" + ))) + })?; + let project = state + .project_store + .list_projects() + .await + .map_err(|err| ErrorDto::from(AppError::Store(err.to_string())))? + .into_iter() + .find(|project| project.id == project_id) + .ok_or_else(|| ErrorDto::from(AppError::NotFound(format!("project {project_id}"))))?; + + let stage_dir = Path::new(project.root.as_str()) + .join(".ideai") + .join("run") + .join(agent_id.to_string()) + .join("attachments") + .join(session_id.to_string()); + if tokio::fs::metadata(&stage_dir).await.is_ok() { + tokio::fs::remove_dir_all(&stage_dir) + .await + .map_err(|err| ErrorDto::from(AppError::FileSystem(err.to_string())))?; + } + tokio::fs::create_dir_all(&stage_dir) + .await + .map_err(|err| ErrorDto::from(AppError::FileSystem(err.to_string())))?; + + let mut staged = Vec::with_capacity(paths.len()); + for (index, raw_path) in paths.iter().enumerate() { + let source = PathBuf::from(raw_path); + let meta = tokio::fs::metadata(&source) + .await + .map_err(|err| ErrorDto::from(AppError::FileSystem(err.to_string())))?; + if !meta.is_file() { + return Err(ErrorDto::from(AppError::Invalid( + "chat attachment source must be a file".to_owned(), + ))); + } + if meta.len() > TICKET_ATTACHMENT_MAX_BYTES { + return Err(ErrorDto::from(AppError::Invalid(format!( + "chat attachment exceeds {} bytes", + TICKET_ATTACHMENT_MAX_BYTES + )))); + } + let filename = source + .file_name() + .and_then(|name| name.to_str()) + .filter(|name| valid_chat_attachment_filename(name)) + .ok_or_else(|| { + ErrorDto::from(AppError::Invalid( + "invalid chat attachment filename".to_owned(), + )) + })?; + let dest = stage_dir.join(format!("{index}-{filename}")); + tokio::fs::copy(&source, &dest) + .await + .map_err(|err| ErrorDto::from(AppError::FileSystem(err.to_string())))?; + staged.push(dest); + } + Ok(staged) +} + +fn valid_chat_attachment_filename(filename: &str) -> bool { + let lowered = filename.to_ascii_lowercase(); + let blocked = [ + "exe", "bat", "cmd", "com", "scr", "msi", "dll", "so", "dylib", "sh", "ps1", "jar", "app", + "deb", "rpm", + ]; + !lowered.trim().is_empty() + && !lowered.contains('/') + && !lowered.contains('\\') + && lowered != "." + && lowered != ".." + && !lowered + .rsplit_once('.') + .is_some_and(|(_, ext)| blocked.contains(&ext)) +} + +fn prompt_with_staged_attachments(prompt: &str, staged: &[PathBuf]) -> String { + if staged.is_empty() { + return prompt.to_owned(); + } + let mut out = String::with_capacity(prompt.len() + staged.len() * 96); + out.push_str(prompt); + out.push_str("\n\nPièces jointes copiées dans le run dir de cette session :\n"); + for path in staged { + out.push_str("- "); + out.push_str(&path.to_string_lossy()); + out.push('\n'); + } + out +} + /// `cancel_resume` — annule la **reprise automatique** armée pour un agent limité /// (ARCHITECTURE §21.1-4, fenêtre annulable). /// @@ -2351,6 +2472,19 @@ pub async fn interrupt_agent( ) -> Result<(), ErrorDto> { let project = resolve_project(&request.project_id, &state).await?; let agent_id = parse_agent_id(&request.agent_id)?; + + if preferred_view_for_agent(&state, project.id, agent_id).await == PreferredView::Chat { + if let Some(session) = state + .structured_sessions + .session_for_agent_in_project(project.id, &agent_id) + { + return session + .cancel_current_turn() + .await + .map_err(|e| ErrorDto::from(AppError::from(e))); + } + } + state .orchestrator_service .interrupt_agent(&project, agent_id) @@ -2359,6 +2493,64 @@ pub async fn interrupt_agent( .map_err(ErrorDto::from) } +/// `cancel_agent_chat` — interrupt the current turn of a live structured chat +/// session without shutting the session down. +/// +/// This is the session-id based twin of [`interrupt_agent`] for a custom chat +/// surface that already owns the structured `sessionId`. +/// +/// # Errors +/// Returns an [`ErrorDto`] (`INVALID` for a malformed id, `NOT_FOUND` if no live +/// structured session owns the id, `PROCESS` if a concrete cancel fails). +#[tauri::command] +pub async fn cancel_agent_chat( + session_id: String, + state: State<'_, AppState>, +) -> Result<(), ErrorDto> { + let sid = parse_session_id(&session_id)?; + let session = state + .structured_sessions + .session(&sid) + .ok_or_else(|| ErrorDto::from(AppError::NotFound(format!("structured session {sid}"))))?; + session + .cancel_current_turn() + .await + .map_err(|e| ErrorDto::from(AppError::from(e))) +} + +async fn preferred_view_for_agent( + state: &AppState, + project_id: domain::ProjectId, + agent_id: domain::AgentId, +) -> PreferredView { + let Ok(out) = state + .load_layout + .execute(LoadLayoutInput { + project_id, + layout_id: None, + }) + .await + else { + return PreferredView::Tui; + }; + preferred_view_in_node(&out.layout.root, agent_id).unwrap_or_default() +} + +fn preferred_view_in_node(node: &LayoutNode, agent_id: domain::AgentId) -> Option { + match node { + LayoutNode::Leaf(leaf) if leaf.agent == Some(agent_id) => Some(leaf.preferred_view), + LayoutNode::Leaf(_) | LayoutNode::CustomPluginLayout(_) => None, + LayoutNode::Split(split) => split + .children + .iter() + .find_map(|child| preferred_view_in_node(&child.node, agent_id)), + LayoutNode::Grid(grid) => grid + .cells + .iter() + .find_map(|cell| preferred_view_in_node(&cell.node, agent_id)), + } +} + /// `delegation_delivered` — the frontend write-portal's **ack** (ARCHITECTURE §20.3). /// /// Called once the cell has physically written a delegation `ticket` into the agent's diff --git a/crates/app-tauri/src/lib.rs b/crates/app-tauri/src/lib.rs index b26786f..edc3b43 100644 --- a/crates/app-tauri/src/lib.rs +++ b/crates/app-tauri/src/lib.rs @@ -344,6 +344,7 @@ pub fn run() { commands::cancel_resume, commands::set_resume_at, commands::interrupt_agent, + commands::cancel_agent_chat, commands::delegation_delivered, commands::set_front_attached, commands::reattach_agent_chat, diff --git a/crates/app-tauri/tests/chat_bridge.rs b/crates/app-tauri/tests/chat_bridge.rs index fe59cce..a680d87 100644 --- a/crates/app-tauri/tests/chat_bridge.rs +++ b/crates/app-tauri/tests/chat_bridge.rs @@ -59,6 +59,7 @@ fn error_chunk(s: &str) -> ReplyChunk { fn chunk_bytes(chunk: &ReplyChunk) -> usize { match chunk { + ReplyChunk::UserPrompt { text } => text.len(), ReplyChunk::TextDelta { text } => text.len(), ReplyChunk::ToolActivity { label } => label.len(), ReplyChunk::Final { content } => content.len(), diff --git a/crates/app-tauri/tests/dto.rs b/crates/app-tauri/tests/dto.rs index d925037..237b94a 100644 --- a/crates/app-tauri/tests/dto.rs +++ b/crates/app-tauri/tests/dto.rs @@ -9,7 +9,7 @@ use app_tauri_lib::dto::{ }; use app_tauri_lib::events::{DomainEventDto, DOMAIN_EVENT}; use application::{CloseTerminalOutput, LayoutOperation, LoadLayoutOutput, OpenTerminalInput}; -use domain::{Direction, LayoutNode, LayoutTree, LeafCell, NodeId}; +use domain::{Direction, LayoutNode, LayoutTree, LeafCell, NodeId, PreferredView}; use application::{AppError, HealthInput}; use domain::events::DomainEvent; @@ -311,6 +311,7 @@ fn layout_dto_serialises_camelcase_tagged_tree() { conversation_id: None, engine_session_id: None, agent_was_running: false, + preferred_view: domain::PreferredView::Tui, }); let dto = LayoutDto::from(LoadLayoutOutput { layout_id: domain::LayoutId::new_random(), @@ -367,6 +368,26 @@ fn layout_operation_dto_set_session_accepts_null_session() { } } +#[test] +fn layout_operation_dto_set_cell_preferred_view_deserialises() { + let json = json!({ + "type": "setCellPreferredView", + "target": nid(1).to_string(), + "preferredView": "chat", + }); + let dto: LayoutOperationDto = serde_json::from_value(json).unwrap(); + match dto.into_operation().unwrap() { + LayoutOperation::SetCellPreferredView { + target, + preferred_view, + } => { + assert_eq!(target, nid(1)); + assert_eq!(preferred_view, PreferredView::Chat); + } + other => panic!("expected SetCellPreferredView, got {other:?}"), + } +} + #[test] fn layout_operation_dto_resize_carries_weights() { let json = json!({ @@ -413,6 +434,7 @@ fn layout_dto_round_trips_a_split_tree_shape() { conversation_id: None, engine_session_id: None, agent_was_running: false, + preferred_view: domain::PreferredView::Tui, }) .split( nid(1), @@ -424,6 +446,7 @@ fn layout_dto_round_trips_a_split_tree_shape() { conversation_id: None, engine_session_id: None, agent_was_running: false, + preferred_view: domain::PreferredView::Tui, }, nid(9), ) diff --git a/crates/app-tauri/tests/dto_chat.rs b/crates/app-tauri/tests/dto_chat.rs index c802275..4b78923 100644 --- a/crates/app-tauri/tests/dto_chat.rs +++ b/crates/app-tauri/tests/dto_chat.rs @@ -18,6 +18,15 @@ use uuid::Uuid; // ReplyChunk — tagged camelCase, exact wire shape + round-trip (zone 5/6) // --------------------------------------------------------------------------- +#[test] +fn reply_chunk_user_prompt_serialises_exact_camel_case() { + let v = serde_json::to_value(ReplyChunk::UserPrompt { + text: "run tests".into(), + }) + .unwrap(); + assert_eq!(v, json!({ "kind": "userPrompt", "text": "run tests" })); +} + #[test] fn reply_chunk_text_delta_serialises_exact_camel_case() { let v = serde_json::to_value(ReplyChunk::TextDelta { @@ -60,6 +69,7 @@ fn reply_chunk_error_serialises_exact_camel_case() { #[test] fn reply_chunk_round_trips_through_json_for_every_variant() { for chunk in [ + ReplyChunk::UserPrompt { text: "u".into() }, ReplyChunk::TextDelta { text: "x".into() }, ReplyChunk::ToolActivity { label: "runs".into(), @@ -81,8 +91,8 @@ fn reply_chunk_round_trips_through_json_for_every_variant() { fn reply_chunk_deserialises_from_camel_case_wire_payload() { // The shape the frontend (or a mock gateway) emits. let back: ReplyChunk = - serde_json::from_value(json!({ "kind": "textDelta", "text": "hi" })).unwrap(); - assert_eq!(back, ReplyChunk::TextDelta { text: "hi".into() }); + serde_json::from_value(json!({ "kind": "userPrompt", "text": "hi" })).unwrap(); + assert_eq!(back, ReplyChunk::UserPrompt { text: "hi".into() }); } #[test] @@ -102,6 +112,7 @@ fn reattach_chat_dto_serialises_camel_case_with_typed_scrollback() { let dto = ReattachChatDto { session_id: "sess-1".into(), scrollback: vec![ + ReplyChunk::UserPrompt { text: "Hi".into() }, ReplyChunk::TextDelta { text: "Hi".into() }, ReplyChunk::Final { content: "Hi".into(), @@ -114,6 +125,7 @@ fn reattach_chat_dto_serialises_camel_case_with_typed_scrollback() { json!({ "sessionId": "sess-1", "scrollback": [ + { "kind": "userPrompt", "text": "Hi" }, { "kind": "textDelta", "text": "Hi" }, { "kind": "final", "content": "Hi" }, ], diff --git a/crates/application/src/layout/usecases.rs b/crates/application/src/layout/usecases.rs index 2f86768..4540291 100644 --- a/crates/application/src/layout/usecases.rs +++ b/crates/application/src/layout/usecases.rs @@ -10,7 +10,7 @@ use std::sync::Arc; use domain::ports::{EventBus, FileSystem, ProjectStore}; use domain::{ AgentId, Direction, DomainEvent, LayoutError, LayoutId, LayoutTree, LeafCell, NodeId, - ProjectId, SessionId, + PreferredView, ProjectId, SessionId, }; use crate::error::AppError; @@ -98,6 +98,13 @@ pub enum LayoutOperation { /// Conversation id to record, or `None` to clear. conversation_id: Option, }, + /// Persist the preferred live view for an agent leaf. + SetCellPreferredView { + /// The hosting leaf. + target: NodeId, + /// Preferred live view. + preferred_view: PreferredView, + }, /// Persist opaque state for a plugin-provided custom layout leaf. SetPluginLayoutState { /// The custom plugin layout node. @@ -130,6 +137,10 @@ impl LayoutOperation { target, conversation_id, } => tree.set_cell_conversation(*target, conversation_id.clone()), + Self::SetCellPreferredView { + target, + preferred_view, + } => tree.set_cell_preferred_view(*target, *preferred_view), Self::SetPluginLayoutState { target, state } => { tree.set_plugin_layout_state(*target, state.clone()) } diff --git a/crates/application/tests/change_agent_profile.rs b/crates/application/tests/change_agent_profile.rs index 06857af..ab0b056 100644 --- a/crates/application/tests/change_agent_profile.rs +++ b/crates/application/tests/change_agent_profile.rs @@ -590,6 +590,7 @@ fn agent_leaf( conversation_id: conversation_id.map(str::to_owned), engine_session_id: None, agent_was_running, + preferred_view: domain::PreferredView::Tui, } } diff --git a/crates/application/tests/layout_usecases.rs b/crates/application/tests/layout_usecases.rs index 5c4f0cc..7e4bc7a 100644 --- a/crates/application/tests/layout_usecases.rs +++ b/crates/application/tests/layout_usecases.rs @@ -20,8 +20,8 @@ use domain::ports::{ use domain::{ AgentId, ContentHash, Direction, LayoutId, LayoutNode, LayoutTree, LeafCell, LocalPath, NodeId, PluginBundleUrl, PluginId, PluginInstallSource, PluginLifecycleState, PluginPackageRef, - PluginRegistry, PluginRegistryEntry, Project, ProjectId, ProjectPath, RelativePath, RemoteRef, - RemovalOutcome, SessionId, StagedPluginPackage, + PluginRegistry, PluginRegistryEntry, PreferredView, Project, ProjectId, ProjectPath, + RelativePath, RemoteRef, RemovalOutcome, SessionId, StagedPluginPackage, }; use uuid::Uuid; @@ -350,6 +350,7 @@ fn single_leaf(node_id: NodeId) -> LayoutTree { conversation_id: None, engine_session_id: None, agent_was_running: false, + preferred_view: domain::PreferredView::Tui, }) } @@ -936,6 +937,49 @@ async fn mutate_set_cell_conversation_missing_leaf_is_not_found() { assert_eq!(err.code(), "NOT_FOUND", "got {err:?}"); } +#[tokio::test] +async fn mutate_set_cell_preferred_view_persists_chat_then_tui_default() { + let env = mut_env(pid(54)).await; + + env.mutate + .execute(MutateLayoutInput { + project_id: env.project_id, + layout_id: None, + operation: LayoutOperation::SetCellPreferredView { + target: nid(1), + preferred_view: PreferredView::Chat, + }, + }) + .await + .expect("set_cell_preferred_view records chat"); + + let tree_json = active_tree_json(&env.fs); + assert_eq!(tree_json["root"]["node"]["preferredView"], "chat"); + + let out = env + .mutate + .execute(MutateLayoutInput { + project_id: env.project_id, + layout_id: None, + operation: LayoutOperation::SetCellPreferredView { + target: nid(1), + preferred_view: PreferredView::Tui, + }, + }) + .await + .expect("set_cell_preferred_view restores tui"); + + match &out.layout.root { + LayoutNode::Leaf(l) => assert_eq!(l.preferred_view, PreferredView::Tui), + _ => panic!("expected leaf root"), + } + let tree_json = active_tree_json(&env.fs); + assert!( + tree_json["root"]["node"].get("preferredView").is_none(), + "default TUI view is omitted from persisted JSON" + ); +} + // --------------------------------------------------------------------------- // Named-layout management (#4) // --------------------------------------------------------------------------- diff --git a/crates/application/tests/list_resumable_agents.rs b/crates/application/tests/list_resumable_agents.rs index 49fd731..9738553 100644 --- a/crates/application/tests/list_resumable_agents.rs +++ b/crates/application/tests/list_resumable_agents.rs @@ -332,6 +332,7 @@ fn agent_leaf( conversation_id: conversation_id.map(str::to_owned), engine_session_id: None, agent_was_running, + preferred_view: domain::PreferredView::Tui, } } diff --git a/crates/application/tests/reconcile_layouts.rs b/crates/application/tests/reconcile_layouts.rs index 2b916ef..e9f84bf 100644 --- a/crates/application/tests/reconcile_layouts.rs +++ b/crates/application/tests/reconcile_layouts.rs @@ -163,6 +163,7 @@ fn agent_leaf(node: NodeId, agent: Option, conv: Option<&str>, running: conversation_id: conv.map(str::to_string), engine_session_id: None, agent_was_running: running, + preferred_view: domain::PreferredView::Tui, } } diff --git a/crates/application/tests/snapshot_running_agents.rs b/crates/application/tests/snapshot_running_agents.rs index 19eb826..6918dfc 100644 --- a/crates/application/tests/snapshot_running_agents.rs +++ b/crates/application/tests/snapshot_running_agents.rs @@ -183,6 +183,7 @@ fn agent_leaf(node: NodeId, agent: Option) -> LeafCell { conversation_id: None, engine_session_id: None, agent_was_running: false, + preferred_view: domain::PreferredView::Tui, } } diff --git a/crates/application/tests/window_usecases.rs b/crates/application/tests/window_usecases.rs index 631e7c0..ab17aa8 100644 --- a/crates/application/tests/window_usecases.rs +++ b/crates/application/tests/window_usecases.rs @@ -267,6 +267,7 @@ fn tab(n: u128) -> Tab { conversation_id: None, engine_session_id: None, agent_was_running: false, + preferred_view: domain::PreferredView::Tui, })), } } diff --git a/crates/backend/src/dto.rs b/crates/backend/src/dto.rs index 84265cb..cab00aa 100644 --- a/crates/backend/src/dto.rs +++ b/crates/backend/src/dto.rs @@ -1120,7 +1120,9 @@ use application::{ CreateLayoutOutput, DeleteLayoutOutput, LayoutInfo, LayoutOperation, ListLayoutsOutput, LoadLayoutOutput, MutateLayoutOutput, PluginLayoutOrigin, SetActiveLayoutOutput, }; -use domain::{AgentId, Direction, LayoutId, LayoutTree, NodeId, PluginId, PluginLayoutType}; +use domain::{ + AgentId, Direction, LayoutId, LayoutTree, NodeId, PluginId, PluginLayoutType, PreferredView, +}; /// Response DTO carrying a layout tree. /// @@ -1214,6 +1216,14 @@ pub enum LayoutOperationDto { #[serde(default)] conversation_id: Option, }, + /// Persist the preferred live view for an agent leaf. + #[serde(rename_all = "camelCase")] + SetCellPreferredView { + /// Hosting leaf. + target: String, + /// Preferred live view. + preferred_view: PreferredView, + }, /// Persist opaque plugin layout state. #[serde(rename_all = "camelCase")] SetPluginLayoutState { @@ -1273,6 +1283,13 @@ impl LayoutOperationDto { target: parse_node_id(&target)?, conversation_id, }, + Self::SetCellPreferredView { + target, + preferred_view, + } => LayoutOperation::SetCellPreferredView { + target: parse_node_id(&target)?, + preferred_view, + }, Self::SetPluginLayoutState { target, state } => LayoutOperation::SetPluginLayoutState { target: parse_node_id(&target)?, state, @@ -2957,6 +2974,12 @@ impl From for TerminalSessionDto { #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(tag = "kind", rename_all = "camelCase")] pub enum ReplyChunk { + /// The user's submitted prompt, retained in live scrollback for reattach. + #[serde(rename_all = "camelCase")] + UserPrompt { + /// Submitted prompt text. + text: String, + }, /// An assistant text fragment (incremental chat rendering). #[serde(rename_all = "camelCase")] TextDelta { diff --git a/crates/domain/src/layout.rs b/crates/domain/src/layout.rs index 13bf8fe..e07f4a5 100644 --- a/crates/domain/src/layout.rs +++ b/crates/domain/src/layout.rs @@ -22,6 +22,23 @@ pub enum Direction { Column, } +/// Preferred live view for an agent leaf. +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub enum PreferredView { + /// Native terminal/TUI view. + #[default] + Tui, + /// Structured chat view. + Chat, +} + +/// Returns `true` when the preferred view is the default `Tui`. +#[allow(clippy::trivially_copy_pass_by_ref)] +fn is_default_preferred_view(view: &PreferredView) -> bool { + *view == PreferredView::Tui +} + /// Returns `true` when a boolean is `false`. Used as a `skip_serializing_if` /// predicate so that default (`false`) flags are omitted from the serialized /// form, preserving backward/forward compatibility with leaves that predate the @@ -64,6 +81,10 @@ pub struct LeafCell { /// last closed. Used to decide whether to auto-resume the agent on reopen. #[serde(default, skip_serializing_if = "is_false")] pub agent_was_running: bool, + /// Preferred live view for this cell. Additive and defaulted to TUI for old + /// layouts; meaningful only when [`Self::agent`] is set. + #[serde(default, skip_serializing_if = "is_default_preferred_view")] + pub preferred_view: PreferredView, } impl LeafCell { @@ -81,6 +102,7 @@ impl LeafCell { conversation_id: None, engine_session_id: None, agent_was_running: false, + preferred_view: PreferredView::Tui, } } @@ -105,6 +127,13 @@ impl LeafCell { self.engine_session_id = engine_session_id; self } + + /// Wither additif : pose la vue live préférée de la cellule. + #[must_use] + pub fn with_preferred_view(mut self, preferred_view: PreferredView) -> Self { + self.preferred_view = preferred_view; + self + } } /// A weighted child within a [`SplitContainer`]. The `weight` is a *relative* @@ -567,6 +596,37 @@ impl LayoutTree { Ok(tree) } + /// Sets the preferred live view on the leaf `target`. + /// + /// Pure: returns a new validated tree. + /// + /// # Errors + /// - [`LayoutError::NodeNotFound`] if `target` is not a leaf in the tree. + pub fn set_cell_preferred_view( + &self, + target: NodeId, + preferred_view: PreferredView, + ) -> Result { + let mut found = false; + let root = map_node(&self.root, &mut |node| { + if let LayoutNode::Leaf(leaf) = node { + if leaf.id == target { + found = true; + let mut leaf = leaf.clone(); + leaf.preferred_view = preferred_view; + return LayoutNode::Leaf(leaf); + } + } + node.clone() + }); + if !found { + return Err(LayoutError::NodeNotFound(target)); + } + let tree = Self { root }; + tree.validate()?; + Ok(tree) + } + /// Sets (or, with `None`, clears) the persistent CLI `conversation_id` on /// the leaf `target`. /// diff --git a/crates/domain/src/lib.rs b/crates/domain/src/lib.rs index 84453ed..490add4 100644 --- a/crates/domain/src/lib.rs +++ b/crates/domain/src/lib.rs @@ -195,8 +195,9 @@ pub use git::GitRepository; pub use layout::{ CustomPluginLayoutCell, Direction, GridCell, GridContainer, LayoutError, LayoutNode, LayoutTree, LeafCell, PersistedMonitorState, PersistedPluginLayoutWindow, PersistedWindowKind, - PersistedWindowPosition, PersistedWindowSize, PersistedWindowState, SplitContainer, Tab, - WeightedChild, Window, WindowStateSnapshot, Workspace, WINDOW_STATE_SNAPSHOT_VERSION, + PersistedWindowPosition, PersistedWindowSize, PersistedWindowState, PreferredView, + SplitContainer, Tab, WeightedChild, Window, WindowStateSnapshot, Workspace, + WINDOW_STATE_SNAPSHOT_VERSION, }; pub use events::{DomainEvent, OrchestrationSource}; diff --git a/crates/domain/src/ports.rs b/crates/domain/src/ports.rs index 9b0ffc4..231b577 100644 --- a/crates/domain/src/ports.rs +++ b/crates/domain/src/ports.rs @@ -1200,6 +1200,19 @@ pub trait AgentSession: Send + Sync { self.send(prompt).await } + /// Interrupts the currently running turn without shutting down the session. + /// + /// Default is a no-op so existing structured adapters/fakes remain valid. + /// Process-backed adapters that retain a live child during `send` should + /// override it and kill only that child. + /// + /// # Errors + /// [`AgentSessionError::Io`] when the adapter attempted a concrete cancel and + /// it failed. + async fn cancel_current_turn(&self) -> Result<(), AgentSessionError> { + Ok(()) + } + /// Termine proprement la session (tue le process/SDK sous-jacent). Idempotent. /// /// # Errors diff --git a/crates/domain/tests/agent_profile_a0.rs b/crates/domain/tests/agent_profile_a0.rs index 4593a08..d6f53cb 100644 --- a/crates/domain/tests/agent_profile_a0.rs +++ b/crates/domain/tests/agent_profile_a0.rs @@ -124,6 +124,7 @@ fn leaf_cell(id: u128, sess: Option) -> LeafCell { conversation_id: None, engine_session_id: None, agent_was_running: false, + preferred_view: domain::PreferredView::Tui, } } diff --git a/crates/domain/tests/layout.rs b/crates/domain/tests/layout.rs index 74b1fe3..69025d4 100644 --- a/crates/domain/tests/layout.rs +++ b/crates/domain/tests/layout.rs @@ -22,6 +22,7 @@ fn leaf(id: u128, sess: Option) -> LeafCell { conversation_id: None, engine_session_id: None, agent_was_running: false, + preferred_view: domain::PreferredView::Tui, } } @@ -35,6 +36,7 @@ fn leaf_with_resume(id: u128, sess: Option) -> LeafCell { conversation_id: Some("conv-1".to_string()), engine_session_id: None, agent_was_running: true, + preferred_view: domain::PreferredView::Tui, } } @@ -680,6 +682,7 @@ fn agent_leaves_collects_only_agent_bearing_leaves() { conversation_id: None, engine_session_id: None, agent_was_running: false, + preferred_view: domain::PreferredView::Tui, }), weight: 1.0, }, @@ -695,6 +698,7 @@ fn agent_leaves_collects_only_agent_bearing_leaves() { conversation_id: None, engine_session_id: None, agent_was_running: false, + preferred_view: domain::PreferredView::Tui, }), weight: 1.0, }, @@ -791,6 +795,7 @@ fn move_session_preserves_resume_fields_on_both_leaves() { conversation_id: Some("conv-from".to_string()), engine_session_id: None, agent_was_running: true, + preferred_view: domain::PreferredView::Tui, }), weight: 1.0, }, @@ -802,6 +807,7 @@ fn move_session_preserves_resume_fields_on_both_leaves() { conversation_id: Some("conv-to".to_string()), engine_session_id: None, agent_was_running: true, + preferred_view: domain::PreferredView::Tui, }), weight: 1.0, }, @@ -847,6 +853,7 @@ fn agent_leaf_full(id: u128, agent: u128, conv: Option<&str>, running: bool) -> conversation_id: conv.map(str::to_string), engine_session_id: None, agent_was_running: running, + preferred_view: domain::PreferredView::Tui, } } @@ -1053,6 +1060,7 @@ fn leaf_serde_all_four_combinations_roundtrip() { conversation_id: conv.clone(), engine_session_id: None, agent_was_running: running, + preferred_view: domain::PreferredView::Tui, }; let json = serde_json::to_string(&cell).unwrap(); let back: LeafCell = serde_json::from_str(&json).unwrap(); @@ -1069,6 +1077,7 @@ fn leaf_serde_omits_defaults() { conversation_id: None, engine_session_id: None, agent_was_running: false, + preferred_view: domain::PreferredView::Tui, }; let json = serde_json::to_string(&cell).unwrap(); assert!( @@ -1090,6 +1099,7 @@ fn leaf_serde_field_names_are_camel_case_when_present() { conversation_id: Some("c".to_string()), engine_session_id: None, agent_was_running: true, + preferred_view: domain::PreferredView::Tui, }; let json = serde_json::to_string(&cell).unwrap(); assert!(json.contains("conversationId"), "json was {json}"); @@ -1117,6 +1127,7 @@ fn leaf_can_carry_conversation_without_session_and_inversely() { conversation_id: Some("c".to_string()), engine_session_id: None, agent_was_running: false, + preferred_view: domain::PreferredView::Tui, }; let a_back: LeafCell = serde_json::from_str(&serde_json::to_string(&a).unwrap()).unwrap(); assert_eq!(a_back, a); @@ -1129,6 +1140,7 @@ fn leaf_can_carry_conversation_without_session_and_inversely() { conversation_id: None, engine_session_id: None, agent_was_running: false, + preferred_view: domain::PreferredView::Tui, }; let b_back: LeafCell = serde_json::from_str(&serde_json::to_string(&b).unwrap()).unwrap(); assert_eq!(b_back, b); diff --git a/crates/domain/tests/serde_roundtrip.rs b/crates/domain/tests/serde_roundtrip.rs index 9addd53..24e2baf 100644 --- a/crates/domain/tests/serde_roundtrip.rs +++ b/crates/domain/tests/serde_roundtrip.rs @@ -408,6 +408,7 @@ fn layout_roundtrip() { conversation_id: None, engine_session_id: None, agent_was_running: false, + preferred_view: domain::PreferredView::Tui, }), weight: 1.5, }, @@ -419,6 +420,7 @@ fn layout_roundtrip() { conversation_id: None, engine_session_id: None, agent_was_running: false, + preferred_view: domain::PreferredView::Tui, }), weight: 2.5, }, @@ -445,6 +447,7 @@ fn leaf_with_agent_roundtrip_and_omits_null() { conversation_id: None, engine_session_id: None, agent_was_running: false, + preferred_view: domain::PreferredView::Tui, })); let rt = roundtrip(&tree); match rt.root { @@ -465,6 +468,7 @@ fn leaf_with_agent_roundtrip_and_omits_null() { conversation_id: None, engine_session_id: None, agent_was_running: false, + preferred_view: domain::PreferredView::Tui, })); let json2 = serde_json::to_string(&tree_no_agent).unwrap(); assert!( diff --git a/crates/domain/tests/window.rs b/crates/domain/tests/window.rs index 0a408c8..7abedbf 100644 --- a/crates/domain/tests/window.rs +++ b/crates/domain/tests/window.rs @@ -23,6 +23,7 @@ fn leaf_tree() -> LayoutTree { conversation_id: None, engine_session_id: None, agent_was_running: false, + preferred_view: domain::PreferredView::Tui, })) } fn tab(n: u128) -> Tab { diff --git a/crates/infrastructure/src/session/opencode.rs b/crates/infrastructure/src/session/opencode.rs index bdc3502..a7d7e2a 100644 --- a/crates/infrastructure/src/session/opencode.rs +++ b/crates/infrastructure/src/session/opencode.rs @@ -14,6 +14,7 @@ use async_trait::async_trait; use serde_json::Value; use tokio::io::{AsyncBufReadExt, AsyncReadExt, BufReader}; use tokio::process::Command; +use tokio::sync::Mutex as AsyncMutex; use domain::ports::{AgentSession, AgentSessionError, ReplyEvent, ReplyStream}; use domain::sandbox::{SandboxEnforcer, SandboxPlan}; @@ -322,11 +323,25 @@ pub struct OpenCodeSession { cwd: String, env: Vec<(String, String)>, engine_session_id: Mutex>, + current_child: Mutex>>>, sandbox: Option, sandbox_enforcer: Option>, } impl OpenCodeSession { + fn clear_current_child(&self, child: &Arc>) { + let mut current = self + .current_child + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if current + .as_ref() + .is_some_and(|stored| Arc::ptr_eq(stored, child)) + { + *current = None; + } + } + /// Construit l'adapter. `command_prefix` peut être `opencode`, un chemin absolu, /// ou un wrapper avec arguments; IdeA ajoute ensuite `run --format json`. pub fn new( @@ -349,6 +364,7 @@ impl OpenCodeSession { cwd: cwd.into(), env, engine_session_id: Mutex::new(seed), + current_child: Mutex::new(None), sandbox, sandbox_enforcer, }) @@ -401,46 +417,65 @@ impl AgentSession for OpenCodeSession { // structuré reste réservé aux chemins process génériques existants. let _ = (&self.sandbox, &self.sandbox_enforcer); - let mut child = cmd + let child = cmd .spawn() .map_err(|e| AgentSessionError::Start(format!("{}: {e}", self.command)))?; - let stdout = child - .stdout - .take() - .ok_or_else(|| AgentSessionError::Io("stdout pipe indisponible".to_owned()))?; - let mut stderr_pipe = child - .stderr - .take() - .ok_or_else(|| AgentSessionError::Io("stderr pipe indisponible".to_owned()))?; - - let expected_session = self.conversation_id(); - let mut lines = BufReader::new(stdout).lines(); - let mut collected = Vec::new(); - while let Some(line) = lines - .next_line() - .await - .map_err(|e| AgentSessionError::Io(e.to_string()))? + let child = Arc::new(AsyncMutex::new(child)); { - if let Some(engine_id) = extract_session_id(&line)? { - if expected_session - .as_deref() - .is_none_or(|expected| expected == engine_id) - { - self.capture_session_id(engine_id); - } - } - collected.push(line); + let mut current = self + .current_child + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + *current = Some(Arc::clone(&child)); } + let outcome = async { + let (stdout, mut stderr_pipe) = + { + let mut locked = child.lock().await; + let stdout = locked.stdout.take().ok_or_else(|| { + AgentSessionError::Io("stdout pipe indisponible".to_owned()) + })?; + let stderr = locked.stderr.take().ok_or_else(|| { + AgentSessionError::Io("stderr pipe indisponible".to_owned()) + })?; + (stdout, stderr) + }; - let mut stderr_bytes = Vec::new(); - stderr_pipe - .read_to_end(&mut stderr_bytes) - .await - .map_err(|e| AgentSessionError::Io(e.to_string()))?; - let status = child - .wait() - .await - .map_err(|e| AgentSessionError::Io(e.to_string()))?; + let expected_session = self.conversation_id(); + let mut lines = BufReader::new(stdout).lines(); + let mut collected = Vec::new(); + while let Some(line) = lines + .next_line() + .await + .map_err(|e| AgentSessionError::Io(e.to_string()))? + { + if let Some(engine_id) = extract_session_id(&line)? { + if expected_session + .as_deref() + .is_none_or(|expected| expected == engine_id) + { + self.capture_session_id(engine_id); + } + } + collected.push(line); + } + + let mut stderr_bytes = Vec::new(); + stderr_pipe + .read_to_end(&mut stderr_bytes) + .await + .map_err(|e| AgentSessionError::Io(e.to_string()))?; + let status = child + .lock() + .await + .wait() + .await + .map_err(|e| AgentSessionError::Io(e.to_string()))?; + Ok((expected_session, collected, stderr_bytes, status)) + } + .await; + self.clear_current_child(&child); + let (expected_session, collected, stderr_bytes, status) = outcome?; let stderr = String::from_utf8_lossy(&stderr_bytes); let parsed_session = expected_session.or_else(|| self.conversation_id()); let parsed = parse_jsonl_turn_scoped(&collected, parsed_session.as_deref()); @@ -474,6 +509,23 @@ impl AgentSession for OpenCodeSession { async fn shutdown(&self) -> Result<(), AgentSessionError> { Ok(()) } + + async fn cancel_current_turn(&self) -> Result<(), AgentSessionError> { + let child = self + .current_child + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone(); + if let Some(child) = child { + child + .lock() + .await + .kill() + .await + .map_err(|e| AgentSessionError::Io(format!("annulation OpenCode: {e}")))?; + } + Ok(()) + } } #[cfg(test)] diff --git a/frontend/src/adapters/agent.test.ts b/frontend/src/adapters/agent.test.ts index 669f3df..79c976d 100644 --- a/frontend/src/adapters/agent.test.ts +++ b/frontend/src/adapters/agent.test.ts @@ -161,4 +161,13 @@ describe("TauriAgentGateway invoke payloads", () => { request: { projectId: "proj-1", agentId: "agent-2", effort: null }, }); }); + + it("cancelAgentChat invokes cancel_agent_chat without closing the session", async () => { + await new TauriAgentGateway().cancelAgentChat("chat-session-1"); + + expect(invoke).toHaveBeenCalledWith("cancel_agent_chat", { + sessionId: "chat-session-1", + }); + expect(invoke).not.toHaveBeenCalledWith("close_agent_session", expect.anything()); + }); }); diff --git a/frontend/src/adapters/agent.ts b/frontend/src/adapters/agent.ts index f416ec4..a8666bb 100644 --- a/frontend/src/adapters/agent.ts +++ b/frontend/src/adapters/agent.ts @@ -18,15 +18,18 @@ import type { Agent, AgentContextDocument, EffortSelection, + ReplyChunk, ResumableAgent, TerminalSession, } from "@/domain"; import type { + AgentChatHandle, AgentGateway, ConversationDetails, CreateAgentInput, LiveAgent, OpenTerminalOptions, + ReattachAgentChatResult, ReattachResult, StoppedLiveAgent, TerminalHandle, @@ -175,6 +178,69 @@ export class TauriAgentGateway implements AgentGateway { }; } + async launchAgentChat( + projectId: string, + agentId: string, + options: OpenTerminalOptions, + ): Promise { + // `launch_agent` is the routing authority. For structured/headless profiles + // the backend ignores the PTY output channel and returns a structured session + // id; for non-structured profiles it may still route to PTY, so callers gate + // this method on `profile.structuredAdapter`. + const channel = new Channel(); + const res = await invoke("launch_agent", { + request: { + projectId, + agentId, + rows: options.rows, + cols: options.cols, + conversationId: options.conversationId ?? null, + nodeId: options.nodeId ?? null, + }, + onOutput: channel, + }); + return { + sessionId: res.sessionId, + ...(res.assignedConversationId + ? { assignedConversationId: res.assignedConversationId } + : {}), + }; + } + + async reattachAgentChat( + sessionId: string, + onChunk: (chunk: ReplyChunk) => void, + ): Promise { + const channel = new Channel(); + channel.onmessage = onChunk; + return invoke("reattach_agent_chat", { + sessionId, + onReply: channel, + }); + } + + async sendAgentChat( + sessionId: string, + prompt: string, + onChunk: (chunk: ReplyChunk) => void, + ): Promise { + const channel = new Channel(); + channel.onmessage = onChunk; + await invoke("agent_send", { + sessionId, + prompt, + onReply: channel, + }); + } + + async closeAgentChat(sessionId: string): Promise { + await invoke("close_agent_session", { sessionId }); + } + + async cancelAgentChat(sessionId: string): Promise { + await invoke("cancel_agent_chat", { sessionId }); + } + async reattach( sessionId: string, onData: (bytes: Uint8Array) => void, diff --git a/frontend/src/adapters/mock/index.ts b/frontend/src/adapters/mock/index.ts index f3e7058..b8554ff 100644 --- a/frontend/src/adapters/mock/index.ts +++ b/frontend/src/adapters/mock/index.ts @@ -99,6 +99,7 @@ import type { Unsubscribe, } from "@/domain"; import type { + AgentChatHandle, AgentGateway, ConversationGateway, ConversationPageRequest, @@ -145,6 +146,7 @@ import type { PluginWorkspaceWriteBinaryInput, PluginWorkspaceWriteTextInput, ReattachResult, + ReattachAgentChatResult, RemoteGateway, ReviewPluginPackageInput, SaveOpenCodeProviderProfileInput, @@ -392,6 +394,10 @@ export class MockAgentGateway implements AgentGateway { private liveByAgent = new Map(); /** Live PTY session id per agent (`agentId → sessionId`). */ private liveSessionByAgent = new Map(); + /** Runtime kind per live agent (`agentId → kind`). */ + private liveKindByAgent = new Map(); + /** Retained structured reply chunks per live chat session. */ + private chatScrollback = new Map(); private getAgents(projectId: string): Agent[] { if (!this.agents.has(projectId)) this.agents.set(projectId, []); @@ -416,7 +422,7 @@ export class MockAgentGateway implements AgentGateway { agentId, nodeId, sessionId: this.liveSessionByAgent.get(agentId)!, - kind: "pty" as const, + kind: this.liveKindByAgent.get(agentId) ?? "pty", })); } @@ -449,7 +455,11 @@ export class MockAgentGateway implements AgentGateway { throw err; } const sessionId = this.liveSessionByAgent.get(agentId); - if (!sessionId || !this.sessions.has(sessionId)) { + if ( + !sessionId || + (this.liveKindByAgent.get(agentId) !== "structured" && + !this.sessions.has(sessionId)) + ) { const err: GatewayError = { code: "NOT_FOUND", message: `agent ${agentId} has no live session`, @@ -457,7 +467,12 @@ export class MockAgentGateway implements AgentGateway { throw err; } this.liveByAgent.set(agentId, nodeId); - return { agentId, nodeId, sessionId, kind: "pty" }; + return { + agentId, + nodeId, + sessionId, + kind: this.liveKindByAgent.get(agentId) ?? "pty", + }; } async stopLiveAgent(projectId: string, agentId: string): Promise { @@ -480,9 +495,12 @@ export class MockAgentGateway implements AgentGateway { const session = this.sessions.get(sessionId); if (session) session.closed = true; this.sessions.delete(sessionId); + this.chatScrollback.delete(sessionId); this.liveSessionByAgent.delete(agentId); this.liveByAgent.delete(agentId); - return { agentId, sessionId, kind: "pty" }; + const kind = this.liveKindByAgent.get(agentId) ?? "pty"; + this.liveKindByAgent.delete(agentId); + return { agentId, sessionId, kind }; } async createAgent(projectId: string, input: CreateAgentInput): Promise { @@ -718,11 +736,13 @@ export class MockAgentGateway implements AgentGateway { if (options.nodeId) { this.liveByAgent.set(agentId, options.nodeId); this.liveSessionByAgent.set(agentId, sessionId); + this.liveKindByAgent.set(agentId, "pty"); } const clearLive = () => { if (this.liveByAgent.get(agentId) === options.nodeId) { this.liveByAgent.delete(agentId); this.liveSessionByAgent.delete(agentId); + this.liveKindByAgent.delete(agentId); } }; @@ -752,6 +772,101 @@ export class MockAgentGateway implements AgentGateway { return handle; } + async launchAgentChat( + projectId: string, + agentId: string, + options: OpenTerminalOptions, + ): Promise { + const list = this.getAgents(projectId); + if (!list.some((a) => a.id === agentId)) { + throw { + code: "NOT_FOUND", + message: `agent ${agentId} not found in project ${projectId}`, + } as GatewayError; + } + const liveNode = this.liveByAgent.get(agentId); + if (liveNode !== undefined && options.nodeId && liveNode !== options.nodeId) { + throw { + code: "AGENT_ALREADY_RUNNING", + message: `agent ${agentId} is already running in cell ${liveNode}`, + } as GatewayError; + } + this.sessionSeq += 1; + const sessionId = `mock-agent-chat-${this.sessionSeq}`; + this.chatScrollback.set(sessionId, []); + if (options.nodeId) { + this.liveByAgent.set(agentId, options.nodeId); + this.liveSessionByAgent.set(agentId, sessionId); + this.liveKindByAgent.set(agentId, "structured"); + } + return { + sessionId, + ...(!options.conversationId + ? { assignedConversationId: `mock-conversation-${sessionId}` } + : {}), + }; + } + + async reattachAgentChat( + sessionId: string, + _onChunk: (chunk: ReplyChunk) => void, + ): Promise { + const chunks = this.chatScrollback.get(sessionId); + if (!chunks) { + throw { + code: "NOT_FOUND", + message: `agent chat session ${sessionId} is not alive`, + } as GatewayError; + } + return { sessionId, scrollback: structuredClone(chunks) }; + } + + async sendAgentChat( + sessionId: string, + prompt: string, + onChunk: (chunk: ReplyChunk) => void, + ): Promise { + const chunks = this.chatScrollback.get(sessionId); + if (!chunks) { + throw { + code: "NOT_FOUND", + message: `agent chat session ${sessionId} not found`, + } as GatewayError; + } + const content = `Agent: reçu « ${prompt} ».`; + const streamed: ReplyChunk[] = [ + { kind: "toolActivity", label: "Analyse du prompt" }, + { kind: "textDelta", text: "Agent: reçu " }, + { kind: "textDelta", text: `« ${prompt} ».` }, + { kind: "final", content }, + ]; + for (const chunk of streamed) { + await Promise.resolve(); + chunks.push(structuredClone(chunk)); + onChunk(chunk); + } + } + + async closeAgentChat(sessionId: string): Promise { + this.chatScrollback.delete(sessionId); + for (const [agentId, liveSessionId] of this.liveSessionByAgent) { + if (liveSessionId === sessionId) { + this.liveSessionByAgent.delete(agentId); + this.liveByAgent.delete(agentId); + this.liveKindByAgent.delete(agentId); + } + } + } + + async cancelAgentChat(sessionId: string): Promise { + if (!this.chatScrollback.has(sessionId)) { + throw { + code: "NOT_FOUND", + message: `agent chat session ${sessionId} not found`, + } as GatewayError; + } + } + async reattach( sessionId: string, onData: (bytes: Uint8Array) => void, @@ -772,6 +887,7 @@ export class MockAgentGateway implements AgentGateway { if (liveSessionId === sessionId) { this.liveSessionByAgent.delete(agentId); this.liveByAgent.delete(agentId); + this.liveKindByAgent.delete(agentId); } } }), diff --git a/frontend/src/features/agents/CustomAgentChatView.test.tsx b/frontend/src/features/agents/CustomAgentChatView.test.tsx new file mode 100644 index 0000000..56815ce --- /dev/null +++ b/frontend/src/features/agents/CustomAgentChatView.test.tsx @@ -0,0 +1,97 @@ +import { fireEvent, render, screen, waitFor } from "@testing-library/react"; +import { describe, expect, it, vi } from "vitest"; + +import { DIProvider } from "@/app/di"; +import type { AgentProfile } from "@/domain"; +import type { Gateways } from "@/ports"; +import { CustomAgentChatView } from "./CustomAgentChatView"; + +const profile: AgentProfile = { + id: "structured", + name: "Structured Codex", + command: "codex", + args: [], + contextInjection: { strategy: "conventionFile", target: "AGENTS.md" }, + detect: null, + cwdTemplate: "{projectRoot}", + structuredAdapter: "codex", +}; + +describe("CustomAgentChatView", () => { + it("cancels only the current turn and keeps the structured session alive", async () => { + const agent = { + launchAgentChat: vi.fn(), + reattachAgentChat: vi.fn(async (sessionId: string) => ({ + sessionId, + scrollback: [], + })), + sendAgentChat: vi.fn(() => new Promise(() => {})), + cancelAgentChat: vi.fn(async () => {}), + closeAgentChat: vi.fn(async () => {}), + }; + const onSessionId = vi.fn(); + + render( + null) }, + } as unknown as Gateways} + > + + , + ); + + await waitFor(() => + expect(agent.reattachAgentChat).toHaveBeenCalledWith( + "chat-session-1", + expect.any(Function), + ), + ); + + fireEvent.change(screen.getByLabelText(/message CLI custom/), { + target: { value: "first turn" }, + }); + fireEvent.click(screen.getByRole("button", { name: "Envoyer" })); + + await waitFor(() => + expect(agent.sendAgentChat).toHaveBeenCalledWith( + "chat-session-1", + "first turn", + expect.any(Function), + ), + ); + + fireEvent.click(await screen.findByRole("button", { name: "Cancel" })); + + await waitFor(() => + expect(agent.cancelAgentChat).toHaveBeenCalledWith("chat-session-1"), + ); + expect(agent.closeAgentChat).not.toHaveBeenCalled(); + expect(onSessionId).not.toHaveBeenCalledWith(null); + expect(screen.getByText("Tour interrompu.")).toBeTruthy(); + + fireEvent.change(screen.getByLabelText(/message CLI custom/), { + target: { value: "second turn" }, + }); + fireEvent.click(screen.getByRole("button", { name: "Envoyer" })); + + await waitFor(() => expect(agent.sendAgentChat).toHaveBeenCalledTimes(2)); + expect(agent.sendAgentChat).toHaveBeenLastCalledWith( + "chat-session-1", + "second turn", + expect.any(Function), + ); + }); +}); diff --git a/frontend/src/features/agents/CustomAgentChatView.tsx b/frontend/src/features/agents/CustomAgentChatView.tsx new file mode 100644 index 0000000..7d79681 --- /dev/null +++ b/frontend/src/features/agents/CustomAgentChatView.tsx @@ -0,0 +1,388 @@ +/** + * Custom agent CLI for structured/headless profiles (#147). + * + * This is an alternative human view for an agent cell, not a replacement for + * the native TUI. It uses the structured chat commands when available and + * deliberately does not try to parse PTY bytes. + */ + +import { useEffect, useMemo, useRef, useState } from "react"; + +import type { AgentProfile, GatewayError, ReplyChunk } from "@/domain"; +import { useGateways } from "@/app/di"; +import { Button, Spinner, cn } from "@/shared"; + +export interface CustomAgentChatViewProps { + projectId: string; + agentId: string; + agentName: string; + profile: AgentProfile; + cwd: string; + nodeId: string; + sessionId: string | null; + conversationId: string | null; + onSessionId: (sessionId: string | null) => void; + onConversationId: (conversationId: string | null) => void; +} + +type ChatTurn = + | { role: "user"; text: string; attachment?: string } + | { role: "agent"; text: string; pending?: boolean } + | { role: "tool"; label: string } + | { role: "final"; text: string } + | { role: "error"; text: string } + | { role: "unknown"; text: string }; + +function describe(e: unknown): string { + if (e && typeof e === "object" && "message" in e) { + return String((e as GatewayError).message); + } + return String(e); +} + +function unknownChunkLabel(chunk: unknown): string { + try { + return JSON.stringify(chunk); + } catch { + return String(chunk); + } +} + +function isReplyRecord(chunk: unknown): chunk is Record { + return Boolean(chunk && typeof chunk === "object" && "kind" in chunk); +} + +function appendAgentDelta(turns: ChatTurn[], text: string): ChatTurn[] { + const next = [...turns]; + const last = next[next.length - 1]; + if (last?.role === "agent") { + next[next.length - 1] = { + role: "agent", + text: last.text + text, + pending: true, + }; + return next; + } + next.push({ role: "agent", text, pending: true }); + return next; +} + +function foldChunk(turns: ChatTurn[], raw: unknown): ChatTurn[] { + if (!isReplyRecord(raw)) { + return [...turns, { role: "unknown", text: unknownChunkLabel(raw) }]; + } + switch (raw.kind) { + case "textDelta": + return appendAgentDelta(turns, String(raw.text ?? "")); + case "toolActivity": + return [...turns, { role: "tool", label: String(raw.label ?? "Activité") }]; + case "final": { + const content = String(raw.content ?? ""); + const next = [...turns]; + const last = next[next.length - 1]; + if (last?.role === "agent") next[next.length - 1] = { ...last, pending: false }; + next.push({ role: "final", text: content }); + return next; + } + case "error": { + const next = [...turns]; + const last = next[next.length - 1]; + if (last?.role === "agent") next[next.length - 1] = { ...last, pending: false }; + next.push({ role: "error", text: String(raw.message ?? "Erreur agent") }); + return next; + } + case "userPrompt": + case "UserPrompt": + return [...turns, { role: "user", text: String(raw.text ?? raw.prompt ?? "") }]; + default: + return [...turns, { role: "unknown", text: unknownChunkLabel(raw) }]; + } +} + +export function CustomAgentChatView({ + projectId, + agentId, + agentName, + profile, + cwd, + nodeId, + sessionId, + conversationId, + onSessionId, + onConversationId, +}: CustomAgentChatViewProps) { + const { agent, system } = useGateways(); + const [turns, setTurns] = useState([]); + const [currentSession, setCurrentSession] = useState(sessionId); + const [draft, setDraft] = useState(""); + const [attachment, setAttachment] = useState(null); + const [opening, setOpening] = useState(false); + const [busy, setBusy] = useState(false); + const [error, setError] = useState(null); + const scrollRef = useRef(null); + const sessionRef = useRef(sessionId); + sessionRef.current = currentSession; + const onSessionIdRef = useRef(onSessionId); + onSessionIdRef.current = onSessionId; + const onConversationIdRef = useRef(onConversationId); + onConversationIdRef.current = onConversationId; + + const supported = Boolean( + profile.structuredAdapter && + agent.launchAgentChat && + agent.reattachAgentChat && + agent.sendAgentChat && + agent.cancelAgentChat && + agent.closeAgentChat, + ); + + useEffect(() => { + const el = scrollRef.current; + if (el) el.scrollTop = el.scrollHeight; + }, [turns]); + + useEffect(() => { + if (!supported) return; + let cancelled = false; + const receive = (chunk: ReplyChunk) => { + setTurns((prev) => foldChunk(prev, chunk)); + if (chunk.kind === "final" || chunk.kind === "error") setBusy(false); + }; + + async function openOrAttach() { + setOpening(true); + setError(null); + try { + if (sessionId) { + const reattached = await agent.reattachAgentChat!(sessionId, receive); + if (cancelled) return; + setCurrentSession(reattached.sessionId); + setTurns(reattached.scrollback.reduce(foldChunk, [] as ChatTurn[])); + return; + } + const launched = await agent.launchAgentChat!(projectId, agentId, { + cwd, + rows: 24, + cols: 80, + conversationId: conversationId ?? undefined, + nodeId, + }); + if (cancelled) return; + setCurrentSession(launched.sessionId); + onSessionIdRef.current(launched.sessionId); + if (launched.assignedConversationId) { + onConversationIdRef.current(launched.assignedConversationId); + } + // Attach the view so any in-flight chunks can be replayed after launch. + await agent.reattachAgentChat!(launched.sessionId, receive).catch(() => {}); + } catch (e) { + if (!cancelled) setError(describe(e)); + } finally { + if (!cancelled) setOpening(false); + } + } + + void openOrAttach(); + return () => { + cancelled = true; + }; + }, [ + supported, + agent, + projectId, + agentId, + cwd, + nodeId, + sessionId, + conversationId, + ]); + + const canSend = useMemo( + () => + supported && + Boolean(currentSession) && + Boolean(draft.trim()) && + !busy && + !opening, + [supported, currentSession, draft, busy, opening], + ); + + async function pickAttachment() { + const path = await system.pickFile(); + if (path) setAttachment(path); + } + + async function send() { + const text = draft.trim(); + if (!canSend || !currentSession || !agent.sendAgentChat) return; + const prompt = attachment ? `${text}\n\n[Fichier joint: ${attachment}]` : text; + setDraft(""); + setAttachment(null); + setBusy(true); + setError(null); + setTurns((prev) => [...prev, { role: "user", text, attachment: attachment ?? undefined }]); + try { + await agent.sendAgentChat(currentSession, prompt, (chunk) => { + setTurns((prev) => foldChunk(prev, chunk)); + if (chunk.kind === "final" || chunk.kind === "error") setBusy(false); + }); + } catch (e) { + setBusy(false); + setError(describe(e)); + setTurns((prev) => [...prev, { role: "error", text: describe(e) }]); + } + } + + async function cancel() { + const sid = sessionRef.current; + if (!sid || !agent.cancelAgentChat) return; + setBusy(false); + setOpening(false); + setError(null); + try { + await agent.cancelAgentChat(sid); + setTurns((prev) => [...prev, { role: "tool", label: "Tour interrompu." }]); + } catch (e) { + setError(describe(e)); + } + } + + return ( +

+
+
+
{agentName}
+
+ CLI custom · {profile.name} +
+
+ {(opening || busy) && ( + + )} +
+ + {!supported && ( +

+ CLI custom indisponible pour ce profil ou ce transport. Utilisez la TUI native. +

+ )} + {error && ( +

+ {error} +

+ )} + +
+ {opening && turns.length === 0 ? ( +
+ + Ouverture de la session structurée… +
+ ) : turns.length === 0 ? ( +

+ Envoyez un message pour démarrer la conversation structurée. +

+ ) : ( + turns.map((turn, index) => ) + )} +
+ +
+ {attachment && ( +
+ Fichier joint: {attachment} + +
+ )} +
+