refactor(client): keep pending interaction change UI-scoped
This commit is contained in:
@@ -76,9 +76,8 @@ describe('runtime client apply', () => {
|
||||
})
|
||||
await Promise.resolve()
|
||||
expect(workspaces.list.getSnapshot().items[0]?.workspaceId).toBe('w-new')
|
||||
// Mux and generation-lifecycle sinks route without throwing (manager semantics own the behavior).
|
||||
// Mux sink and onConnected route without throwing (manager semantics own the behavior).
|
||||
bench.sinks?.onMuxEnvelope?.({ rpcId: 'r2' as never, payload: { type: 'stream/error', message: 'x' } as never })
|
||||
bench.sinks?.onDisconnected?.()
|
||||
bench.sinks?.onConnected?.()
|
||||
})
|
||||
|
||||
|
||||
@@ -956,30 +956,19 @@ describe('pending-interaction list status', () => {
|
||||
expect(manager.getListSnapshot().items).toHaveLength(0)
|
||||
})
|
||||
|
||||
it('drops stale waits at generation death and preserves replay arriving before onConnected', async () => {
|
||||
const api = new FakeApiClient()
|
||||
api.onHistory = () => Promise.resolve(ok({
|
||||
events: entries(plainTurn(0, 0, 'a', 'b')) as never[],
|
||||
hasMore: false,
|
||||
modelTarget: { provider: 'deepseek-official', model: 'deepseek-chat' },
|
||||
}))
|
||||
const manager = new SessionManager(api)
|
||||
it('drops stale status at generation death before replay re-adds live interactions', () => {
|
||||
const manager = new SessionManager(new FakeApiClient())
|
||||
manager.handleHostEnvelope({ rpcId: 'h1' as never, payload: { type: 'host/session-added', sessionId: S1, blank: false } })
|
||||
const session = manager.get(S1)
|
||||
await session.open()
|
||||
manager.handleMuxEnvelope({ rpcId: 'ra' as never, payload: { type: 'approval/requested', sessionId: S1, approvalId: 'ap1' as never, toolName: 'rm' } })
|
||||
expect(manager.getListSnapshot().items[0]?.pendingInteraction).toBe('approval')
|
||||
expect(session.getSnapshot().pending).toMatchObject([{ key: 'a:ra' }])
|
||||
// Generation death clears (resolved-while-disconnected questions send no frame)…
|
||||
manager.handleDisconnected()
|
||||
expect(manager.getListSnapshot().items[0]?.pendingInteraction).toBeUndefined()
|
||||
expect(session.getSnapshot().pending).toEqual([])
|
||||
// …and a replayed frame arriving before onConnected (stream open precedes
|
||||
// the readiness handshake) survives the later resync untouched.
|
||||
// the readiness handshake) survives the later handleConnected untouched.
|
||||
manager.handleMuxEnvelope({ rpcId: 'ra' as never, payload: { type: 'approval/requested', sessionId: S1, approvalId: 'ap1' as never, toolName: 'rm' } })
|
||||
manager.handleConnected()
|
||||
expect(manager.getListSnapshot().items[0]?.pendingInteraction).toBe('approval')
|
||||
expect(session.getSnapshot().pending).toMatchObject([{ key: 'a:ra' }])
|
||||
})
|
||||
|
||||
it('generation death drops buffered answerable frames (a dead generation cannot be answered)', () => {
|
||||
|
||||
@@ -1098,19 +1098,16 @@ describe('remaining branches', () => {
|
||||
})
|
||||
|
||||
describe('resync', () => {
|
||||
it('rebuilds the window without clearing a fresh-generation wait; cold instances no-op', async () => {
|
||||
it('rebuilds the window and clears pending; cold instances no-op', async () => {
|
||||
const { api, session } = makeSession()
|
||||
api.onHistory = () => histResponse(plainTurn(0, 0, 'a', 'b'))
|
||||
await session.open()
|
||||
session.handleMuxEnvelope('ra' as never, { type: 'approval/requested', sessionId: SID, approvalId: 'ap1' as never, toolName: 'rm' })
|
||||
session.handleDisconnected()
|
||||
expect(session.getSnapshot().pending).toEqual([])
|
||||
session.handleMuxEnvelope('ra' as never, { type: 'approval/requested', sessionId: SID, approvalId: 'ap1' as never, toolName: 'rm' })
|
||||
api.onHistory = () => histResponse([...plainTurn(0, 0, 'a', 'b'), ...plainTurn(6, 1, 'c', 'd')])
|
||||
await session.resync()
|
||||
const snapshot = session.getSnapshot()
|
||||
expect(snapshot.openState).toBe('open')
|
||||
expect(snapshot.pending).toMatchObject([{ key: 'a:ra', kind: 'approval' }])
|
||||
expect(snapshot.pending).toEqual([]) // baseline replay re-sends still-pending frames
|
||||
expect(snapshot.nodes).toHaveLength(4)
|
||||
|
||||
const cold = makeSession()
|
||||
|
||||
Reference in New Issue
Block a user