mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-21 00:02:30 +00:00
fix(ai-sessions): release the staged prompt when a send is refused
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
d441f3c5a3
commit
ad8fdb24bf
@@ -2005,6 +2005,10 @@ export class AIChatManager {
|
||||
options.files
|
||||
)
|
||||
}
|
||||
// Handed off, so this manager no longer holds it. Only sendRequestImpl clears
|
||||
// the field and a refusal never reaches it; left set, it reads to the
|
||||
// auto-resume guard and the sidebar's draft cue as text the user is writing.
|
||||
this.instructions = ''
|
||||
}
|
||||
|
||||
/** Send the queued message, if there is one, as its own turn. The queue only
|
||||
|
||||
@@ -265,9 +265,10 @@ describe('AIChatManager cross-tab run guard', () => {
|
||||
expect(mocks.runChatLoop).not.toHaveBeenCalled()
|
||||
expect(manager.displayMessages).toHaveLength(2)
|
||||
expect(manager.messages).toHaveLength(2)
|
||||
// The prompt is handed back rather than lost, so the retry is still the
|
||||
// user's to run once the other tab finishes.
|
||||
expect(manager.instructions).toBe('first')
|
||||
// Handed back rather than lost — and off the manager, which reports a
|
||||
// non-empty `instructions` to the sidebar as text the user is writing.
|
||||
expect(manager.queuedMessage).toBe('first')
|
||||
expect(manager.instructions).toBe('')
|
||||
})
|
||||
|
||||
// A turn flushes its queued message by re-entering sendRequest, and the lock
|
||||
|
||||
@@ -187,8 +187,8 @@ export async function withSessionRunLock<T>(
|
||||
}
|
||||
}
|
||||
|
||||
/** What exclusion amounts to with no lock to take: refuse while another tab's run
|
||||
* is visibly on screen, and otherwise go.
|
||||
/** What exclusion amounts to with no lock to take: nothing beyond the refusal
|
||||
* `withSessionRunLock` already made on a visibly running turn.
|
||||
*
|
||||
* Not mutual exclusion, and cannot be made into it — a throttled hidden driver
|
||||
* goes silent before its turn ends and is reaped as dead, and no probe tells a
|
||||
|
||||
@@ -278,8 +278,6 @@ describe('sessionState IndexedDB persistence', () => {
|
||||
await vi.waitFor(() => expect(sessionState.sessions.map((s) => s.id)).toEqual(['keep']))
|
||||
})
|
||||
|
||||
// A mirrored delete that announced itself would be mirrored straight back, and
|
||||
// the two tabs would trade the same message and transaction without end.
|
||||
it('removes a mirrored delete without announcing it', async () => {
|
||||
const user = freshUser()
|
||||
await login(user)
|
||||
@@ -289,8 +287,7 @@ describe('sessionState IndexedDB persistence', () => {
|
||||
await deleteSessionRecord('mirrored', false)
|
||||
expect(deleteBroadcasts).toEqual([])
|
||||
|
||||
// Still genuinely removed — silence is not a no-op, it is what collects the
|
||||
// row a write racing the other tab's delete left behind.
|
||||
// Silence is not a no-op: the row still goes.
|
||||
await rehydrate(user)
|
||||
await vi.waitFor(() => expect(sessionState.sessions).toEqual([]))
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user