mirror of
https://github.com/openai/codex.git
synced 2026-10-22 17:58:46 +00:00
refactor(protocol): map canonical tool items to legacy events (#31296)
## Description This PR adds legacy `EventMsg` mappings for the `TurnItem` types introduced in [#30282](https://github.com/openai/codex/pull/30282): - `CommandExecution` - `DynamicToolCall` - `CollabAgentToolCall` - `SubAgentActivity` When their producers move to canonical `ItemStarted` / `ItemCompleted`, raw core event consumers can still receive the existing begin/end-style events. The canonical item lifecycle remains the live source of truth. We also record the mapped legacy events in rollout trace so the producer migration preserves the existing tool-runtime trace entries. ## Why This is the compatibility layer for the follow-up producer migrations. Splitting it out first keeps each producer PR small and keeps the legacy mapping in one place. ## What changed - Added `TurnItem` → legacy `EventMsg` mappings in `protocol/src/legacy_events.rs`. - Added the command execution status conversion used by the exec mapping. - Added focused coverage for command execution and dynamic tool mappings.
This commit is contained in:
@@ -4309,6 +4309,10 @@ pub struct CollabResumeEndEvent {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::items::CommandExecutionItem;
|
||||
use crate::items::CommandExecutionStatus;
|
||||
use crate::items::DynamicToolCallItem;
|
||||
use crate::items::DynamicToolCallStatus;
|
||||
use crate::items::FileChangeItem;
|
||||
use crate::items::ImageGenerationItem;
|
||||
use crate::items::McpToolCallItem;
|
||||
@@ -5290,6 +5294,139 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn command_execution_item_lifecycle_emits_legacy_exec_events() {
|
||||
let cwd = PathUri::from_abs_path(&test_path_buf("/tmp").abs());
|
||||
let started = ItemStartedEvent {
|
||||
thread_id: ThreadId::new(),
|
||||
turn_id: "turn-1".into(),
|
||||
started_at_ms: 10,
|
||||
item: TurnItem::CommandExecution(CommandExecutionItem {
|
||||
id: "exec-1".into(),
|
||||
process_id: Some("pid-1".into()),
|
||||
command: vec!["echo".into(), "done".into()],
|
||||
cwd: cwd.clone(),
|
||||
parsed_cmd: vec![ParsedCommand::Unknown {
|
||||
cmd: "echo done".into(),
|
||||
}],
|
||||
source: ExecCommandSource::Agent,
|
||||
interaction_input: None,
|
||||
status: CommandExecutionStatus::InProgress,
|
||||
stdout: None,
|
||||
stderr: None,
|
||||
aggregated_output: None,
|
||||
exit_code: None,
|
||||
duration: None,
|
||||
formatted_output: None,
|
||||
}),
|
||||
};
|
||||
let completed = ItemCompletedEvent {
|
||||
thread_id: ThreadId::new(),
|
||||
turn_id: "turn-1".into(),
|
||||
completed_at_ms: 20,
|
||||
item: TurnItem::CommandExecution(CommandExecutionItem {
|
||||
id: "exec-1".into(),
|
||||
process_id: Some("pid-1".into()),
|
||||
command: vec!["echo".into(), "done".into()],
|
||||
cwd,
|
||||
parsed_cmd: vec![ParsedCommand::Unknown {
|
||||
cmd: "echo done".into(),
|
||||
}],
|
||||
source: ExecCommandSource::Agent,
|
||||
interaction_input: None,
|
||||
status: CommandExecutionStatus::Completed,
|
||||
stdout: Some("done\n".into()),
|
||||
stderr: Some(String::new()),
|
||||
aggregated_output: Some("done\n".into()),
|
||||
exit_code: Some(0),
|
||||
duration: Some(Duration::from_millis(5)),
|
||||
formatted_output: Some("done\n".into()),
|
||||
}),
|
||||
};
|
||||
|
||||
assert!(matches!(
|
||||
started.as_legacy_events(/*show_raw_agent_reasoning*/ false).as_slice(),
|
||||
[EventMsg::ExecCommandBegin(ExecCommandBeginEvent {
|
||||
call_id,
|
||||
turn_id,
|
||||
started_at_ms: 10,
|
||||
..
|
||||
})] if call_id == "exec-1" && turn_id == "turn-1"
|
||||
));
|
||||
assert!(matches!(
|
||||
completed
|
||||
.as_legacy_events(/*show_raw_agent_reasoning*/ false)
|
||||
.as_slice(),
|
||||
[EventMsg::ExecCommandEnd(ExecCommandEndEvent {
|
||||
call_id,
|
||||
turn_id,
|
||||
completed_at_ms: 20,
|
||||
aggregated_output,
|
||||
..
|
||||
})] if call_id == "exec-1" && turn_id == "turn-1" && aggregated_output == "done\n"
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dynamic_tool_call_item_lifecycle_emits_legacy_dynamic_tool_events() {
|
||||
let started = ItemStartedEvent {
|
||||
thread_id: ThreadId::new(),
|
||||
turn_id: "turn-1".into(),
|
||||
started_at_ms: 10,
|
||||
item: TurnItem::DynamicToolCall(DynamicToolCallItem {
|
||||
id: "dynamic-1".into(),
|
||||
namespace: Some("apps".into()),
|
||||
tool: "lookup".into(),
|
||||
arguments: json!({"id": "123"}),
|
||||
status: DynamicToolCallStatus::InProgress,
|
||||
content_items: None,
|
||||
success: None,
|
||||
error: None,
|
||||
duration: None,
|
||||
}),
|
||||
};
|
||||
let completed = ItemCompletedEvent {
|
||||
thread_id: ThreadId::new(),
|
||||
turn_id: "turn-1".into(),
|
||||
completed_at_ms: 20,
|
||||
item: TurnItem::DynamicToolCall(DynamicToolCallItem {
|
||||
id: "dynamic-1".into(),
|
||||
namespace: Some("apps".into()),
|
||||
tool: "lookup".into(),
|
||||
arguments: json!({"id": "123"}),
|
||||
status: DynamicToolCallStatus::Completed,
|
||||
content_items: Some(vec![DynamicToolCallOutputContentItem::InputText {
|
||||
text: "ok".into(),
|
||||
}]),
|
||||
success: Some(true),
|
||||
error: None,
|
||||
duration: Some(Duration::from_millis(5)),
|
||||
}),
|
||||
};
|
||||
|
||||
assert!(matches!(
|
||||
started.as_legacy_events(/*show_raw_agent_reasoning*/ false).as_slice(),
|
||||
[EventMsg::DynamicToolCallRequest(DynamicToolCallRequest {
|
||||
call_id,
|
||||
turn_id,
|
||||
started_at_ms: 10,
|
||||
..
|
||||
})] if call_id == "dynamic-1" && turn_id == "turn-1"
|
||||
));
|
||||
assert!(matches!(
|
||||
completed
|
||||
.as_legacy_events(/*show_raw_agent_reasoning*/ false)
|
||||
.as_slice(),
|
||||
[EventMsg::DynamicToolCallResponse(DynamicToolCallResponseEvent {
|
||||
call_id,
|
||||
turn_id,
|
||||
completed_at_ms: 20,
|
||||
success: true,
|
||||
..
|
||||
})] if call_id == "dynamic-1" && turn_id == "turn-1"
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn item_started_event_requires_started_at_ms() {
|
||||
let mut value = serde_json::to_value(ItemStartedEvent {
|
||||
|
||||
Reference in New Issue
Block a user