use super::*; #[tauri::command] pub(crate) fn init_local_game_project( project_path: String, project_id: String, name: String, ) -> Result { let root = Path::new(project_path.trim()); enforce_project_permission_policy(root, "project.create")?; let _lock = acquire_project_write_lock(root, "project.create")?; init_local_game_project_at(root, project_id.trim(), name.trim()) } #[tauri::command] pub(crate) fn import_local_godot_project( project_path: String, project_id: String, name: String, ) -> Result { let root = Path::new(project_path.trim()); enforce_project_permission_policy(root, "project.create")?; let _lock = acquire_project_write_lock(root, "project.create")?; import_local_godot_project_at(root, project_id.trim(), name.trim()) } #[tauri::command] pub(crate) fn is_local_project_directory_non_empty(project_path: String) -> Result { let root = Path::new(project_path.trim()); if root.as_os_str().is_empty() { return Err("项目目录不能为空".to_string()); } if !root.is_absolute() { return Err("项目目录必须是绝对路径".to_string()); } if project_path_has_control_chars(root) { return Err("项目目录不能包含控制字符".to_string()); } if !root.exists() { return Ok(false); } if !root.is_dir() { return Err("项目目录已存在但不是文件夹".to_string()); } fs::read_dir(root) .map_err(|error| format!("读取项目目录失败:{}: {error}", root.display()))? .next() .transpose() .map_err(|error| format!("读取项目目录失败:{}: {error}", root.display())) .map(|entry| entry.is_some()) } #[tauri::command] pub(crate) fn inspect_local_project_directory( project_path: String, ) -> Result { let root = Path::new(project_path.trim()); if root.as_os_str().is_empty() { return Err("项目目录不能为空".to_string()); } if !root.is_absolute() { return Err("项目目录必须是绝对路径".to_string()); } if project_path_has_control_chars(root) { return Err("项目目录不能包含控制字符".to_string()); } let recent_run_trace = recent_game_creator_run_trace(root); Ok(LocalProjectDirectoryStatus { project_path: root.to_string_lossy().into_owned(), exists: root.exists(), is_directory: root.is_dir(), is_game_creator_project: is_game_creator_project_directory(root), is_godot_project: is_godot_project_directory(root), project_name: game_creator_project_name(root), manifest_error: game_creator_project_manifest_error(root), recent_run_status: recent_run_trace.as_ref().map(|trace| trace.status.clone()), recent_run_stop_reason: recent_run_trace.map(|trace| trace.stop_reason), }) } pub(crate) fn is_godot_project_directory(root: &Path) -> bool { if !root.is_dir() { return false; } let project_file = root.join("project.godot"); fs::symlink_metadata(project_file) .map(|metadata| metadata.is_file() && !metadata.file_type().is_symlink()) .unwrap_or(false) } pub(crate) fn is_game_creator_project_directory(root: &Path) -> bool { if !root.is_dir() { return false; } let manifest_path = root.join(".agent/manifest.json"); read_manifest(&manifest_path).is_ok() } pub(crate) fn game_creator_project_name(root: &Path) -> Option { let manifest_path = root.join(".agent/manifest.json"); let manifest = read_manifest(&manifest_path).ok()?; let name = manifest.name.trim(); if name.is_empty() { None } else { Some(name.to_string()) } } pub(crate) fn game_creator_project_manifest_error(root: &Path) -> Option { if !root.is_dir() { return None; } let manifest_path = root.join(".agent/manifest.json"); if !manifest_storage_exists(&manifest_path).unwrap_or(true) { return None; } read_manifest(&manifest_path).err() } pub(crate) fn recent_game_creator_run_trace(root: &Path) -> Option { let trace_path = root.join(".agent/run.latest.json"); let content = fs::read_to_string(trace_path).ok()?; serde_json::from_str::(&content).ok() } #[tauri::command] pub(crate) async fn pick_local_project_directory( app: tauri::AppHandle, ) -> Result, String> { let (sender, receiver) = tokio::sync::oneshot::channel(); let mut dialog = app.dialog().file().set_title("选择游戏项目目录"); if let Some(window) = app.get_webview_window("client") { dialog = dialog.set_parent(&window); } dialog.pick_folder(move |path| { let _ = sender.send(path); }); let Some(path) = receiver .await .map_err(|_| "项目目录选择器意外关闭".to_string())? else { return Ok(None); }; path.into_path() .map(|path| Some(path.to_string_lossy().into_owned())) .map_err(|error| format!("读取项目目录失败:{error}")) } #[tauri::command] pub(crate) async fn pick_local_file(app: tauri::AppHandle) -> Result, String> { let (sender, receiver) = tokio::sync::oneshot::channel(); let mut dialog = app.dialog().file().set_title("选择本地文件"); if let Some(window) = app.get_webview_window("client") { dialog = dialog.set_parent(&window); } dialog.pick_file(move |path| { let _ = sender.send(path); }); let Some(path) = receiver .await .map_err(|_| "本地文件选择器意外关闭".to_string())? else { return Ok(None); }; path.into_path() .map(|path| Some(path.to_string_lossy().into_owned())) .map_err(|error| format!("读取本地文件失败:{error}")) } #[tauri::command] pub(crate) fn open_local_project_directory( app: tauri::AppHandle, project_path: String, ) -> Result<(), String> { let path = validated_local_project_directory_path(project_path.trim())?; app.opener() .open_path(path.to_string_lossy().into_owned(), None::<&str>) .map_err(|error| format!("打开项目目录失败:{error}")) } pub(crate) fn validated_local_project_directory_path( project_path: &str, ) -> Result { let path = Path::new(project_path); if path.as_os_str().is_empty() { return Err("项目目录不能为空".to_string()); } if !path.is_absolute() { return Err("项目目录必须是绝对路径".to_string()); } if !path.exists() { return Err("项目目录不存在".to_string()); } if !path.is_dir() { return Err("项目路径不是文件夹".to_string()); } Ok(path.to_path_buf()) } #[tauri::command] pub(crate) fn get_local_game_manifest( project_path: String, command_id: Option, ) -> Result { let root = Path::new(project_path.trim()); let command_id = command_id .as_deref() .map(str::trim) .filter(|value| !value.is_empty()) .unwrap_or("project.status"); if !matches!( command_id, "project.status" | "asset.list" | "task.list" | "agent.audit" ) { return Err(format!("不支持通过 manifest 执行命令:{command_id}")); } enforce_project_permission_policy(root, command_id)?; read_manifest_for_project(root) } #[tauri::command] pub(crate) fn read_local_project_resource_canvas_layout( project_path: String, mode: ProjectResourceCanvasLayoutMode, ) -> Result { read_project_resource_canvas_layout_at(Path::new(project_path.trim()), mode) } #[tauri::command] pub(crate) fn read_local_project_resource_graph( project_path: String, expected_project_id: String, resources: Vec, ) -> Result { let root = validated_local_project_directory_path(project_path.trim())?; enforce_project_auto_permission_policy(&root, "asset.list")?; read_project_resource_graph_at(&root, expected_project_id.trim(), resources) } #[tauri::command] pub(crate) fn update_local_project_resource_canvas_layout( project_path: String, expected_project_id: String, mode: ProjectResourceCanvasLayoutMode, expected_revision: u64, positions: Vec, ) -> Result { update_project_resource_canvas_layout_at( Path::new(project_path.trim()), mode, &expected_project_id, expected_revision, positions, ) } #[tauri::command] pub(crate) fn create_local_project_asset_canvas_draft( input: CreateAssetCanvasDraftInput, ) -> Result { let root = validated_local_project_directory_path(input.project_path.trim())?; create_asset_canvas_draft_at(&root, &input) } #[tauri::command] pub(crate) fn read_local_project_asset_canvas_draft( input: ReadAssetCanvasDraftInput, ) -> Result { let root = validated_local_project_directory_path(input.project_path.trim())?; read_asset_canvas_draft_at(&root, &input) } #[tauri::command] pub(crate) fn discover_local_project_asset_canvas_draft( input: DiscoverAssetCanvasDraftInput, ) -> Result { let root = validated_local_project_directory_path(input.project_path.trim())?; discover_asset_canvas_draft_at(&root, &input) } #[tauri::command] pub(crate) fn update_local_project_asset_canvas_draft( input: UpdateAssetCanvasDraftInput, ) -> Result { let root = validated_local_project_directory_path(input.project_path.trim())?; update_asset_canvas_draft_at(&root, &input) } #[tauri::command] pub(crate) fn store_local_project_asset_canvas_media( input: StoreAssetCanvasMediaInput, ) -> Result { let root = validated_local_project_directory_path(input.project_path.trim())?; store_asset_canvas_media_at(&root, &input) } #[tauri::command] pub(crate) fn stage_local_project_asset_canvas_image( input: StageAssetCanvasImageInput, ) -> Result { let root = validated_local_project_directory_path(input.project_path.trim())?; stage_asset_canvas_image_at(&root, &input) } #[tauri::command] pub(crate) async fn generate_local_project_asset_canvas_image( app: tauri::AppHandle, input: GenerateAssetCanvasImageInput, ) -> Result { let root = validated_local_project_directory_path(input.project_path.trim())?; let progress_app = app.clone(); let execution = generate_asset_canvas_image_at(&root, &input, move |payload| { let _ = progress_app.emit(ASSET_CANVAS_GENERATION_PROGRESS_EVENT, payload); }) .await?; if let Some(event) = execution.event.as_ref() { publish_asset_canvas_event_after_commit_at(&root, event, |payload| { app.emit( "game-creator-local-asset-committed", asset_canvas_committed_public_event(payload), ) .map_err(|error| error.to_string()) }); } Ok(execution.result) } #[tauri::command] pub(crate) async fn recover_local_project_asset_canvas_generations( app: tauri::AppHandle, input: RecoverAssetCanvasGenerationsInput, ) -> Result { let root = validated_local_project_directory_path(input.project_path.trim())?; let progress_app = app.clone(); let execution = recover_asset_canvas_generations_at(&root, &input, move |payload| { let _ = progress_app.emit(ASSET_CANVAS_GENERATION_PROGRESS_EVENT, payload); }) .await?; for event in &execution.events { publish_asset_canvas_event_after_commit_at(&root, event, |payload| { app.emit( "game-creator-local-asset-committed", asset_canvas_committed_public_event(payload), ) .map_err(|error| error.to_string()) }); } Ok(execution.result) } #[tauri::command] pub(crate) async fn confirm_local_project_asset_canvas_generation_service_identity( input: ConfirmAssetCanvasGenerationServiceIdentityInput, ) -> Result { let root = validated_local_project_directory_path(input.project_path.trim())?; confirm_asset_canvas_generation_service_identity_at(&root, &input).await } #[tauri::command] pub(crate) fn read_local_project_asset_canvas_media( input: ReadAssetCanvasMediaInput, ) -> Result { let root = validated_local_project_directory_path(input.project_path.trim())?; read_asset_canvas_media_at(&root, &input) } #[tauri::command] pub(crate) fn discard_local_project_asset_canvas_draft( input: DiscardAssetCanvasDraftInput, ) -> Result { let root = validated_local_project_directory_path(input.project_path.trim())?; discard_asset_canvas_draft_at(&root, &input) } #[tauri::command] pub(crate) fn commit_local_project_asset( app: tauri::AppHandle, input: CommitAssetCanvasInput, ) -> Result { let root = validated_local_project_directory_path(input.project_path.trim())?; let execution = commit_asset_canvas_at(&root, &input)?; if let Some(event) = execution.event.as_ref() { publish_asset_canvas_event_after_commit_at(&root, event, |payload| { app.emit( "game-creator-local-asset-committed", asset_canvas_committed_public_event(payload), ) .map_err(|error| error.to_string()) }); } Ok(execution.result) } #[tauri::command] pub(crate) fn recover_local_project_asset_canvas_transactions( app: tauri::AppHandle, input: RecoverAssetCanvasTransactionsInput, ) -> Result { let root = validated_local_project_directory_path(input.project_path.trim())?; let execution = recover_asset_canvas_transactions_at(&root, &input.expected_project_id)?; for event in &execution.events { publish_asset_canvas_event_after_commit_at(&root, event, |payload| { app.emit( "game-creator-local-asset-committed", asset_canvas_committed_public_event(payload), ) .map_err(|error| error.to_string()) }); } Ok(execution.result) } #[tauri::command] pub(crate) async fn control_agent_run( app: tauri::AppHandle, project_path: String, action: String, detail: Option, ) -> Result { let root = Path::new(project_path.trim()); let command_id = match action.trim() { "status" => "agent.run_status", "kill" => "agent.kill", "retry" => "agent.retry", "resume" => "agent.resume", _ => "agent.run_status", }; enforce_project_permission_policy(root, command_id)?; if matches!(action.trim(), "retry" | "resume") { enforce_project_permission_policy(root, "game.generate_draft")?; } let _lock = if command_id == "agent.run_status" { None } else { Some(acquire_project_write_lock(root, command_id)?) }; control_agent_run_at( root, action.trim(), detail .as_deref() .map(str::trim) .filter(|value| !value.is_empty()), Some(&AgentProgressEmitter::new(&app, project_path.trim())), ) .await } #[tauri::command] pub(crate) async fn generate_local_game_draft( app: tauri::AppHandle, project_path: String, prompt: String, ) -> Result { let root = Path::new(project_path.trim()); enforce_project_permission_policy(root, "game.generate_draft")?; let _lock = acquire_project_write_lock(root, "game.generate_draft")?; advance_agent_runtime_project_revision_locked(root)?; generate_local_game_draft_at( root, prompt.trim(), Some(&AgentProgressEmitter::new(&app, project_path.trim())), ) .await } #[tauri::command] pub(crate) async fn chat_with_game_creator_agent( project_path: String, prompt: String, ) -> Result { let root = Path::new(project_path.trim()); enforce_project_permission_policy(root, "conversation.read")?; chat_with_game_creator_agent_at(root, prompt.trim()).await } #[tauri::command] pub(crate) async fn chat_with_game_creator_role_agent( project_path: String, agent_id: String, session_id: Option, prompt: String, ) -> Result { let root = Path::new(project_path.trim()); let agent_id = normalize_game_creator_runtime_agent_id(agent_id.trim())?; enforce_project_permission_policy(root, "conversation.read")?; enforce_project_permission_policy(root, "conversation.write")?; let runtime_lock = try_acquire_game_creator_agent_runtime_task_lock(root, &agent_id)? .ok_or_else(|| format!("Agent 正在执行其他前台或后台任务:{agent_id}"))?; let result = chat_with_game_creator_role_agent_runtime_for_session_at( root, &agent_id, session_id.as_deref(), prompt.trim(), "", ) .await .map(|(reply, _runtime)| reply); spawn_next_game_creator_agent_background_task_drain_with_lock(root, &agent_id, runtime_lock); match result { Ok(reply) => Ok(reply), Err(error) => Err(error), } } #[tauri::command] pub(crate) async fn chat_with_game_creator_role_agent_stream( app: tauri::AppHandle, project_path: String, agent_id: String, session_id: Option, prompt: String, run_id: String, ) -> Result { let project_path = project_path.trim().to_string(); let agent_id = normalize_game_creator_runtime_agent_id(agent_id.trim())?; let run_id = run_id.trim().to_string(); let root = Path::new(project_path.as_str()); let session_id = resolve_agent_conversation_session_id_at(root, &agent_id, session_id.as_deref(), true)?; enforce_project_permission_policy(root, "conversation.read")?; enforce_project_permission_policy(root, "conversation.write")?; let runtime_lock = try_acquire_game_creator_agent_runtime_task_lock(root, &agent_id)? .ok_or_else(|| format!("Agent 正在执行其他前台或后台任务:{agent_id}"))?; let emit_app = app.clone(); let event_project_path = project_path.clone(); let event_agent_id = agent_id.clone(); let event_run_id = run_id.clone(); let command_result = async { let mut runtime_state = start_game_creator_agent_runtime_turn_for_session_at( root, agent_id.as_str(), Some(&session_id), prompt.trim(), &run_id, )?; runtime_state = advance_game_creator_agent_runtime_turn_at( root, runtime_state, "llm", "请求 Agent LLM", "已读取项目上下文,正在让 Agent 独立推理。", )?; let mut streaming_runtime_state = runtime_state.clone(); streaming_runtime_state.current_action = "正在接收 Agent 回复".to_string(); let _ = app.emit( "game-creator-role-agent-chat-stream", GameCreatorRoleAgentChatStreamEvent { project_path: event_project_path.clone(), agent_id: event_agent_id.clone(), run_id: event_run_id.clone(), status: "started".to_string(), delta_text: String::new(), accumulated_text: String::new(), finish_reason: None, session_id: Some(runtime_state.session_id.clone()), runtime_status: Some(runtime_state.status.clone()), runtime_phase: Some(runtime_state.phase.clone()), runtime_summary: Some(runtime_state.current_action.clone()), runtime_state: Some(runtime_state.clone()), }, ); let result = chat_with_game_creator_role_agent_stream_for_session_at( root, agent_id.as_str(), Some(&session_id), prompt.trim(), |delta| { let _ = emit_app.emit( "game-creator-role-agent-chat-stream", GameCreatorRoleAgentChatStreamEvent { project_path: event_project_path.clone(), agent_id: event_agent_id.clone(), run_id: event_run_id.clone(), status: "delta".to_string(), delta_text: delta.delta_text.clone(), accumulated_text: delta.accumulated_text.clone(), finish_reason: delta.finish_reason.clone(), session_id: Some(streaming_runtime_state.session_id.clone()), runtime_status: Some("running".to_string()), runtime_phase: Some("llm".to_string()), runtime_summary: Some("正在接收 Agent 回复".to_string()), runtime_state: Some(streaming_runtime_state.clone()), }, ); }, ) .await; match result { Ok(reply) => { let completed_runtime = finish_game_creator_agent_runtime_turn_at( root, runtime_state, &reply.reply_text, )?; let _ = app.emit( "game-creator-role-agent-chat-stream", GameCreatorRoleAgentChatStreamEvent { project_path: project_path.clone(), agent_id: agent_id.clone(), run_id: run_id.clone(), status: "completed".to_string(), delta_text: String::new(), accumulated_text: reply.reply_text.clone(), finish_reason: None, session_id: Some(completed_runtime.session_id.clone()), runtime_status: Some(completed_runtime.status.clone()), runtime_phase: Some(completed_runtime.phase.clone()), runtime_summary: Some(completed_runtime.current_action.clone()), runtime_state: Some(completed_runtime), }, ); Ok(reply) } Err(error) => { let failed_runtime = fail_game_creator_agent_runtime_turn_at(root, runtime_state, &error).ok(); let _ = app.emit( "game-creator-role-agent-chat-stream", GameCreatorRoleAgentChatStreamEvent { project_path: project_path.clone(), agent_id: agent_id.clone(), run_id: run_id.clone(), status: "failed".to_string(), delta_text: String::new(), accumulated_text: String::new(), finish_reason: None, session_id: failed_runtime .as_ref() .map(|runtime| runtime.session_id.clone()), runtime_status: failed_runtime .as_ref() .map(|runtime| runtime.status.clone()), runtime_phase: failed_runtime.as_ref().map(|runtime| runtime.phase.clone()), runtime_summary: failed_runtime .as_ref() .map(|runtime| runtime.current_action.clone()), runtime_state: failed_runtime, }, ); Err(error) } } } .await; spawn_next_game_creator_agent_background_task_drain_with_lock(root, &agent_id, runtime_lock); match command_result { Ok(reply) => Ok(reply), Err(error) => Err(error), } } #[tauri::command] pub(crate) fn start_game_creator_agent_runtime_task( project_path: String, agent_id: String, session_id: Option, task: String, run_id: String, ) -> Result { let root = Path::new(project_path.trim()); enforce_project_permission_policy(root, "conversation.read")?; enforce_project_permission_policy(root, "conversation.write")?; enforce_project_permission_policy(root, "agent.run_status")?; start_game_creator_agent_background_task_for_session_at( root, agent_id.trim(), session_id.as_deref(), task.trim(), run_id.trim(), ) } #[tauri::command] pub(crate) fn start_game_creator_supervisor_runtime_task( project_path: String, session_id: Option, task: String, run_id: String, run_profile: Option, source: Option, ) -> Result { let root = Path::new(project_path.trim()); enforce_project_permission_policy(root, "conversation.read")?; enforce_project_permission_policy(root, "conversation.write")?; enforce_project_permission_policy(root, "agent.run_status")?; let run_profile = run_profile .as_deref() .map(str::trim) .filter(|value| !value.is_empty()) .unwrap_or(AGENT_RUNTIME_RUN_PROFILE_STANDARD); let source = source .as_deref() .map(str::trim) .filter(|value| !value.is_empty()) .unwrap_or(AGENT_RUNTIME_SUPERVISOR_GUI_SOURCE); if !agent_runtime_supervisor_source_is_trusted(source) { return Err("Project Supervisor 提交 source 不受信任".to_string()); } start_game_creator_supervisor_background_task_for_session_at( root, session_id.as_deref(), task.trim(), run_id.trim(), source, run_profile, ) } #[tauri::command] pub(crate) async fn compact_game_creator_agent_runtime_context( project_path: String, agent_id: String, session_id: Option, ) -> Result { let root = Path::new(project_path.trim()); enforce_project_permission_policy(root, "conversation.read")?; enforce_project_permission_policy(root, "agent.compact")?; if external_agent_runner_enabled() && !external_agent_runner_is_server_process() { compact_external_agent_runner_context(root, agent_id.trim(), session_id.as_deref()) } else { compact_game_creator_agent_runtime_session_at(root, agent_id.trim(), session_id.as_deref()) .await } } #[tauri::command] pub(crate) fn read_game_creator_agent_goal( project_path: String, agent_id: String, session_id: String, ) -> Result, String> { let root = Path::new(project_path.trim()); enforce_project_permission_policy(root, "conversation.read")?; enforce_project_permission_policy(root, "agent.run_status")?; read_game_creator_agent_goal_at(root, agent_id.trim(), session_id.trim()) } #[tauri::command] pub(crate) fn start_game_creator_agent_goal( project_path: String, agent_id: String, session_id: Option, outcome: String, constraints: Vec, verification: Vec, run_id: String, ) -> Result { let root = Path::new(project_path.trim()); enforce_project_permission_policy(root, "conversation.read")?; enforce_project_permission_policy(root, "conversation.write")?; enforce_project_permission_policy(root, "agent.run_status")?; start_game_creator_agent_goal_at( root, agent_id.trim(), session_id.as_deref(), outcome.trim(), constraints, verification, run_id.trim(), ) } #[tauri::command] pub(crate) fn edit_game_creator_agent_goal( project_path: String, agent_id: String, session_id: String, goal_id: String, expected_revision: u64, outcome: String, constraints: Vec, verification: Vec, ) -> Result { let root = Path::new(project_path.trim()); enforce_project_permission_policy(root, "conversation.read")?; enforce_project_permission_policy(root, "conversation.write")?; enforce_project_permission_policy(root, "agent.run_status")?; edit_game_creator_agent_goal_at( root, agent_id.trim(), session_id.trim(), goal_id.trim(), expected_revision, outcome.trim(), constraints, verification, ) } #[tauri::command] pub(crate) fn pause_game_creator_agent_goal( project_path: String, agent_id: String, session_id: String, goal_id: String, expected_revision: u64, ) -> Result { let root = Path::new(project_path.trim()); enforce_project_permission_policy(root, "conversation.read")?; enforce_project_permission_policy(root, "conversation.write")?; enforce_project_permission_policy(root, "agent.run_status")?; pause_game_creator_agent_goal_at( root, agent_id.trim(), session_id.trim(), goal_id.trim(), expected_revision, ) } #[tauri::command] pub(crate) fn resume_game_creator_agent_goal( project_path: String, agent_id: String, session_id: String, goal_id: String, expected_revision: u64, ) -> Result { let root = Path::new(project_path.trim()); enforce_project_permission_policy(root, "conversation.read")?; enforce_project_permission_policy(root, "conversation.write")?; enforce_project_permission_policy(root, "agent.run_status")?; enforce_project_permission_policy(root, "agent.resume")?; resume_game_creator_agent_goal_at( root, agent_id.trim(), session_id.trim(), goal_id.trim(), expected_revision, ) } #[tauri::command] pub(crate) fn clear_game_creator_agent_goal( project_path: String, agent_id: String, session_id: String, goal_id: String, expected_revision: u64, ) -> Result { let root = Path::new(project_path.trim()); enforce_project_permission_policy(root, "conversation.read")?; enforce_project_permission_policy(root, "conversation.write")?; enforce_project_permission_policy(root, "agent.run_status")?; clear_game_creator_agent_goal_at( root, agent_id.trim(), session_id.trim(), goal_id.trim(), expected_revision, ) } #[tauri::command] pub(crate) async fn steer_game_creator_agent_runtime_task( project_path: String, agent_id: String, session_id: String, run_id: String, steer_id: String, instruction: String, run_profile: Option, source: Option, ) -> Result { let root = Path::new(project_path.trim()); enforce_project_permission_policy(root, "conversation.read")?; enforce_project_permission_policy(root, "conversation.write")?; enforce_project_permission_policy(root, "agent.run_status")?; if let Some(source) = source .as_deref() .map(str::trim) .filter(|value| !value.is_empty()) { if !agent_runtime_supervisor_source_is_trusted(source) { return Err("Project Supervisor steer source 不受信任".to_string()); } let task = read_latest_game_creator_agent_runtime_task_by_run_id( root, agent_id.trim(), run_id.trim(), )? .ok_or_else(|| "Agent Runtime steer 的 Run 不存在".to_string())?; if task.source != source { return Err("Agent Runtime steer source 与当前 Run 不一致".to_string()); } } let mut result = steer_game_creator_agent_runtime_task_for_profile_at( root, agent_id.trim(), session_id.trim(), run_id.trim(), steer_id.trim(), instruction.trim(), run_profile.as_deref(), "tauri", )?; let external_runner = external_agent_runner_enabled() && !external_agent_runner_is_server_process(); let wake_external_runner_without_interrupt = || -> Result<(), String> { let provider_interrupted = steer_external_agent_runner(root, agent_id.trim(), run_id.trim(), steer_id.trim())?; if provider_interrupted { return Err("Agent Runner 的 runtime.steer 非法中断了 Provider".to_string()); } Ok(()) }; let state = result.runtime.state.clone(); let requires_supervisor_decision = state.agent_id == GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID && state.parent_agent_id.is_none() && state.parent_run_id.is_none(); if requires_supervisor_decision { match decide_game_creator_agent_runtime_steer_at( root, &state, steer_id.trim(), result.sequence, instruction.trim(), ) .await { Ok(decision) => { result.assistant_reply = Some(decision.reply.clone()); result.interrupt_decision = Some(decision.interrupt_current_provider); result.decision_reason = Some(decision.reason.clone()); if decision.interrupt_current_provider { result.provider_interrupted = if external_runner { interrupt_external_agent_runner_provider_for_steer_decision( root, agent_id.trim(), run_id.trim(), steer_id.trim(), )? } else { interrupt_game_creator_agent_runtime_provider_for_decided_steer_at( root, agent_id.trim(), run_id.trim(), steer_id.trim(), )? }; if !external_runner { wake_pending_game_creator_agent_background_tasks_at(root) .map_err(|error| error.to_string())?; } } else if external_runner { wake_external_runner_without_interrupt()?; } else { wake_pending_game_creator_agent_background_tasks_at(root) .map_err(|error| error.to_string())?; } } Err(error) => { result.assistant_reply = Some( append_game_creator_agent_runtime_steer_decision_failure_reply_at( root, &state, steer_id.trim(), &error, )?, ); result.decision_reason = Some("decision-failed".to_string()); if external_runner { wake_external_runner_without_interrupt()?; } else { wake_pending_game_creator_agent_background_tasks_at(root) .map_err(|error| error.to_string())?; } } } } else if external_runner { wake_external_runner_without_interrupt()?; } else { wake_pending_game_creator_agent_background_tasks_at(root) .map_err(|error| error.to_string())?; } result.runtime = read_game_creator_agent_runtime_for_session_at( root, agent_id.trim(), Some(session_id.trim()), )?; Ok(result) } #[tauri::command] pub(crate) fn cancel_game_creator_agent_runtime_task( project_path: String, agent_id: String, run_id: String, ) -> Result { let root = Path::new(project_path.trim()); enforce_project_permission_policy(root, "conversation.read")?; enforce_project_permission_policy(root, "conversation.write")?; enforce_project_permission_policy(root, "agent.run_status")?; cancel_game_creator_agent_runtime_task_at(root, agent_id.trim(), run_id.trim()) } #[tauri::command] pub(crate) fn retry_game_creator_agent_runtime_task( project_path: String, agent_id: String, run_id: String, next_run_id: String, ) -> Result { let root = Path::new(project_path.trim()); enforce_project_permission_policy(root, "conversation.read")?; enforce_project_permission_policy(root, "conversation.write")?; enforce_project_permission_policy(root, "agent.run_status")?; enforce_project_auto_permission_policy(root, "agent.resume")?; retry_game_creator_agent_runtime_task_at( root, agent_id.trim(), run_id.trim(), next_run_id.trim(), ) } #[tauri::command] pub(crate) fn confirm_retry_game_creator_agent_runtime_task( project_path: String, agent_id: String, run_id: String, next_run_id: String, ) -> Result { let root = Path::new(project_path.trim()); enforce_project_permission_policy(root, "conversation.read")?; enforce_project_permission_policy(root, "conversation.write")?; enforce_project_permission_policy(root, "agent.run_status")?; // 正式工作台的“在当前项目重试”按钮本身就是用户对本次 agent.resume 的明确确认。 enforce_project_permission_policy(root, "agent.resume")?; retry_game_creator_agent_runtime_task_at( root, agent_id.trim(), run_id.trim(), next_run_id.trim(), ) } #[tauri::command] pub(crate) fn confirm_game_creator_agent_runtime_task( project_path: String, agent_id: String, run_id: String, action_id: String, note: String, ) -> Result { let root = Path::new(project_path.trim()); enforce_project_permission_policy(root, "conversation.read")?; enforce_project_permission_policy(root, "conversation.write")?; enforce_project_permission_policy(root, "agent.run_status")?; // 该命令只由开发者显式点击“确认继续”触发。 enforce_project_permission_policy(root, "agent.resume")?; confirm_game_creator_agent_runtime_task_at( root, agent_id.trim(), run_id.trim(), action_id.trim(), note.trim(), ) } #[tauri::command] pub(crate) fn reject_game_creator_agent_runtime_task( project_path: String, agent_id: String, run_id: String, action_id: String, note: String, ) -> Result { let root = Path::new(project_path.trim()); enforce_project_permission_policy(root, "conversation.read")?; enforce_project_permission_policy(root, "conversation.write")?; enforce_project_permission_policy(root, "agent.run_status")?; // 该命令只由开发者显式点击“拒绝并继续”触发。 enforce_project_permission_policy(root, "agent.resume")?; reject_game_creator_agent_runtime_task_at( root, agent_id.trim(), run_id.trim(), action_id.trim(), note.trim(), ) } #[tauri::command] pub(crate) fn answer_game_creator_agent_runtime_user_input( project_path: String, agent_id: String, run_id: String, action_id: String, request_id: String, response_id: String, answers: BTreeMap, ) -> Result { let root = Path::new(project_path.trim()); enforce_project_permission_policy(root, "conversation.read")?; enforce_project_permission_policy(root, "conversation.write")?; enforce_project_permission_policy(root, "agent.run_status")?; enforce_project_permission_policy(root, "agent.resume")?; answer_game_creator_agent_runtime_user_input_at( root, agent_id.trim(), run_id.trim(), action_id.trim(), request_id.trim(), response_id.trim(), answers, ) } #[tauri::command] pub(crate) fn read_game_creator_agent_runtime( project_path: String, agent_id: String, session_id: Option, ) -> Result { let root = Path::new(project_path.trim()); enforce_project_permission_policy(root, "conversation.read")?; read_game_creator_agent_runtime_for_session_at(root, agent_id.trim(), session_id.as_deref()) } #[tauri::command] pub(crate) fn read_game_creator_agent_runtimes( project_path: String, ) -> Result, String> { let root = Path::new(project_path.trim()); enforce_project_permission_policy(root, "conversation.read")?; read_game_creator_agent_runtimes_at(root) } #[tauri::command] pub(crate) fn resume_game_creator_agent_runtime_tasks( project_path: String, ) -> Result, String> { let root = Path::new(project_path.trim()); enforce_project_permission_policy(root, "conversation.read")?; enforce_project_permission_policy(root, "agent.run_status")?; if !has_recoverable_game_creator_agent_background_tasks_at(root)? { return Ok(Vec::new()); } enforce_project_permission_policy(root, "conversation.write")?; enforce_project_auto_permission_policy(root, "agent.resume")?; resume_game_creator_agent_background_tasks_at(root) } #[tauri::command] pub(crate) fn confirm_resume_game_creator_agent_runtime_tasks( project_path: String, ) -> Result, String> { let root = Path::new(project_path.trim()); enforce_project_permission_policy(root, "conversation.read")?; enforce_project_permission_policy(root, "conversation.write")?; enforce_project_permission_policy(root, "agent.run_status")?; // 该命令只由开发者在 agent.resume 确认卡中明确批准后调用。 enforce_project_permission_policy(root, "agent.resume")?; resume_game_creator_agent_background_tasks_at(root) } #[tauri::command] pub(crate) fn schedule_game_creator_agent_ready_tasks( project_path: String, limit: usize, ) -> Result, String> { let root = Path::new(project_path.trim()); enforce_project_permission_policy(root, "conversation.read")?; enforce_project_permission_policy(root, "conversation.write")?; enforce_project_permission_policy(root, "agent.run_status")?; enforce_project_auto_permission_policy(root, "agent.schedule_ready")?; schedule_game_creator_agent_ready_tasks_at(root, limit) } #[tauri::command] pub(crate) fn check_game_creator_llm_config() -> GameCreatorLlmConfigStatus { check_game_creator_llm_config_from_config() } #[tauri::command] pub(crate) fn read_game_creator_app_config() -> Result { game_creator_app_config_view(load_game_creator_app_config()?) } #[tauri::command] pub(crate) fn write_game_creator_app_config( config: GameCreatorAppConfig, ) -> Result { let config = normalize_game_creator_app_config(config)?; let path = writable_game_creator_config_path()?; let content = serde_json::to_string_pretty(&config) .map_err(|error| format!("序列化客户端配置失败:{error}"))?; write_game_creator_config_atomically(&path, &format!("{content}\n"))?; game_creator_app_config_view(load_game_creator_app_config()?) } #[tauri::command] pub(crate) fn read_game_creator_mcp_catalog( project_path: String, ) -> Result { read_external_agent_runner_mcp_catalog(Path::new(project_path.trim())) } #[tauri::command] pub(crate) fn upload_local_asset( project_path: String, file_name: String, media_type: String, bytes: Vec, ) -> Result { let root = Path::new(project_path.trim()); enforce_project_permission_policy(root, "asset.upload")?; let _lock = acquire_project_write_lock(root, "asset.upload")?; advance_agent_runtime_project_revision_locked(root)?; upload_local_asset_at(root, file_name.trim(), media_type.trim(), &bytes) } #[tauri::command] pub(crate) fn register_local_asset( project_path: String, local_path: String, kind: String, media_type: String, source_kind: String, canvas_project_id: Option, resource_id: Option, asset_object_id: Option, task_id: Option, prompt: Option, model: Option, ) -> Result { let root = Path::new(project_path.trim()); enforce_project_permission_policy(root, "asset.register")?; let _lock = acquire_project_write_lock(root, "asset.register")?; advance_agent_runtime_project_revision_locked(root)?; register_local_asset_at( root, local_path.trim(), kind.trim(), media_type.trim(), source_kind.trim(), GameCreationAppAssetSource { kind: parse_asset_source_kind(source_kind.trim())?, canvas_project_id: trim_optional_string(canvas_project_id), resource_id: trim_optional_string(resource_id), asset_object_id: trim_optional_string(asset_object_id), task_id: trim_optional_string(task_id), prompt: trim_optional_string(prompt), model: trim_optional_string(model), generation_route: None, generation_kind: None, reference_resource_ids: Vec::new(), }, ) } #[tauri::command] pub(crate) async fn derive_local_project_resource( input: DeriveLocalProjectResourceInput, ) -> Result { let root = Path::new(input.project_path.trim()); enforce_project_permission_policy(root, "asset.register")?; derive_local_project_resource_at(input).await } #[tauri::command] pub(crate) fn list_pending_local_project_resource_edits( input: ListPendingLocalProjectResourceEditsInput, ) -> Result, String> { let root = Path::new(input.project_path.trim()); enforce_project_permission_policy(root, "file.list")?; list_pending_local_project_resource_edits_at(input) } #[tauri::command] pub(crate) async fn resume_local_project_resource_edit( input: ResumeLocalProjectResourceEditInput, ) -> Result { let root = Path::new(input.project_path.trim()); enforce_project_permission_policy(root, "asset.register")?; resume_local_project_resource_edit_at(input).await } #[tauri::command] pub(crate) async fn request_local_project_resource_edit_service_identity_confirmation( input: RequestResourceEditServiceIdentityConfirmationInput, ) -> Result { let root = Path::new(input.project_path.trim()); enforce_project_permission_policy(root, "asset.register")?; request_resource_edit_service_identity_confirmation_at(input).await } #[tauri::command] pub(crate) async fn confirm_local_project_resource_edit_service_identity( input: ConfirmResourceEditServiceIdentityInput, ) -> Result { let root = Path::new(input.project_path.trim()); enforce_project_permission_policy(root, "asset.register")?; confirm_resource_edit_service_identity_at(input).await } #[tauri::command] pub(crate) async fn archive_failed_local_project_resource_edit( input: ArchiveFailedLocalProjectResourceEditInput, ) -> Result { let root = Path::new(input.project_path.trim()); enforce_project_permission_policy(root, "asset.register")?; archive_failed_local_project_resource_edit_at(input).await } #[tauri::command] pub(crate) fn normalize_local_project_raster_resource( input: NormalizeLocalProjectRasterResourceInput, ) -> Result { let root = Path::new(input.project_path.trim()); enforce_project_permission_policy(root, "asset.register")?; normalize_local_project_raster_resource_at(input) } #[tauri::command] pub(crate) fn import_canvas_asset( project_path: String, local_path: String, kind: String, media_type: String, canvas_project_id: String, resource_id: Option, asset_object_id: Option, task_id: Option, prompt: Option, model: Option, ) -> Result { let root = Path::new(project_path.trim()); enforce_project_permission_policy(root, "canvas.asset_import")?; let _lock = acquire_project_write_lock(root, "canvas.asset_import")?; advance_agent_runtime_project_revision_locked(root)?; import_canvas_asset_at( root, local_path.trim(), kind.trim(), media_type.trim(), canvas_project_id.trim(), trim_optional_string(resource_id), trim_optional_string(asset_object_id), trim_optional_string(task_id), trim_optional_string(prompt), trim_optional_string(model), ) } #[tauri::command] pub(crate) fn import_canvas_export( project_path: String, export_path: String, canvas_project_id: String, ) -> Result { let root = Path::new(project_path.trim()); enforce_project_permission_policy(root, "canvas.export_import")?; let _lock = acquire_project_write_lock(root, "canvas.export_import")?; advance_agent_runtime_project_revision_locked(root)?; import_canvas_export_at( root, Path::new(export_path.trim()), canvas_project_id.trim(), ) } #[tauri::command] pub(crate) async fn sync_canvas_project_assets( project_path: String, canvas_project_id: String, api_base_url: Option, api_key: Option, ) -> Result { let root = Path::new(project_path.trim()); enforce_project_permission_policy(root, "canvas.project_sync")?; let _lock = acquire_project_write_lock(root, "canvas.project_sync")?; advance_agent_runtime_project_revision_locked(root)?; sync_canvas_project_assets_at(root, canvas_project_id.trim(), api_base_url, api_key).await } #[tauri::command] pub(crate) async fn generate_platform_art_asset( project_path: String, prompt: String, ) -> Result { let root = Path::new(project_path.trim()); enforce_project_permission_policy(root, "canvas.asset_generate")?; let generated = generate_platform_art_asset_at(root, prompt.trim(), &[]).await?; Ok(generated.asset) } #[tauri::command] pub(crate) fn open_canvas_project( app: tauri::AppHandle, canvas_project_id: Option, editor_base_url: Option, ) -> Result { let url = build_canvas_project_url(editor_base_url.as_deref(), canvas_project_id.as_deref())?; app.opener() .open_url(&url, None::<&str>) .map_err(|error| format!("打开画板失败:{error}"))?; Ok(OpenCanvasProjectResult { url }) } #[tauri::command] pub(crate) fn get_game_creation_agent_capabilities() -> Vec { GAME_CREATION_AGENT_CAPABILITIES.to_vec() } #[tauri::command] pub(crate) fn get_limited_local_commands() -> Vec { GAME_CREATION_APP_LIMITED_RUN_COMMANDS.to_vec() } #[tauri::command] pub(crate) fn run_limited_local_command( project_path: String, command_id: String, ) -> Result { let root = Path::new(project_path.trim()); let command_id = command_id.trim(); enforce_project_permission_policy(root, "command.run_limited")?; let _lock = acquire_project_write_lock(root, "command.run_limited")?; let result = run_limited_local_command_at(root, command_id)?; if command_id == "game.static_smoke" { append_static_smoke_manual_trace_step(root, &result)?; } Ok(result) } #[tauri::command] pub(crate) fn append_local_permission_log( project_path: String, event: String, command_id: String, ) -> Result<(), String> { append_local_permission_log_at( Path::new(project_path.trim()), event.trim(), command_id.trim(), ) } #[tauri::command] pub(crate) fn list_local_project_files( project_path: String, ) -> Result { let root = Path::new(project_path.trim()); enforce_project_permission_policy(root, "file.list")?; list_local_project_files_at(root) } #[tauri::command] pub(crate) fn read_local_project_file( project_path: String, relative_path: String, command_id: Option, ) -> Result { let root = Path::new(project_path.trim()); let normalized_path = normalize_relative_path(relative_path.trim())?; let command_id = command_id .as_deref() .map(str::trim) .filter(|value| !value.is_empty()) .unwrap_or("file.read"); if command_id == "agent.trace_read" { if !is_agent_trace_read_path(&normalized_path) { return Err("agent.trace_read 只能读取 Agent run trace".to_string()); } } else if command_id != "file.read" { return Err(format!("不支持通过文件读取执行命令:{command_id}")); } enforce_project_permission_policy(root, command_id)?; read_local_project_file_at(root, &normalized_path) } #[tauri::command] pub(crate) async fn read_local_project_image_preview( preview_manager: tauri::State<'_, ProjectResourcePreviewReadManager>, project_path: String, relative_path: String, scope_id: String, request_id: String, ) -> Result { preview_manager .run(&scope_id, &request_id, move |cancellation| { read_local_project_image_preview_at(&project_path, &relative_path, cancellation) }) .await } pub(crate) fn read_local_project_image_preview_at( project_path: &str, relative_path: &str, cancellation: &ProjectResourcePreviewScopeCancellation, ) -> Result { cancellation.check()?; let root = Path::new(project_path.trim()); enforce_project_auto_permission_policy(root, "file.read")?; cancellation.check()?; let normalized_path = normalize_relative_path(relative_path.trim())?; let manifest = read_manifest(&root.join(".agent/manifest.json"))?; cancellation.check()?; let is_registered_asset = manifest .assets .iter() .any(|asset| asset.local_path == normalized_path); let is_completed_task_artifact = manifest.tasks.iter().any(|task| { task.status == GameCreationAppTaskStatus::Completed && task.artifacts.iter().any(|path| path == &normalized_path) }); if !is_registered_asset && !is_completed_task_artifact { return Err("只能预览已登记资源或已完成任务的图片产物".to_string()); } cancellation.check()?; load_local_project_image_preview_with_cancellation(root, &normalized_path, cancellation) } #[tauri::command] pub(crate) async fn read_local_project_text_preview( preview_manager: tauri::State<'_, ProjectResourcePreviewReadManager>, project_path: String, relative_path: String, scope_id: String, request_id: String, ) -> Result { preview_manager .run(&scope_id, &request_id, move |cancellation| { read_local_project_text_preview_at(&project_path, &relative_path, cancellation) }) .await } pub(crate) fn read_local_project_text_preview_at( project_path: &str, relative_path: &str, cancellation: &ProjectResourcePreviewScopeCancellation, ) -> Result { cancellation.check()?; let root = Path::new(project_path.trim()); enforce_project_auto_permission_policy(root, "file.read")?; cancellation.check()?; let normalized_path = normalize_relative_path(relative_path.trim())?; let manifest = read_manifest(&root.join(".agent/manifest.json"))?; cancellation.check()?; let is_registered_document = manifest.assets.iter().any(|asset| { asset.local_path == normalized_path && is_supported_project_text_resource(&asset.local_path, &asset.media_type) }) || manifest.tasks.iter().any(|task| { task.status == GameCreationAppTaskStatus::Completed && task.artifacts.iter().any(|path| path == &normalized_path) && is_supported_project_text_resource(&normalized_path, "") }); if !is_registered_document { return Err("只能读取当前项目已登记的文档资源".to_string()); } cancellation.check()?; load_local_project_text_preview_with_cancellation(root, &normalized_path, cancellation) } #[tauri::command] pub(crate) async fn read_local_project_media_preview( preview_manager: tauri::State<'_, ProjectResourcePreviewReadManager>, project_path: String, relative_path: String, category: String, scope_id: String, request_id: String, ) -> Result { preview_manager .run(&scope_id, &request_id, move |cancellation| { read_local_project_media_preview_at( &project_path, &relative_path, &category, cancellation, ) }) .await } pub(crate) fn read_local_project_media_preview_at( project_path: &str, relative_path: &str, category: &str, cancellation: &ProjectResourcePreviewScopeCancellation, ) -> Result { cancellation.check()?; let root = Path::new(project_path.trim()); enforce_project_auto_permission_policy(root, "file.read")?; cancellation.check()?; let normalized_path = normalize_relative_path(relative_path.trim())?; let manifest = read_manifest(&root.join(".agent/manifest.json"))?; cancellation.check()?; let kind = match category.trim() { "art" => ProjectMediaPreviewKind::Art, "audio" => ProjectMediaPreviewKind::Audio, _ => return Err("媒体预览类别只支持 art 或 audio".to_string()), }; let is_registered_media = manifest.assets.iter().any(|asset| { asset.local_path == normalized_path && match kind { ProjectMediaPreviewKind::Art => { is_supported_project_art_media_resource(&asset.local_path, &asset.media_type) } ProjectMediaPreviewKind::Audio => { is_supported_project_audio_resource(&asset.local_path, &asset.media_type) } } }) || manifest.tasks.iter().any(|task| { task.status == GameCreationAppTaskStatus::Completed && task.artifacts.iter().any(|path| path == &normalized_path) && match kind { ProjectMediaPreviewKind::Art => { is_supported_project_art_media_resource(&normalized_path, "") } ProjectMediaPreviewKind::Audio => { is_supported_project_audio_resource(&normalized_path, "") } } }); if !is_registered_media { return Err("只能预览当前项目已登记的媒体资源".to_string()); } cancellation.check()?; load_local_project_media_preview_with_cancellation(root, &normalized_path, kind, cancellation) } #[tauri::command] pub(crate) fn cancel_local_project_resource_preview_scope( preview_manager: tauri::State<'_, ProjectResourcePreviewReadManager>, scope_id: String, ) -> Result<(), String> { preview_manager.cancel_scope(&scope_id) } #[tauri::command] pub(crate) fn write_local_project_file( project_path: String, relative_path: String, content: String, ) -> Result { let root = Path::new(project_path.trim()); enforce_project_permission_policy(root, "file.write")?; let _lock = acquire_project_write_lock(root, "file.write")?; advance_agent_runtime_project_revision_locked(root)?; write_local_project_file_at(root, relative_path.trim(), &content) } #[tauri::command] pub(crate) fn delete_local_project_file( project_path: String, relative_path: String, ) -> Result { let root = Path::new(project_path.trim()); enforce_project_permission_policy(root, "file.delete")?; let _lock = acquire_project_write_lock(root, "file.delete")?; advance_agent_runtime_project_revision_locked(root)?; delete_local_project_file_at(root, relative_path.trim()) } #[tauri::command] pub(crate) fn read_local_game_memory( project_path: String, scope: String, ) -> Result { let root = Path::new(project_path.trim()); enforce_project_permission_policy(root, "memory.read")?; read_local_game_memory_at(root, scope.trim()) } #[tauri::command] pub(crate) fn read_local_agent_memory( project_path: String, task_id: String, ) -> Result { let root = Path::new(project_path.trim()); let task_id = normalize_game_creator_runtime_agent_id(task_id.trim())?; enforce_project_permission_policy(root, "memory.read")?; read_local_agent_memory_at(root, &task_id) } #[tauri::command] pub(crate) fn write_local_agent_memory( project_path: String, task_id: String, content: String, ) -> Result { let root = Path::new(project_path.trim()); let task_id = normalize_game_creator_runtime_agent_id(task_id.trim())?; enforce_project_permission_policy(root, "memory.write")?; let _lock = acquire_project_write_lock(root, "memory.write")?; advance_agent_runtime_project_revision_locked(root)?; write_local_agent_memory_at(root, &task_id, &content) } #[tauri::command] pub(crate) fn write_local_game_memory( project_path: String, scope: String, content: String, ) -> Result { let root = Path::new(project_path.trim()); enforce_project_permission_policy(root, "memory.write")?; let _lock = acquire_project_write_lock(root, "memory.write")?; advance_agent_runtime_project_revision_locked(root)?; write_local_game_memory_at(root, scope.trim(), &content) } #[tauri::command] pub(crate) fn delete_local_game_memory( project_path: String, scope: String, ) -> Result { let root = Path::new(project_path.trim()); enforce_project_permission_policy(root, "memory.delete")?; let _lock = acquire_project_write_lock(root, "memory.delete")?; advance_agent_runtime_project_revision_locked(root)?; delete_local_game_memory_at(root, scope.trim()) } #[tauri::command] pub(crate) fn list_game_creator_agent_sessions( project_path: String, agent_id: String, ) -> Result { let root = Path::new(project_path.trim()); enforce_project_permission_policy(root, "conversation.read")?; list_game_creator_agent_sessions_at(root, agent_id.trim()) } #[tauri::command] pub(crate) fn create_game_creator_agent_session( project_path: String, agent_id: String, title: String, ) -> Result { let root = Path::new(project_path.trim()); enforce_project_permission_policy(root, "conversation.read")?; enforce_project_permission_policy(root, "conversation.write")?; let _lock = acquire_project_write_lock(root, "conversation.write")?; create_game_creator_agent_session_at(root, agent_id.trim(), title.trim()) } #[tauri::command] pub(crate) fn fork_game_creator_agent_session( project_path: String, agent_id: String, source_session_id: String, title: String, ) -> Result { let root = Path::new(project_path.trim()); enforce_project_permission_policy(root, "conversation.read")?; enforce_project_permission_policy(root, "conversation.write")?; let _lock = acquire_project_write_lock(root, "conversation.write")?; fork_game_creator_agent_session_at( root, agent_id.trim(), source_session_id.trim(), title.trim(), ) } #[tauri::command] pub(crate) fn set_active_game_creator_agent_session( project_path: String, agent_id: String, session_id: String, ) -> Result { let root = Path::new(project_path.trim()); enforce_project_permission_policy(root, "conversation.read")?; enforce_project_permission_policy(root, "conversation.write")?; let _lock = acquire_project_write_lock(root, "conversation.write")?; set_active_game_creator_agent_session_at(root, agent_id.trim(), session_id.trim()) } #[tauri::command] pub(crate) fn archive_game_creator_agent_session( project_path: String, agent_id: String, session_id: String, ) -> Result { let root = Path::new(project_path.trim()); enforce_project_permission_policy(root, "conversation.read")?; enforce_project_permission_policy(root, "conversation.write")?; let _lock = acquire_project_write_lock(root, "conversation.write")?; archive_game_creator_agent_session_at(root, agent_id.trim(), session_id.trim()) } #[tauri::command] pub(crate) fn read_local_conversation( project_path: String, agent_id: Option, session_id: Option, ) -> Result { let root = Path::new(project_path.trim()); enforce_project_permission_policy(root, "conversation.read")?; read_local_conversation_for_session_at(root, agent_id.as_deref(), session_id.as_deref()) } #[tauri::command] pub(crate) fn append_local_conversation_message( project_path: String, agent_id: Option, session_id: Option, message: LocalConversationMessage, message_id: Option, ) -> Result { let root = Path::new(project_path.trim()); enforce_project_permission_policy(root, "conversation.write")?; let _lock = acquire_project_write_lock(root, "conversation.write")?; match message_id.as_deref() { Some(message_id) => append_local_conversation_message_for_session_idempotent_at( root, agent_id.as_deref(), session_id.as_deref(), message, message_id, ), None => append_local_conversation_message_for_session_at( root, agent_id.as_deref(), session_id.as_deref(), message, ), } } #[tauri::command] pub(crate) fn build_local_project_index( project_path: String, ) -> Result { let root = Path::new(project_path.trim()); enforce_project_permission_policy(root, "project.index")?; let _lock = acquire_project_write_lock(root, "project.index")?; build_local_project_index_at(root) } #[tauri::command] pub(crate) fn create_local_project_checkpoint( project_path: String, ) -> Result { let root = Path::new(project_path.trim()); enforce_project_permission_policy(root, "project.checkpoint")?; let _lock = acquire_project_write_lock(root, "project.checkpoint")?; create_local_project_checkpoint_at(root) } #[tauri::command] pub(crate) fn export_local_project_package( project_path: String, ) -> Result { let root = Path::new(project_path.trim()); enforce_project_permission_policy(root, "project.export_package")?; let _lock = acquire_project_write_lock(root, "project.export_package")?; advance_agent_runtime_project_revision_locked(root)?; export_local_project_package_at(root) } #[tauri::command] pub(crate) fn list_local_project_export_packages( project_path: String, ) -> Result { let root = Path::new(project_path.trim()); enforce_project_permission_policy(root, "project.export_list")?; list_local_project_export_packages_at(root) } #[tauri::command] pub(crate) fn diff_local_project_checkpoint( project_path: String, checkpoint_id: String, ) -> Result { let root = Path::new(project_path.trim()); enforce_project_permission_policy(root, "project.diff")?; diff_local_project_checkpoint_at(root, checkpoint_id.trim()) } #[tauri::command] pub(crate) fn restore_local_project_checkpoint( project_path: String, checkpoint_id: String, ) -> Result { let root = Path::new(project_path.trim()); enforce_project_permission_policy(root, "project.restore")?; let _lock = acquire_project_write_lock(root, "project.restore")?; advance_agent_runtime_project_revision_locked(root)?; restore_local_project_checkpoint_at(root, checkpoint_id.trim()) } #[tauri::command] pub(crate) fn read_project_permission_policy( project_path: String, ) -> Result { let root = Path::new(project_path.trim()); enforce_project_permission_policy(root, "project.policy_read")?; read_project_permission_policy_at(root) } #[tauri::command] pub(crate) fn write_project_permission_policy( project_path: String, policy: ProjectPermissionPolicy, ) -> Result { let root = Path::new(project_path.trim()); enforce_project_permission_policy(root, "project.policy_write")?; let _lock = acquire_project_write_lock(root, "project.policy_write")?; write_project_permission_policy_at(root, policy) }