继续收敛直连输出指纹读取错误
区分文件不存在与磁盘权限读取失败 让输出指纹错误沿 Direct 回合正文上送
This commit is contained in:
@@ -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 的清单版本读取、失效事件重读和事件订阅失败原先只写日志或静默回退;现把安全正文送入启动器状态提示,仍保留已有回退刷新路径。
|
||||
|
||||
Reference in New Issue
Block a user