Skip to content

Commit b3b69fe

Browse files
committed
[app-server] fix: emit item/fileChange/outputDelta for file change items
1 parent 6eeaf46 commit b3b69fe

File tree

4 files changed

+50
-8
lines changed

4 files changed

+50
-8
lines changed

codex-rs/app-server-protocol/src/protocol/common.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,7 @@ server_notification_definitions! {
510510
ItemCompleted => "item/completed" (v2::ItemCompletedNotification),
511511
AgentMessageDelta => "item/agentMessage/delta" (v2::AgentMessageDeltaNotification),
512512
CommandExecutionOutputDelta => "item/commandExecution/outputDelta" (v2::CommandExecutionOutputDeltaNotification),
513+
FileChangeOutputDelta => "item/fileChange/outputDelta" (v2::FileChangeOutputDeltaNotification),
513514
McpToolCallProgress => "item/mcpToolCall/progress" (v2::McpToolCallProgressNotification),
514515
AccountUpdated => "account/updated" (v2::AccountUpdatedNotification),
515516
AccountRateLimitsUpdated => "account/rateLimits/updated" (v2::AccountRateLimitsUpdatedNotification),

codex-rs/app-server-protocol/src/protocol/v2.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1295,6 +1295,14 @@ pub struct CommandExecutionOutputDeltaNotification {
12951295
pub delta: String,
12961296
}
12971297

1298+
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
1299+
#[serde(rename_all = "camelCase")]
1300+
#[ts(export_to = "v2/")]
1301+
pub struct FileChangeOutputDeltaNotification {
1302+
pub item_id: String,
1303+
pub delta: String,
1304+
}
1305+
12981306
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
12991307
#[serde(rename_all = "camelCase")]
13001308
#[ts(export_to = "v2/")]

codex-rs/app-server/src/bespoke_event_handling.rs

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use codex_app_server_protocol::ContextCompactedNotification;
1818
use codex_app_server_protocol::ErrorNotification;
1919
use codex_app_server_protocol::ExecCommandApprovalParams;
2020
use codex_app_server_protocol::ExecCommandApprovalResponse;
21+
use codex_app_server_protocol::FileChangeOutputDeltaNotification;
2122
use codex_app_server_protocol::FileChangeRequestApprovalParams;
2223
use codex_app_server_protocol::FileChangeRequestApprovalResponse;
2324
use codex_app_server_protocol::FileUpdateChange;
@@ -490,15 +491,28 @@ pub(crate) async fn apply_bespoke_event_handling(
490491
.await;
491492
}
492493
EventMsg::ExecCommandOutputDelta(exec_command_output_delta_event) => {
493-
let notification = CommandExecutionOutputDeltaNotification {
494-
item_id: exec_command_output_delta_event.call_id.clone(),
495-
delta: String::from_utf8_lossy(&exec_command_output_delta_event.chunk).to_string(),
494+
let item_id = exec_command_output_delta_event.call_id.clone();
495+
let delta = String::from_utf8_lossy(&exec_command_output_delta_event.chunk).to_string();
496+
let is_file_change = {
497+
let map = turn_summary_store.lock().await;
498+
map.get(&conversation_id)
499+
.is_some_and(|summary| summary.file_change_started.contains(&item_id))
496500
};
497-
outgoing
498-
.send_server_notification(ServerNotification::CommandExecutionOutputDelta(
499-
notification,
500-
))
501-
.await;
501+
if is_file_change {
502+
let notification = FileChangeOutputDeltaNotification { item_id, delta };
503+
outgoing
504+
.send_server_notification(ServerNotification::FileChangeOutputDelta(
505+
notification,
506+
))
507+
.await;
508+
} else {
509+
let notification = CommandExecutionOutputDeltaNotification { item_id, delta };
510+
outgoing
511+
.send_server_notification(ServerNotification::CommandExecutionOutputDelta(
512+
notification,
513+
))
514+
.await;
515+
}
502516
}
503517
EventMsg::ExecCommandEnd(exec_command_end_event) => {
504518
let ExecCommandEndEvent {

codex-rs/app-server/tests/suite/v2/turn_start.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use app_test_support::to_response;
1111
use codex_app_server_protocol::ApprovalDecision;
1212
use codex_app_server_protocol::CommandExecutionRequestApprovalResponse;
1313
use codex_app_server_protocol::CommandExecutionStatus;
14+
use codex_app_server_protocol::FileChangeOutputDeltaNotification;
1415
use codex_app_server_protocol::FileChangeRequestApprovalResponse;
1516
use codex_app_server_protocol::ItemCompletedNotification;
1617
use codex_app_server_protocol::ItemStartedNotification;
@@ -725,6 +726,24 @@ async fn turn_start_file_change_approval_v2() -> Result<()> {
725726
)
726727
.await?;
727728

729+
let output_delta_notif = timeout(
730+
DEFAULT_READ_TIMEOUT,
731+
mcp.read_stream_until_notification_message("item/fileChange/outputDelta"),
732+
)
733+
.await??;
734+
let output_delta: FileChangeOutputDeltaNotification = serde_json::from_value(
735+
output_delta_notif
736+
.params
737+
.clone()
738+
.expect("item/fileChange/outputDelta params"),
739+
)?;
740+
assert_eq!(output_delta.item_id, "patch-call");
741+
assert!(
742+
output_delta.delta.contains("README.md"),
743+
"expected delta to mention README.md, got: {}",
744+
output_delta.delta
745+
);
746+
728747
let completed_file_change = timeout(DEFAULT_READ_TIMEOUT, async {
729748
loop {
730749
let completed_notif = mcp

0 commit comments

Comments
 (0)