diff --git a/apps/ai-game-creator-shell/scripts/check-config.mjs b/apps/ai-game-creator-shell/scripts/check-config.mjs index b5a1bf7b5..7af269b5b 100644 --- a/apps/ai-game-creator-shell/scripts/check-config.mjs +++ b/apps/ai-game-creator-shell/scripts/check-config.mjs @@ -646,6 +646,10 @@ for (const snippet of [ 'open_developer_window(app.handle())?', 'tauri::WebviewWindowBuilder::new(app, "developer", developer_window_url())', 'index.html?agent-chat', + 'supervisorChatMode', + 'supervisorChatOnly', + 'open_project_supervisor_chat_window', + 'index.html?supervisor-chat&projectPath=', ]) { if (!`${appEntrypointSource}\n${tauriRustSource}`.includes(snippet)) { throw new Error( diff --git a/apps/ai-game-creator-shell/src-tauri/src/main.rs b/apps/ai-game-creator-shell/src-tauri/src/main.rs index 2011937f7..6ecd10abe 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/main.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/main.rs @@ -1749,6 +1749,7 @@ fn main() { write_project_permission_policy, open_game_creator_workspace_window, open_game_creator_launcher_window, + open_project_supervisor_chat_window, start_local_game_preview, open_local_game_preview, stop_local_game_preview, diff --git a/apps/ai-game-creator-shell/src-tauri/src/tests.rs b/apps/ai-game-creator-shell/src-tauri/src/tests.rs index c9e2c971b..141d66e26 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/tests.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/tests.rs @@ -48860,6 +48860,14 @@ fn developer_window_uses_agent_chat_route() { assert_eq!(developer_window_url().to_string(), "index.html?agent-chat"); } +#[test] +fn supervisor_chat_window_carries_encoded_project_path() { + assert_eq!( + supervisor_chat_window_url("/tmp/AI Game 项目").to_string(), + "index.html?supervisor-chat&projectPath=%2Ftmp%2FAI%20Game%20%E9%A1%B9%E7%9B%AE" + ); +} + #[test] fn workspace_window_url_carries_encoded_project_path() { assert_eq!( diff --git a/apps/ai-game-creator-shell/src-tauri/src/windows.rs b/apps/ai-game-creator-shell/src-tauri/src/windows.rs index c141602dc..784a4a014 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/windows.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/windows.rs @@ -15,6 +15,13 @@ pub(crate) fn developer_window_url() -> tauri::WebviewUrl { tauri::WebviewUrl::App(PathBuf::from("index.html?agent-chat")) } +pub(crate) fn supervisor_chat_window_url(project_path: &str) -> tauri::WebviewUrl { + tauri::WebviewUrl::App(PathBuf::from(format!( + "index.html?supervisor-chat&projectPath={}", + percent_encode_query_value(project_path) + ))) +} + pub(crate) fn validate_workspace_window_project_path(project_path: &str) -> Result<&str, String> { let project_path = project_path.trim(); if project_path.is_empty() { @@ -81,6 +88,37 @@ pub(crate) fn open_game_creator_launcher_window( Ok(()) } +#[tauri::command] +pub(crate) fn open_project_supervisor_chat_window( + app: tauri::AppHandle, + project_path: String, +) -> Result<(), String> { + let project_path = validate_workspace_window_project_path(&project_path)?; + #[cfg(not(debug_assertions))] + { + let _ = app; + let _ = project_path; + return Err("项目总控对话窗口仅在开发构建中可用".to_string()); + } + #[cfg(debug_assertions)] + { + if let Some(existing) = app.get_webview_window("supervisor-chat") { + existing.close().map_err(|error| error.to_string())?; + } + tauri::WebviewWindowBuilder::new( + &app, + "supervisor-chat", + supervisor_chat_window_url(project_path), + ) + .title("项目总控 Agent 对话") + .inner_size(820.0, 720.0) + .min_inner_size(560.0, 480.0) + .build() + .map_err(|error| error.to_string())?; + Ok(()) + } +} + #[cfg(all(debug_assertions, not(test)))] pub(crate) fn open_developer_window(app: &tauri::AppHandle) -> Result<(), String> { if let Some(existing) = app.get_webview_window("developer") { diff --git a/apps/ai-game-creator-shell/src/App.tsx b/apps/ai-game-creator-shell/src/App.tsx index 960f3f863..92cc89bd6 100644 --- a/apps/ai-game-creator-shell/src/App.tsx +++ b/apps/ai-game-creator-shell/src/App.tsx @@ -1642,6 +1642,22 @@ function agentRuntimeConversationStatus(runtime: AgentRuntimeState) { return waitingOn ? `Agent 正在运行,等待${waitingOn}` : 'Agent 正在运行'; } +function projectSupervisorChatRuntimeStatus(runtime: AgentRuntimeState) { + if (runtime.status === 'idle' || runtime.phase === 'idle') { + return '等待输入'; + } + if (isAgentRuntimeTerminalState(runtime)) { + if (runtime.status === 'failed' || runtime.phase === 'failed') { + return runtime.error || 'Agent 运行失败'; + } + if (runtime.status === 'cancelled' || runtime.phase === 'cancelled') { + return '本轮已取消'; + } + return '本轮已完成'; + } + return agentRuntimeConversationStatus(runtime); +} + function formatAgentRuntimeEvent(event: AgentRuntimeEventRecord) { const summary = event.summary || event.detail || event.runId; const detail = @@ -6561,6 +6577,32 @@ export function WorkspaceLauncher({ agentChatGoalDialog !== null && !agentChatBackgroundBusy, ); + async function handleOpenProjectSupervisorChatWindow() { + if (agentChatBusy || agentChatBackgroundBusy) { + return; + } + const projectPathForChat = validateAgentChatProjectPath(); + if (!projectPathForChat) { + return; + } + const invoke = resolveTauriInvoke(); + if (!invoke) { + setAgentChatStatus('需要在 Tauri App 内运行'); + return; + } + setAgentChatStatus('正在打开项目总控对话'); + try { + await invoke('open_project_supervisor_chat_window', { + projectPath: projectPathForChat, + }); + setAgentChatStatus('已打开项目总控对话'); + } catch (error) { + setAgentChatStatus( + error instanceof Error ? error.message : String(error), + ); + } + } + async function handleAgentChatPickProjectDirectory() { if (agentChatBusy || agentChatBackgroundBusy) { return; @@ -8896,6 +8938,13 @@ export function WorkspaceLauncher({

{agentChatStatus}

+ +
+ +
+ {hiddenConversationCount > 0 ? ( + + ) : null} + {visibleMessages.map((message, index) => ( +

+ {message.text} +

+ ))} + {projectSupervisorTransientReply ? ( +

+ {projectSupervisorTransientReply} +

+ ) : null} + {projectSupervisorChatRunning && + !projectSupervisorTransientReply ? ( +
+
+ ) : null} +
+
+