fix(desktop): isolate explicit queued submit recovery
Signed-off-by: Shawn Wang <32839114+enwaiax@users.noreply.github.com>
This commit is contained in:
@@ -4708,6 +4708,78 @@ describe('usePromptActions busy-gateway churn tolerance (#64327)', () => {
|
||||
])
|
||||
})
|
||||
|
||||
it('keeps an explicit background queue target isolated from foreground routed recovery (#90428)', async () => {
|
||||
const FOREGROUND_STORED_ID = 'stored-foreground-b'
|
||||
const FOREGROUND_RUNTIME_ID = 'rt-foreground-b'
|
||||
const QUEUED_STORED_ID = 'stored-queued-c'
|
||||
const QUEUED_RUNTIME_ID = 'rt-queued-c-recovered'
|
||||
const calls: { method: string; params?: Record<string, unknown> }[] = []
|
||||
const stateWrites: { sessionId: string; storedSessionId: null | string | undefined }[] = []
|
||||
const selectedStoredSessionIdRef: MutableRefObject<string | null> = { current: FOREGROUND_STORED_ID }
|
||||
const activeSessionIdRef: MutableRefObject<string | null> = { current: FOREGROUND_RUNTIME_ID }
|
||||
|
||||
const runtimeIdByStoredSessionIdRef: MutableRefObject<Map<string, string>> = {
|
||||
current: new Map([[FOREGROUND_STORED_ID, FOREGROUND_RUNTIME_ID]])
|
||||
}
|
||||
|
||||
const resumeStoredSession = vi.fn(async () => undefined)
|
||||
|
||||
const requestGateway = vi.fn(async (method: string, params?: Record<string, unknown>) => {
|
||||
calls.push({ method, params })
|
||||
|
||||
if (method === 'session.resume') {
|
||||
expect(params?.session_id).toBe(QUEUED_STORED_ID)
|
||||
|
||||
return { session_id: QUEUED_RUNTIME_ID } as never
|
||||
}
|
||||
|
||||
return {} as never
|
||||
})
|
||||
|
||||
let handle: HarnessHandle | null = null
|
||||
render(
|
||||
<Harness
|
||||
activeSessionId={FOREGROUND_RUNTIME_ID}
|
||||
activeSessionIdRef={activeSessionIdRef}
|
||||
getRoutedStoredSessionId={() => FOREGROUND_STORED_ID}
|
||||
getRouteToken={() => `/${FOREGROUND_STORED_ID}::`}
|
||||
getRuntimeIdForStoredSession={storedId => runtimeIdByStoredSessionIdRef.current.get(storedId) ?? null}
|
||||
onReady={h => (handle = h)}
|
||||
onUpdateState={(sessionId, storedSessionId) => stateWrites.push({ sessionId, storedSessionId })}
|
||||
refreshSessions={async () => undefined}
|
||||
requestGateway={requestGateway}
|
||||
resumeStoredSession={resumeStoredSession}
|
||||
runtimeIdByStoredSessionIdRef={runtimeIdByStoredSessionIdRef}
|
||||
selectedStoredSessionIdRef={selectedStoredSessionIdRef}
|
||||
storedSessionId={FOREGROUND_STORED_ID}
|
||||
/>
|
||||
)
|
||||
await waitFor(() => expect(handle).not.toBeNull())
|
||||
|
||||
expect(
|
||||
await handle!.submitText('queued prompt for C', {
|
||||
fromQueue: true,
|
||||
sessionId: null,
|
||||
storedSessionId: QUEUED_STORED_ID
|
||||
})
|
||||
).toBe(true)
|
||||
expect(resumeStoredSession).not.toHaveBeenCalled()
|
||||
expect(calls).toEqual([
|
||||
{
|
||||
method: 'session.resume',
|
||||
params: { session_id: QUEUED_STORED_ID, source: 'desktop', omit_messages: true }
|
||||
},
|
||||
{
|
||||
method: 'prompt.submit',
|
||||
params: { session_id: QUEUED_RUNTIME_ID, text: 'queued prompt for C', queued: true }
|
||||
}
|
||||
])
|
||||
expect(stateWrites.some(write => write.sessionId === FOREGROUND_RUNTIME_ID)).toBe(false)
|
||||
expect(selectedStoredSessionIdRef.current).toBe(FOREGROUND_STORED_ID)
|
||||
expect(activeSessionIdRef.current).toBe(FOREGROUND_RUNTIME_ID)
|
||||
expect(runtimeIdByStoredSessionIdRef.current).toEqual(new Map([[FOREGROUND_STORED_ID, FOREGROUND_RUNTIME_ID]]))
|
||||
})
|
||||
|
||||
it('still aborts when the user genuinely moves to a different chat mid-submit', async () => {
|
||||
// The churn tolerance must not weaken the real guard: selection AND route
|
||||
// moving to another actual chat is a user switch and must abort.
|
||||
|
||||
@@ -527,7 +527,7 @@ export function useSubmitPrompt(deps: SubmitPromptDeps) {
|
||||
scope.setMessages(current => [...current, buildUserMessage()])
|
||||
}
|
||||
|
||||
if (!sessionId && routedStoredSessionId && routedSessionNeedsResume) {
|
||||
if (!options?.storedSessionId && !sessionId && routedStoredSessionId && routedSessionNeedsResume) {
|
||||
// The URL still names a durable conversation, but a profile
|
||||
// swap/reconnect left its volatile session binding incomplete or
|
||||
// cross-wired. Run the full profile-aware resume path. Creating here
|
||||
|
||||
Reference in New Issue
Block a user