import { describe, expect, it } from 'vitest'; import { createGameCreationAppSeedTasks, GAME_CREATION_AGENT_RUN_SCHEMA_VERSION, type GameCreationAgentRunTrace, } from '../../../packages/shared/src/contracts/gameCreationApp'; import { summarizeAgentRunTrace } from '../src/App'; describe('AI 游戏创作 Agent loop 摘要', () => { it('shows repair loop state in the chat trace summary', () => { const tasks = createGameCreationAppSeedTasks(); tasks[0]!.status = 'completed'; tasks[1]!.status = 'completed'; const trace: GameCreationAgentRunTrace = { schemaVersion: GAME_CREATION_AGENT_RUN_SCHEMA_VERSION, runId: 'run-test', commandId: 'game.generate_draft', status: 'needs-revision', lifecycleStatus: 'pending', passes: 2, maxPasses: 3, toolCallCount: 12, maxToolCalls: 128, stopReason: 'max-passes-exhausted', goal: '做一个弹幕厨房游戏', coordination: 'Planner -> Orchestrator -> Generator -> Evaluator', steps: [ ...Array.from({ length: 7 }, (_, index) => ({ pass: 1, agent: `LLM-${index + 1}`, phase: 'llm', taskId: null, group: null, role: null, status: 'completed', inputPaths: [], outputPaths: [], summary: `LLM 调用 ${index + 1}`, toolCalls: [ { toolId: `llm.call.${index + 1}`, status: 'ok', inputPaths: [], outputPaths: [], summary: `LLM 工具 ${index + 1}`, }, ], })), { pass: 2, agent: 'Orchestrator', phase: 'plan', taskId: 'code-director', group: 'code', role: 'Code', status: 'completed', inputPaths: ['.agent/findings.md'], outputPaths: ['.agent/passes/pass-2/task-graph.json'], summary: '重跑程序链路及下游发布包装', toolCalls: [], }, ...Array.from({ length: 4 }, (_, index) => ({ pass: 2, agent: `Bridge-${index + 1}`, phase: 'handoff', taskId: null, group: null, role: null, status: 'completed', inputPaths: [], outputPaths: [], summary: `中间步骤 ${index + 1}`, toolCalls: [], })), { pass: 2, agent: '美术组 / Asset', phase: 'role-brief', taskId: 'art-asset-plan', group: 'art', role: 'Asset', status: 'completed', inputPaths: ['.agent/manifest.json'], outputPaths: ['.agent/passes/pass-2/groups/art/asset.md'], summary: '需要回流画板角色素材', toolCalls: [ { toolId: 'agent.role.brief.art.asset', status: 'completed', inputPaths: ['.agent/manifest.json'], outputPaths: ['.agent/passes/pass-2/groups/art/asset.md'], summary: '规划角色资产', }, { toolId: 'agent.tool.suggest.canvas.project_sync', status: 'suggested', inputPaths: ['.agent/manifest.json'], outputPaths: [], summary: '项目还没有画板回流资产;建议用户确认 /sync-canvas-project <画板项目ID>。', }, ...Array.from({ length: 5 }, (_, index) => ({ toolId: `agent.tool.suggest.extra.${index + 1}`, status: 'suggested', inputPaths: ['.agent/manifest.json'], outputPaths: [], summary: `额外建议命令 ${index + 1}`, })), ], }, ], artifacts: [ { path: '.agent/older-artifact.json', sizeBytes: 64, checksum: 'fnv1a64:older', }, { path: '.agent/passes/pass-2/task-graph.json', sizeBytes: 128, checksum: 'fnv1a64:test', }, ...Array.from({ length: 4 }, (_, index) => ({ path: `.agent/passes/pass-2/artifact-${index + 1}.json`, sizeBytes: 128 + index, checksum: `fnv1a64:artifact-${index + 1}`, })), ], taskGraph: { goal: '做一个弹幕厨房游戏', readyTaskIds: ['code-director'], activeTaskIds: ['code-director', 'quality-review', 'publish-package'], carriedTaskIds: ['design-director'], repairFocus: ['gameHtml 缺少 canvas'], repairRoutes: [ { issue: 'gameHtml 缺少 canvas', taskIds: [ 'code-director', 'quality-review', 'preview-readiness', 'publish-package', ], reason: 'code-repair+dependency-impact', }, { issue: '缺少输入绑定', taskIds: ['code-director'], reason: 'input-binding', }, { issue: '缺少胜负条件', taskIds: ['quality-review'], reason: 'win-condition', }, { issue: '缺少发布说明', taskIds: ['publish-package'], reason: 'publish-readme', }, ], tasks, }, passPlans: [ { pass: 1, mode: 'initial', summary: '第 1 轮全量调度', activeTaskIds: ['design-director', 'code-director'], carriedTaskIds: [], dependencyWaves: [['design-director'], ['code-director']], repairFocus: [], repairRoutes: [], }, { pass: 2, mode: 'repair', summary: '第 2 轮重跑程序链路及下游发布包装', activeTaskIds: ['code-director', 'quality-review', 'publish-package'], carriedTaskIds: ['design-director'], dependencyWaves: [ ['code-director'], ['quality-review'], ['publish-package'], ], repairFocus: ['gameHtml 缺少 canvas'], repairRoutes: [ { issue: 'gameHtml 缺少 canvas', taskIds: [ 'code-director', 'quality-review', 'preview-readiness', 'publish-package', ], reason: 'code-repair+dependency-impact', }, { issue: '缺少输入绑定', taskIds: ['code-director'], reason: 'input-binding', }, { issue: '缺少胜负条件', taskIds: ['quality-review'], reason: 'win-condition', }, { issue: '缺少发布说明', taskIds: ['publish-package'], reason: 'publish-readme', }, ], }, { pass: 3, mode: 'repair', summary: '第 3 轮复核', activeTaskIds: ['quality-review'], carriedTaskIds: ['design-director'], dependencyWaves: [['quality-review']], repairFocus: [], repairRoutes: [], }, { pass: 4, mode: 'repair', summary: '第 4 轮收尾', activeTaskIds: ['publish-package'], carriedTaskIds: [], dependencyWaves: [['publish-package']], repairFocus: [], repairRoutes: [], }, ], nextStep: 'repair-next-pass', error: null, updatedAt: 1, }; const summary = summarizeAgentRunTrace(trace); expect(summary).toContain( 'needs-revision / pending · 2/3 轮 · max-passes-exhausted', ); expect(summary).toContain('工具调用:12/128'); expect(summary).toContain('任务:已完成 2'); expect(summary).toContain( 'active 任务:程序组 / Director 拆解程序实现(code-director), 程序组 / Review 执行质量评审(quality-review), 运营组 / Publish 整理发布包装(publish-package)', ); expect(summary).toContain( 'carry-over 任务:设计实现组 / Director 拆解创作方向(design-director)', ); expect(summary).toContain('返工焦点:gameHtml 缺少 canvas'); expect(summary).toContain( '返工路线:code-repair+dependency-impact: 程序组 / Director 拆解程序实现(code-director), 程序组 / Review 执行质量评审(quality-review), 程序组 / Preview 执行静态自检(preview-readiness), 运营组 / Publish 整理发布包装(publish-package)', ); expect(summary).toContain('还有 1 条路线'); expect(summary).not.toContain('publish-readme:'); expect(summary).toContain('建议命令:'); expect(summary).toContain('agent.tool.suggest.canvas.project_sync'); expect(summary).toContain('/sync-canvas-project <画板项目ID>'); expect(summary).toContain('agent.tool.suggest.extra.4'); expect(summary).not.toContain('agent.tool.suggest.extra.5'); expect(summary).toContain('还有 1 个建议命令'); expect(summary).toContain('编排轮次:'); expect(summary).toContain( 'pass 2 · repair · active 程序组 / Director 拆解程序实现(code-director), 程序组 / Review 执行质量评审(quality-review), 运营组 / Publish 整理发布包装(publish-package) · carry 设计实现组 / Director 拆解创作方向(design-director) · waves 程序组 / Director 拆解程序实现(code-director) / 程序组 / Review 执行质量评审(quality-review) / 运营组 / Publish 整理发布包装(publish-package) · repair gameHtml 缺少 canvas · routes code-repair+dependency-impact: 程序组 / Director 拆解程序实现(code-director), 程序组 / Review 执行质量评审(quality-review), 程序组 / Preview 执行静态自检(preview-readiness), 运营组 / Publish 整理发布包装(publish-package)', ); expect(summary).not.toContain('pass 1 · initial'); expect(summary).toContain('还有 1 个较早轮次'); expect(summary).toContain('.agent/passes/pass-2/task-graph.json'); expect(summary).not.toContain('.agent/older-artifact.json'); expect(summary).toContain('还有 1 个较早产物'); expect(summary).not.toContain('Orchestrator #2 · completed · plan'); expect(summary).toContain('Bridge-1 #2 · completed · handoff'); expect(summary).toContain('还有 8 个较早步骤'); expect(summary).toContain('LLM-2 #1 · completed · llm · llm.call.2'); expect(summary).not.toContain('LLM-1 #1 · completed · llm · llm.call.1'); expect(summary).toContain('还有 1 个较早 LLM 步骤'); }); });