fix(phase2): restore agent streams and persist extension installations

This commit is contained in:
2026-09-06 02:21:27 +08:00
parent 670f4c076b
commit 9e8603d010
32 changed files with 815 additions and 39 deletions
+43
View File
@@ -0,0 +1,43 @@
// @vitest-environment happy-dom
import { afterEach, beforeEach, expect, it, vi } from 'vitest'
import { createPinia, setActivePinia, disposePinia } from 'pinia'
import { useAgentStore } from './agent'
const mock = vi.hoisted(() => ({ stream: vi.fn(), get: vi.fn(), cancel: vi.fn() }))
vi.mock('@/services/agentService', () => ({ streamAgentEvents: mock.stream, getAgentRun: mock.get, cancelAgentRun: mock.cancel }))
let pinia: ReturnType<typeof createPinia>
beforeEach(() => { vi.useFakeTimers(); vi.clearAllMocks(); pinia = createPinia(); setActivePinia(pinia); mock.stream.mockReturnValue({ cancel: vi.fn() }); mock.get.mockImplementation(async id => ({ run_id: id, status: 'running' })) })
afterEach(() => { disposePinia(pinia); vi.useRealTimers() })
const handler = () => mock.stream.mock.calls.at(-1)![1]
const event = (sequence: number, name = 'RunStarted') => ({ run_id: 'r', sequence, event: name, data: {}, timestamp: 'now' })
it.each(['error', 'eof'])('retains cancellation and resumes after sequence on %s', async kind => {
const store = useAgentStore(); await store.loadRun('r')
handler().onEvent(event(5))
kind === 'error' ? handler().onError(new Error('offline')) : handler().onDone()
expect(store.isRunning).toBe(true)
await vi.advanceTimersByTimeAsync(1000)
expect(mock.stream.mock.calls.at(-1)![2]).toBe(5)
handler().onEvent(event(5)); handler().onEvent(event(6, 'RunCompleted')); handler().onDone()
expect(store.events).toHaveLength(2)
expect(store.isRunning).toBe(false)
await vi.advanceTimersByTimeAsync(40000)
expect(mock.stream).toHaveBeenCalledTimes(2)
})
it('invalidates old streams and pending retry when changing run or cancelling', async () => {
const store = useAgentStore(); await store.loadRun('r')
const old = handler(); old.onError(new Error('offline'))
await store.loadRun('s'); old.onEvent(event(8))
expect(store.events).toHaveLength(0)
handler().onError(new Error('offline')); await store.cancelRun('s')
await vi.advanceTimersByTimeAsync(40000)
expect(mock.stream).toHaveBeenCalledTimes(2)
})
it('bounds retry attempts and supports explicit retry without losing events', async () => {
const store = useAgentStore(); await store.loadRun('r')
handler().onEvent(event(1))
for (let attempt = 0; attempt < 6; attempt++) { handler().onError(new Error('offline')); await vi.advanceTimersByTimeAsync(16000) }
expect(mock.stream).toHaveBeenCalledTimes(6)
expect(store.connectionState).toBe('disconnected')
expect(store.isRunning).toBe(true)
store.reconnect()
expect(mock.stream.mock.calls.at(-1)![2]).toBe(1)
})