From c0c9179d5cf559e74df2466f3d6c9d6e342d4e66 Mon Sep 17 00:00:00 2001 From: kdletters <61648117+kdletters@users.noreply.github.com> Date: Tue, 6 Oct 2026 11:57:52 +0800 Subject: [PATCH] =?UTF-8?q?=E8=AE=A9=E9=94=99=E8=AF=AF=E6=8A=A5=E5=91=8A?= =?UTF-8?q?=E6=AD=A3=E6=96=87=E4=BF=9D=E7=95=99=E5=85=B7=E4=BD=93=E4=BC=A0?= =?UTF-8?q?=E8=BE=93=E8=AF=A6=E6=83=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 直连错误上报同时显示脱敏 detail、摘要和错误码 兼容 v2 详情解析并补充回归断言 --- .../src-tauri/src/agent/direct_runtime/mod.rs | 13 ++++++++++++- .../src/features/agent-runtime/model.ts | 17 +++++++++++++---- 2 files changed, 25 insertions(+), 5 deletions(-) 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 1d4aed6c7..1c23134ec 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 @@ -2113,8 +2113,17 @@ fn record_direct_codex_failure_facts( error ); } + let safe_detail = redact_agent_runtime_error(root, &detail, 420) + .split_whitespace() + .collect::>() + .join(" "); + let detail_suffix = if safe_detail.trim().is_empty() { + "详情:未提供具体错误正文".to_string() + } else { + format!("详情:{safe_detail}") + }; let public_text = format!( - "direct-codex-failure:v2 stage={} code={} retryable={} summary={};建议:{};{}", + "direct-codex-failure:v2 stage={} code={} retryable={} summary={};建议:{};{};{}", stage, error_code, retryable, @@ -2123,6 +2132,7 @@ fn record_direct_codex_failure_facts( .unwrap_or("未提供可安全展示的详细原因"), recovery_hint, diagnostics_suffix, + detail_suffix, ); // 失败也进错误上报池:命令入队化之后前端 catch 只剩"入队失败",池不能只靠前端填。 // 两条通道同时上报也不会变成两条——池按 fingerprint 合并同一份文案。 @@ -7537,6 +7547,7 @@ mod tests { .starts_with("direct-codex-failure:v2 stage=art-preparation code=runtime-unclassified retryable=true summary=")); assert!(error.contains(""), "{error}"); assert!(error.contains(""), "{error}"); + assert!(error.contains("详情:读取陶泥儿画布资源失败"), "{error}"); assert!(!error.contains("authorization=Bearer secret"), "{error}"); assert!(!error.contains("?token=secret"), "{error}"); assert!(!error.contains("provider.example"), "{error}"); diff --git a/apps/ai-game-creator-shell/src/features/agent-runtime/model.ts b/apps/ai-game-creator-shell/src/features/agent-runtime/model.ts index d5cc25cc7..0a1ed7bb1 100644 --- a/apps/ai-game-creator-shell/src/features/agent-runtime/model.ts +++ b/apps/ai-game-creator-shell/src/features/agent-runtime/model.ts @@ -116,6 +116,7 @@ type DirectDiagnosticParts = { stageLabel: string; summary: string; hint: string; + detail: string; retryable: boolean; }; @@ -132,13 +133,13 @@ function directCodexDiagnosticFailureParts( // v2 的现行形状(比 v1 多一段 `code=`)由上面那条覆盖;这里额外容忍**历史记录**里 // 出现过的诊断引用尾巴(`;详情:.agent/runtime/errors/.json`):宿主早已不再 // 往文案里拼引用,但旧会话记录仍可能带着它,解析时一并吃掉,`code=` 与引用都不进捕获组。 - /^direct-codex-failure:v2 stage=(request|art-preparation|code-generation|browser-validation|version-registration) code=[a-z0-9-]+ retryable=(true|false) summary=(.+?);建议:(.+?);(?:已保存脱敏项目诊断|未能保存项目诊断)(?:;详情:\S+)?$/u.exec( + /^direct-codex-failure:v2 stage=(request|art-preparation|code-generation|browser-validation|version-registration) code=[a-z0-9-]+ retryable=(true|false) summary=(.+?);建议:(.+?);(?:已保存脱敏项目诊断|未能保存项目诊断)(?:;详情:(.+))?$/u.exec( trimmed, ); if (!match) { return null; } - const [, stage, retryable, rawSummary, rawHint] = match; + const [, stage, retryable, rawSummary, rawHint, rawDetail = ''] = match; if (!stage || !retryable || !rawSummary || !rawHint) { return null; } @@ -155,6 +156,13 @@ function directCodexDiagnosticFailureParts( .replace(/\s+/g, ' ') .trim() .slice(0, 180); + const detail = ( + visibleRuntimeDetail(rawDetail, 420) || + redactDirectFailureMarkers(rawDetail) + ) + .replace(/\s+/g, ' ') + .trim() + .slice(0, 420); const stageLabel = { request: '智能创作请求失败', 'art-preparation': '平台资源准备失败', @@ -165,7 +173,7 @@ function directCodexDiagnosticFailureParts( if (!stageLabel || !summary || !hint) { return null; } - return { stageLabel, summary, hint, retryable: retryable === 'true' }; + return { stageLabel, summary, hint, detail, retryable: retryable === 'true' }; } /** 收口文案的一句话:阶段标签只有"回合失败"才成立,请求被拒那边传 `null`(见下面的导出函数)。 */ @@ -173,7 +181,8 @@ function directDiagnosticSentence( parts: DirectDiagnosticParts, stageLabel: string | null, ) { - return `${stageLabel ? `${stageLabel}:` : ''}${parts.summary}。${parts.hint}${ + const detail = parts.detail ? `;详情:${parts.detail}` : ''; + return `${stageLabel ? `${stageLabel}:` : ''}${parts.summary}。${parts.hint}${detail}${ parts.retryable ? '(可直接重试)' : '' }`; }