diff --git a/apps/ai-game-creator-shell/src/App.tsx b/apps/ai-game-creator-shell/src/App.tsx index fc4bee85b..87683987d 100644 --- a/apps/ai-game-creator-shell/src/App.tsx +++ b/apps/ai-game-creator-shell/src/App.tsx @@ -833,6 +833,11 @@ export function App({ view: DesignView; } | null>(null); const [planningV2Reasoning, setPlanningV2Reasoning] = useState(''); + const designAgentReasoningTurnRef = useRef<{ + projectPath: string; + clientTurnId: string; + text: string; + } | null>(null); const planningV2TurnRef = useRef<{ projectPath: string; clientTurnId: string; @@ -1125,6 +1130,11 @@ export function App({ projectPath: nextProjectPath, clientTurnId, }; + designAgentReasoningTurnRef.current = { + projectPath: nextProjectPath, + clientTurnId, + text: '', + }; designAgentPendingViewRef.current = null; await designAgentEventSubscriptionReady(); setChatAgentBusy(true); @@ -1648,6 +1658,7 @@ export function App({ setPlanningV2Session(null); setPlanningV2TransientReplyTarget(''); designAgentPendingViewRef.current = null; + designAgentReasoningTurnRef.current = null; setPlanningV2Reasoning(''); setPlanningV2Active(planningStartMode); planningV2ActiveRef.current = planningStartMode; @@ -2108,7 +2119,15 @@ export function App({ setPlanningV2TransientReplyTarget(payload.text); } if (payload.reasoningText != null) { - setPlanningV2Reasoning(payload.reasoningText); + const reasoningTurn = designAgentReasoningTurnRef.current; + if ( + reasoningTurn && + reasoningTurn.projectPath === payload.projectPath && + reasoningTurn.clientTurnId === payload.clientTurnId + ) { + reasoningTurn.text = payload.reasoningText; + setPlanningV2Reasoning(payload.reasoningText); + } } if (payload.kind === 'tool' && payload.text) { setPlanningV2TransientReplyTarget(payload.text); @@ -11936,6 +11955,11 @@ export function App({ projectPath: nextProjectPath, clientTurnId, }; + designAgentReasoningTurnRef.current = { + projectPath: nextProjectPath, + clientTurnId, + text: '', + }; designAgentPendingViewRef.current = null; setPlanningV2TransientReplyTarget(''); setPlanningV2Reasoning(''); diff --git a/apps/ai-game-creator-shell/src/features/project-workspace/ProjectSupervisorView.tsx b/apps/ai-game-creator-shell/src/features/project-workspace/ProjectSupervisorView.tsx index ef8a6b119..f111ac332 100644 --- a/apps/ai-game-creator-shell/src/features/project-workspace/ProjectSupervisorView.tsx +++ b/apps/ai-game-creator-shell/src/features/project-workspace/ProjectSupervisorView.tsx @@ -263,8 +263,11 @@ export function ProjectSupervisorView({ ))} {designReasoning ? ( -
- 显示思考过程 +
+ 思考过程(点击展开)
{designReasoning}
) : null} diff --git a/apps/ai-game-creator-shell/tests/appSurface/design-agent.suite.ts b/apps/ai-game-creator-shell/tests/appSurface/design-agent.suite.ts index 5fa401401..3160d8756 100644 --- a/apps/ai-game-creator-shell/tests/appSurface/design-agent.suite.ts +++ b/apps/ai-game-creator-shell/tests/appSurface/design-agent.suite.ts @@ -7,6 +7,7 @@ import { React, render, screen, + setComposerText, waitFor, } from './harness'; @@ -55,6 +56,24 @@ function designClarificationView() { }; } +function designConversationView() { + return { + session: { + sessionId: 'design-session-reasoning', + projectId: 'local-project-draft', + currentPhase: 'concept', + approvedPhases: [], + pendingApproval: null, + pendingClarification: null, + turnIndex: 1, + lastError: null, + }, + messages: [], + running: false, + canRetry: false, + }; +} + export function registerDesignAgentSurfaceTests() { it('hydrates an existing design session and decides approval through design commands', async () => { const harness = createProjectSupervisorRuntimeHarness({ @@ -141,4 +160,54 @@ export function registerDesignAgentSurfaceTests() { ).toBe(true); }); }); + + it('keeps the current turn reasoning after completion and supports collapse/expand', async () => { + const harness = createProjectSupervisorRuntimeHarness({ + designAgentView: designConversationView(), + designAgentContinueView: designConversationView(), + }); + window.__TAURI__ = { + core: { invoke: harness.invoke }, + event: { listen: harness.listen }, + }; + window.history.pushState({}, '', '/'); + render( + React.createElement(App, { + initialProjectPath: harness.projectPath, + orchestrationMode: 'single-supervisor', + planningStartMode: true, + projectSupervisorOnly: true, + }), + ); + + const input = await screen.findByLabelText('项目需求'); + await setComposerText(input, '请给出核心玩法方案'); + fireEvent.submit(input.closest('form') as HTMLFormElement); + await waitFor(() => { + expect( + harness.invoke.mock.calls.some( + ([command]) => command === 'continue_design_agent_session', + ), + ).toBe(true); + }); + const continueCall = [...harness.invoke.mock.calls] + .reverse() + .find(([command]) => command === 'continue_design_agent_session'); + const clientTurnId = String( + (continueCall?.[1] as { clientTurnId?: string }).clientTurnId, + ); + harness.emitDesignAgentEvent({ + projectPath: harness.projectPath, + clientTurnId, + kind: 'reasoning', + reasoningText: '先分析需求,再组织方案。', + }); + + const summary = await screen.findByText('思考过程(点击展开)'); + const details = summary.closest('details') as HTMLDetailsElement; + expect(details.open).toBe(false); + fireEvent.click(summary); + expect(details.open).toBe(true); + expect(screen.getByText('先分析需求,再组织方案。')).not.toBeNull(); + }); } diff --git a/apps/ai-game-creator-shell/tests/appSurface/harness.ts b/apps/ai-game-creator-shell/tests/appSurface/harness.ts index 3c6da89f6..2400ea259 100644 --- a/apps/ai-game-creator-shell/tests/appSurface/harness.ts +++ b/apps/ai-game-creator-shell/tests/appSurface/harness.ts @@ -741,6 +741,9 @@ function createProjectSupervisorRuntimeHarness({ }; }) => void) | null = null; + let designAgentUpdateHandler: + | ((event: { payload: Record }) => void) + | null = null; const conversationRecord = ( role: 'user' | 'assistant', @@ -1113,6 +1116,10 @@ function createProjectSupervisorRuntimeHarness({ if (eventName === 'game-creator-agent-progress') { progressHandler = handler as unknown as typeof progressHandler; } + if (eventName === 'design-agent-update') { + designAgentUpdateHandler = + handler as unknown as typeof designAgentUpdateHandler; + } return () => { if (runtimeUpdateHandler === handler) { runtimeUpdateHandler = null; @@ -1123,6 +1130,9 @@ function createProjectSupervisorRuntimeHarness({ if (progressHandler === handler) { progressHandler = null; } + if (designAgentUpdateHandler === handler) { + designAgentUpdateHandler = null; + } }; }, ); @@ -1149,6 +1159,9 @@ function createProjectSupervisorRuntimeHarness({ setPlanningV2Result(state: Record | null) { currentPlanningV2Result = state; }, + emitDesignAgentEvent(payload: Record) { + designAgentUpdateHandler?.({ payload }); + }, setPlanningV2StartResult(state: Record | null) { currentPlanningV2StartResult = state; }, diff --git a/docs/project-memory/plans/【里程碑】Provider推理与正文分离及策划Agent展示-2026-09-14.md b/docs/project-memory/plans/【里程碑】Provider推理与正文分离及策划Agent展示-2026-09-14.md index 44322109d..dc122b85c 100644 --- a/docs/project-memory/plans/【里程碑】Provider推理与正文分离及策划Agent展示-2026-09-14.md +++ b/docs/project-memory/plans/【里程碑】Provider推理与正文分离及策划Agent展示-2026-09-14.md @@ -3,7 +3,7 @@ | 字段 | 值 | | --- | --- | | Version | 1.0 | -| Status | awaiting-review | +| Status | in-progress | | Date | 2026-09-14 | | Parent Spec | `docs/technical/【技术方案】策划Agent生产迁移与工作区浏览-2026-09-10.md` | | Related Issue | `GenarrativeAI/Genarrative#331` | @@ -12,6 +12,12 @@ 让策划 Agent 能在流式回合中单独收到 Provider reasoning,并在 UI 中以默认折叠的思考过程展示;用户可见正文、工具调用和 GameAgent 现有行为保持不变。 +## 当前实现进度(2026-09-14) + +- 已完成共享 reasoning 字段、Provider 解析、策划事件映射以及正文流式收尾的前两轮提交。 +- 当前第三轮聚焦策划 Agent 前端 reasoning 生命周期:按 `projectPath + clientTurnId` 绑定事件,回合结束后保留本轮 reasoning,下一轮或项目切换时清理。 +- 现有 `
` 展示保持默认收起,并提供明确的展开/收起入口;不新增持久化字段,也不改动 GameAgent、Direct/Codex、supervisor 或已退役链路。 + ## 背景与现状 - `platform-llm` 当前只向上层提供正文累计值、正文增量和结束状态。