修复应用日志身份行的摘要只压行未脱敏
- 身份行的 summary 由调用方给,direct_tool_bridge 传的是工具错误原文,而 app_log! 会把整行写 stderr,那里没有 sanitize_diagnostic_message 兜底 - 新增 AGENT_RUNTIME_ERROR_APP_LOG_SUMMARY_CHARS(320 字符,与 sidecar 摘要预算同口径),summary 落日志前先脱敏再截断 - 函数合同写明 detail/metadata/summary 三类外来文本都在这里脱敏,不再只是压平换行 - 同步【技术方案】AGC错误报告与诊断上传与共享记忆 decision-log 的预算口径 - 验证:cargo test runtime_error(7 passed)
This commit is contained in:
@@ -23,6 +23,12 @@ pub(crate) const AGENT_RUNTIME_ERROR_MAX_DETAIL_CHARS: usize = 8 * 1024;
|
||||
pub(crate) const AGENT_RUNTIME_ERROR_APP_LOG_DETAIL_CHARS: usize = 1_200;
|
||||
pub(crate) const AGENT_RUNTIME_ERROR_APP_LOG_METADATA_CHARS: usize = 200;
|
||||
|
||||
/// 身份行里 public summary 的字符预算。
|
||||
///
|
||||
/// 身份行必须短小:`summary` 由调用方给,`direct_tool_bridge` 传的是工具错误原文,
|
||||
/// 所以这里与 sidecar 侧的摘要预算同口径(320 字符)截断,并再脱敏一次。
|
||||
pub(crate) const AGENT_RUNTIME_ERROR_APP_LOG_SUMMARY_CHARS: usize = 320;
|
||||
|
||||
static ERROR_EVENT_SEQUENCE: AtomicU64 = AtomicU64::new(1);
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
|
||||
@@ -128,8 +134,10 @@ pub(crate) fn persist_agent_runtime_error(
|
||||
|
||||
/// 把统一错误事件投影成 AppData `diagnostics/application.log` 里的两行。
|
||||
///
|
||||
/// 传进来的 `detail` 已经是 sidecar 用的脱敏文本;这里只再按应用日志的预算截一次,
|
||||
/// 让日志与项目内 sidecar 是同一份诊断,不再单独拼一套字段。
|
||||
/// 传进来的 `detail` 是 sidecar 用的脱敏文本,`metadata` 则从未脱敏过。两行落到 `app_log!`
|
||||
/// 时都会先按应用日志预算(1200 / 200 字符)再脱敏、再截断:`app_log!` 还会把同一行写到
|
||||
/// stderr,那里没有 `sanitize_diagnostic_message` 兜底,所以每个调用方给的外来文本
|
||||
/// (`summary` 按 320 字符预算)都在这里过一遍脱敏。
|
||||
///
|
||||
/// 拆成「身份行 + 详情行」是因为整行只要出现凭据标记就会被
|
||||
/// [`crate::sanitize_diagnostic_message`] 整体替换成脱敏占位,详情行可能因此消失;
|
||||
@@ -159,7 +167,11 @@ pub(crate) fn agent_runtime_error_app_log_lines(
|
||||
let stage = single_line_log_field(stage);
|
||||
let code = single_line_log_field(code);
|
||||
let detail_ref = single_line_log_field(detail_ref);
|
||||
let public_text = single_line_log_field(public_text);
|
||||
let public_text = single_line_log_field(&redact_agent_runtime_error(
|
||||
root,
|
||||
public_text,
|
||||
AGENT_RUNTIME_ERROR_APP_LOG_SUMMARY_CHARS,
|
||||
));
|
||||
let elapsed_ms = elapsed_ms
|
||||
.map(|value| value.to_string())
|
||||
.unwrap_or_else(|| "none".to_string());
|
||||
|
||||
@@ -9177,7 +9177,7 @@ CI 上 `background_agent_runtime_recovers_stale_running_before_pending_task` 在
|
||||
## 2026-09-21 统一错误事件额外投影到 AppData 应用日志
|
||||
|
||||
- 背景:`direct-codex-failure:v2 ... 详情:.agent/runtime/errors/error-<id>-9.json`(例如 `stage=code-generation code=turn-idle-timeout`)里的诊断正文只落在项目目录,而“报告问题”只上传 AppData `diagnostics/application.log`;用户提交上来的失败消息因此只有一个指向项目文件的引用,团队复现不到 stderr 摘要、退出状态这些真因。
|
||||
- 决策:统一错误写入边界 `agent/runtime_error.rs` 在落 `.agent/runtime/errors/<eventId>.json` 之前先把同一份已脱敏诊断投影成应用日志两行——`agent.runtime.error`(身份行:eventId / source / stage / code / retryable / clientTurnId / elapsedMs / detailRef / summary)与 `agent.runtime.error.detail`(详情行:hint / detail / metadata)。字段仍只从 sidecar 那份 diagnosis 来,不新增第二份来源;detail / metadata 先按应用日志预算(1200 / 200 字符)脱敏截断,自由文本先压平换行(`app_log!` 同时写 stderr,那里没人压行)。
|
||||
- 决策:统一错误写入边界 `agent/runtime_error.rs` 在落 `.agent/runtime/errors/<eventId>.json` 之前先把同一份已脱敏诊断投影成应用日志两行——`agent.runtime.error`(身份行:eventId / source / stage / code / retryable / clientTurnId / elapsedMs / detailRef / summary)与 `agent.runtime.error.detail`(详情行:hint / detail / metadata)。字段仍只从 sidecar 那份 diagnosis 来,不新增第二份来源;落盘前 detail / metadata 按应用日志预算(1200 / 200 字符)脱敏截断,身份行的 `summary` 也按 320 字符预算脱敏截断(`direct_tool_bridge` 会把它当自由文本传工具错误原文,而 `app_log!` 同时写 stderr,那里没有 `sanitize_diagnostic_message` 兜底),自由文本先压平换行。
|
||||
- 决策(两行而不是一行):整行一旦命中 `sanitize_diagnostic_message` 的凭据标记(token / bearer / authorization / credential / api key)会被整体替换成 `<sensitive diagnostic details redacted>`;拆开后详情行即使被吃掉,身份行仍能定位 eventId 与 detailRef。
|
||||
- 边界:进程内错误报告事件池(`error_report`)与项目内 `.agent/runtime/errors/<eventId>.json` sidecar 是两套东西——本次只把 sidecar 的同一份诊断作为**应用日志行**落盘,不进事件池、不改报告上传协议、不改 `read_agent_runtime_error_detail` 详情入口。
|
||||
- 影响面:只改 `apps/ai-game-creator-shell/src-tauri/src/agent/runtime_error.rs` 这一处写入边界(覆盖 direct-codex / agc-tools / agent-runtime 三类来源);sidecar schema、对话投影、前端详情入口与错误报告协议均未改动。
|
||||
|
||||
@@ -13,7 +13,7 @@ AI Game Creator Shell 采用 IDEA 风格的当前进程错误报告:错误事
|
||||
- 指纹计算可使用调用方的 page/action 及脱敏后的首个调用点作为进程内区分输入,但这些上下文不会作为事件字段上传;消息与 stack 在入池前统一脱敏,WebCrypto 失败时降级为稳定可读指纹,采集本身不得产生新的未处理拒绝。
|
||||
- 客户端 API 自动采集只覆盖网络错误、408 和 5xx;预期的 4xx 登录/鉴权失败不进入错误报告池。
|
||||
- Rust 侧通过 `app_log!` 将普通文本日志同时输出到 stderr 和 AppData `diagnostics/application.log`,超出 256 KiB 滚动到 `application.previous.log`;WebView 的 console 输出通过 `append_application_log` 镜像到同一 raw log,并在客户端桥接处再次脱敏;`read_diagnostic_logs` 只读取应用级日志。
|
||||
- 这里有两套互不相干的东西,不要互相代入:**错误报告事件池**是进程内 `error_report` 的结构化事件(本次变更不动它,仍然只在内存里、提交时才生成 `events.jsonl`);**统一 Agent Runtime 错误事件**是项目内 sidecar `.agent/runtime/errors/<eventId>.json`,既不进事件池也不进报告包。因为报告包里的日志附件只有 AppData 应用日志,所以 sidecar 的同一份已脱敏诊断再作为**日志行**(不是报告事件)投影成两行:`agent.runtime.error`(身份行:eventId / source / stage / code / retryable / clientTurnId / elapsedMs / detailRef / summary)与 `agent.runtime.error.detail`(详情行:hint / detail / metadata)。两行都由 `agent/runtime_error.rs` 从同一份 diagnosis 生成,detail 与 metadata 各按应用日志预算截断,不新增字段来源;详情行可能因整行凭据标记被整体脱敏,身份行保证事件仍可定位。
|
||||
- 这里有两套互不相干的东西,不要互相代入:**错误报告事件池**是进程内 `error_report` 的结构化事件(本次变更不动它,仍然只在内存里、提交时才生成 `events.jsonl`);**统一 Agent Runtime 错误事件**是项目内 sidecar `.agent/runtime/errors/<eventId>.json`,既不进事件池也不进报告包。因为报告包里的日志附件只有 AppData 应用日志,所以 sidecar 的同一份已脱敏诊断再作为**日志行**(不是报告事件)投影成两行:`agent.runtime.error`(身份行:eventId / source / stage / code / retryable / clientTurnId / elapsedMs / detailRef / summary)与 `agent.runtime.error.detail`(详情行:hint / detail / metadata)。两行都由 `agent/runtime_error.rs` 从同一份 diagnosis 生成,不新增字段来源;落到日志前 detail / metadata 各按应用日志预算(1200 / 200 字符)脱敏截断,身份行里的 `summary` 也按 320 字符预算脱敏截断(该字段由调用方给,`direct_tool_bridge` 会传工具错误原文,而 `app_log!` 同时把整行写 stderr,那里没有 `sanitize_diagnostic_message` 兜底)。详情行可能因整行凭据标记被整体脱敏,身份行保证事件仍可定位。
|
||||
- 报告面板只由自动诊断通知中的“查看并报告”打开,不提供聊天命令、崩溃页按钮或其他手动入口;默认选中当前快照中的全部事件,用户可取消不想提交的事件。允许填写最多 2,000 字中文描述并取消日志附件;本版本不支持截图或任意文件附件。
|
||||
- 报告面板读取当前错误快照失败时,必须明确显示“错误事件暂不可用,请关闭后重试”,不能把失败误显示为“当前没有待报告的错误”。
|
||||
- 通知中的“查看并报告”打开面板时必须保留该次通知快照;最新快照读取瞬时失败时使用这份 fallback 继续展示和提交,不能因先清空通知而丢失用户刚看到的事件。
|
||||
|
||||
Reference in New Issue
Block a user