未绑定执行器时的交互拒绝按真实结果留痕

- 未绑定宿主执行器的交互兜底不再一律记成审批拒绝
- 新增稳定键 agent.direct_codex.interaction.no_adapter,字段为 method / outcome / request_id
- outcome 区分 decline / empty-permissions / unsupported-method,权限申请与未知方法不再被误标为 decline
- 同步更新 shared-memory 决策记录里的日志键口径
This commit is contained in:
2026-10-01 15:38:05 +08:00
parent ea33e8c166
commit c34e45e0c4
2 changed files with 14 additions and 4 deletions
@@ -41,11 +41,21 @@ pub(super) fn validate_approval_version(version: &str) -> Result<(), String> {
}
}
/// 未绑定宿主执行器时的交互兜底:一律拒绝,并把**真实结果形状**留痕。
///
/// 这是「回合没接单 / 适配器已释放」的唯一表现,不留痕线上就只剩一个没有原因的拒绝。
/// 不同交互方法的拒绝形状并不相同(审批是 `decline`,权限申请是空授权,未知方法直接报错),
/// 所以日志按 `outcome` 分类,不能一律记成审批拒绝。
pub(super) fn denied_response(id: u64, method: &str) -> Value {
// 没绑定宿主执行器时一律拒绝。这是「回合没接单 / 适配器已释放」的唯一表现,
// 不留痕线上就只剩一个没有原因的 decline。
let outcome = match method {
"item/commandExecution/requestApproval"
| "item/fileChange/requestApproval"
| "mcpServer/elicitation/request" => "decline",
"item/permissions/requestApproval" => "empty-permissions",
_ => "unsupported-method",
};
app_log!(
"agent.direct_codex.approval.denied reason=no-bound-host-execution-adapter request_id={id} method={method}"
"agent.direct_codex.interaction.no_adapter method={method} outcome={outcome} request_id={id}"
);
denied(id, method)
}
@@ -2,7 +2,7 @@
## 2026-10-01 DirectProject 审批拒绝原因留痕
- 决策:宿主拒绝 app-server 的审批 / 交互请求时,原因必须落 AppData `RUST` 日志。稳定键 `agent.direct_codex.approval.denied`,字段为 `thread_id` / `method` / `reason` / `distinct_reasons`;未绑定宿主执行器时 `reason=no-bound-host-execution-adapter`,回包写入失败另记 `agent.direct_codex.approval.response_write_failed`。
- 决策:宿主拒绝 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_write_failed`。
- 原因:线上对审批只回 `{"decision":"decline"}`,模型与用户都看不到是哪一道闸门(合同缺失 / 预算耗尽 / 阶段已收束 / item 不在途 / 无适配器)拦下的,只能靠复现。
- 边界:`reason` 只接受 `execution.rs` 内的静态分类或宿主自己的错误文本,不带请求参数、上游正文、路径或凭据;同一回合内 `(method, reason)` 只记一次,最多 `MAX_DENIED_REASON_LOGS = 64` 条。
- 影响范围:`apps/ai-game-creator-shell/src-tauri/src/agent/codex_app_server/execution.rs`。