Move apply-patch file changes into turn items (#20540)

## Why

Apply-patch file changes are now part of the core turn item stream, so
v2 clients can consume the same first-class item lifecycle path used by
other turn items instead of relying on app-server-specific remapping
from legacy patch events.

## What changed

- Added a core `TurnItem::FileChange` carrying apply-patch changes and
completion metadata.
- Updated the apply-patch tool emitter to send `ItemStarted` /
`ItemCompleted` with the new `FileChange` item while preserving legacy
`PatchApplyBegin` / `PatchApplyEnd` fan-out.
- Updated app-server v2 conversion to render the new core item directly
and stopped `event_mapping` from remapping old patch begin/end events
into item notifications.
- Kept thread history reconstruction based on the existing old
apply-patch events for rollout compatibility.

## Verification

- `cargo test -p codex-protocol -p codex-app-server-protocol`
- `cargo test -p codex-core --test all
apply_patch_tool_executes_and_emits_patch_events`
- `cargo test -p codex-app-server bespoke_event_handling`
This commit is contained in:
pakrym-oai
2026-05-01 08:47:18 -07:00
committed by GitHub
Unverified
parent 0b04d1b3cc
commit f476338f93
10 changed files with 240 additions and 165 deletions
+16 -20
View File
@@ -6,6 +6,8 @@ use crate::tools::sandboxing::ToolError;
use codex_protocol::error::CodexErr;
use codex_protocol::error::SandboxErr;
use codex_protocol::exec_output::ExecToolCallOutput;
use codex_protocol::items::FileChangeItem;
use codex_protocol::items::TurnItem;
use codex_protocol::parse_command::ParsedCommand;
use codex_protocol::protocol::EventMsg;
use codex_protocol::protocol::ExecCommandBeginEvent;
@@ -13,8 +15,6 @@ use codex_protocol::protocol::ExecCommandEndEvent;
use codex_protocol::protocol::ExecCommandSource;
use codex_protocol::protocol::ExecCommandStatus;
use codex_protocol::protocol::FileChange;
use codex_protocol::protocol::PatchApplyBeginEvent;
use codex_protocol::protocol::PatchApplyEndEvent;
use codex_protocol::protocol::PatchApplyStatus;
use codex_protocol::protocol::TurnDiffEvent;
use codex_shell_command::parse_command::parse_command;
@@ -183,13 +183,15 @@ impl ToolEmitter {
guard.on_patch_begin(changes);
}
ctx.session
.send_event(
.emit_turn_item_started(
ctx.turn,
EventMsg::PatchApplyBegin(PatchApplyBeginEvent {
call_id: ctx.call_id.to_string(),
turn_id: ctx.turn.sub_id.clone(),
auto_approved: *auto_approved,
&TurnItem::FileChange(FileChangeItem {
id: ctx.call_id.to_string(),
changes: changes.clone(),
status: None,
auto_approved: Some(*auto_approved),
stdout: None,
stderr: None,
}),
)
.await;
@@ -200,7 +202,6 @@ impl ToolEmitter {
changes.clone(),
output.stdout.text.clone(),
output.stderr.text.clone(),
output.exit_code == 0,
if output.exit_code == 0 {
PatchApplyStatus::Completed
} else {
@@ -218,7 +219,6 @@ impl ToolEmitter {
changes.clone(),
output.stdout.text.clone(),
output.stderr.text.clone(),
output.exit_code == 0,
if output.exit_code == 0 {
PatchApplyStatus::Completed
} else {
@@ -236,7 +236,6 @@ impl ToolEmitter {
changes.clone(),
String::new(),
(*message).to_string(),
/*success*/ false,
PatchApplyStatus::Failed,
)
.await;
@@ -250,7 +249,6 @@ impl ToolEmitter {
changes.clone(),
String::new(),
(*message).to_string(),
/*success*/ false,
PatchApplyStatus::Declined,
)
.await;
@@ -496,20 +494,18 @@ async fn emit_patch_end(
changes: HashMap<PathBuf, FileChange>,
stdout: String,
stderr: String,
success: bool,
status: PatchApplyStatus,
) {
ctx.session
.send_event(
.emit_turn_item_completed(
ctx.turn,
EventMsg::PatchApplyEnd(PatchApplyEndEvent {
call_id: ctx.call_id.to_string(),
turn_id: ctx.turn.sub_id.clone(),
stdout,
stderr,
success,
TurnItem::FileChange(FileChangeItem {
id: ctx.call_id.to_string(),
changes,
status,
status: Some(status),
auto_approved: None,
stdout: Some(stdout),
stderr: Some(stderr),
}),
)
.await;