拒绝原因留痕前统一脱敏
- log_denied_reason 在唯一出口调用 sanitize_diagnostic_message,透传的宿主错误文本不再把绝对路径写进日志 - 去重键改用脱敏后的文本,内存与 stderr 拷贝同样不带原文 - 新增 denied_reason_logging_scrubs_embedded_paths 用例固化该约定 - 同步更新 shared-memory 决策记录里的脱敏口径
This commit is contained in:
@@ -389,6 +389,10 @@ impl ExecutionAdapter {
|
||||
/// 同一回合内 `(method, reason)` 只记一次:模型被拒后常反复重试同一动作,逐次记录会把真正
|
||||
/// 有用的诊断刷掉;"这一轮被拒过哪些原因"仍然完整。
|
||||
fn log_denied_reason(&self, method: &str, reason: &str) {
|
||||
// 契约:留痕不带路径、上游正文或凭据。静态分类本来就没有;`lease-admit-failed` 这类透传的
|
||||
// 宿主错误文本可能内嵌绝对项目路径(如 `validate_patch_paths` 的路径校验错误),所以统一
|
||||
// 在唯一出口脱敏——去重键用脱敏后的文本,日志拷贝与 stderr 拷贝都不会带原文。
|
||||
let reason = crate::sanitize_diagnostic_message(reason, None);
|
||||
let key = format!("{method}\u{1}{reason}");
|
||||
let thread_id = &self.thread_id;
|
||||
self.log_once(key, |distinct| {
|
||||
@@ -1847,4 +1851,17 @@ mod tests {
|
||||
MAX_DENIED_REASON_LOGS
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn denied_reason_logging_scrubs_embedded_paths() {
|
||||
let (_temp, adapter) = fixture();
|
||||
adapter.log_denied_reason(
|
||||
"item/commandExecution/requestApproval",
|
||||
"lease-admit-failed: 读取路径失败:/home/someone/secret-project/app.ts",
|
||||
);
|
||||
let logged = adapter.denied_reasons_logged.lock().unwrap();
|
||||
let key = logged.iter().next().expect("拒绝原因应留痕");
|
||||
assert!(key.contains("lease-admit-failed"), "{key}");
|
||||
assert!(!key.contains("/home/someone"), "留痕不得带绝对路径:{key}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
- 决策:宿主拒绝 app-server 的审批 / 交互请求时,原因必须落 AppData `RUST` 日志。稳定键 `agent.direct_codex.approval.denied`,字段为 `thread_id` / `method` / `reason` / `distinct_reasons`;未绑定宿主执行器时另记 `agent.direct_codex.interaction.no_adapter`(`method` / `outcome`,`outcome` 区分 `decline` / `empty-permissions` / `unsupported-method`);回包未送达另记 `agent.direct_codex.approval.response_delivery_failed`,`cause` 区分 `response-write-failed` 与 `turn-bind-mismatch`。
|
||||
- 原因:线上对审批只回 `{"decision":"decline"}`,模型与用户都看不到是哪一道闸门(合同缺失 / 预算耗尽 / 阶段已收束 / item 不在途 / 无适配器)拦下的,只能靠复现。
|
||||
- 边界:`reason` 只接受 `execution.rs` 内的静态分类或宿主自己的错误文本,不带请求参数、上游正文、路径或凭据;有界去重一律按 `MAX_DENIED_REASON_LOGS = 64` 条封顶——适配器路径按回合内 `(method, reason)`、未绑定执行器路径按进程级 `(method, outcome)`、回包未送达按回合内原因各只记一次。
|
||||
- 边界:`reason` 只接受 `execution.rs` 内的静态分类或宿主自己的错误文本;透传的宿主错误在留痕出口统一走 `sanitize_diagnostic_message`,不带请求参数、上游正文、路径或凭据(去重键也用脱敏后的文本)。有界去重一律按 `MAX_DENIED_REASON_LOGS = 64` 条封顶——适配器路径按回合内 `(method, reason)`、未绑定执行器路径按进程级 `(method, outcome)`、回包未送达按回合内原因各只记一次。
|
||||
- 影响范围:`apps/ai-game-creator-shell/src-tauri/src/agent/codex_app_server/execution.rs`。
|
||||
- 验证:`cargo test --bin genarrative-ai-game-creator-shell agent::codex_app_server::execution::tests`(15 passed,含 `denied_approval_records_its_reason_once_per_turn`、`denied_reason_logging_is_bounded`)、`npm run check:encoding`、`git diff --check`。
|
||||
|
||||
|
||||
Reference in New Issue
Block a user