diff --git a/apps/ai-game-creator-shell/scripts/check-config.mjs b/apps/ai-game-creator-shell/scripts/check-config.mjs index 9833f1251..4c8c53722 100644 --- a/apps/ai-game-creator-shell/scripts/check-config.mjs +++ b/apps/ai-game-creator-shell/scripts/check-config.mjs @@ -107,11 +107,13 @@ const rustSharedContractSource = fs.readFileSync( 'utf8', ); const allowedUncalledTauriCommands = [ + 'append_direct_project_conversation_message', 'chat_with_game_creator_agent', 'check_ui_editor_font_glyph_coverage', 'create_ui_design_resource', 'open_game_creator_launcher_window', 'open_game_creator_workspace_window', + 'read_direct_project_conversation', 'stop_local_game_preview_if_matches', ]; const sourceExtensions = new Set([ diff --git a/apps/ai-game-creator-shell/src/App.tsx b/apps/ai-game-creator-shell/src/App.tsx index 5674806e7..8d4c77580 100644 --- a/apps/ai-game-creator-shell/src/App.tsx +++ b/apps/ai-game-creator-shell/src/App.tsx @@ -5307,7 +5307,10 @@ export function App({ if (directProjectPath && directInvoke) { const clientTurnId = directConversationTurnId ?? createDirectCodexConversationTurnId(); - if (!directPolicyChecked) { + if ( + !directPolicyChecked && + projectConversationWriteConfirmedRef.current !== directProjectPath + ) { try { const policyPaused = await queueProjectPolicyConfirmationIfNeeded( directInvoke, @@ -5316,6 +5319,8 @@ export function App({ '写入 DirectProject 对话历史', 'DirectProject 对话写入需要确认。', () => { + projectConversationWriteConfirmedRef.current = + directProjectPath; void executeChatAgentReply({ prompt, clientTurnId, @@ -5328,6 +5333,7 @@ export function App({ if (policyPaused) { return; } + projectConversationWriteConfirmedRef.current = directProjectPath; } catch (error) { if (localProjectPathRef.current === directProjectPath) { setProjectSupervisorRuntimeError( diff --git a/apps/ai-game-creator-shell/tests/appSurface/home.suite.ts b/apps/ai-game-creator-shell/tests/appSurface/home.suite.ts index 5ab564020..5274bda75 100644 --- a/apps/ai-game-creator-shell/tests/appSurface/home.suite.ts +++ b/apps/ai-game-creator-shell/tests/appSurface/home.suite.ts @@ -2122,6 +2122,7 @@ export function registerHomeProjectCreationTests() { 'existing-project', '已有项目', ); + const persistedMessages: Array> = []; const invoke = vi.fn( async (command: string, args?: Record) => { if (command === 'get_local_game_manifest') { @@ -2131,14 +2132,45 @@ export function registerHomeProjectCreationTests() { if (command === 'append_local_permission_log') { return {}; } - if (command === 'append_local_conversation_message') { + if (command === 'hydrate_game_creator_plan_gdd_state') { + return null; + } + if (command === 'read_project_permission_policy') { + return { + path: '.agent/policy.json', + policy: { deniedCommands: [], confirmCommands: [] }, + }; + } + if ( + command === 'read_local_conversation' || + command === 'read_direct_project_conversation' + ) { return { path: `${projectPath}/.agent/conversations/project.jsonl`, agentId: null, - messages: [], + sessionId: null, + messages: [...persistedMessages], }; } + if (command === 'append_local_conversation_message') { + throw new Error( + 'DirectProject must not use browser conversation writer', + ); + } if (command === 'chat_with_game_creator_direct_codex') { + const clientTurnId = String(args?.clientTurnId ?? ''); + persistedMessages.push( + { + role: 'user', + content: String(args?.prompt ?? ''), + messageId: `direct-codex:${clientTurnId}:user`, + }, + { + role: 'assistant', + content: 'DIRECT_EXISTING_PROJECT_OK', + messageId: `direct-codex:${clientTurnId}:assistant`, + }, + ); return 'DIRECT_EXISTING_PROJECT_OK'; } throw new Error(`unexpected invoke ${command}`); @@ -2165,32 +2197,18 @@ export function registerHomeProjectCreationTests() { }, ); }); - const persistedTurnCall = invoke.mock.calls.findIndex( - ([command, args]) => - command === 'append_local_conversation_message' && - (args as Record | undefined)?.messageId !== undefined, - ); const directTurnCall = invoke.mock.calls.findIndex( ([command]) => command === 'chat_with_game_creator_direct_codex', ); - expect(persistedTurnCall).toBeGreaterThanOrEqual(0); - expect(persistedTurnCall).toBeLessThan(directTurnCall); - const persistedTurnArgs = invoke.mock.calls[persistedTurnCall]?.[1] as - | Record - | undefined; const directTurnArgs = invoke.mock.calls[directTurnCall]?.[1] as | Record | undefined; - expect(persistedTurnArgs).toEqual({ - projectPath, - agentId: null, - messageId: `direct-codex:${String(directTurnArgs?.clientTurnId ?? '')}:user`, - message: { - role: 'user', - content: '继续修改已有项目', - agentId: null, - }, - }); + expect(directTurnCall).toBeGreaterThanOrEqual(0); + expect(invoke).not.toHaveBeenCalledWith( + 'append_local_conversation_message', + expect.anything(), + ); + expect(directTurnArgs?.clientTurnId).toEqual(expect.any(String)); expect(invoke).not.toHaveBeenCalledWith( 'create_automatic_local_game_project', ); @@ -2214,7 +2232,10 @@ export function registerHomeProjectCreationTests() { expect(args).toEqual({ projectPath }); return manifest; } - if (command === 'read_local_conversation') { + if ( + command === 'read_local_conversation' || + command === 'read_direct_project_conversation' + ) { return { path: `${projectPath}/.agent/conversations/project.jsonl`, agentId: null, @@ -2257,6 +2278,23 @@ export function registerHomeProjectCreationTests() { }; } if (command === 'chat_with_game_creator_direct_codex') { + const clientTurnId = String(args?.clientTurnId ?? ''); + persistedMessages.push( + { + schemaVersion: 'game-creator-conversation.v1', + role: 'user', + content: String(args?.prompt ?? ''), + messageId: `direct-codex:${clientTurnId}:user`, + updatedAt: 1, + }, + { + schemaVersion: 'game-creator-conversation.v1', + role: 'assistant', + content: '陶泥儿智能创作 鉴权失败,请检查 API Key 或登录态', + messageId: `direct-codex:${clientTurnId}:assistant`, + updatedAt: 2, + }, + ); throw new Error('codex-app-server-error:unauthorized'); } throw new Error(`unexpected invoke ${command}`);