宿主终态接线:失败写进 turn.completed 的 failure 载荷,并在 turn.started 之后武装兜底守卫
- codex_app_server 的 DirectProject 终态改用 direct_turn_failure 判定:失败走 turn_completed_failed(原因脱敏 + 截断后写进同一个事件),其余仍走 turn_completed(status) - 失败判定两个来源:collect_result 是 Err 时用错误本身当原因;collect_result 是交付报告但 status 已判成 failed 时用那份报告当原因 - turn.started 进入队列后立即武装 DirectTurnFailureGuard,写完终态 disarm:panic、回合 future 被丢弃、终态之前的早退都会补一条 host-dropped 失败终态,前端不会停在"还在跑" - 定向 `cargo test direct_`(438 passed,含 wire / manager / 失败策略模块)
This commit is contained in:
@@ -3548,12 +3548,20 @@ impl CodexAppServerConnection {
|
||||
let direct_turn_user_item_id = direct_persisted_user_item
|
||||
.as_ref()
|
||||
.and_then(direct_thread_item_identity);
|
||||
// 回合终态兜底:`turn.started` 进队列之后就武装,写完终态即解除。宿主在这两者之间任何
|
||||
// 提前收场(panic、future 被丢弃、以后新增的早退)都由它补一条失败终态,否则前端只能
|
||||
// 永远停在"还在跑"。
|
||||
let mut direct_turn_failure_guard: Option<DirectTurnFailureGuard> = None;
|
||||
if self.inner.workspace_mode == CodexAppServerWorkspaceMode::DirectProject {
|
||||
append_direct_thread_event(
|
||||
&direct_thread_id,
|
||||
DirectThreadEvent::turn_started(direct_turn_started_at_ms)
|
||||
.with_user_item_id(direct_turn_user_item_id.as_deref()),
|
||||
);
|
||||
direct_turn_failure_guard = Some(DirectTurnFailureGuard::arm(
|
||||
direct_thread_id.clone(),
|
||||
direct_turn_user_item_id.clone(),
|
||||
));
|
||||
if let Some(user_item) = direct_persisted_user_item.as_ref() {
|
||||
if let Some(entry_item) = direct_thread_event_item(history_root, user_item) {
|
||||
// 这里的条目时间可能是启动应答后的观测时间;前端按同一用户条目身份
|
||||
@@ -4061,11 +4069,30 @@ impl CodexAppServerConnection {
|
||||
.map(|(_, at)| *at)
|
||||
.unwrap_or_else(direct_tool_call_now_ms)
|
||||
};
|
||||
append_direct_thread_event(
|
||||
&direct_thread_id,
|
||||
DirectThreadEvent::turn_completed(status, completed_at)
|
||||
.with_user_item_id(direct_turn_user_item_id.as_deref()),
|
||||
// 终态只有 `turn.completed` 一种事件:失败时同一个事件带 `failure` 载荷(原因由宿主
|
||||
// 脱敏 + 截断后写进去),其余(`completed` / `interrupted` / `aborted`)不带载荷。
|
||||
// 失败不再只写一个 `status="failed"`:那让失败与正常结束在协议上长得一样,前端只能
|
||||
// 另开一条通道(命令返回 / 另一条 IPC)去拿原因,也就等于承认事件流讲不清一轮怎么结束。
|
||||
let failure = direct_turn_failure(
|
||||
&status,
|
||||
collect_result.as_ref().map(String::as_str),
|
||||
history_root,
|
||||
);
|
||||
match failure {
|
||||
Some(failure) => append_direct_thread_event(
|
||||
&direct_thread_id,
|
||||
DirectThreadEvent::turn_completed_failed(failure, completed_at)
|
||||
.with_user_item_id(direct_turn_user_item_id.as_deref()),
|
||||
),
|
||||
None => append_direct_thread_event(
|
||||
&direct_thread_id,
|
||||
DirectThreadEvent::turn_completed(status, completed_at)
|
||||
.with_user_item_id(direct_turn_user_item_id.as_deref()),
|
||||
),
|
||||
};
|
||||
if let Some(guard) = direct_turn_failure_guard.as_mut() {
|
||||
guard.disarm();
|
||||
}
|
||||
}
|
||||
let text = collect_result?;
|
||||
guard.armed = false;
|
||||
|
||||
Reference in New Issue
Block a user