From 8853e3b48e36d2d94dfc04428287abe06d0b3d6b Mon Sep 17 00:00:00 2001 From: Linghong Date: Sat, 12 Sep 2026 05:06:48 +0000 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E5=96=84=E7=AD=96=E5=88=92=E8=B0=83?= =?UTF-8?q?=E8=AF=95=E5=85=A5=E5=8F=A3=E4=B8=8E=E9=A1=BE=E9=97=AE=E6=80=81?= =?UTF-8?q?=E5=88=87=E6=8D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 统一策划 Debug 日志、快速推进按钮和快速推进命令的开关。 将做成游戏入口放到顾问阶段条末尾并接入正常运行时切换。 登记策划产物资产并补充工作区调试入口测试与技术文档。 --- .../scripts/start-tauri-dev.mjs | 14 +++- .../src-tauri/src/agent/design_runtime.rs | 77 +++++++++++++++++++ .../src-tauri/src/assets.rs | 64 +++++++++++++++ .../src-tauri/src/main.rs | 1 + apps/ai-game-creator-shell/src/App.tsx | 11 --- .../features/app-shell/WorkspaceLauncher.tsx | 3 + .../project-workspace/DesignAgentSurface.tsx | 12 --- .../DesignWorkspacePanel.tsx | 75 +++++++++++++++--- .../ProjectSupervisorView.tsx | 3 - apps/ai-game-creator-shell/src/styles.css | 26 +++++++ .../src/view/project-development/index.tsx | 7 +- .../tests/designWorkspaceDebug.test.tsx | 60 +++++++++++++++ ...】策划Agent生产迁移与工作区浏览-2026-09-10.md | 6 +- 13 files changed, 317 insertions(+), 42 deletions(-) create mode 100644 apps/ai-game-creator-shell/tests/designWorkspaceDebug.test.tsx diff --git a/apps/ai-game-creator-shell/scripts/start-tauri-dev.mjs b/apps/ai-game-creator-shell/scripts/start-tauri-dev.mjs index 87144db48..4f734f7fe 100644 --- a/apps/ai-game-creator-shell/scripts/start-tauri-dev.mjs +++ b/apps/ai-game-creator-shell/scripts/start-tauri-dev.mjs @@ -21,6 +21,9 @@ const appRoot = fileURLToPath(new URL('..', import.meta.url)); const repoRoot = resolve(appRoot, '../..'); const tauriCliPath = resolve(repoRoot, 'node_modules/@tauri-apps/cli/tauri.js'); const AGC_DESIGN_DEBUG_ENV = 'GENARRATIVE_AGC_DESIGN_DEBUG'; +const AGC_DESIGN_DEBUG_VITE_ENV = 'VITE_GENARRATIVE_AGC_DESIGN_DEBUG'; +const designDebugEnabled = + process.env[AGC_DESIGN_DEBUG_ENV]?.trim() === '0' ? '0' : '1'; function buildTauriArguments(argv, devUrl = readAgcDevEndpoint().url) { const args = [...argv]; @@ -111,7 +114,8 @@ async function runTauriDev( child = spawnCli(tauriArguments, { env: { ...withAgcDevEndpointEnv(endpoint), - [AGC_DESIGN_DEBUG_ENV]: '1', + [AGC_DESIGN_DEBUG_ENV]: designDebugEnabled, + [AGC_DESIGN_DEBUG_VITE_ENV]: designDebugEnabled, }, }); const childResult = waitForCli(child); @@ -161,7 +165,13 @@ async function prepareFrontendDev(endpoint, { onChild, signal }) { const frontend = spawnChild( process.platform === 'win32' ? 'npm.cmd' : 'npm', ['run', 'agc:serve'], - { cwd: repoRoot, env: withAgcDevEndpointEnv(endpoint) }, + { + cwd: repoRoot, + env: { + ...withAgcDevEndpointEnv(endpoint), + [AGC_DESIGN_DEBUG_VITE_ENV]: designDebugEnabled, + }, + }, ); onChild(frontend); console.log( 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 36e32fdff..92952b610 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 @@ -857,9 +857,86 @@ pub(crate) fn set_design_agent_runtime_mode( root, "design.runtime-mode", )?; + if active_runtime.trim() == "game" { + crate::assets::register_design_artifacts_at(root)?; + } write_design_runtime_mode(root, active_runtime.trim()) } +#[tauri::command] +pub(crate) fn debug_fast_forward_design_session( + app: tauri::AppHandle, + project_path: String, + target_phase: String, +) -> Result { + if !cfg!(debug_assertions) + || std::env::var("GENARRATIVE_AGC_DESIGN_DEBUG").ok().as_deref() != Some("1") + { + return Err("策划 Agent 快速推进仅可用于 Debug 构建".to_string()); + } + let root = Path::new(project_path.trim()); + let project_id = design_project_id(root)?; + let target_index = design_phase_index(target_phase.trim())?; + let _lock = acquire_game_creator_agent_runtime_project_write_lock_with_wait( + root, + "design.debug-fast-forward", + )?; + let artifact_paths = [ + "project/00_concept/design.md", + "project/速览卡.md", + "project/01_top_design/design.md", + "project/02_architecture/design.md", + "project/03_systems/debug-system.md", + "project/04_tdd/01_技术实现.md", + "project/04_tdd/02_美术圣经.md", + "project/04_tdd/03_数据与配表.md", + "project/04_tdd/总册.md", + ]; + for relative in artifact_paths { + let path = root.join("design_artifacts").join(relative); + if let Some(parent) = path.parent() { + std::fs::create_dir_all(parent).map_err(|error| { + format!("创建调试策划产物目录失败:{}: {error}", parent.display()) + })?; + } + if !path.exists() { + std::fs::write( + &path, + "# Debug Design Artifact\n\n这是生产 Runtime 快速推进测试生成的占位产物,不代表真实策划内容。\n", + ) + .map_err(|error| format!("写入调试策划产物失败:{}: {error}", path.display()))?; + } + } + let mut session = + read_design_session(root)?.unwrap_or_else(|| new_design_session(&project_id, "")); + session.current_phase = target_phase.trim().to_string(); + session.approved_phases = DESIGN_PHASES[..target_index] + .iter() + .map(|phase| phase.to_string()) + .collect(); + session.pending_approval = None; + session.pending_clarification = None; + session.turn = None; + session.pending_batch = None; + session.last_error = None; + session.updated_at = unix_timestamp(); + write_design_session(root, &session)?; + let mode = write_design_runtime_mode(root, "design")?; + app.emit( + "design-agent-update", + design_event( + root, + "debug-fast-forward", + "state", + None, + None, + Some(design_view(&session, false)), + ), + ) + .map_err(|error| format!("刷新策划调试状态失败:{error}"))?; + Ok(mode) +} + #[tauri::command] pub(crate) async fn continue_design_agent_session( app: tauri::AppHandle, diff --git a/apps/ai-game-creator-shell/src-tauri/src/assets.rs b/apps/ai-game-creator-shell/src-tauri/src/assets.rs index ad87b4f5e..515d425a9 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/assets.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/assets.rs @@ -533,6 +533,70 @@ pub(crate) fn register_local_asset_at( register_local_asset_entry(root, local_path, kind, media_type, id_prefix, source) } +pub(crate) fn register_design_artifacts_at(root: &Path) -> Result { + let design_root = root.join("design_artifacts"); + if !design_root.exists() { + return Ok(0); + } + let mut files = Vec::new(); + let mut directories = vec![design_root]; + while let Some(directory) = directories.pop() { + for entry in fs::read_dir(&directory) + .map_err(|error| format!("读取策划产物目录失败:{}: {error}", directory.display()))? + { + let entry = entry.map_err(|error| format!("读取策划产物失败:{error}"))?; + let path = entry.path(); + let metadata = fs::symlink_metadata(&path) + .map_err(|error| format!("读取策划产物元数据失败:{}: {error}", path.display()))?; + if metadata.file_type().is_symlink() { + continue; + } + if metadata.is_dir() { + directories.push(path); + } else if metadata.is_file() { + files.push(path); + } + } + } + files.sort(); + let mut registered = 0; + for path in files { + let relative = path + .strip_prefix(root) + .map_err(|_| "策划产物路径不在项目根目录内".to_string())? + .to_string_lossy() + .replace('\\', "/"); + let media_type = match path.extension().and_then(|value| value.to_str()) { + Some("md") => "text/markdown", + Some("txt") => "text/plain", + Some("json") => "application/json", + Some("yaml" | "yml") => "text/yaml", + _ => "application/octet-stream", + }; + register_local_asset_at( + root, + &relative, + "design-document", + media_type, + "design-document", + GameCreationAppAssetSource { + kind: GameCreationAppAssetSourceKind::Uploaded, + canvas_project_id: None, + resource_id: None, + asset_object_id: None, + task_id: None, + prompt: None, + model: None, + generation_route: None, + generation_kind: None, + reference_resource_ids: Vec::new(), + }, + )?; + registered += 1; + } + Ok(registered) +} + pub(crate) fn import_canvas_asset_at( root: &Path, local_path: &str, 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 ef9529238..3c93f0697 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/main.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/main.rs @@ -2584,6 +2584,7 @@ fn main() { hydrate_planning_session_v2, hydrate_design_agent_session, set_design_agent_runtime_mode, + debug_fast_forward_design_session, continue_design_agent_session, decide_design_phase, list_design_workspace, diff --git a/apps/ai-game-creator-shell/src/App.tsx b/apps/ai-game-creator-shell/src/App.tsx index 0bb65ee89..9ad52805c 100644 --- a/apps/ai-game-creator-shell/src/App.tsx +++ b/apps/ai-game-creator-shell/src/App.tsx @@ -525,7 +525,6 @@ type AppProps = { ) => void; onAgentResultsChange?: (results: ProjectAgentResultSummary[]) => void; onMakeGameFromApprovedGdd?: (projectPath: string) => Promise; - onSwitchToGameRuntime?: (projectPath: string) => Promise; }; type ExecuteChatAgentReplyInput = { @@ -554,7 +553,6 @@ export function App({ onAgentRuntimeSummariesChange, onAgentResultsChange, onMakeGameFromApprovedGdd, - onSwitchToGameRuntime, }: AppProps = {}) { const { setTitle: setWindowTitle } = useWindowChrome(); const [planningV2Active, setPlanningV2Active] = useState(planningStartMode); @@ -11690,15 +11688,6 @@ export function App({ } : undefined } - onDesignMakeGame={ - useDesignAgentSurface && onSwitchToGameRuntime - ? () => { - const nextProjectPath = resolveChatProjectPath(localProject); - if (nextProjectPath) - void onSwitchToGameRuntime(nextProjectPath); - } - : undefined - } onMakeGameFromApprovedGdd={ onMakeGameFromApprovedGdd ? () => diff --git a/apps/ai-game-creator-shell/src/features/app-shell/WorkspaceLauncher.tsx b/apps/ai-game-creator-shell/src/features/app-shell/WorkspaceLauncher.tsx index 2d5c33203..95b286115 100644 --- a/apps/ai-game-creator-shell/src/features/app-shell/WorkspaceLauncher.tsx +++ b/apps/ai-game-creator-shell/src/features/app-shell/WorkspaceLauncher.tsx @@ -363,6 +363,9 @@ export function WorkspaceLauncherShell({ onPlay={() => requestCurrentProjectPlay(currentProjectContext.projectPath) } + onMakeGame={() => + void switchToGameRuntime(currentProjectContext.projectPath) + } onManifestChange={syncActiveProjectManifest} onHomeOpen={() => setLauncherView('home')} onProjectsOpen={() => setLauncherView('projects')} diff --git a/apps/ai-game-creator-shell/src/features/project-workspace/DesignAgentSurface.tsx b/apps/ai-game-creator-shell/src/features/project-workspace/DesignAgentSurface.tsx index 78c586fa7..91483c865 100644 --- a/apps/ai-game-creator-shell/src/features/project-workspace/DesignAgentSurface.tsx +++ b/apps/ai-game-creator-shell/src/features/project-workspace/DesignAgentSurface.tsx @@ -23,7 +23,6 @@ type DesignAgentSurfaceProps = { text: string, ) => void; onRetry: () => void; - onMakeGame: () => void; }; export function DesignAgentSurface({ @@ -33,7 +32,6 @@ export function DesignAgentSurface({ onApprove, onClarify, onRetry, - onMakeGame, }: DesignAgentSurfaceProps) { const [clarifyText, setClarifyText] = useState(''); const phase = view?.session.currentPhase ?? 'concept'; @@ -49,16 +47,6 @@ export function DesignAgentSurface({ 当前阶段
{PHASE_LABELS[phase] ?? phase} - {phase === 'consultant' ? ( - - ) : null}
{view?.running ? ( diff --git a/apps/ai-game-creator-shell/src/features/project-workspace/DesignWorkspacePanel.tsx b/apps/ai-game-creator-shell/src/features/project-workspace/DesignWorkspacePanel.tsx index 0cd20991c..d8a894b9b 100644 --- a/apps/ai-game-creator-shell/src/features/project-workspace/DesignWorkspacePanel.tsx +++ b/apps/ai-game-creator-shell/src/features/project-workspace/DesignWorkspacePanel.tsx @@ -24,6 +24,7 @@ const PHASES = [ type DesignWorkspacePanelProps = { projectPath: string; + onMakeGame?: () => void; }; function phaseLabel(phase: string) { @@ -171,6 +172,7 @@ function DesignTreeBranch({ export function DesignWorkspacePanel({ projectPath, + onMakeGame, }: DesignWorkspacePanelProps) { const [view, setView] = useState(null); const [files, setFiles] = useState([]); @@ -178,6 +180,7 @@ export function DesignWorkspacePanel({ const [previewText, setPreviewText] = useState(''); const [loading, setLoading] = useState(true); const [previewLoading, setPreviewLoading] = useState(false); + const [debugPreparing, setDebugPreparing] = useState(false); const [error, setError] = useState(''); const [expandedPaths, setExpandedPaths] = useState>( () => new Set(), @@ -344,18 +347,56 @@ export function DesignWorkspacePanel({

{phaseLabel(currentPhase)}

Agent 产生的策划文档会显示在这里。

- +
+ {import.meta.env.VITE_GENARRATIVE_AGC_DESIGN_DEBUG === '1' ? ( + + ) : null} + +
{error ? ( diff --git a/apps/ai-game-creator-shell/src/features/project-workspace/ProjectSupervisorView.tsx b/apps/ai-game-creator-shell/src/features/project-workspace/ProjectSupervisorView.tsx index ebbc51866..41bb6d564 100644 --- a/apps/ai-game-creator-shell/src/features/project-workspace/ProjectSupervisorView.tsx +++ b/apps/ai-game-creator-shell/src/features/project-workspace/ProjectSupervisorView.tsx @@ -109,7 +109,6 @@ type ProjectSupervisorViewProps = RuntimePanelProps & { text: string, ) => void; onDesignRetry?: () => void; - onDesignMakeGame?: () => void; }; export function ProjectSupervisorView({ @@ -150,7 +149,6 @@ export function ProjectSupervisorView({ onDesignApprove, onDesignClarify, onDesignRetry, - onDesignMakeGame, ...runtimePanelProps }: ProjectSupervisorViewProps) { const planningSurfaceActive = @@ -187,7 +185,6 @@ export function ProjectSupervisorView({ onApprove={onDesignApprove ?? (() => undefined)} onClarify={onDesignClarify ?? (() => undefined)} onRetry={onDesignRetry ?? (() => undefined)} - onMakeGame={onDesignMakeGame ?? (() => undefined)} /> ) : ( void; onProjectsOpen: () => void; onPlay?: () => void; + onMakeGame?: () => void; onManifestChange?: ( projectPath: string, manifest: GameCreationAppManifest, @@ -857,6 +858,7 @@ export default function ProjectDevelopmentView({ walletEntry, onManifestChange, onPlay, + onMakeGame, }: ProjectDevelopmentViewProps) { const professionalDagVisible = orchestrationMode === 'professional-dag'; const [mode, setMode] = useState('resources'); @@ -4218,7 +4220,10 @@ export default function ProjectDevelopmentView({ >
- +