From 975a2e577cae87fcb4a973676cd38258578a0ec4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Thu, 17 Sep 2026 00:10:20 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BB=88=E6=AD=A2=E6=8C=89=E9=92=AE=E6=94=B9?= =?UTF-8?q?=E6=8C=89=E7=BB=BC=E5=90=88=E7=B9=81=E5=BF=99=E4=BF=A1=E5=8F=B7?= =?UTF-8?q?=E5=88=A4=E5=AE=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - handleCancelDirectCodexTurn 的守卫改用 supervisorChatBusy,与「终止」按钮的可见条件对齐 - 刚提交、turn.started 还没到达的窗口里点击终止不再回"当前没有正在运行的回合" - chat-composer 用例补一条:生命周期事件缺席时也能发出 cancel_direct_codex_turn --- apps/ai-game-creator-shell/src/App.tsx | 4 +- .../tests/appSurface/chat-composer.suite.ts | 37 +++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/apps/ai-game-creator-shell/src/App.tsx b/apps/ai-game-creator-shell/src/App.tsx index 2abc16df0..e2a652ee9 100644 --- a/apps/ai-game-creator-shell/src/App.tsx +++ b/apps/ai-game-creator-shell/src/App.tsx @@ -12051,7 +12051,9 @@ export function App({ } const invoke = resolveTauriInvoke(); const directProjectPath = resolveChatProjectPath(localProject); - if (!invoke || !directProjectPath || !directTurnRunning) { + // 繁忙判据与「终止」按钮的可见条件一致:本地 invoke 正在跑,或订阅说还有一条回合没结束。 + // 只认 `turn.started` 推导出来的 `directTurnRunning` 会漏掉"刚提交、事件还没到"的窗口。 + if (!invoke || !directProjectPath || !supervisorChatBusy) { setProjectSupervisorRuntimeError('当前没有正在运行的回合,无法终止。'); return; } diff --git a/apps/ai-game-creator-shell/tests/appSurface/chat-composer.suite.ts b/apps/ai-game-creator-shell/tests/appSurface/chat-composer.suite.ts index 57bb32690..ccaae80aa 100644 --- a/apps/ai-game-creator-shell/tests/appSurface/chat-composer.suite.ts +++ b/apps/ai-game-creator-shell/tests/appSurface/chat-composer.suite.ts @@ -524,6 +524,43 @@ export function registerChatComposerControlTests() { expect(within(surface).getByText('已终止本次回合。')).not.toBeNull(); }); + it('terminates a turn that was submitted before its lifecycle event arrived', async () => { + // 刚提交、`turn.started` 还没下发:按钮已经变成「终止」,点击不能回"没有正在运行的回合"。 + const pending: Array<{ resolve: (value: string) => void }> = []; + const { invoke, path, surface } = await openDirectCodexSurface({ + chat_with_game_creator_direct_codex: () => + new Promise((resolve) => { + pending.push({ resolve }); + }), + cancel_direct_codex_turn: async () => undefined, + }); + const composer = within(surface).getByLabelText('陶泥儿对话内容'); + await submitDirectTurn(surface, composer, '做一个小游戏'); + await waitFor(() => { + expect(invoke).toHaveBeenCalledWith( + 'chat_with_game_creator_direct_codex', + expect.objectContaining({ prompt: '做一个小游戏' }), + ); + }); + + const stopButton = await within(surface).findByRole('button', { + name: '终止', + }); + fireEvent.click(stopButton); + await waitFor(() => { + expect(invoke).toHaveBeenCalledWith('cancel_direct_codex_turn', { + projectPath: path, + }); + }); + expect( + within(surface).queryByText('当前没有正在运行的回合,无法终止。'), + ).toBeNull(); + + await act(async () => { + pending[0]?.resolve('回合回复'); + }); + }); + it('moves the reasoning effort control next to the model selector and persists only for later turns', async () => { let stored = 'high'; const { invoke, surface } = await openDirectCodexSurface({