回包失败留痕改为记录真实原因

- response_write_failed 改名为 response_delivery_failed,新增 ResponseDeliveryFailure 枚举
- 两个调用点分别传 Write(stdin 写入 / flush 失败)与 TurnBindMismatch(bind_turn 返回 false)
- 日志键改为 agent.direct_codex.approval.response_delivery_failed 并带 cause 字段,回合绑定失败不再被误报成写入失败
- 同步更新 shared-memory 决策记录里的日志键口径
This commit is contained in:
2026-10-01 15:44:25 +08:00
parent e3041d84e5
commit ce4ada8bb3
3 changed files with 32 additions and 9 deletions
@@ -291,6 +291,26 @@ fn accepted(id: u64, method: &str) -> Value {
}
}
/// app-server 交互回包未能送达的原因。
///
/// 两种失败共用同一条收束路径,但来源不同:写失败是 Codex 根本没收到 decision,回合绑定不一致
/// 是回包已经产出、宿主无法确认执行作用域。留痕必须写真实原因,不能按函数名反推。
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(super) enum ResponseDeliveryFailure {
/// stdin 写入 / flush 失败。
Write,
/// `bind_turn` 返回 false:回合绑定不一致。
TurnBindMismatch,
}
impl ResponseDeliveryFailure {
fn as_str(self) -> &'static str {
match self {
Self::Write => "response-write-failed",
Self::TurnBindMismatch => "turn-bind-mismatch",
}
}
}
impl ExecutionAdapter {
pub(super) async fn for_turn(
root: &Path,
@@ -789,15 +809,16 @@ impl ExecutionAdapter {
.await;
}
pub(super) fn response_write_failed(self: &Arc<Self>) {
// 回包写不出去时 Codex 侧等不到 decision,表现为「审批没有回应」;与主动 decline 分开留痕。
pub(super) fn response_delivery_failed(self: &Arc<Self>, cause: ResponseDeliveryFailure) {
// 回包没送达 Codex 时线上表现为「审批没有回应」;与主动 decline 分开留痕。
// 同一回合内同一原因只留一行:这一步可能被重复触发。
self.log_once(
"host-interaction\u{1}response-write-failed".to_string(),
format!("host-interaction\u{1}response-delivery:{}", cause.as_str()),
|_| {
format!(
"agent.direct_codex.approval.response_write_failed thread_id={}",
self.thread_id
"agent.direct_codex.approval.response_delivery_failed thread_id={} cause={}",
self.thread_id,
cause.as_str()
)
},
);
@@ -18,7 +18,7 @@ mod execution;
mod model_catalog;
pub(crate) use direct_project_identity::thread_id_for_project;
use direct_project_identity::*;
use execution::ExecutionAdapter;
use execution::{ExecutionAdapter, ResponseDeliveryFailure};
const GAME_CREATOR_CODEX_APP_SERVER_PROVIDER_ID: &str = "genarrative_agc";
const GAME_CREATOR_CODEX_APP_SERVER_API_KEY_ENV: &str = "GENARRATIVE_AGC_CODEX_API_KEY";
@@ -4884,7 +4884,7 @@ async fn read_game_creator_codex_app_server_stdout(
};
if !sent {
if let Some(adapter) = adapter {
adapter.response_write_failed();
adapter.response_delivery_failed(ResponseDeliveryFailure::Write);
}
}
});
@@ -4916,7 +4916,9 @@ async fn read_game_creator_codex_app_server_stdout(
let adapter = inner.execution.lock().ok().and_then(|slot| slot.clone());
if let Some(adapter) = adapter {
if !adapter.bind_turn(turn_id) {
adapter.response_write_failed();
adapter.response_delivery_failed(
ResponseDeliveryFailure::TurnBindMismatch,
);
}
}
}