diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/autonomous_policy.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/autonomous_policy.rs index eeb2f00ff..3fa69f520 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/autonomous_policy.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/autonomous_policy.rs @@ -48,7 +48,9 @@ pub(in crate::agent) fn validate_agent_runtime_autonomous_complete_game_index_wr ) -> Result<(), String> { if path .and_then(serde_json::Value::as_str) - .map_or(true, |path| !is_agent_runtime_game_entry_relative_path(path)) + .map_or(true, |path| { + !is_agent_runtime_game_entry_relative_path(path) + }) { return Ok(()); } @@ -793,7 +795,7 @@ fn autonomous_manifest_parent_has_active_ready_task_at( let records = latest_game_creator_agent_runtime_tasks(read_all_game_creator_agent_runtime_tasks( &game_creator_agent_runtime_task_path(root, task_id), - )?); + )?); for record in records { if record.source != "agent-ready-task-scheduler" || game_creator_agent_runtime_terminal_status(&record).is_some() @@ -806,10 +808,9 @@ fn autonomous_manifest_parent_has_active_ready_task_at( // binding's root id solely to associate an in-flight child with // this root; never reject or block the child for a mismatch. if record.run_profile == AGENT_RUNTIME_RUN_PROFILE_AUTONOMOUS_GAME_BUILD { - let parent_hint_matches = - record.parent_agent_id.as_deref() - == Some(GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID) - && record.parent_run_id.as_deref() == Some(parent_run_id); + let parent_hint_matches = record.parent_agent_id.as_deref() + == Some(GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID) + && record.parent_run_id.as_deref() == Some(parent_run_id); let binding_root_matches = read_game_creator_agent_runtime_run_profile_binding( root, &record.agent_id, @@ -821,8 +822,7 @@ fn autonomous_manifest_parent_has_active_ready_task_at( } continue; } - if record.parent_agent_id.as_deref() - != Some(GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID) + if record.parent_agent_id.as_deref() != Some(GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID) || record.parent_run_id.as_deref() != Some(parent_run_id) { continue; diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/pending_confirmation_ledger.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/pending_confirmation_ledger.rs index c1376c327..639bc29d4 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/pending_confirmation_ledger.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/pending_confirmation_ledger.rs @@ -338,7 +338,8 @@ pub(in crate::agent) fn validate_agent_runtime_pending_tool_action_record( validate_agent_runtime_pending_goal_binding(pending)?; match pending.planning_session_binding.as_ref() { Some(binding) => { - validate_plan_provider_session_binding(binding).map_err(|error| error.to_string())?; + validate_plan_provider_session_binding(binding) + .map_err(|error| error.to_string())?; if pending.action.tool.trim() != PLAN_SUBMIT_GDD_TOOL || binding.agent_id != pending.agent_id || binding.task_id != pending.task_id diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/provider_action_batch.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/provider_action_batch.rs index b80a778ab..f48ce7884 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/provider_action_batch.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/provider_action_batch.rs @@ -541,14 +541,14 @@ pub(crate) async fn prepare_game_creator_agent_runtime_provider_action_batch_wit } if !relaxed_autonomous { if let Some(violation) = collaboration_preflight.violation { - return Ok(AgentRuntimeProviderActionBatchPreparation::Blocked( - AgentRuntimeToolObservation { - tool: "runtime.collaboration_policy".to_string(), - status: "blocked".to_string(), - summary: violation.summary, - detail: Some(violation.detail), - }, - )); + return Ok(AgentRuntimeProviderActionBatchPreparation::Blocked( + AgentRuntimeToolObservation { + tool: "runtime.collaboration_policy".to_string(), + status: "blocked".to_string(), + summary: violation.summary, + detail: Some(violation.detail), + }, + )); } } if provider_action_batch_is_not_needed( @@ -594,12 +594,13 @@ pub(crate) async fn prepare_game_creator_agent_runtime_provider_action_batch_wit "当前 Agent 身份不允许执行该原始工具".to_string(), ) }); - let art_director_canvas_only_block = agent_runtime_autonomous_art_director_canvas_only_action_block( - &runtime.agent_id, - task, - action.tool.trim(), - ) - .map(|observation| AgentRuntimeToolPolicyBlock::Denied(observation.summary)); + let art_director_canvas_only_block = + agent_runtime_autonomous_art_director_canvas_only_action_block( + &runtime.agent_id, + task, + action.tool.trim(), + ) + .map(|observation| AgentRuntimeToolPolicyBlock::Denied(observation.summary)); let isolated_scope_block = if runtime.agent_id.starts_with("child-") { validate_isolated_agent_tool_scope_at( root, diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/provider_tool_plan.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/provider_tool_plan.rs index d2c75a829..e63d79b96 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/provider_tool_plan.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/provider_tool_plan.rs @@ -439,19 +439,18 @@ pub(in crate::agent) async fn request_game_creator_agent_background_tool_plan_at let code_prototype_requires_static_smoke = !relaxed_autonomous && run_profile == AGENT_RUNTIME_RUN_PROFILE_AUTONOMOUS_GAME_BUILD && agent_id == "code-prototype"; - let verified_delivery = if !relaxed_autonomous - && run_profile == AGENT_RUNTIME_RUN_PROFILE_AUTONOMOUS_GAME_BUILD - { - let verification_gate = - read_game_creator_agent_runtime_verification_gate(root, agent_id, run_id)?; - runtime_owner_artifact_validation_available - || agent_runtime_autonomous_verified_delivery_allows_plan_completion( - agent_id, - &verification_gate, - ) - } else { - false - }; + let verified_delivery = + if !relaxed_autonomous && run_profile == AGENT_RUNTIME_RUN_PROFILE_AUTONOMOUS_GAME_BUILD { + let verification_gate = + read_game_creator_agent_runtime_verification_gate(root, agent_id, run_id)?; + runtime_owner_artifact_validation_available + || agent_runtime_autonomous_verified_delivery_allows_plan_completion( + agent_id, + &verification_gate, + ) + } else { + false + }; let allow_runtime_plan_completion = read_only_delivery || verified_delivery; let autonomous_project_verify_available = relaxed_autonomous || run_profile != AGENT_RUNTIME_RUN_PROFILE_AUTONOMOUS_GAME_BUILD @@ -758,8 +757,7 @@ pub(in crate::agent) async fn request_game_creator_agent_background_tool_plan_at }; let parsed = match parsed { Ok((parsed, source_payload)) - if agent_id == GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID - && !relaxed_autonomous => + if agent_id == GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID && !relaxed_autonomous => { let collaboration_policy = resolve_supervisor_collaboration_policy_for_run_at(root, agent_id, run_id)? diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/finalization.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/finalization.rs index 5f146400a..6dae3634e 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/finalization.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/finalization.rs @@ -245,36 +245,39 @@ pub(in crate::agent) fn resume_game_creator_agent_finalization_at( let assistant_exists = game_creator_agent_runtime_finalization_assistant_exists(root, &journal)?; if !relaxed_autonomous { - match classify_game_creator_agent_runtime_finalization_goal_snapshot_at(root, &journal, &state)? - { - AgentRuntimeFinalizationGoalSnapshotRelation::Matches => {} - AgentRuntimeFinalizationGoalSnapshotRelation::StaleRevision { - journal_revision, - current_revision, - } if journal.status == AGENT_RUNTIME_FINALIZATION_STATUS_PREPARED && !assistant_exists => { - remove_game_creator_agent_runtime_finalization_recovery_sidecars( - root, - &journal.agent_id, - &journal.run_id, - )?; - let _ = append_agent_db_record( - root, - serde_json::json!({ - "recordType": "agent.runtime.background_task.finalization_stale_recovered", - "agentId": journal.agent_id, - "taskId": journal.task_id, - "sessionId": journal.session_id, - "runId": journal.run_id, - "source": journal.source, - "summary": "Goal revision 已更新,旧最终回复已丢弃", - "journalGoalRevision": journal_revision, - "currentGoalRevision": current_revision, - }), - ); - return Ok(AgentRuntimeFinalizationResume::NotFound(runtime_lock)); - } - relation => { - let detail = match relation { + match classify_game_creator_agent_runtime_finalization_goal_snapshot_at( + root, &journal, &state, + )? { + AgentRuntimeFinalizationGoalSnapshotRelation::Matches => {} + AgentRuntimeFinalizationGoalSnapshotRelation::StaleRevision { + journal_revision, + current_revision, + } if journal.status == AGENT_RUNTIME_FINALIZATION_STATUS_PREPARED + && !assistant_exists => + { + remove_game_creator_agent_runtime_finalization_recovery_sidecars( + root, + &journal.agent_id, + &journal.run_id, + )?; + let _ = append_agent_db_record( + root, + serde_json::json!({ + "recordType": "agent.runtime.background_task.finalization_stale_recovered", + "agentId": journal.agent_id, + "taskId": journal.task_id, + "sessionId": journal.session_id, + "runId": journal.run_id, + "source": journal.source, + "summary": "Goal revision 已更新,旧最终回复已丢弃", + "journalGoalRevision": journal_revision, + "currentGoalRevision": current_revision, + }), + ); + return Ok(AgentRuntimeFinalizationResume::NotFound(runtime_lock)); + } + relation => { + let detail = match relation { AgentRuntimeFinalizationGoalSnapshotRelation::StaleRevision { journal_revision, current_revision, @@ -284,14 +287,16 @@ pub(in crate::agent) fn resume_game_creator_agent_finalization_at( AgentRuntimeFinalizationGoalSnapshotRelation::Conflict(detail) => detail, AgentRuntimeFinalizationGoalSnapshotRelation::Matches => unreachable!(), }; - let error = format!("Agent Runtime finalization 恢复已阻断:Goal 快照冲突:{detail}"); - record_game_creator_agent_runtime_finalization_pending(root, &state, &error); - return read_game_creator_agent_runtime_at(root, agent_id) - .map(AgentRuntimeFinalizationResume::Blocked); - } + let error = + format!("Agent Runtime finalization 恢复已阻断:Goal 快照冲突:{detail}"); + record_game_creator_agent_runtime_finalization_pending(root, &state, &error); + return read_game_creator_agent_runtime_at(root, agent_id) + .map(AgentRuntimeFinalizationResume::Blocked); + } } } - if !relaxed_autonomous && assistant_exists + if !relaxed_autonomous + && assistant_exists && !state_reconstructed_from_task && !game_creator_agent_runtime_finalization_plan_matches_state(&journal, &state) { @@ -415,11 +420,13 @@ pub(in crate::agent) fn resume_game_creator_agent_finalization_at( goal_contract_acceptance_completion_blocker_at_locked(&root, &state) { Some(blocker) - } else if let Some(blocker) = agent_runtime_non_verification_completion_blocker_at_locked( - &root, - &journal.agent_id, - &journal.run_id, - ) { + } else if let Some(blocker) = + agent_runtime_non_verification_completion_blocker_at_locked( + &root, + &journal.agent_id, + &journal.run_id, + ) + { Some(blocker) } else if let Some(blocker) = autonomous_game_build_completion_blocker_at_locked(&root, &state) diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/main_loop.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/main_loop.rs index 611cfdc15..abcd741a8 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/main_loop.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/main_loop.rs @@ -810,101 +810,15 @@ async fn run_game_creator_agent_background_task_pass_without_deadline( // impossible for a relaxed run to read the DAG and accidentally // re-enter `waiting-for-manifest-tasks`. if !relaxed_autonomous { - let autonomous_root_goal_contract_persisted = - if agent_id == GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID - && runtime.run_profile == AGENT_RUNTIME_RUN_PROFILE_AUTONOMOUS_GAME_BUILD - { - match autonomous_root_goal_contract_persisted_at( - &root, - &agent_id, - &runtime.run_id, - ) { - Ok(value) => value, - Err(error) => { - return fail_game_creator_agent_background_context_at( - &root, - &agent_id, - &session_id, - runtime, - &format!("读取自主构建根 Goal Contract 门失败:{error}"), - ); - } - } - } else { - false - }; - let autonomous_manifest_parent_can_wait = - autonomous_root_goal_contract_persisted - && agent_id == GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID - && runtime.run_profile == AGENT_RUNTIME_RUN_PROFILE_AUTONOMOUS_GAME_BUILD - && !game_creator_agent_runtime_provider_action_batch_exists( - &root, - &agent_id, - &runtime.run_id, - ) - && supervisor_collaboration_policy_completion_blocker_at_locked( - &root, - &agent_id, - &runtime.run_id, - ) - .is_none() - && isolated_join_completion_blocker_at(&root, &agent_id, &runtime.run_id) - .is_none() - && static_delegate_completion_blocker_at(&root, &agent_id, &runtime.run_id) - .is_none(); - if autonomous_manifest_parent_can_wait { - let manifest_state_before_schedule = match autonomous_manifest_dag_state_at(&root) { - Ok(value) => value, - Err(error) => { - return fail_game_creator_agent_background_context_at( - &root, - &agent_id, - &session_id, - runtime, - &format!("读取自主构建 manifest 等待屏障失败:{error}"), - ); - } - }; - // 已登记但损坏的派生视觉需要先由父 Run 规划修复,因此此时不再调度新的 - // manifest child;但已经持久化并运行的 child 仍是当前 DAG 的活跃工作, - // 父 Run 必须继续等待,不能提前结束。 - // 同理,已有失败任务时只能等待已在途 child 收束或立即安全失败,不能再 - // 启动新的 pending sibling 并用它遮蔽原始失败。 - let manifest_scheduler_blocked = matches!( - &manifest_state_before_schedule, - AutonomousManifestDagState::Completed - | AutonomousManifestDagState::Failed { .. } - ) || autonomous_registered_derived_visuals_block_manifest_scheduler_at( - &root, &runtime, - ); - let scheduled_ready_tasks = if manifest_scheduler_blocked { - Vec::new() - } else { - match schedule_autonomous_game_build_ready_tasks_at( + let autonomous_root_goal_contract_persisted = if agent_id + == GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID + && runtime.run_profile == AGENT_RUNTIME_RUN_PROFILE_AUTONOMOUS_GAME_BUILD + { + match autonomous_root_goal_contract_persisted_at( &root, &agent_id, &runtime.run_id, - 3, ) { - Ok(tasks) => tasks, - Err(error) => { - return fail_game_creator_agent_background_context_at( - &root, - &agent_id, - &session_id, - runtime, - &format!("调度自主构建 manifest 任务失败:{error}"), - ); - } - } - }; - let manifest_state = if matches!( - &manifest_state_before_schedule, - AutonomousManifestDagState::Failed { .. } - ) { - manifest_state_before_schedule - } else if scheduled_ready_tasks.is_empty() { - match autonomous_manifest_dag_state_at(&root) { Ok(value) => value, Err(error) => { return fail_game_creator_agent_background_context_at( @@ -912,68 +826,155 @@ async fn run_game_creator_agent_background_task_pass_without_deadline( &agent_id, &session_id, runtime, - &format!("读取自主构建 manifest 等待屏障失败:{error}"), + &format!("读取自主构建根 Goal Contract 门失败:{error}"), ); } } } else { - AutonomousManifestDagState::InProgress + false }; - let manifest_parent_must_wait = matches!( - &manifest_state, - AutonomousManifestDagState::InProgress - | AutonomousManifestDagState::Failed { - has_active_children: true, - .. - } - ); - if manifest_parent_must_wait { - let blocker = - autonomous_game_build_completion_blocker_at_locked(&root, &runtime) - .unwrap_or_else(|| AgentRuntimeToolObservation { - tool: "runtime.autonomous_completion".to_string(), - status: "blocked".to_string(), - summary: "自主构建 manifest DAG 尚未完成".to_string(), - detail: Some("manifest 子任务仍在运行".to_string()), - }); - if let Err(error) = persist_waiting_autonomous_manifest_parent_context_at( + let autonomous_manifest_parent_can_wait = autonomous_root_goal_contract_persisted + && agent_id == GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID + && runtime.run_profile == AGENT_RUNTIME_RUN_PROFILE_AUTONOMOUS_GAME_BUILD + && !game_creator_agent_runtime_provider_action_batch_exists( &root, - &mut runtime, - &task, - &plan, - &mut observations, - loop_index, - &mut context_tracker, - blocker, + &agent_id, + &runtime.run_id, + ) + && supervisor_collaboration_policy_completion_blocker_at_locked( + &root, + &agent_id, + &runtime.run_id, + ) + .is_none() + && isolated_join_completion_blocker_at(&root, &agent_id, &runtime.run_id) + .is_none() + && static_delegate_completion_blocker_at(&root, &agent_id, &runtime.run_id) + .is_none(); + if autonomous_manifest_parent_can_wait { + let manifest_state_before_schedule = + match autonomous_manifest_dag_state_at(&root) { + Ok(value) => value, + Err(error) => { + return fail_game_creator_agent_background_context_at( + &root, + &agent_id, + &session_id, + runtime, + &format!("读取自主构建 manifest 等待屏障失败:{error}"), + ); + } + }; + // 已登记但损坏的派生视觉需要先由父 Run 规划修复,因此此时不再调度新的 + // manifest child;但已经持久化并运行的 child 仍是当前 DAG 的活跃工作, + // 父 Run 必须继续等待,不能提前结束。 + // 同理,已有失败任务时只能等待已在途 child 收束或立即安全失败,不能再 + // 启动新的 pending sibling 并用它遮蔽原始失败。 + let manifest_scheduler_blocked = matches!( + &manifest_state_before_schedule, + AutonomousManifestDagState::Completed + | AutonomousManifestDagState::Failed { .. } + ) + || autonomous_registered_derived_visuals_block_manifest_scheduler_at( + &root, &runtime, + ); + let scheduled_ready_tasks = if manifest_scheduler_blocked { + Vec::new() + } else { + match schedule_autonomous_game_build_ready_tasks_at( + &root, + &agent_id, + &runtime.run_id, + 3, + ) { + Ok(tasks) => tasks, + Err(error) => { + return fail_game_creator_agent_background_context_at( + &root, + &agent_id, + &session_id, + runtime, + &format!("调度自主构建 manifest 任务失败:{error}"), + ); + } + } + }; + let manifest_state = if matches!( + &manifest_state_before_schedule, + AutonomousManifestDagState::Failed { .. } ) { + manifest_state_before_schedule + } else if scheduled_ready_tasks.is_empty() { + match autonomous_manifest_dag_state_at(&root) { + Ok(value) => value, + Err(error) => { + return fail_game_creator_agent_background_context_at( + &root, + &agent_id, + &session_id, + runtime, + &format!("读取自主构建 manifest 等待屏障失败:{error}"), + ); + } + } + } else { + AutonomousManifestDagState::InProgress + }; + let manifest_parent_must_wait = matches!( + &manifest_state, + AutonomousManifestDagState::InProgress + | AutonomousManifestDagState::Failed { + has_active_children: true, + .. + } + ); + if manifest_parent_must_wait { + let blocker = + autonomous_game_build_completion_blocker_at_locked(&root, &runtime) + .unwrap_or_else(|| AgentRuntimeToolObservation { + tool: "runtime.autonomous_completion".to_string(), + status: "blocked".to_string(), + summary: "自主构建 manifest DAG 尚未完成".to_string(), + detail: Some("manifest 子任务仍在运行".to_string()), + }); + if let Err(error) = persist_waiting_autonomous_manifest_parent_context_at( + &root, + &mut runtime, + &task, + &plan, + &mut observations, + loop_index, + &mut context_tracker, + blocker, + ) { + return fail_game_creator_agent_background_context_at( + &root, + &agent_id, + &session_id, + runtime, + &format!("持久化 manifest 父 run 等待状态失败:{error}"), + ); + } + return AgentBackgroundTaskOutcome::WaitingForManifestTasks; + } + if let AutonomousManifestDagState::Failed { + failed_task_ids, .. + } = manifest_state + { return fail_game_creator_agent_background_context_at( &root, &agent_id, &session_id, runtime, - &format!("持久化 manifest 父 run 等待状态失败:{error}"), + &format!( + "自主构建 manifest 专业任务失败;failedTaskIds={}", + failed_task_ids.join(",") + ), ); } - return AgentBackgroundTaskOutcome::WaitingForManifestTasks; - } - if let AutonomousManifestDagState::Failed { - failed_task_ids, .. - } = manifest_state - { - return fail_game_creator_agent_background_context_at( - &root, - &agent_id, - &session_id, - runtime, - &format!( - "自主构建 manifest 专业任务失败;failedTaskIds={}", - failed_task_ids.join(",") - ), - ); } } } - } let mut resumed_provider_batch = if game_creator_agent_runtime_provider_action_batch_exists( &root, &agent_id, @@ -1706,7 +1707,9 @@ async fn run_game_creator_agent_background_task_pass_without_deadline( &agent_id, &runtime.run_id, ) - .or_else(|| process_session_completion_blocker_at(&root, &agent_id, &runtime.run_id)) + .or_else(|| { + process_session_completion_blocker_at(&root, &agent_id, &runtime.run_id) + }) .or_else(|| autonomous_game_build_completion_blocker_at_locked(&root, &runtime)) } else { structured_plan_completion_blocker(&runtime) @@ -1731,7 +1734,9 @@ async fn run_game_creator_agent_background_task_pass_without_deadline( .or_else(|| { game_creator_agent_goal_completion_blocker_at_locked(&root, &runtime) }) - .or_else(|| goal_contract_acceptance_completion_blocker_at_locked(&root, &runtime)) + .or_else(|| { + goal_contract_acceptance_completion_blocker_at_locked(&root, &runtime) + }) .or_else(|| { supervisor_collaboration_policy_completion_blocker_at_locked( &root, @@ -1790,26 +1795,22 @@ async fn run_game_creator_agent_background_task_pass_without_deadline( match art_wait_state { AutonomousCodePrototypeArtAssetWaitState::Waiting => { let next_loop_index = loop_index.saturating_add(1); - if let Err(error) = - persist_waiting_autonomous_manifest_child_context_at( - &root, - &mut runtime, - &task, - &plan, - &mut observations, - next_loop_index, - &mut context_tracker, - blocker, - ) - { + if let Err(error) = persist_waiting_autonomous_manifest_child_context_at( + &root, + &mut runtime, + &task, + &plan, + &mut observations, + next_loop_index, + &mut context_tracker, + blocker, + ) { return fail_game_creator_agent_background_context_at( &root, &agent_id, &session_id, runtime, - &format!( - "持久化 code-prototype 美术依赖等待状态失败:{error}" - ), + &format!("持久化 code-prototype 美术依赖等待状态失败:{error}"), ); } return AgentBackgroundTaskOutcome::WaitingForManifestTasks; diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/provider_recovery.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/provider_recovery.rs index 8e3368d3e..cc208c003 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/provider_recovery.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/provider_recovery.rs @@ -178,13 +178,13 @@ async fn drive_waiting_autonomous_manifest_parent_wake_pass_with_budget( reconciliation_delay_ms: u64, request_deferred_rerun: bool, ) -> Result<(), String> { - if read_latest_game_creator_agent_runtime_task_by_run_id(root, agent_id, run_id)? - .is_some_and(|task| { + if read_latest_game_creator_agent_runtime_task_by_run_id(root, agent_id, run_id)?.is_some_and( + |task| { task.status == "running" && task.phase == "waiting-for-manifest-tasks" && autonomous_relaxed_run_profile(&task.run_profile) - }) - { + }, + ) { // Free-form autonomous runs never reconcile a manifest barrier. The // phase can only be legacy durable state, so make a bounded attempt to // resume it and leave any lane contention for the ordinary recovery @@ -317,7 +317,10 @@ pub(in crate::agent) fn autonomous_manifest_ready_task_waiting_child_record( && task.source == "agent-ready-task-scheduler" && task.run_profile == AGENT_RUNTIME_RUN_PROFILE_AUTONOMOUS_GAME_BUILD && task.parent_agent_id.as_deref() == Some(GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID) - && task.parent_run_id.as_deref().is_some_and(|value| !value.trim().is_empty()) + && task + .parent_run_id + .as_deref() + .is_some_and(|value| !value.trim().is_empty()) && task.delegation_id.is_none() } @@ -327,9 +330,9 @@ async fn drive_waiting_autonomous_manifest_child_wake_pass( run_id: &str, ) -> Result<(), String> { for _ in 0..AUTONOMOUS_MANIFEST_PARENT_WAKE_MAX_ATTEMPTS { - let Some(task) = read_latest_game_creator_agent_runtime_task_by_run_id( - root, agent_id, run_id, - )? else { + let Some(task) = + read_latest_game_creator_agent_runtime_task_by_run_id(root, agent_id, run_id)? + else { return Ok(()); }; if !autonomous_manifest_ready_task_waiting_child_record(&task) { diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/recovery_scan.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/recovery_scan.rs index 97b563b19..4d06d8a7a 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/recovery_scan.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/recovery_scan.rs @@ -1628,23 +1628,24 @@ pub(in crate::agent) fn resume_game_creator_agent_background_tasks_unredacted_at continue; } AutonomousCodePrototypeArtAssetWaitState::NotWaiting => { - let parent_agent_id = task.parent_agent_id.as_deref().ok_or_else(|| { - "code-prototype 等待态缺少 parentAgentId".to_string() - })?; - let parent_run_id = task.parent_run_id.as_deref().ok_or_else(|| { - "code-prototype 等待态缺少 parentRunId".to_string() - })?; + let parent_agent_id = task + .parent_agent_id + .as_deref() + .ok_or_else(|| "code-prototype 等待态缺少 parentAgentId".to_string())?; + let parent_run_id = task + .parent_run_id + .as_deref() + .ok_or_else(|| "code-prototype 等待态缺少 parentRunId".to_string())?; // The child execution lane is held by this recovery // scan. Release it before the parent scheduler tries // to reacquire the deterministic child lane. drop(runtime_lock); - let scheduled_ready_tasks = - schedule_autonomous_game_build_ready_tasks_at( - root, - parent_agent_id, - parent_run_id, - 3, - )?; + let scheduled_ready_tasks = schedule_autonomous_game_build_ready_tasks_at( + root, + parent_agent_id, + parent_run_id, + 3, + )?; if !scheduled_ready_tasks.is_empty() { resumed.push(read_game_creator_agent_runtime_at(root, &agent_id)?); } diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/autonomous_completion.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/autonomous_completion.rs index 5779bf6bc..c7794fdb6 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/autonomous_completion.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/autonomous_completion.rs @@ -60,8 +60,10 @@ pub(in crate::agent) fn autonomous_relaxed_run_at( { return Ok(autonomous_relaxed_run_profile(&binding.profile)); } - Ok(read_latest_game_creator_agent_runtime_task_by_run_id(root, agent_id, run_id)? - .is_some_and(|task| autonomous_relaxed_run_profile(&task.run_profile))) + Ok( + read_latest_game_creator_agent_runtime_task_by_run_id(root, agent_id, run_id)? + .is_some_and(|task| autonomous_relaxed_run_profile(&task.run_profile)), + ) } pub(crate) fn autonomous_game_build_root_run_active_at(root: &Path) -> bool { @@ -669,11 +671,11 @@ pub(in crate::agent) fn autonomous_code_prototype_art_asset_wait_state_at( .map(|task| task.status.clone()) .ok_or_else(|| "当前 seed manifest 缺少 art-asset-plan".to_string())?; match art_status { - GameCreationAppTaskStatus::Failed => Ok( - AutonomousCodePrototypeArtAssetWaitState::DependencyFailed( + GameCreationAppTaskStatus::Failed => { + Ok(AutonomousCodePrototypeArtAssetWaitState::DependencyFailed( "art-asset-plan 已失败,code-prototype 不能继续等待美术回执".to_string(), - ), - ), + )) + } GameCreationAppTaskStatus::Pending | GameCreationAppTaskStatus::Running | GameCreationAppTaskStatus::WaitingForConfirmation => { diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/planning_storage.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/planning_storage.rs index 889d85f06..1d6e98691 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/planning_storage.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/planning_storage.rs @@ -5066,7 +5066,10 @@ mod tests { assert_eq!(parse(&bytes).expect("parse input"), value); let mut newline = bytes.clone(); newline.push(b'\n'); - assert_eq!(parse(&newline).unwrap_err().code(), "PLAN_NON_CANONICAL_BYTES"); + assert_eq!( + parse(&newline).unwrap_err().code(), + "PLAN_NON_CANONICAL_BYTES" + ); let mut object = serde_json::from_slice::(&bytes).expect("input json"); object["projectId"] = serde_json::Value::String("forged-project".to_string()); let forged = serde_json::to_vec(&object).expect("forged input"); diff --git a/apps/ai-game-creator-shell/src-tauri/src/cli.rs b/apps/ai-game-creator-shell/src-tauri/src/cli.rs index 9782da67c..9c9b1d506 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/cli.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/cli.rs @@ -220,9 +220,7 @@ impl CliCommand { | Self::PlanGddDecide { project_path, .. } | Self::PreviewServe { project_path } | Self::AgentRun { project_path, .. } => Some((project_path, false)), - | Self::LlmStatus - | Self::RunnerStatus - | Self::RunnerShutdownIfIdle => None, + Self::LlmStatus | Self::RunnerStatus | Self::RunnerShutdownIfIdle => None, } } } diff --git a/apps/ai-game-creator-shell/src-tauri/src/commands.rs b/apps/ai-game-creator-shell/src-tauri/src/commands.rs index 465d07443..a051deab5 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/commands.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/commands.rs @@ -481,12 +481,9 @@ pub(crate) fn game_creator_project_manifest_error(root: &Path) -> Option pub(crate) fn recent_game_creator_run_trace(root: &Path) -> Option { let trace_path = root.join(".agent/run.latest.json"); - let content = crate::read_game_creator_private_file_to_string( - &trace_path, - "最近运行状态", - 256 * 1024, - ) - .ok()?; + let content = + crate::read_game_creator_private_file_to_string(&trace_path, "最近运行状态", 256 * 1024) + .ok()?; serde_json::from_str::(&content).ok() } @@ -530,11 +527,9 @@ pub(crate) async fn pick_local_project_directory( // The native picker is the explicit user-selection boundary. Prepare the // selected root before returning it so inspect/create/open never races the // first ACL read. - if let Err(error) = crate::prepare_game_creator_project_root_for_read( - &path, - true, - "用户选择项目目录", - ) { + if let Err(error) = + crate::prepare_game_creator_project_root_for_read(&path, true, "用户选择项目目录") + { #[cfg(windows)] crate::revoke_game_creator_user_selected_path(&path); return Err(error); @@ -563,11 +558,9 @@ pub(crate) async fn pick_local_file(app: tauri::AppHandle) -> Result Option { - let [ - command, - path, - target_user_sid_flag, - target_user_sid, - authorization_flag, - nonce, - scope_flag, - scope_value, - ] = args + let [command, path, target_user_sid_flag, target_user_sid, authorization_flag, nonce, scope_flag, scope_value] = + args else { if args.first().map(String::as_str) == Some("--repair-private-acl") { eprintln!( @@ -1942,13 +1934,11 @@ fn run_windows_acl_repair_if_requested(args: &[String]) -> Option { return Some(1); } }; - let result = config::consume_windows_acl_repair_authorization( - &path, - target_user_sid, - nonce, - scope, - ) - .and_then(|()| config::repair_game_creator_private_acl_for_user_sid(&path, target_user_sid)); + let result = + config::consume_windows_acl_repair_authorization(&path, target_user_sid, nonce, scope) + .and_then(|()| { + config::repair_game_creator_private_acl_for_user_sid(&path, target_user_sid) + }); match result { Ok(()) => Some(0), Err(error) => { diff --git a/apps/ai-game-creator-shell/src-tauri/src/platform_session.rs b/apps/ai-game-creator-shell/src-tauri/src/platform_session.rs index daf5cd59f..0e24b3b53 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/platform_session.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/platform_session.rs @@ -1,5 +1,5 @@ -use sha2::{Digest, Sha256}; use serde::Deserialize; +use sha2::{Digest, Sha256}; use std::fs::{self, OpenOptions}; use std::io::Read; use std::path::{Path, PathBuf}; @@ -8,10 +8,8 @@ use std::sync::{Mutex, OnceLock}; /// Debug-only fixture hook used by the deterministic AGC E2E. The hook takes /// a path, rather than credentials on argv, so a child Runner can inherit the /// test identity without putting the bearer token in process listings. -pub(crate) const PLATFORM_SESSION_FIXTURE_ENV: &str = - "GENARRATIVE_AGC_PLATFORM_SESSION_FIXTURE"; -const PLATFORM_SESSION_FIXTURE_SCHEMA_VERSION: &str = - "genarrative-agc-platform-session-fixture.v1"; +pub(crate) const PLATFORM_SESSION_FIXTURE_ENV: &str = "GENARRATIVE_AGC_PLATFORM_SESSION_FIXTURE"; +const PLATFORM_SESSION_FIXTURE_SCHEMA_VERSION: &str = "genarrative-agc-platform-session-fixture.v1"; const PLATFORM_SESSION_FIXTURE_MAX_BYTES: u64 = 16 * 1024; #[derive(Clone, Debug, Eq, PartialEq)] @@ -144,8 +142,8 @@ fn validate_fixture_path(config_dir: &Path, fixture_path: &Path) -> Result Result, String> { - let before = fs::symlink_metadata(path) - .map_err(|_| "平台登录态 fixture 文件不可读取".to_string())?; + let before = + fs::symlink_metadata(path).map_err(|_| "平台登录态 fixture 文件不可读取".to_string())?; if metadata_is_link_or_reparse(&before) || !before.is_file() { return Err("平台登录态 fixture 必须是普通文件".to_string()); } @@ -169,7 +167,8 @@ fn read_fixture_file(path: &Path) -> Result, String> { if metadata_is_link_or_reparse(&opened) || !opened.is_file() || opened.len() > before.len() { return Err("平台登录态 fixture 文件身份校验失败".to_string()); } - let mut bytes = Vec::with_capacity(opened.len().min(PLATFORM_SESSION_FIXTURE_MAX_BYTES) as usize); + let mut bytes = + Vec::with_capacity(opened.len().min(PLATFORM_SESSION_FIXTURE_MAX_BYTES) as usize); file.take(PLATFORM_SESSION_FIXTURE_MAX_BYTES + 1) .read_to_end(&mut bytes) .map_err(|_| "读取平台登录态 fixture 失败".to_string())?; @@ -188,10 +187,18 @@ fn parse_platform_session_fixture(bytes: &[u8]) -> Result Result {{}}, 1000); break; } } - assert!(cancellation_output.contains(READY_SENTINEL), "{cancellation_output}"); + assert!( + cancellation_output.contains(READY_SENTINEL), + "{cancellation_output}" + ); terminate_process_sessions_for_run_at(&root, AGENT_ID, RUN_ID) .expect("cancel cleanup terminates active process"); assert!(!has_active_process_sessions_at(&root).expect("cancel cleanup terminal")); diff --git a/apps/ai-game-creator-shell/src-tauri/src/tests/provider.rs b/apps/ai-game-creator-shell/src-tauri/src/tests/provider.rs index d5aa0f318..265c01415 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/tests/provider.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/tests/provider.rs @@ -6202,8 +6202,14 @@ async fn agent_loop_writes_spec_findings_and_retries_generator() { assert!(first_agenda.contains("mode: initial")); assert!(first_agenda.contains("activeTasks: design-director")); assert!(first_agenda.contains("wave 1: design-director")); - assert!(first_agenda.contains("wave 11: publish-package"), "{first_agenda}"); - assert!(first_agenda.contains("wave 12: preview-playtest"), "{first_agenda}"); + assert!( + first_agenda.contains("wave 11: publish-package"), + "{first_agenda}" + ); + assert!( + first_agenda.contains("wave 12: preview-playtest"), + "{first_agenda}" + ); let first_task_graph: Value = serde_json::from_str( &fs::read_to_string(root.join(".agent/passes/pass-1/task-graph.json")) .expect("task graph 1"),