diff --git a/codex-rs/app-server-protocol/src/protocol/v2.rs b/codex-rs/app-server-protocol/src/protocol/v2.rs index df81803bfb..9c92fb8309 100644 --- a/codex-rs/app-server-protocol/src/protocol/v2.rs +++ b/codex-rs/app-server-protocol/src/protocol/v2.rs @@ -2288,7 +2288,7 @@ impl<'de> Deserialize<'de> for TurnTodosUpdatedNotificationWire { D: Deserializer<'de>, { // Use Option> so we can detect whether fields are present and - // error if clients send both legacy aliases at once. + // prefer `todo` if both legacy aliases are sent. #[derive(Deserialize)] #[serde(rename_all = "camelCase")] struct WireIn { @@ -2302,11 +2302,6 @@ impl<'de> Deserialize<'de> for TurnTodosUpdatedNotificationWire { } let wire = WireIn::deserialize(deserializer)?; - if wire.todo.is_some() && wire.plan.is_some() { - return Err(serde::de::Error::custom( - "todo and plan cannot both be provided; prefer todo", - )); - } let todo = wire.todo.or(wire.plan).unwrap_or_default(); Ok(Self { thread_id: wire.thread_id, @@ -2961,19 +2956,21 @@ mod tests { } #[test] - fn turn_todos_updated_deserialize_errors_when_both_todo_and_plan_present() { + fn turn_todos_updated_deserialize_prefers_todo_when_both_present() { let value = json!({ "threadId": "thread-1", "turnId": "turn-1", - "todo": [], + "todo": [{ "step": "current", "status": "completed" }], "plan": [{ "step": "stale", "status": "pending" }], }); - let err = serde_json::from_value::(value).unwrap_err(); - assert!( - err.to_string() - .contains("todo and plan cannot both be provided"), - "{err}" + let decoded = serde_json::from_value::(value).unwrap(); + assert_eq!( + decoded.todo, + vec![TurnTodoStep { + step: "current".to_string(), + status: TurnTodoStepStatus::Completed, + }] ); } } diff --git a/codex-rs/core/src/tools/spec.rs b/codex-rs/core/src/tools/spec.rs index bdb76df94b..d3c5bc2075 100644 --- a/codex-rs/core/src/tools/spec.rs +++ b/codex-rs/core/src/tools/spec.rs @@ -1333,7 +1333,7 @@ pub(crate) fn build_specs( builder.register_handler("read_mcp_resource", mcp_resource_handler); builder.push_spec(TODO_WRITE_TOOL.clone()); - builder.register_handler("todo_write", todo_handler.clone()); + builder.register_handler("todo_write", todo_handler); if config.collaboration_modes_tools { builder.push_spec(create_request_user_input_tool());