mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-18 13:23:33 +08:00
fix: prevent idle tabs showing active during prewarm (#681)
Desktop reconnects can attach a normal output callback while a session prewarm is still pending. CLI startup status then reaches the renderer and marks restored idle tabs as running even though no user turn is active. Treat pending prewarm like completed prewarm and bind metadata-only output until a real user turn claims the session. Constraint: Prewarm still needs init and slash-command metadata without streaming UI status.\nRejected: Filter status in TabBar | leaves other clients and sidebar state exposed to the stale activity signal.\nConfidence: high\nScope-risk: narrow\nTested: bun test src/server/__tests__/websocket-handler.test.ts\nTested: bun test src/server/__tests__/conversations.test.ts -t prewarm\nTested: bun run check:server\nNot-tested: Manual desktop restart visual smoke
This commit is contained in:
parent
e9e4f3e1e5
commit
d075276a17
@ -1,6 +1,7 @@
|
||||
import { afterEach, describe, expect, it, mock, spyOn } from 'bun:test'
|
||||
import type { ServerWebSocket } from 'bun'
|
||||
import {
|
||||
__markPrewarmPendingForTests,
|
||||
__resetWebSocketHandlerStateForTests,
|
||||
closeSessionConnection,
|
||||
getActiveSessionIds,
|
||||
@ -135,4 +136,30 @@ describe('WebSocket handler session isolation', () => {
|
||||
expect(setTimeoutSpy).toHaveBeenCalled()
|
||||
expect(setTimeoutSpy.mock.calls[0]?.[1]).toBeGreaterThan(30_000)
|
||||
})
|
||||
|
||||
it('does not forward prewarm startup status to a reconnecting client', async () => {
|
||||
const sessionId = `prewarm-reconnect-${crypto.randomUUID()}`
|
||||
const second = makeClientSocket(sessionId)
|
||||
let outputCallback: ((cliMsg: any) => void) | null = null
|
||||
|
||||
__markPrewarmPendingForTests(sessionId)
|
||||
spyOn(conversationService, 'hasSession').mockReturnValue(true)
|
||||
spyOn(conversationService, 'getRecentSdkMessages').mockReturnValue([])
|
||||
spyOn(conversationService, 'onOutput').mockImplementation((_sid, callback) => {
|
||||
outputCallback = callback
|
||||
})
|
||||
spyOn(conversationService, 'removeOutputCallback').mockImplementation(() => {})
|
||||
spyOn(conversationService, 'clearOutputCallbacks').mockImplementation(() => {
|
||||
outputCallback = null
|
||||
})
|
||||
|
||||
handleWebSocket.open(second)
|
||||
outputCallback?.({
|
||||
type: 'stream_event',
|
||||
event: { type: 'message_start' },
|
||||
})
|
||||
|
||||
const secondMessages = second.sent.map((payload) => JSON.parse(payload))
|
||||
expect(secondMessages).not.toContainEqual({ type: 'status', state: 'thinking' })
|
||||
})
|
||||
})
|
||||
|
||||
@ -150,7 +150,7 @@ export const handleWebSocket = {
|
||||
}
|
||||
|
||||
addActiveClient(sessionId, ws)
|
||||
if (prewarmedSessions.has(sessionId)) {
|
||||
if (prewarmPendingSessions.has(sessionId) || prewarmedSessions.has(sessionId)) {
|
||||
bindPrewarmMetadataCapture(sessionId)
|
||||
} else {
|
||||
bindClientSessionOutput(sessionId, ws)
|
||||
@ -2235,5 +2235,11 @@ export function __resetWebSocketHandlerStateForTests(): void {
|
||||
activeSessions.clear()
|
||||
clientOutputCallbacks.clear()
|
||||
sessionCleanupTimers.clear()
|
||||
prewarmPendingSessions.clear()
|
||||
prewarmedSessions.clear()
|
||||
prewarmIdleTimers.clear()
|
||||
}
|
||||
|
||||
export function __markPrewarmPendingForTests(sessionId: string): void {
|
||||
prewarmPendingSessions.add(sessionId)
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user