diff --git a/apps/ai-game-creator-shell/src/App.tsx b/apps/ai-game-creator-shell/src/App.tsx index 7861eccad..434fe8e84 100644 --- a/apps/ai-game-creator-shell/src/App.tsx +++ b/apps/ai-game-creator-shell/src/App.tsx @@ -596,6 +596,12 @@ export function App({ projectPath: string; clientTurnId: string; } | null>(null); + const designAgentEventSubscriptionReadyRef = useRef | null>( + null, + ); + const designAgentEventSubscriptionResolveRef = useRef<(() => void) | null>( + null, + ); // 做方案入口独立成链:立项策划需要委派、澄清 pending 与 GDD 审批,这些只存在于 // Supervisor Runtime;direct-codex 是单回合「生成→试玩→修」循环,没有对应机制。 // 因此策划入口不走产品默认的 direct-codex,做游戏与做素材保持 master 的新默认。 @@ -849,6 +855,22 @@ export function App({ } } + function designAgentEventSubscriptionReady() { + if (!designAgentEventSubscriptionReadyRef.current) { + designAgentEventSubscriptionReadyRef.current = new Promise( + (resolve) => { + designAgentEventSubscriptionResolveRef.current = resolve; + }, + ); + } + return designAgentEventSubscriptionReadyRef.current; + } + + function resolveDesignAgentEventSubscriptionReady() { + designAgentEventSubscriptionResolveRef.current?.(); + designAgentEventSubscriptionResolveRef.current = null; + } + useEffect(() => { const timer = window.setInterval(() => { const target = planningV2TransientReplyTargetRef.current; @@ -1038,6 +1060,7 @@ export function App({ projectPath: nextProjectPath, clientTurnId, }; + await designAgentEventSubscriptionReady(); setChatAgentBusy(true); setProjectSupervisorRuntimeError(''); setPlanningV2TransientReplyTarget(''); @@ -1992,8 +2015,14 @@ export function App({ }, [planningV2Active]); useEffect(() => { + const ready = designAgentEventSubscriptionReady(); if (!canSubscribeTauriEvents() || !planningV2Active) { - return; + resolveDesignAgentEventSubscriptionReady(); + return () => { + if (designAgentEventSubscriptionReadyRef.current === ready) { + designAgentEventSubscriptionReadyRef.current = null; + } + }; } let cleanup: (() => void) | null = null; let disposed = false; @@ -2020,16 +2049,23 @@ export function App({ } }) .then((unlisten) => { + resolveDesignAgentEventSubscriptionReady(); if (disposed) { unlisten(); return; } cleanup = unlisten; }) - .catch(() => undefined); + .catch(() => { + resolveDesignAgentEventSubscriptionReady(); + }); return () => { disposed = true; cleanup?.(); + resolveDesignAgentEventSubscriptionReady(); + if (designAgentEventSubscriptionReadyRef.current === ready) { + designAgentEventSubscriptionReadyRef.current = null; + } }; // applyDesignView 读的是 refs 和当前项目路径, // 把它写进依赖会在每轮回复时重订事件。 @@ -11831,12 +11867,15 @@ export function App({ setPlanningV2Reasoning(''); setChatAgentBusy(true); setPlanGddDecisionBusy(true); - void invoke('decide_design_phase', { - projectPath: nextProjectPath, - clientTurnId, - requestId, - approved, - }) + void designAgentEventSubscriptionReady() + .then(() => + invoke('decide_design_phase', { + projectPath: nextProjectPath, + clientTurnId, + requestId, + approved, + }), + ) .then((view) => { if ( localProjectPathRef.current !== nextProjectPath ||