diff --git a/frontend/src/features/agents/CustomAgentChatView.test.tsx b/frontend/src/features/agents/CustomAgentChatView.test.tsx index dce09046..458e7190 100644 --- a/frontend/src/features/agents/CustomAgentChatView.test.tsx +++ b/frontend/src/features/agents/CustomAgentChatView.test.tsx @@ -1764,6 +1764,118 @@ describe("CustomAgentChatView", () => { expect(screen.getAllByTestId("chat-tool-progress")).toHaveLength(2); }); + it("keeps second-turn progress visible when the provider reuses the same progressId across turns", async () => { + const harness = createStreamingAgentHarness(); + + render( + null) }, + } as unknown as Gateways} + > + + , + ); + + await waitFor(() => + expect(harness.agent.reattachAgentChat).toHaveBeenCalledWith( + "chat-session-1", + expect.any(Function), + ), + ); + + fireEvent.change(screen.getByLabelText(/message CLI custom/), { + target: { value: "first request" }, + }); + fireEvent.click(screen.getByRole("button", { name: "Envoyer" })); + await waitFor(() => expect(harness.emitterCount()).toBe(1)); + + act(() => { + harness.emit(0, { + kind: "progress", + progress: { + progressId: "codex:item:call_1", + source: "providerNative", + kind: "tool", + stage: "started", + label: "shell", + toolName: "shell", + text: "Running first request", + }, + }); + harness.emit(0, { + kind: "progress", + progress: { + progressId: "codex:item:call_1", + source: "providerNative", + kind: "tool", + stage: "completed", + label: "shell", + toolName: "shell", + text: "Done first request", + }, + }); + harness.emit(0, { kind: "final", content: "done first request" }); + }); + await screen.findByText("done first request"); + + fireEvent.change(screen.getByLabelText(/message CLI custom/), { + target: { value: "second request" }, + }); + fireEvent.click(screen.getByRole("button", { name: "Envoyer" })); + await waitFor(() => expect(harness.emitterCount()).toBe(2)); + + act(() => { + harness.emit(1, { + kind: "progress", + progress: { + progressId: "codex:item:call_1", + source: "providerNative", + kind: "tool", + stage: "started", + label: "shell", + toolName: "shell", + text: "Running second request", + }, + }); + }); + + expect(screen.getByText("Running second request")).toBeTruthy(); + + act(() => { + harness.emit(1, { + kind: "progress", + progress: { + progressId: "codex:item:call_1", + source: "providerNative", + kind: "tool", + stage: "completed", + label: "shell", + toolName: "shell", + text: "Done second request", + }, + }); + harness.emit(1, { kind: "final", content: "done second request" }); + }); + + await screen.findByText("done second request"); + expect(screen.getByText("Done first request")).toBeTruthy(); + expect(screen.getByText("Done second request")).toBeTruthy(); + expect(screen.getAllByTestId("chat-tool-progress")).toHaveLength(2); + }); + it("ignores a late first-turn progress chunk but still shows the real second-turn progress from the next callback", async () => { const harness = createStreamingAgentHarness(); diff --git a/frontend/src/features/agents/CustomAgentChatView.tsx b/frontend/src/features/agents/CustomAgentChatView.tsx index dc80bb9d..c7a0529b 100644 --- a/frontend/src/features/agents/CustomAgentChatView.tsx +++ b/frontend/src/features/agents/CustomAgentChatView.tsx @@ -325,7 +325,7 @@ function progressKey(progress: ReplyProgress): string { function progressUpsertKey(progress: ReplyProgress, scope: number): string { const progressId = progress.progressId?.trim(); - if (progressId) return progressId; + if (progressId) return `progress:${scope}:${progressId}`; return `legacy:${scope}:${progressDisplayKey(progress)}`; }