diff --git a/apps/ai-game-creator-shell/scripts/check-config.mjs b/apps/ai-game-creator-shell/scripts/check-config.mjs index 037526aaa..91e3a982c 100644 --- a/apps/ai-game-creator-shell/scripts/check-config.mjs +++ b/apps/ai-game-creator-shell/scripts/check-config.mjs @@ -114,6 +114,7 @@ const allowedUncalledTauriCommands = [ 'open_game_creator_launcher_window', 'open_game_creator_workspace_window', 'read_direct_project_conversation', + 'reset_design_agent_session', 'stop_local_game_preview_if_matches', 'start_game_creator_external_mcp', 'stop_game_creator_external_mcp', diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/design_runtime.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/design_runtime.rs index 3fad0ae77..a8bb580e9 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/agent/design_runtime.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/agent/design_runtime.rs @@ -835,7 +835,15 @@ pub(crate) fn hydrate_design_agent_session( return Ok(None); } let project_id = design_project_id(root)?; - let Some(session) = read_design_session(root)? else { + let session = match read_design_session(root) { + Ok(session) => session, + Err(error) if design_session_error_is_recoverable(&error) => { + quarantine_corrupted_design_session(root)?; + return Ok(None); + } + Err(error) => return Err(error), + }; + let Some(session) = session else { return Ok(None); }; if session.project_id != project_id { @@ -845,20 +853,35 @@ pub(crate) fn hydrate_design_agent_session( Ok(Some(design_view(&session, active.is_none()))) } +fn design_session_error_is_recoverable(error: &str) -> bool { + error.contains("策划 Agent 会话") + && (error.contains("解析") + || error.contains("字段无效") + || error.contains("超过") + || error.contains("JSON")) +} + +fn quarantine_corrupted_design_session(root: &Path) -> Result<(), String> { + let _lock = acquire_game_creator_agent_runtime_project_write_lock_with_wait( + root, + "design.session-quarantine", + )?; + let path = crate::agent::runtime_protocol::design_session_path(root); + if !path.exists() { + return Ok(()); + } + let backup = path.with_file_name(format!("session.corrupted.{}.json", unix_timestamp())); + std::fs::rename(&path, &backup).map_err(|error| format!("隔离损坏的策划会话失败:{error}")) +} + #[tauri::command] pub(crate) fn reset_design_agent_session(project_path: String) -> Result<(), String> { let root = Path::new(project_path.trim()); enforce_project_permission_policy(root, "conversation.write")?; design_project_id(root)?; - let _lock = acquire_game_creator_agent_runtime_project_write_lock_with_wait( - root, - "design.session-reset", - )?; let path = crate::agent::runtime_protocol::design_session_path(root); if path.exists() { - let backup = path.with_file_name(format!("session.corrupted.{}.json", unix_timestamp())); - std::fs::rename(&path, &backup) - .map_err(|error| format!("备份损坏的策划会话失败:{error}"))?; + quarantine_corrupted_design_session(root)?; } Ok(()) }