继续收敛直连输出指纹读取错误

区分文件不存在与磁盘权限读取失败

让输出指纹错误沿 Direct 回合正文上送
This commit is contained in:
kdletters
2026-10-06 00:26:38 +08:00
parent 72053b9dc0
commit d1d54ebcb1
2 changed files with 30 additions and 11 deletions
@@ -3555,7 +3555,7 @@ fn direct_codex_generated_source() -> GameCreationAppAssetSource {
}
}
fn direct_codex_output_fingerprint(root: &Path) -> String {
fn direct_codex_output_fingerprint_checked(root: &Path) -> Result<String, String> {
let mut hasher = Sha256::new();
let mut paths: Vec<String> = direct_codex_game_outputs(root)
.into_iter()
@@ -3568,16 +3568,27 @@ fn direct_codex_output_fingerprint(root: &Path) -> String {
hasher.update(local_path.as_bytes());
hasher.update([0]);
let path = root.join(local_path);
match std::fs::read(path) {
match std::fs::read(&path) {
Ok(bytes) => {
hasher.update([1]);
hasher.update((bytes.len() as u64).to_le_bytes());
hasher.update(bytes);
}
Err(_) => hasher.update([0]),
Err(error) if error.kind() == std::io::ErrorKind::NotFound => hasher.update([0]),
Err(error) => {
return Err(format!(
"读取直连 Codex 输出文件失败:{}:{error}",
path.display()
));
}
}
}
format!("{:x}", hasher.finalize())
Ok(format!("{:x}", hasher.finalize()))
}
#[cfg(test)]
fn direct_codex_output_fingerprint(root: &Path) -> String {
direct_codex_output_fingerprint_checked(root).expect("读取直连 Codex 输出指纹")
}
fn direct_npm_source_paths(root: &Path) -> Vec<String> {
@@ -4116,7 +4127,7 @@ fn sync_direct_codex_project_file_projection_at(
root: &Path,
previous_output_fingerprint: Option<&str>,
) -> Result<(), String> {
let current_output_fingerprint = direct_codex_output_fingerprint(root);
let current_output_fingerprint = direct_codex_output_fingerprint_checked(root)?;
let outputs_changed = previous_output_fingerprint
.map(|previous| previous != current_output_fingerprint)
.unwrap_or(true);
@@ -4554,7 +4565,8 @@ async fn run_direct_game_creator_turn_inner(
let stream_enabled = load_game_creator_app_config()
.map(|config| config.llm.stream)
.map_err(|error| TurnError::turn_failed(FailureStage::CodeGeneration, error))?;
let previous_output_fingerprint = direct_codex_output_fingerprint(root);
let previous_output_fingerprint = direct_codex_output_fingerprint_checked(root)
.map_err(|error| TurnError::turn_failed(FailureStage::CodeGeneration, error))?;
let base_system_prompt =
build_direct_codex_system_prompt_with_creation_type(root, creation_type)
.map_err(|error| TurnError::turn_failed(FailureStage::CodeGeneration, error))?;
@@ -4748,7 +4760,9 @@ async fn run_direct_game_creator_turn_inner(
if let Some(emitter) = turn_emitter {
emitter.emit("finalizing", Some("response-finalization"));
}
if direct_codex_output_fingerprint(root) != previous_output_fingerprint {
let current_output_fingerprint = direct_codex_output_fingerprint_checked(root)
.map_err(|error| TurnError::turn_failed(FailureStage::VersionRegistration, error))?;
if current_output_fingerprint != previous_output_fingerprint {
emit_direct_game_creator_progress(
root,
"project.sync",
@@ -4894,7 +4908,8 @@ async fn run_direct_game_creator_turn_with_private_editor_credentials(
.await
.map_err(|error| TurnError::turn_failed(FailureStage::ArtPreparation, error))?;
}
let previous_output_fingerprint = direct_codex_output_fingerprint(root);
let previous_output_fingerprint = direct_codex_output_fingerprint_checked(root)
.map_err(|error| TurnError::turn_failed(FailureStage::CodeGeneration, error))?;
let mut system_prompt = build_direct_codex_system_prompt(root)
.map_err(|error| TurnError::turn_failed(FailureStage::CodeGeneration, error))?;
if prepare_art {
@@ -4906,7 +4921,8 @@ async fn run_direct_game_creator_turn_with_private_editor_credentials(
}
let _initial_reply =
direct_game_creator_codex_chat_at(root, system_prompt.clone(), prompt.to_string()).await?;
let initial_output_fingerprint = direct_codex_output_fingerprint(root);
let initial_output_fingerprint = direct_codex_output_fingerprint_checked(root)
.map_err(|error| TurnError::turn_failed(FailureStage::CodeGeneration, error))?;
let mut completion_error = direct_game_output_completion_error(root);
let mut evidence_attempts = 0_usize;
let mut evidence: Option<BrowserValidationResult> = None;
@@ -4937,7 +4953,8 @@ async fn run_direct_game_creator_turn_with_private_editor_credentials(
emit_direct_game_creator_progress(root, "codex.review", "正在根据试玩证据自主检查游戏");
let mut reply =
direct_game_creator_codex_chat_at(root, system_prompt.clone(), repair_prompt).await?;
let repaired_fingerprint = direct_codex_output_fingerprint(root);
let repaired_fingerprint = direct_codex_output_fingerprint_checked(root)
.map_err(|error| TurnError::turn_failed(FailureStage::CodeGeneration, error))?;
completion_error = direct_game_output_completion_error(root);
if completion_error.is_none()
&& (browser_checked_output_fingerprint.as_deref() != Some(repaired_fingerprint.as_str())
@@ -4968,7 +4985,8 @@ async fn run_direct_game_creator_turn_with_private_editor_credentials(
"试玩未通过,正在进行最后一次自主修复",
);
reply = direct_game_creator_codex_chat_at(root, system_prompt, repair_prompt).await?;
let final_fingerprint = direct_codex_output_fingerprint(root);
let final_fingerprint = direct_codex_output_fingerprint_checked(root)
.map_err(|error| TurnError::turn_failed(FailureStage::CodeGeneration, error))?;
completion_error = direct_game_output_completion_error(root);
if completion_error.is_none()
&& browser_checked_output_fingerprint.as_deref() != Some(final_fingerprint.as_str())
@@ -81,5 +81,6 @@ DirectProject 回合失败在确认不是客户端内部不可归类故障时,
- 策划 Agent 的 Tauri 事件订阅失败原先只放行回合等待、没有任何用户提示;现把订阅 IPC 正文写入项目聊天错误出口,避免只收到不完整的最终态。
- 专业 Agent 历史回读与项目清单同步的后台 Promise 失败原先只返回 `failed` 或静默结束;现把具体读取/同步正文写入项目聊天错误出口。
- Direct 交付计划/合同 JSON 解析、策划工作区目录枚举与文本搜索还有 `map_err(|_|)` / `Err(_)` 丢细节;现保留 serde、目录和文件读取正文,安全路径边界仍单独拒绝。
- 直连 Codex 输出指纹此前把除 `NotFound` 之外的所有读取错误都当成“文件不存在”,可能误判产物未变化;现只对确实不存在的文件保留空位,其余磁盘/权限错误沿回合失败正文上送。
- 错误报告对话框读取最新错误事件或诊断日志失败时原先只显示“暂不可用/使用快照”;现把读取 IPC/文件正文附在状态提示中,快照回退行为保持不变。
- WorkspaceLauncher 的清单版本读取、失效事件重读和事件订阅失败原先只写日志或静默回退;现把安全正文送入启动器状态提示,仍保留已有回退刷新路径。