fix: prevent send queue flicker when the agent is idle

Claim idle conversation inputs before publishing queue changes so immediately dispatched messages do not briefly appear as pending. Preserve busy conversation queueing and add a regression assertion for notification ordering.

Validation: 369 focused tests, typecheck, and changed-file lint passed. Full-suite runs timed out with failures in other modules; repository lint reports errors in the existing story-graph demo.

Release note: Fix a brief pending-queue flash when sending a message while the agent is idle.
This commit is contained in:
mesalogo
2026-09-16 16:35:13 +08:00
parent 7e11d1099a
commit cc9ea7c718
2 changed files with 17 additions and 10 deletions
+14 -6
View File
@@ -7342,7 +7342,7 @@ describe('registerIpcHandlers agent terminal state', () => {
await harness.dispose() await harness.dispose()
}) })
it('dispatches and accepts a queued user message through the renderer', async () => { it('claims an idle user message before notifying the renderer and accepts its dispatch', async () => {
const runtime = { const runtime = {
runtimeId: 'model', runtimeId: 'model',
capability: 'chat', capability: 'chat',
@@ -7406,14 +7406,22 @@ describe('registerIpcHandlers agent terminal state', () => {
const enqueueHandler = electronMocks.handlers.get( const enqueueHandler = electronMocks.handlers.get(
ipcChannels.conversationQueueEnqueueUser ipcChannels.conversationQueueEnqueueUser
) )
harness.webContents.send.mockClear()
expect( expect(
enqueueHandler?.(trustedEvent(harness.webContents), input) enqueueHandler?.(trustedEvent(harness.webContents), input)
).toEqual(item) ).toEqual(item)
await vi.waitFor(() => expect(harness.webContents.send).toHaveBeenCalledWith(
expect(harness.webContents.send).toHaveBeenCalledWith( ipcChannels.conversationQueueDispatch,
ipcChannels.conversationQueueDispatch, { item, input }
{ item, input } )
) const queueChangeIndex = harness.webContents.send.mock.calls.findIndex(
([channel]) => channel === ipcChannels.conversationQueueChanged
)
expect(queueChangeIndex).toBeGreaterThanOrEqual(0)
expect(
harness.assistantDatabase.claimConversationQueueItem.mock.invocationCallOrder[0]
).toBeLessThan(
harness.webContents.send.mock.invocationCallOrder[queueChangeIndex] ?? 0
) )
await harness.handler?.(trustedEvent(harness.webContents), { await harness.handler?.(trustedEvent(harness.webContents), {
+3 -4
View File
@@ -6947,13 +6947,12 @@ export function registerIpcHandlers(
contextManager.remove(attachment.id) contextManager.remove(attachment.id)
} }
rendererReadyConversationQueues.add(item.conversationId) rendererReadyConversationQueues.add(item.conversationId)
publishConversationQueueChange(item.conversationId)
if (!isConversationExecuting(item.conversationId)) { if (!isConversationExecuting(item.conversationId)) {
readyConversationQueues.add(item.conversationId) readyConversationQueues.add(item.conversationId)
setTimeout(() => { // Claim idle sends before exposing pending items to the renderer.
void pumpConversationQueue(item.conversationId) void pumpConversationQueue(item.conversationId)
}, 0)
} }
publishConversationQueueChange(item.conversationId)
return item return item
} }
) )