diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/codex_app_server/execution.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/codex_app_server/execution.rs index 2c31b17c9..72b02c321 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/agent/codex_app_server/execution.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/agent/codex_app_server/execution.rs @@ -322,6 +322,19 @@ impl ResponseDeliveryFailure { Self::TurnBindMismatch => "turn-bind-mismatch", } } + + /// 写进回合终态说明的收束原因。 + /// + /// 两种失败对用户的意义不同:写失败是回包真的没送出去;绑定不一致是回包已经产出,只是宿主 + /// 无法确认它属于哪一轮。不能共用「回包丢失」这一句,否则报告会把绑定问题说成丢包。 + fn interrupt_reason(self) -> &'static str { + match self { + Self::Write => "执行许可回包丢失,不能确认原操作是否已执行。", + Self::TurnBindMismatch => { + "回合绑定与当前 app-server 不一致,无法确认本轮操作的作用域;已停止本轮执行。" + } + } + } } impl ExecutionAdapter { @@ -862,9 +875,7 @@ impl ExecutionAdapter { ); let adapter = Arc::clone(self); tokio::spawn(async move { - adapter - .interrupt("执行许可回包丢失,不能确认原操作是否已执行。") - .await; + adapter.interrupt(cause.interrupt_reason()).await; }); } @@ -1898,4 +1909,14 @@ mod tests { assert!(key.contains("lease-admit-failed"), "{key}"); assert!(!key.contains("/home/someone"), "留痕不得带绝对路径:{key}"); } + + #[test] + fn response_delivery_failure_texts_match_their_cause() { + assert!(ResponseDeliveryFailure::Write + .interrupt_reason() + .contains("回包丢失")); + let bind = ResponseDeliveryFailure::TurnBindMismatch.interrupt_reason(); + assert!(bind.contains("绑定"), "{bind}"); + assert!(!bind.contains("回包丢失"), "绑定失败不是回包丢失:{bind}"); + } } diff --git a/docs/project-memory/shared-memory/decision-log.md b/docs/project-memory/shared-memory/decision-log.md index 8e1fc3c67..6b3318345 100644 --- a/docs/project-memory/shared-memory/decision-log.md +++ b/docs/project-memory/shared-memory/decision-log.md @@ -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` 内的静态分类或宿主自己的错误文本;透传的宿主错误在留痕出口统一走 `sanitize_diagnostic_message`,不带请求参数、上游正文、路径或凭据(去重键也用脱敏后的文本)。有界去重一律按 `MAX_DENIED_REASON_LOGS = 64` 条封顶——适配器路径按回合内 `(method, reason)`、未绑定执行器路径按进程级 `(method, outcome)`、回包未送达按回合内原因各只记一次。 +- 边界:`reason` 只接受 `execution.rs` 内的静态分类或宿主自己的错误文本;透传的宿主错误在留痕出口统一走 `sanitize_diagnostic_message`,不带请求参数、上游正文、路径或凭据(去重键也用脱敏后的文本)。有界去重一律按 `MAX_DENIED_REASON_LOGS = 64` 条封顶——适配器路径按回合内 `(method, reason)`、未绑定执行器路径按进程级 `(method, outcome)`、回包未送达按回合内原因各只记一次。拒绝留痕在释放审批临界区(`state`)之后进行;回合收束说明按 `cause` 区分,只有 `response-write-failed` 才说「回包丢失」,`turn-bind-mismatch` 说明作用域无法确认。 - 影响范围:`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`。