diff --git a/.ideai/tickets/149/carnet.md b/.ideai/tickets/149/carnet.md index 9c106d6..5136ea1 100644 --- a/.ideai/tickets/149/carnet.md +++ b/.ideai/tickets/149/carnet.md @@ -1,8 +1,8 @@ --- issueRef: "#149" -version: 14 +version: 15 updatedBy: {"kind":"agent","agent_id":"a6c6ea12-bfc6-4bdc-8031-324102dfa34d"} -updatedAt: 1785934744273 +updatedAt: 1785937428431 --- # Historique des tentatives @@ -13,7 +13,7 @@ updatedAt: 1785934744273 - Action Main: reouverture du ticket et relance d'un cycle complet Architect -> Git -> DevFrontend -> QA. ## Piste precedemment documentee dans la description -- Hypothese precedente: fallback premature dans `frontend/src/features/layout/LayoutGrid.tsx` pendant le chargement asynchrone initial du catalogue agent/profil, ecrasant `cellMode=custom` restaure depuis le storage. +- Hypothese precedente: fallback premature dans `frontend/src/features/layout/LayoutGrid.tsx` pendant le chargement asynchrone initial du catalogue agent/profil, ecrasant `cellMode="custom"` restaure depuis le storage. - Cette piste n'a pas suffi a eliminer le symptome utilisateur, donc elle doit etre revalidee ou completee avant nouvelle correction. ## 2026-08-05 — Recadrage Architecture (avant nouvelle implementation) @@ -124,3 +124,28 @@ updatedAt: 1785934744273 2. rebuild/reinstall AppImage depuis la branche contenant `ce9ba0dc`, relancer IdeA, puis retester ; 3. si la cellule retombe encore vers Plain/TUI, capturer les logs `[ticket149]` au moment exact du repro et comparer: `openOrAttach:self-session-echo:skip`, `vm.setSession`, `shouldFallbackCustomCliMode`, `customCliAvailable`, `effectiveCustomCliAvailable`, `trustedCustomCli`; 4. si les logs montrent que `openOrAttach` reste stable, la prochaine hypothese doit sortir de `CustomAgentChatView` et porter sur la condition de rendu `customCliAgent/customCliProfile/effectiveCustomCliAvailable` ou sur un evenement externe qui remplace le mode/vue, avec trace runtime avant patch. + +## 2026-08-05 — Nouveau symptome apres le fix session echo +- Retour utilisateur recu apres `ce9ba0dc`: la CLI custom peut maintenant se lancer, mais l'UI affiche l'erreur exacte `not found: structured session 69b57638-0ac7-4a79-8252-e0436a3265f6`. +- Changement de symptome important: on n'est plus sur la retombee immediate vers Plain/TUI telle que documentee plus haut ; on est maintenant sur un echec de rattachement/reutilisation d'une session structuree. +- Verification statique Main (sans nouvelle implementation a ce stade): + 1. `frontend/src/features/agents/CustomAgentChatView.tsx` est cense **absorber** `NOT_FOUND` sur `reattachAgentChat` puis relancer une session fraiche, y compris pour un `sessionId` stale restaure depuis le parent ; les tests couvrent deja ce cas. + 2. Le meme composant sait aussi recuperer d'un premier `launchAgentChat` qui renvoie un `sessionId` mort, a condition qu'un second launch/reattach reussisse ; ce cas est aussi teste. + 3. Voir quand meme cette erreur dans l'UI suggere donc plutot l'un de ces ecarts restants: + - l'erreur reelle renvoyee par `invoke(...)` n'est pas typée `code: "NOT_FOUND"` cote frontend, donc `isNotFound(...)` ne la reconnait pas et elle remonte telle quelle ; + - ou bien `launch_agent` renvoie lui-meme un `sessionId` structure qui n'est deja plus present dans `structured_sessions` au moment du `reattach`, potentiellement plus d'une fois, ce qui epuise la logique de retry et surface finalement `not found: structured session ...`. +- Ownership provisoire pour la reprise: + - piste prioritaire **frontend/runtime contract**: confirmer la forme exacte de l'erreur recue par `CustomAgentChatView` (`code`, `message`, `name`, `raw`) dans les logs `[ticket149]` deja poses ; + - piste secondaire **backend structured registry**: si `code === "NOT_FOUND"` est bien present sur un `sessionId` fraichement renvoye par `launch_agent`, alors l'anomalie bascule cote creation/enregistrement/liveness de `structured_sessions`. +- Commandes de lecture executees par Main pour ce recadrage: + 1. `git status --short --branch` -> branche courante `feature/ticket149-customchat-session-instrumentation`, avec un changement non lie `.ideai/idea-android-plugin.json` deja present ; + 2. `git log --oneline --decorate -n 12` -> HEAD `62ac05a0` au-dessus de `ce9ba0dc` ; + 3. lecture de `frontend/src/features/agents/CustomAgentChatView.tsx`, de ses tests, et de `crates/app-tauri/src/commands.rs` autour de `reattach_agent_chat` / `launch_agent`. +- Blocage de pilotage dans ce tour: + - `idea_ask_agent(Architect)` et `idea_ask_agent(Git)` ont tous deux repondu `You've hit your session limit · resets 4:50pm (Europe/Paris)` ; la delegation specialisee est donc temporairement indisponible jusqu'au **2026-08-05 16:50 Europe/Paris**. +- Etape suivante imposee a la reprise apres 16:50: + 1. refaire la delegation `Architect` pour trancher frontend vs backend sur ce nouveau symptome ; + 2. faire trancher `Git` sur la branche de travail a conserver/reprendre ; + 3. envoyer a `DevFrontend` une tache d'instrumentation/verification du contrat d'erreur si `code` manque ; + 4. envoyer a `DevBackend` une tache de verification de l'enregistrement/liveness `structured_sessions` si un `sessionId` fraichement lance ressort deja en `NOT_FOUND` ; + 5. faire valider par `QA` sur repro reel avec le message exact et, si possible, les logs `[ticket149]` au moment du repro. \ No newline at end of file diff --git a/.ideai/tickets/149/issue.md b/.ideai/tickets/149/issue.md index e95c3e7..7751956 100644 --- a/.ideai/tickets/149/issue.md +++ b/.ideai/tickets/149/issue.md @@ -11,8 +11,8 @@ attachments: [] createdBy: {"kind":"agent","agent_id":"a6c6ea12-bfc6-4bdc-8031-324102dfa34d"} updatedBy: {"kind":"agent","agent_id":"a6c6ea12-bfc6-4bdc-8031-324102dfa34d"} createdAt: 1785930073255 -updatedAt: 1785934744273 -version: 14 +updatedAt: 1785937428431 +version: 15 --- Bug report utilisateur du 2026-08-05: la CLI custom ne se lance plus; à l'ouverture elle se ferme immédiatement et la cellule revient sur Plain/TUI. Diagnostic Architecture: bug distinct de #148. Dans `frontend/src/features/layout/LayoutGrid.tsx`, `cellMode` restauré à `custom` depuis le storage est forcé trop tôt vers `tui` par l'effet garde-fou `if (!customCliAvailable && cellMode !== \"tui\") setCellMode(\"tui\")`, alors que `agents`/`profiles` sont encore vides pendant leur chargement asynchrone initial. Résultat: la préférence `custom` est écrasée silencieusement avant même que la vue puisse se monter. Objectif: retarder ce fallback jusqu'à la fin du premier chargement du catalogue, couvrir par test de non-régression, valider QA, puis rebuild AppImage Linux. diff --git a/.ideai/tickets/index.json b/.ideai/tickets/index.json index 4e89981..c47c91b 100644 --- a/.ideai/tickets/index.json +++ b/.ideai/tickets/index.json @@ -1960,7 +1960,7 @@ "kind": "agent", "agent_id": "a6c6ea12-bfc6-4bdc-8031-324102dfa34d" }, - "updatedAt": 1785934744273 + "updatedAt": 1785937428431 } ] } \ No newline at end of file diff --git a/frontend/src/features/agents/CustomAgentChatView.test.tsx b/frontend/src/features/agents/CustomAgentChatView.test.tsx index 903638a..a02e706 100644 --- a/frontend/src/features/agents/CustomAgentChatView.test.tsx +++ b/frontend/src/features/agents/CustomAgentChatView.test.tsx @@ -152,6 +152,65 @@ describe("CustomAgentChatView", () => { expect(screen.queryByText(/structured session gone/)).toBeNull(); }); + it("falls back when Tauri surfaces structured session NOT_FOUND as a raw string", async () => { + const agent = { + launchAgentChat: vi.fn(async () => ({ + sessionId: "chat-session-2", + assignedConversationId: "conversation-2", + })), + reattachAgentChat: vi + .fn() + .mockRejectedValueOnce( + "not found: structured session 69b57638-0ac7-4a79-8252-e0436a3265f6", + ) + .mockImplementationOnce(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.launchAgentChat).toHaveBeenCalledTimes(1)); + expect(agent.reattachAgentChat).toHaveBeenNthCalledWith( + 1, + "69b57638-0ac7-4a79-8252-e0436a3265f6", + expect.any(Function), + ); + expect(agent.reattachAgentChat).toHaveBeenNthCalledWith( + 2, + "chat-session-2", + expect.any(Function), + ); + expect(onSessionId).toHaveBeenCalledWith(null); + expect(onSessionId).toHaveBeenCalledWith("chat-session-2"); + expect(screen.queryByText(/not found: structured session/)).toBeNull(); + }); + it("does not reopen when the parent echoes a launched session id", async () => { const agent = { launchAgentChat: vi.fn(async () => ({ @@ -286,6 +345,74 @@ describe("CustomAgentChatView", () => { expect(screen.queryByText(/not found: structured session/)).toBeNull(); }); + it("recovers and retries the prompt when send reports structured session NOT_FOUND as a raw string", async () => { + const agent = { + launchAgentChat: vi.fn(async () => ({ + sessionId: "chat-session-2", + assignedConversationId: "conversation-2", + })), + reattachAgentChat: vi.fn(async (sessionId: string) => ({ + sessionId, + scrollback: [], + })), + sendAgentChat: vi + .fn() + .mockRejectedValueOnce("not found: structured session stale-session") + .mockImplementationOnce(async (_sessionId: string, _prompt: string, onChunk) => { + onChunk({ kind: "final", content: "Recovered reply" }); + }), + cancelAgentChat: vi.fn(async () => {}), + closeAgentChat: vi.fn(async () => {}), + }; + const onSessionId = vi.fn(); + + render( + null) }, + } as unknown as Gateways} + > + + , + ); + + await waitFor(() => + expect(agent.reattachAgentChat).toHaveBeenCalledWith( + "stale-session", + expect.any(Function), + ), + ); + + fireEvent.change(screen.getByLabelText(/message CLI custom/), { + target: { value: "please recover" }, + }); + fireEvent.click(screen.getByRole("button", { name: "Envoyer" })); + + await waitFor(() => expect(agent.sendAgentChat).toHaveBeenCalledTimes(2)); + expect(agent.sendAgentChat).toHaveBeenNthCalledWith( + 2, + "chat-session-2", + "please recover", + expect.any(Function), + ); + expect(onSessionId).toHaveBeenCalledWith(null); + expect(onSessionId).toHaveBeenCalledWith("chat-session-2"); + expect(await screen.findByText("Recovered reply")).toBeTruthy(); + expect(screen.queryByText(/not found: structured session/)).toBeNull(); + }); + it("treats NOT_FOUND during cancel as an already-gone session", async () => { const agent = { launchAgentChat: vi.fn(), diff --git a/frontend/src/features/agents/CustomAgentChatView.tsx b/frontend/src/features/agents/CustomAgentChatView.tsx index 9f262da..ab2ef8b 100644 --- a/frontend/src/features/agents/CustomAgentChatView.tsx +++ b/frontend/src/features/agents/CustomAgentChatView.tsx @@ -54,7 +54,16 @@ function instrumentationError(e: unknown): Record { } function isNotFound(e: unknown): boolean { - return Boolean(e && typeof e === "object" && (e as GatewayError).code === "NOT_FOUND"); + if (e && typeof e === "object" && (e as GatewayError).code === "NOT_FOUND") { + return true; + } + const message = + typeof e === "string" + ? e + : e && typeof e === "object" && "message" in e + ? String((e as GatewayError).message) + : ""; + return /^not found: structured session\b/i.test(message); } function unknownChunkLabel(chunk: unknown): string {