fix(FN-5104): complete Step 3 — refresh stale useChat session before reattach
Fusion-Task-Id: FN-5104 Fusion-Task-Lineage: e92a26cb-a930-442a-98be-6ace9f376e73
This commit is contained in:
committed by
gsxdsm
parent
3baef3a266
commit
1fa22ac1b4
@@ -1134,6 +1134,53 @@ describe("useChat", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("FN-5104 reattaches once when selectSession refresh reveals in-flight generation from stale cache", async () => {
|
||||||
|
const staleSession = {
|
||||||
|
...makeSession({ id: "session-001", agentId: "agent-001" }),
|
||||||
|
isGenerating: false,
|
||||||
|
inFlightGeneration: null,
|
||||||
|
};
|
||||||
|
const generatingSession = {
|
||||||
|
...staleSession,
|
||||||
|
isGenerating: true,
|
||||||
|
inFlightGeneration: {
|
||||||
|
status: "generating" as const,
|
||||||
|
streamingText: "partial text",
|
||||||
|
streamingThinking: "thinking",
|
||||||
|
toolCalls: [{ id: "tool-1", type: "function", function: { name: "search", arguments: "{}" } }],
|
||||||
|
replayFromEventId: 19,
|
||||||
|
updatedAt: "2026-04-08T00:00:00.000Z",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
mockFetchChatSessions.mockResolvedValueOnce({ sessions: [staleSession] });
|
||||||
|
mockFetchChatSession.mockResolvedValueOnce({ session: generatingSession });
|
||||||
|
mockFetchChatMessages.mockResolvedValue({ messages: [] });
|
||||||
|
|
||||||
|
const { result } = renderHook(() => useChat());
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(result.current.sessions).toHaveLength(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.selectSession("session-001");
|
||||||
|
});
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(mockAttachChatStream).toHaveBeenCalledTimes(1);
|
||||||
|
expect(mockAttachChatStream).toHaveBeenCalledWith(
|
||||||
|
"session-001",
|
||||||
|
expect.any(Object),
|
||||||
|
undefined,
|
||||||
|
{ lastEventId: 19 },
|
||||||
|
);
|
||||||
|
expect(result.current.streamingText).toBe("partial text");
|
||||||
|
expect(result.current.streamingThinking).toBe("thinking");
|
||||||
|
expect(result.current.streamingToolCalls).toHaveLength(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it("fetches session on visible return only when no live stream and swallows reconnect failures", async () => {
|
it("fetches session on visible return only when no live stream and swallows reconnect failures", async () => {
|
||||||
const session = {
|
const session = {
|
||||||
...makeSession({ id: "session-001", agentId: "agent-001" }),
|
...makeSession({ id: "session-001", agentId: "agent-001" }),
|
||||||
|
|||||||
@@ -555,6 +555,27 @@ export function useChat(
|
|||||||
const session = sessionOverride ?? sessions.find((s) => s.id === id);
|
const session = sessionOverride ?? sessions.find((s) => s.id === id);
|
||||||
setActiveSession(session || null);
|
setActiveSession(session || null);
|
||||||
|
|
||||||
|
if (id) {
|
||||||
|
void fetchChatSession(id, projectId)
|
||||||
|
.then(({ session: refreshedSession }) => {
|
||||||
|
if (!refreshedSession.isGenerating || !refreshedSession.inFlightGeneration) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setActiveSession((prev) => {
|
||||||
|
if (!prev || prev.id !== id) {
|
||||||
|
return prev;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...prev,
|
||||||
|
...refreshedSession,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
// Ignore stale-cache recovery fetch failures.
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Reset transient state
|
// Reset transient state
|
||||||
resetTransientComposerState();
|
resetTransientComposerState();
|
||||||
setHasMoreMessages(true);
|
setHasMoreMessages(true);
|
||||||
|
|||||||
Reference in New Issue
Block a user