import { describe, expect, it } from 'vitest'; import { buildTaskUiModel } from '@/lib/task-ui-model'; import type { BackendTask, ProcessEvent, ProcessRun, SessionProcessProjection, TaskTimelineCard } from '@/types'; function task(overrides: Partial = {}): BackendTask { return { task_id: 'task-1', session_id: 'web:default', description: 'Build MGM report', goal: 'Build MGM report', constraints: [], priority: 0, status: 'awaiting_acceptance', creator: 'user', created_at: '2026-06-24T00:00:00.000Z', updated_at: '2026-06-24T00:01:00.000Z', run_ids: ['main-run'], skill_names: ['mgm-galaxy-financial-chart-report-safe'], feedback: [], metadata: {}, ...overrides, }; } function process(overrides: Partial = {}): SessionProcessProjection { return { runs: [], events: [], artifacts: [], ...overrides, }; } describe('buildTaskUiModel', () => { it('keeps single-agent tasks out of the Agent Team tree and summarizes tool results', () => { const cards: TaskTimelineCard[] = [ { id: 'tool-start', taskId: 'task-1', runId: 'main-run', type: 'tool_call', title: 'Calling tool: web_search', actorName: 'web_search', status: 'running', createdAt: '2026-06-24T00:00:10.000Z', details: { tool_name: 'web_search', tool_call_id: 'call-1', arguments: '{"query":"MGM China annual results"}', }, }, { id: 'tool-result', taskId: 'task-1', runId: 'main-run', type: 'tool_result', title: 'Tool result: web_search', actorName: 'web_search', status: 'done', createdAt: '2026-06-24T00:00:20.000Z', summary: '{"success":true,"query":"MGM China annual results","quality":"low","results":[{"title":"bad"}]}', details: { tool_name: 'web_search', tool_call_id: 'call-1', result_summary: '{"success":true,"query":"MGM China annual results","quality":"low","results":[{"title":"bad"}]}', }, }, ]; const model = buildTaskUiModel({ task: task(), process: process({ runs: [ { run_id: 'main-run', parent_run_id: 'task:task-1:attempt:1', session_id: 'web:default', actor_type: 'agent', actor_id: 'main-agent', actor_name: 'Main Agent', title: 'Final synthesis', source: 'task_synthesis', status: 'done', started_at: '2026-06-24T00:00:00.000Z', }, ], }), cards, locale: 'zh-Hans', }); expect(model.executionMode).toBe('single'); expect(model.team.hasTeam).toBe(false); expect(model.agentTree).toEqual([]); expect(model.tools).toHaveLength(1); expect(model.tools[0].status).toBe('done'); expect(model.tools[0].quality).toBe('low'); expect(model.tools[0].summary).toContain('MGM China annual results'); }); it('models real task team nodes separately from the main agent', () => { const cards: TaskTimelineCard[] = [ { id: 'team', taskId: 'task-1', runId: 'task:task-1:attempt:1', type: 'agent_team', title: 'Agent Team', actorName: 'Task Team', status: 'error', createdAt: '2026-06-24T00:00:10.000Z', details: { task_outcome: 'incomplete', node_ids: ['collect', 'report'], incomplete_node_ids: ['collect'], }, }, ]; const runs: ProcessRun[] = [ { run_id: 'main-run', parent_run_id: 'task:task-1:attempt:1', session_id: 'web:default', actor_type: 'agent', actor_id: 'main-agent', actor_name: 'Main Agent', title: 'Final synthesis', source: 'task_synthesis', status: 'done', started_at: '2026-06-24T00:00:00.000Z', }, { run_id: 'collect-run', parent_run_id: 'task:task-1:attempt:1', session_id: 'web:default:team:collect', actor_type: 'agent', actor_id: 'collect', actor_name: 'collect', title: 'collect', source: 'task_team', status: 'error', started_at: '2026-06-24T00:00:10.000Z', metadata: { node_id: 'collect' }, }, ]; const model = buildTaskUiModel({ task: task(), process: process({ runs, events: [] as ProcessEvent[] }), cards, locale: 'zh-Hans', }); expect(model.executionMode).toBe('team'); expect(model.team.hasTeam).toBe(true); expect(model.team.outcome).toBe('incomplete'); expect(model.agentTree.map((node) => node.name)).toEqual(['collect']); }); it('groups tools and result versions by task attempt', () => { const cards: TaskTimelineCard[] = [ { id: 'run-1-tool-call', taskId: 'task-1', runId: 'main-run-1', type: 'tool_call', title: 'Calling tool: web_fetch', actorName: 'web_fetch', status: 'running', createdAt: '2026-06-24T00:00:10.000Z', details: { tool_name: 'web_fetch', tool_call_id: 'call-1' }, }, { id: 'run-1-tool-result', taskId: 'task-1', runId: 'main-run-1', type: 'tool_result', title: 'Tool result: web_fetch', actorName: 'web_fetch', status: 'error', createdAt: '2026-06-24T00:00:15.000Z', details: { tool_name: 'web_fetch', tool_call_id: 'call-1' }, }, { id: 'run-1-result', taskId: 'task-1', runId: 'main-run-1', type: 'result', title: 'Result ready', actorName: 'Main Agent', status: 'error', createdAt: '2026-06-24T00:00:20.000Z', summary: 'First result', }, { id: 'run-2-tool-call', taskId: 'task-1', runId: 'main-run-2', type: 'tool_call', title: 'Calling tool: web_search', actorName: 'web_search', status: 'running', createdAt: '2026-06-24T00:02:10.000Z', details: { tool_name: 'web_search', tool_call_id: 'call-2' }, }, { id: 'run-2-tool-result', taskId: 'task-1', runId: 'main-run-2', type: 'tool_result', title: 'Tool result: web_search', actorName: 'web_search', status: 'done', createdAt: '2026-06-24T00:02:30.000Z', details: { tool_name: 'web_search', tool_call_id: 'call-2' }, }, { id: 'run-2-result', taskId: 'task-1', runId: 'main-run-2', type: 'result', title: 'Result ready', actorName: 'Main Agent', status: 'done', createdAt: '2026-06-24T00:03:00.000Z', summary: 'Second result', }, ]; const runs: ProcessRun[] = [ { run_id: 'task:task-1:attempt:1', session_id: 'web:default', actor_type: 'system', actor_id: 'task', actor_name: 'Task Planner', title: 'single plan', source: 'task_mode', status: 'error', started_at: '2026-06-24T00:00:00.000Z', metadata: { attempt_index: 1 }, }, { run_id: 'main-run-1', parent_run_id: 'task:task-1:attempt:1', session_id: 'web:default', actor_type: 'agent', actor_id: 'main-agent', actor_name: 'Main Agent', title: 'Final synthesis', source: 'task_synthesis', status: 'error', started_at: '2026-06-24T00:00:05.000Z', finished_at: '2026-06-24T00:00:20.000Z', metadata: { attempt_index: 1 }, }, { run_id: 'task:task-1:attempt:2', session_id: 'web:default', actor_type: 'system', actor_id: 'task', actor_name: 'Task Planner', title: 'single plan', source: 'task_mode', status: 'done', started_at: '2026-06-24T00:02:00.000Z', finished_at: '2026-06-24T00:03:00.000Z', metadata: { attempt_index: 2 }, }, { run_id: 'main-run-2', parent_run_id: 'task:task-1:attempt:2', session_id: 'web:default', actor_type: 'agent', actor_id: 'main-agent', actor_name: 'Main Agent', title: 'Final synthesis', source: 'task_synthesis', status: 'done', started_at: '2026-06-24T00:02:05.000Z', finished_at: '2026-06-24T00:03:00.000Z', metadata: { attempt_index: 2 }, }, ]; const model = buildTaskUiModel({ task: task({ run_ids: ['main-run-1', 'main-run-2'] }), process: process({ runs }), cards, locale: 'zh-Hans', }); expect(model.tools).toHaveLength(2); expect(model.attempts).toHaveLength(2); expect(model.attempts[0].index).toBe(1); expect(model.attempts[0].tools.map((tool) => tool.toolName)).toEqual(['web_fetch']); expect(model.attempts[0].result?.summary).toBe('First result'); expect(model.attempts[1].index).toBe(2); expect(model.attempts[1].tools.map((tool) => tool.toolName)).toEqual(['web_search']); expect(model.attempts[1].result?.summary).toBe('Second result'); }); });