feat: update desktop workflows and app center
This commit is contained in:
@@ -6,6 +6,9 @@ const chatStateMock = vi.hoisted(() => ({
|
||||
state: {
|
||||
currentSessionKey: 'session-1',
|
||||
sessions: [] as Array<{ key: string }>,
|
||||
messages: [] as Array<{ id?: string; role: string; content: unknown; timestamp?: number }>,
|
||||
sessionLabels: {} as Record<string, string>,
|
||||
sessionLastActivity: {} as Record<string, number>,
|
||||
sending: true,
|
||||
activeRunId: 'run-1' as string | null,
|
||||
pendingFinal: true,
|
||||
@@ -28,8 +31,8 @@ vi.mock('@/lib/host-events', () => ({
|
||||
vi.mock('@/stores/chat', () => ({
|
||||
useChatStore: {
|
||||
getState: () => chatStateMock.state,
|
||||
setState: (patch: Record<string, unknown>) => {
|
||||
Object.assign(chatStateMock.state, patch);
|
||||
setState: (patch: Record<string, unknown> | ((state: typeof chatStateMock.state) => Record<string, unknown>)) => {
|
||||
Object.assign(chatStateMock.state, typeof patch === 'function' ? patch(chatStateMock.state) : patch);
|
||||
},
|
||||
},
|
||||
}));
|
||||
@@ -41,6 +44,9 @@ describe('gateway store event wiring', () => {
|
||||
Object.assign(chatStateMock.state, {
|
||||
currentSessionKey: 'session-1',
|
||||
sessions: [],
|
||||
messages: [],
|
||||
sessionLabels: {},
|
||||
sessionLastActivity: {},
|
||||
sending: true,
|
||||
activeRunId: 'run-1',
|
||||
pendingFinal: true,
|
||||
@@ -138,6 +144,120 @@ describe('gateway store event wiring', () => {
|
||||
expect(chatStateMock.state.pendingFinal).toBe(true);
|
||||
});
|
||||
|
||||
it('makes newly started background sessions visible before sessions.list refreshes', async () => {
|
||||
const nowSpy = vi.spyOn(Date, 'now').mockReturnValue(1773300000000);
|
||||
hostApiFetchMock.mockResolvedValueOnce({ state: 'running', port: 18789 });
|
||||
|
||||
const handlers = new Map<string, (payload: unknown) => void>();
|
||||
subscribeHostEventMock.mockImplementation((eventName: string, handler: (payload: unknown) => void) => {
|
||||
handlers.set(eventName, handler);
|
||||
return () => {};
|
||||
});
|
||||
|
||||
const { useGatewayStore } = await import('@/stores/gateway');
|
||||
await useGatewayStore.getState().init();
|
||||
|
||||
handlers.get('gateway:notification')?.({
|
||||
method: 'agent',
|
||||
params: {
|
||||
phase: 'started',
|
||||
runId: 'run-cron-1',
|
||||
sessionKey: 'agent:main:cron:job-1',
|
||||
},
|
||||
});
|
||||
|
||||
await vi.waitFor(() => {
|
||||
expect(chatStateMock.state.sessions).toContainEqual(expect.objectContaining({
|
||||
key: 'agent:main:cron:job-1',
|
||||
updatedAt: 1773300000000,
|
||||
}));
|
||||
});
|
||||
expect(chatStateMock.state.sessionLastActivity['agent:main:cron:job-1']).toBe(1773300000000);
|
||||
nowSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('folds isolated cron run events back into the fixed task conversation', async () => {
|
||||
const nowSpy = vi.spyOn(Date, 'now').mockReturnValue(1773300000000);
|
||||
hostApiFetchMock.mockResolvedValueOnce({ state: 'running', port: 18789 });
|
||||
Object.assign(chatStateMock.state, {
|
||||
currentSessionKey: 'agent:main:cron:job-1',
|
||||
activeRunId: null,
|
||||
activeRunSessionKey: 'agent:main:cron:job-1',
|
||||
sending: true,
|
||||
});
|
||||
|
||||
const handlers = new Map<string, (payload: unknown) => void>();
|
||||
subscribeHostEventMock.mockImplementation((eventName: string, handler: (payload: unknown) => void) => {
|
||||
handlers.set(eventName, handler);
|
||||
return () => {};
|
||||
});
|
||||
|
||||
const { useGatewayStore } = await import('@/stores/gateway');
|
||||
const { useCronStore } = await import('@/stores/cron');
|
||||
useCronStore.setState({
|
||||
jobs: [{
|
||||
id: 'job-1',
|
||||
name: '每日经营日报',
|
||||
message: '生成日报',
|
||||
schedule: '0 9 * * *',
|
||||
enabled: true,
|
||||
createdAt: '2026-05-13T00:00:00.000Z',
|
||||
updatedAt: '2026-05-13T00:00:00.000Z',
|
||||
agentId: 'main',
|
||||
}],
|
||||
loading: false,
|
||||
error: null,
|
||||
});
|
||||
await useGatewayStore.getState().init();
|
||||
|
||||
handlers.get('gateway:notification')?.({
|
||||
method: 'agent',
|
||||
params: {
|
||||
phase: 'started',
|
||||
runId: 'run-cron-1',
|
||||
sessionKey: 'agent:main:cron:job-1:run:session-a',
|
||||
},
|
||||
});
|
||||
|
||||
await vi.waitFor(() => {
|
||||
expect(chatStateMock.state.sessions).toContainEqual(expect.objectContaining({
|
||||
key: 'agent:main:cron:job-1',
|
||||
updatedAt: 1773300000000,
|
||||
}));
|
||||
});
|
||||
expect(chatStateMock.state.sessions.find((session) => session.key === 'agent:main:cron:job-1:run:session-a')).toBeUndefined();
|
||||
expect(chatStateMock.state.handleChatEvent).toHaveBeenCalledWith(expect.objectContaining({
|
||||
state: 'started',
|
||||
runId: 'run-cron-1',
|
||||
sessionKey: 'agent:main:cron:job-1',
|
||||
}));
|
||||
expect(chatStateMock.state.messages).toContainEqual(expect.objectContaining({
|
||||
role: 'user',
|
||||
content: '生成日报',
|
||||
timestamp: 1773300000000,
|
||||
}));
|
||||
expect(chatStateMock.state.messages).toContainEqual(expect.objectContaining({
|
||||
role: 'assistant',
|
||||
content: '任务已开始执行,完成后会自动在这里显示结果。',
|
||||
}));
|
||||
|
||||
handlers.get('gateway:notification')?.({
|
||||
method: 'agent',
|
||||
params: {
|
||||
phase: 'completed',
|
||||
runId: 'run-cron-1',
|
||||
sessionKey: 'agent:main:cron:job-1:run:session-a',
|
||||
},
|
||||
});
|
||||
|
||||
await vi.waitFor(() => {
|
||||
expect(chatStateMock.state.loadHistory).toHaveBeenCalledWith(false);
|
||||
});
|
||||
expect(chatStateMock.state.sending).toBe(false);
|
||||
expect(chatStateMock.state.activeRunSessionKey).toBeNull();
|
||||
nowSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('clears sending state on terminal agent completion notifications', async () => {
|
||||
hostApiFetchMock.mockResolvedValueOnce({ state: 'running', port: 18789 });
|
||||
|
||||
|
||||
Reference in New Issue
Block a user