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 df71818da..2974078d9 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 @@ -35,7 +35,6 @@ enum DirectPlanStepStatus { } pub(super) async fn update_plan( - root: &Path, session: &Arc, input: &Value, ) -> Result { @@ -51,13 +50,13 @@ pub(super) async fn update_plan( { return Err("direct-plan-invalid: 每步需为1到500字".into()); } - item.step = redact_agent_runtime_error(root, item.step.trim(), 500); + item.step = truncate_agent_runtime_text(item.step.trim(), 500); } if let Some(explanation) = &mut plan.explanation { if explanation.chars().count() > 1200 || explanation.contains('\0') { return Err("direct-plan-invalid: 说明超过上限".into()); } - *explanation = redact_agent_runtime_error(root, explanation, 1200); + *explanation = truncate_agent_runtime_text(explanation, 1200); } let value = serde_json::to_value(plan).map_err(|_| "direct-plan-invalid")?; let session = Arc::clone(session); @@ -689,7 +688,7 @@ pub(super) async fn finish_sealing( session.reopen_for_repair()?; return Ok(None); } let contract: FrozenContract = serde_json::from_value(ledger.contract.clone().ok_or("delivery-contract-missing")?).map_err(|_| "delivery-contract-invalid")?; - let report = format!("本轮已完成宿主验收。\n\n范围:{}\n\n{}\n\n执行/返修批次:{}/{}。仅证明以上登记范围;固定场景不代表完整长关卡已通关。执行预算按宿主观察的占用计入;第三方并发调用可能按组占用上界计入。", redact_agent_runtime_error(&root,&contract.scope,1200), + let report = format!("本轮已完成宿主验收。\n\n范围:{}\n\n{}\n\n执行/返修批次:{}/{}。仅证明以上登记范围;固定场景不代表完整长关卡已通关。执行预算按宿主观察的占用计入;第三方并发调用可能按组占用上界计入。", truncate_agent_runtime_text(&contract.scope,1200), final_check.checks.iter().map(|check|format!("- 已通过:{}",check["label"].as_str().unwrap_or("验收项"))).collect::>().join("\n"), ledger.used_passes,ledger.max_runs); session.complete(report.clone())?; if mark_initial_delivered(&root,&ledger).is_err() { app_log!("首次交付标记未写入,后续保持更严格的新项目验收要求"); } @@ -746,7 +745,7 @@ pub(super) async fn review_reply( .join("\n"); let report = format!( "本轮未完成宿主验收,已到有界复核上限。保留已有修改与证据;仍未通过:\n{}", - redact_agent_runtime_error(root, &missing, 6000) + truncate_agent_runtime_text(&missing, 6000) ); let session = Arc::clone(session); let saved = report.clone(); @@ -830,23 +829,21 @@ mod tests { let plan = json!({"explanation":"任务进度", "plan":[{"step":"已完成", "status":"completed"}]}); assert_eq!( - update_plan(root.path(), &session, &plan).await.unwrap()["acceptancePassed"], + update_plan(&session, &plan).await.unwrap()["acceptancePassed"], false ); assert!(!try_seal(root.path(), &session).await.unwrap()); assert_eq!(session.snapshot().unwrap().contract, contract); let mut forged = plan.clone(); forged["passed"] = json!(true); - assert!(update_plan(root.path(), &session, &forged).await.is_err()); + assert!(update_plan(&session, &forged).await.is_err()); assert!(update_plan( - root.path(), &session, &json!({"plan":[{"step":"task", "status":"passed"}]}) ) .await .is_err()); assert!(update_plan( - root.path(), &session, &json!({"plan":[{"step":"a".repeat(501), "status":"pending"}]}) ) diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/direct_patch.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/direct_patch.rs index 668920998..5539ee149 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/agent/direct_patch.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/agent/direct_patch.rs @@ -332,7 +332,7 @@ fn run_transaction( .collect::>(); let changed = started && !changed_paths.is_empty(); let mut uncertain = needs_reconciliation || timed_out; - let mut output = redact_agent_runtime_error(root, &output, 6000); + let mut output = truncate_agent_runtime_text(&output, 6000); let passed = exit_code == Some(0) && !uncertain; if uncertain { session.interrupt( 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 03c24f96c..a6b101c07 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 @@ -361,23 +361,21 @@ pub(crate) struct DirectTaonierArtPackage { } impl DirectTaonierArtPackage { - fn redact_for_completed_workflow_at(mut self, root: &Path) -> Result { + /// 告警原文给模型,只做长度收口;脱敏只发生在写审计/日志的那一份。 + fn bound_for_completed_workflow_at(mut self) -> Result { for warning in self .warnings .iter_mut() .chain(self.slice_warnings.iter_mut()) { - *warning = redact_agent_runtime_error( - root, - warning, - DIRECT_TAONIER_COMPLETED_WARNING_MAX_CHARS, - ); + *warning = + truncate_agent_runtime_text(warning, DIRECT_TAONIER_COMPLETED_WARNING_MAX_CHARS); } - self.validate_completed_workflow_result_at(root)?; + self.validate_completed_workflow_result_at()?; Ok(self) } - fn validate_completed_workflow_result_at(&self, root: &Path) -> Result<(), String> { + fn validate_completed_workflow_result_at(&self) -> Result<(), String> { let expected_asset_paths = DIRECT_CODEX_ART_ASSET_PATHS .iter() .map(|path| (*path).to_string()) @@ -398,14 +396,8 @@ impl DirectTaonierArtPackage { return Err("陶泥儿整包重生成完成结果的告警数量超过上限".to_string()); } for warning in self.warnings.iter().chain(self.slice_warnings.iter()) { - if warning.chars().count() > DIRECT_TAONIER_COMPLETED_WARNING_MAX_CHARS - || redact_agent_runtime_error( - root, - warning, - DIRECT_TAONIER_COMPLETED_WARNING_MAX_CHARS, - ) != *warning - { - return Err("陶泥儿整包重生成完成结果包含未脱敏或超限告警".to_string()); + if warning.chars().count() > DIRECT_TAONIER_COMPLETED_WARNING_MAX_CHARS { + return Err("陶泥儿整包重生成完成结果的告警超过长度上限".to_string()); } } Ok(()) @@ -574,7 +566,7 @@ fn read_direct_taonier_regeneration_workflow_at( rollback.validate()?; } if let Some(result) = workflow.completed_result.as_ref() { - result.validate_completed_workflow_result_at(root)?; + result.validate_completed_workflow_result_at()?; } match workflow.state { DirectTaonierRegenerationWorkflowState::Completed @@ -1266,7 +1258,7 @@ fn complete_direct_taonier_regeneration_workflow_locked_at( expected: &DirectTaonierRegenerationWorkflow, result: DirectTaonierArtPackage, ) -> Result { - let result = result.redact_for_completed_workflow_at(root)?; + let result = result.bound_for_completed_workflow_at()?; if !direct_taonier_strict_art_package_is_valid(root) || direct_registered_taonier_slice_paths(root) != result.slice_paths { @@ -3614,7 +3606,7 @@ fn direct_browser_evidence_prompt( completion_error: Option<&str>, ) -> String { let completion_status = completion_error - .map(|error| redact_agent_runtime_error(root, error, 320)) + .map(|error| truncate_agent_runtime_text(error, 320)) .unwrap_or_else(|| prompt_text!("direct.browser.noCompletionError").to_string()); let (browser_status, viewport_lines, details, advisory) = match result { Some(result) => { @@ -3651,7 +3643,7 @@ fn direct_browser_evidence_prompt( .iter() .flat_map(|viewport| viewport.diagnostics.iter()) .filter(|diagnostic| diagnostic.starts_with("[advisory]")) - .map(|diagnostic| redact_agent_runtime_project_paths(root, diagnostic, 240)) + .map(|diagnostic| truncate_agent_runtime_text(diagnostic, 240)) .collect::>(); let details = result .viewport_results @@ -3670,20 +3662,20 @@ fn direct_browser_evidence_prompt( .map(|diagnostic| { format!( "{viewport_name} diagnostic: {}", - redact_agent_runtime_error(root, diagnostic, 320) + truncate_agent_runtime_text(diagnostic, 320) ) }), ); details.extend(viewport.console_errors.iter().take(4).map(|message| { format!( "{viewport_name} console.error: {}", - redact_agent_runtime_error(root, &message.text, 320) + truncate_agent_runtime_text(&message.text, 320) ) })); details.extend(viewport.exceptions.iter().take(4).map(|exception| { format!( "{viewport_name} exception: {}", - redact_agent_runtime_error(root, &exception.text, 320) + truncate_agent_runtime_text(&exception.text, 320) ) })); details.extend( @@ -3696,8 +3688,8 @@ fn direct_browser_evidence_prompt( format!( "{viewport_name} fatal request: {} {} ({})", request.method, - redact_agent_runtime_error(root, &request.url, 320), - redact_agent_runtime_error(root, &request.error_text, 220), + truncate_agent_runtime_text(&request.url, 320), + truncate_agent_runtime_text(&request.error_text, 220), ) }), ); @@ -3904,7 +3896,7 @@ fn write_direct_browser_acceptance_summary( .diagnostics .iter() .take(12) - .map(|detail| redact_agent_runtime_project_paths(root, detail, 320)) + .map(|detail| truncate_agent_runtime_text(detail, 320)) .collect::>(); let rendered_assets_by_viewport = evidence .viewport_results @@ -3982,7 +3974,7 @@ fn render_direct_browser_acceptance_report( .iter() .flat_map(|viewport| viewport.diagnostics.iter()) .filter(|diagnostic| diagnostic.starts_with("[advisory]")) - .map(|diagnostic| redact_agent_runtime_project_paths(root, diagnostic, 180)) + .map(|diagnostic| truncate_agent_runtime_text(diagnostic, 180)) .collect::>(); let rendered_assets = evidence .viewport_results @@ -4799,7 +4791,7 @@ async fn run_direct_game_creator_turn_inner( if attempt < DIRECT_CODEX_ERROR_FEEDBACK_MAX_ATTEMPTS && error.is_model_repairable() => { - let detail = redact_agent_runtime_error(root, &error.to_string(), 1800); + let detail = truncate_agent_runtime_text(&error.to_string(), 1800); emitter.emit( "running", Some("error-feedback"), @@ -4868,7 +4860,7 @@ async fn run_direct_game_creator_turn_inner( if attempt < DIRECT_CODEX_ERROR_FEEDBACK_MAX_ATTEMPTS && error.is_model_repairable() => { - let detail = redact_agent_runtime_error(root, &error.to_string(), 1800); + let detail = truncate_agent_runtime_text(&error.to_string(), 1800); attempt += 1; feedback_prompt = direct_codex_error_feedback_prompt(&detail); } @@ -6610,7 +6602,7 @@ mod tests { } #[tokio::test] - async fn completed_regeneration_replays_the_exact_redacted_lost_response() { + async fn completed_regeneration_replays_the_exact_lost_response() { let root = tempfile::tempdir().expect("temp dir"); init_local_game_project_at(root.path(), "direct-workflow-cleanup", "整包账本清理") .expect("init project"); @@ -6669,8 +6661,9 @@ mod tests { ) .expect("complete workflow"); assert_eq!(completed_result.slice_warnings, vec!["切片告警保持原样"]); - assert!(!completed_result.warnings[0].contains("secret.example")); - assert!(!completed_result.warnings[0].contains(root.path().to_string_lossy().as_ref())); + // 完成结果原文交回模型:URL 与项目绝对路径不再被抹掉,只做长度收口。 + assert!(completed_result.warnings[0].contains("secret.example")); + assert!(completed_result.warnings[0].contains(root.path().to_string_lossy().as_ref())); assert!(stage_paths.iter().all(|path| path.is_file())); let persisted = read_direct_taonier_regeneration_workflow_at(root.path()) .expect("read completed workflow") @@ -7528,7 +7521,7 @@ mod tests { } #[test] - fn browser_diagnostics_are_redacted_before_same_thread_repair() { + fn browser_diagnostics_go_verbatim_to_the_model_before_same_thread_repair() { let root = tempfile::tempdir().expect("temp dir"); let browser_error = format!( "Uncaught ReferenceError at {}?token=secret", @@ -7586,9 +7579,10 @@ mod tests { assert!(prompt.contains("desktop exception:")); assert!(prompt.contains("AGC 浏览器事实证据")); - assert!(!prompt.contains("token=secret"), "{prompt}"); + // 事实证据原文进 prompt:URL 里的 token 与项目绝对路径不再被抹掉,只做长度收口。 + assert!(prompt.contains("token=secret"), "{prompt}"); assert!( - !prompt.contains(&root.path().display().to_string()), + prompt.contains(&root.path().display().to_string()), "{prompt}" ); } diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/direct_tool_bridge.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/direct_tool_bridge.rs index cc703f63b..5b2ddf614 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/agent/direct_tool_bridge.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/agent/direct_tool_bridge.rs @@ -3406,7 +3406,7 @@ async fn bridge_update_plan( )) } }; - let value = super::direct_delivery::update_plan(&state.root, &session, arguments) + let value = super::direct_delivery::update_plan(&session, arguments) .await .map_err(|cause| UpdatePlanError::PlanRejected { cause })?; Ok(bridge_tool_result(value.to_string(), Vec::new())) diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/direct_validation.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/direct_validation.rs index 4dc25f47f..d2b6f847b 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/agent/direct_validation.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/agent/direct_validation.rs @@ -495,7 +495,7 @@ async fn run_browser_with_budget( ) } Err(error) => ( - json!({"mode":input.mode,"needsReconciliation":error.starts_with("browser-cleanup-unconfirmed:"),"error":redact_agent_runtime_error(root, &error, 1800)}), + json!({"mode":input.mode,"needsReconciliation":error.starts_with("browser-cleanup-unconfirmed:"),"error":truncate_agent_runtime_text(&error, 1800)}), false, ), }; @@ -585,13 +585,13 @@ pub(super) async fn run_command(root: &Path, arguments: &Value) -> Result ( - json!({"mode":"command","needsReconciliation":error.needs_reconciliation(),"error":redact_agent_runtime_error(root,&error.to_string(),1800)}), + json!({"mode":"command","needsReconciliation":error.needs_reconciliation(),"error":truncate_agent_runtime_text(&error.to_string(),1800)}), false, ), }; diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/provider_request_builders.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/provider_request_builders.rs index 993b95a55..0da446422 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/provider_request_builders.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/provider_request_builders.rs @@ -632,13 +632,10 @@ pub(in crate::agent) fn build_game_creator_agent_background_final_reply_request( )?; let observations_json = serde_json::to_string_pretty(&prompt_observations) .map_err(|error| format!("序列化 Agent 工具观察失败:{error}"))?; + // 回灌给模型的是它自己上一轮的计划原文,只做长度收口;脱敏只发生在写审计/日志时。 let plan_json = serde_json::to_string_pretty(&serde_json::json!({ - "thinkingSummary": redact_agent_runtime_project_paths( - root, - &plan.thinking_summary, - 240, - ), - "fallbackResponse": redact_agent_runtime_project_paths(root, &plan.response, 1_200), + "thinkingSummary": truncate_agent_runtime_text(&plan.thinking_summary, 240), + "fallbackResponse": truncate_agent_runtime_text(&plan.response, 1_200), })) .map_err(|error| format!("序列化 Agent 收束摘要失败:{error}"))?; let steers_json =