diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/codex_app_server/execution.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/codex_app_server/execution.rs index 36777bb12..52db46ea0 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/agent/codex_app_server/execution.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/agent/codex_app_server/execution.rs @@ -1056,9 +1056,13 @@ impl ExecutionAdapter { } fn report(&self) -> String { - direct_delivery::terminal_report(&self.session).unwrap_or_else(|| { - "本轮执行已停止,宿主尚未确认交付完成;请核对保留的证据与未完成项。".into() - }) + match direct_delivery::terminal_report(&self.session) { + Ok(Some(report)) => report, + Ok(None) => "本轮执行已停止,宿主尚未确认交付完成;请核对保留的证据与未完成项。".into(), + Err(error) => format!( + "本轮执行已停止,读取宿主交付状态失败:{error};请核对保留的证据与未完成项。" + ), + } } async fn drain(&self) { diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/direct_delivery.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/direct_delivery.rs index 72c372230..f1828f1ca 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/agent/direct_delivery.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/agent/direct_delivery.rs @@ -644,8 +644,8 @@ fn assess(root: &Path, ledger: &ExecutionLedger) -> Result { }) } -pub(super) fn terminal_report(session: &Arc) -> Option { - session.snapshot().ok()?.terminal_report +pub(super) fn terminal_report(session: &Arc) -> Result, String> { + session.snapshot().map(|snapshot| snapshot.terminal_report) } pub(super) async fn status(root: &Path, session: &Arc) -> Result { @@ -710,7 +710,13 @@ pub(super) async fn review_reply( root: &Path, session: &Arc, ) -> Result, TurnError> { - if let Some(report) = terminal_report(session) { + let existing_report = terminal_report(session).map_err(|error| { + TurnError::turn_failed( + FailureStage::CodeGeneration, + format!("读取交付终态失败:{error}"), + ) + })?; + if let Some(report) = existing_report { return Ok(Some(report)); } let ledger = session.snapshot()?; @@ -721,7 +727,13 @@ pub(super) async fn review_reply( if let Some(report) = finish_sealing(root, session).await? { return Ok(Some(report)); } - if let Some(report) = terminal_report(session) { + let terminal_report = terminal_report(session).map_err(|error| { + TurnError::turn_failed( + FailureStage::CodeGeneration, + format!("读取交付终态失败:{error}"), + ) + })?; + if let Some(report) = terminal_report { return Ok(Some(report)); } } diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/direct_runtime/mod.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/direct_runtime/mod.rs index 8c17fd38b..69c7708e9 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/agent/direct_runtime/mod.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/agent/direct_runtime/mod.rs @@ -4528,7 +4528,9 @@ async fn run_direct_game_creator_turn_inner( direct_turn_trace("run-analytics-accepted"); // 在 guard 仍存活时冻结整体结果,避免 Drop 的中断收尾覆盖真实失败原因。 let result: Result = async { - if let Some(report) = super::direct_delivery::terminal_report(&execution_session) { + let terminal_report = super::direct_delivery::terminal_report(&execution_session) + .map_err(|error| TurnError::turn_failed(FailureStage::CodeGeneration, format!("读取交付终态失败:{error}")))?; + if let Some(report) = terminal_report { return Ok(report); } // CLI 没有结构化条目;在进入反馈循环前固定原始消息,避免后续反馈以同一 ID @@ -4670,20 +4672,28 @@ async fn run_direct_game_creator_turn_inner( } // 交付报告的兜底只读一次:guard 与取值各调一次会在两次之间换出不同结果, // 第二次拿到 `None` 时还会把空串当成回复返回。 - Err(error) => match super::direct_delivery::terminal_report(&execution_session) - { - Some(report) => break Ok(report), - None if attempt < DIRECT_CODEX_ERROR_FEEDBACK_MAX_ATTEMPTS - && error.is_model_repairable() => - { - let detail = - truncate_agent_runtime_text(&error.diagnostic_detail(), 1800); - emitter.emit("running", Some("error-feedback")); - attempt += 1; - feedback_prompt = direct_codex_error_feedback_prompt(&detail); + Err(error) => { + let original_detail = error.diagnostic_detail(); + match super::direct_delivery::terminal_report(&execution_session) { + Ok(Some(report)) => break Ok(report), + Ok(None) if attempt < DIRECT_CODEX_ERROR_FEEDBACK_MAX_ATTEMPTS + && error.is_model_repairable() => + { + let detail = + truncate_agent_runtime_text(&original_detail, 1800); + emitter.emit("running", Some("error-feedback")); + attempt += 1; + feedback_prompt = direct_codex_error_feedback_prompt(&detail); + } + Ok(None) => break Err(error), + Err(report_error) => break Err(TurnError::turn_failed( + FailureStage::CodeGeneration, + format!( + "读取交付终态失败:{report_error};原始回合失败:{original_detail}" + ), + )), } - None => break Err(error), - }, + } } }; drop(observer); @@ -4732,19 +4742,29 @@ async fn run_direct_game_creator_turn_inner( } // 同上:交付报告只读一次,避免两次调用之间换出不同结果(第二次拿到 `None` // 时还会绕过下面那条"未返回结果"的兜底错误)。 - Err(error) => match super::direct_delivery::terminal_report(&execution_session) - { - Some(report) => break Some(report), - None if attempt < DIRECT_CODEX_ERROR_FEEDBACK_MAX_ATTEMPTS - && error.is_model_repairable() => - { - let detail = - truncate_agent_runtime_text(&error.diagnostic_detail(), 1800); - attempt += 1; - feedback_prompt = direct_codex_error_feedback_prompt(&detail); + Err(error) => { + let original_detail = error.diagnostic_detail(); + match super::direct_delivery::terminal_report(&execution_session) { + Ok(Some(report)) => break Some(report), + Ok(None) if attempt < DIRECT_CODEX_ERROR_FEEDBACK_MAX_ATTEMPTS + && error.is_model_repairable() => + { + let detail = + truncate_agent_runtime_text(&original_detail, 1800); + attempt += 1; + feedback_prompt = direct_codex_error_feedback_prompt(&detail); + } + Ok(None) => return Err(error), + Err(report_error) => { + return Err(TurnError::turn_failed( + FailureStage::CodeGeneration, + format!( + "读取交付终态失败:{report_error};原始回合失败:{original_detail}" + ), + )) + } } - None => return Err(error), - }, + } } }; response.ok_or_else(|| { diff --git a/docs/project-memory/plans/【里程碑】AGC错误具体文本展示-2026-10-04.md b/docs/project-memory/plans/【里程碑】AGC错误具体文本展示-2026-10-04.md index adb112298..973ba6c01 100644 --- a/docs/project-memory/plans/【里程碑】AGC错误具体文本展示-2026-10-04.md +++ b/docs/project-memory/plans/【里程碑】AGC错误具体文本展示-2026-10-04.md @@ -88,3 +88,4 @@ DirectProject 回合失败在确认不是客户端内部不可归类故障时, - Direct 补丁事务的目标指纹读取原先把元数据、打开、正文读取和文件超限都折叠为 `None`,可能误判文件缺失或继续比较;现只对确实 `NotFound` 保留 `missing`,其余失败返回目标相对路径、阶段和底层正文,避免补丁结果被错误归因。 - CC/Direct 工具桥的执行前后成果指纹、回执结算 JoinError/落盘失败,以及外部 MCP journal 的读取/JSON 行解析原先仍会被 `.ok()`、固定回执句或“无记录”吞掉;现分别保留指纹阶段、回执任务/落盘原因、journal 行号和底层正文,只有真实 `NotFound` 才视为空记录。 - 外部 MCP `resources/read` 仍把已能返回具体原因的 Codex journal 读取错误 `unwrap_or_default()` 成空资源;现把读取失败按 MCP 错误响应返回,Direct 执行会话当前项目 canonicalize 也保留底层 I/O 正文。 +- Direct 交付终态读取原先把 `session.snapshot()` 锁/状态错误折叠成 `None`,上层继续显示“尚未确认交付完成”;现沿 `code-generation` 失败出口保留终态读取正文,并在已有回合失败时同时保留原始失败与终态读取失败。