From 938bb6bd0d84c8ecd9b8fdce303776bed87bf10f Mon Sep 17 00:00:00 2001 From: Charles Vien Date: Wed, 8 Apr 2026 21:51:05 -0700 Subject: [PATCH] Clear initialPrompt on first agent event to preserve history on retry --- .../features/sessions/service/service.test.ts | 40 +++++++++++++++++++ .../features/sessions/service/service.ts | 8 ++++ 2 files changed, 48 insertions(+) diff --git a/apps/code/src/renderer/features/sessions/service/service.test.ts b/apps/code/src/renderer/features/sessions/service/service.test.ts index 3c38cee7f..867e72584 100644 --- a/apps/code/src/renderer/features/sessions/service/service.test.ts +++ b/apps/code/src/renderer/features/sessions/service/service.test.ts @@ -872,6 +872,46 @@ describe("SessionService", () => { expect(mockTrpcAgent.reconnect.mutate).toHaveBeenCalled(); }); + it("creates fresh session when initialPrompt is set (prompt never delivered)", async () => { + const service = getSessionService(); + const mockSession = createMockSession({ + status: "error", + initialPrompt: [{ type: "text", text: "fix the bug" }], + }); + // First call returns the error session, subsequent calls return connected + mockSessionStoreSetters.getSessionByTaskId + .mockReturnValueOnce(mockSession) + .mockReturnValue( + createMockSession({ + taskRunId: "new-run", + status: "connected", + }), + ); + mockTrpcAgent.start.mutate.mockResolvedValue({ + channel: "agent-event:new-run", + configOptions: [], + }); + mockTrpcAgent.onSessionEvent.subscribe.mockReturnValue({ + unsubscribe: vi.fn(), + }); + mockTrpcAgent.onPermissionRequest.subscribe.mockReturnValue({ + unsubscribe: vi.fn(), + }); + mockTrpcAgent.prompt.mutate.mockResolvedValue({ stopReason: "end_turn" }); + mockBuildAuthenticatedClient.mockReturnValue({ + createTaskRun: vi.fn().mockResolvedValue({ id: "new-run" }), + appendTaskRunLog: vi.fn(), + }); + + await service.clearSessionError("task-123", "/repo"); + + // Should tear down old session and create a new one + expect(mockTrpcAgent.cancel.mutate).toHaveBeenCalledWith({ + sessionId: "run-123", + }); + expect(mockTrpcAgent.start.mutate).toHaveBeenCalled(); + }); + it("handles missing session gracefully", async () => { const service = getSessionService(); mockSessionStoreSetters.getSessionByTaskId.mockReturnValue(undefined); diff --git a/apps/code/src/renderer/features/sessions/service/service.ts b/apps/code/src/renderer/features/sessions/service/service.ts index faca70e45..51757b69b 100644 --- a/apps/code/src/renderer/features/sessions/service/service.ts +++ b/apps/code/src/renderer/features/sessions/service/service.ts @@ -750,6 +750,14 @@ export class SessionService { isJsonRpcRequest(acpMsg.message) && acpMsg.message.method === "session/prompt"; + // Once the agent starts responding, clear initialPrompt so that + // retry reconnects to this session instead of creating a new one. + if (!isUserPromptEcho && session.initialPrompt?.length) { + sessionStoreSetters.updateSession(taskRunId, { + initialPrompt: undefined, + }); + } + if (isUserPromptEcho) { sessionStoreSetters.replaceOptimisticWithEvent(taskRunId, acpMsg); } else {