@@ -849,6 +849,66 @@ fn convert_codex_event(
849849 } ) ) ;
850850 Some ( msg)
851851 }
852+ "agent_reasoning" => {
853+ let text = payload. get ( "text" ) . and_then ( Value :: as_str) ?. trim ( ) ;
854+ if text. is_empty ( ) {
855+ return None ;
856+ }
857+ * counter += 1 ;
858+ let content = serde_json:: json!( [ {
859+ "type" : "thinking" ,
860+ "thinking" : text
861+ } ] ) ;
862+ Some ( build_codex_message (
863+ format ! ( "codex-event-{counter}" ) ,
864+ session_id,
865+ line_timestamp. to_string ( ) ,
866+ "assistant" ,
867+ Some ( "assistant" ) ,
868+ Some ( content) ,
869+ None ,
870+ ) )
871+ }
872+ "agent_message" => {
873+ let text = payload. get ( "message" ) . and_then ( Value :: as_str) ?. trim ( ) ;
874+ if text. is_empty ( ) {
875+ return None ;
876+ }
877+ * counter += 1 ;
878+ let content = serde_json:: json!( [ {
879+ "type" : "text" ,
880+ "text" : text
881+ } ] ) ;
882+ Some ( build_codex_message (
883+ format ! ( "codex-event-{counter}" ) ,
884+ session_id,
885+ line_timestamp. to_string ( ) ,
886+ "assistant" ,
887+ Some ( "assistant" ) ,
888+ Some ( content) ,
889+ None ,
890+ ) )
891+ }
892+ "user_message" => {
893+ let text = payload. get ( "message" ) . and_then ( Value :: as_str) ?. trim ( ) ;
894+ if text. is_empty ( ) {
895+ return None ;
896+ }
897+ * counter += 1 ;
898+ let content = serde_json:: json!( [ {
899+ "type" : "text" ,
900+ "text" : text
901+ } ] ) ;
902+ Some ( build_codex_message (
903+ format ! ( "codex-event-{counter}" ) ,
904+ session_id,
905+ line_timestamp. to_string ( ) ,
906+ "user" ,
907+ Some ( "user" ) ,
908+ Some ( content) ,
909+ None ,
910+ ) )
911+ }
852912 // Unsupported/duplicated Codex events are intentionally ignored.
853913 _ => None ,
854914 }
@@ -902,7 +962,7 @@ fn extract_last_token_usage(payload: &Value) -> Option<(u32, u32)> {
902962
903963fn map_codex_tool_name ( name : & str ) -> & str {
904964 match name {
905- "exec_command" | "shell" => "Bash" ,
965+ "exec_command" | "shell" | "write_stdin" => "Bash" ,
906966 _ => name,
907967 }
908968}
@@ -1215,6 +1275,7 @@ mod tests {
12151275 fn map_exec_command_to_bash ( ) {
12161276 assert_eq ! ( map_codex_tool_name( "exec_command" ) , "Bash" ) ;
12171277 assert_eq ! ( map_codex_tool_name( "shell" ) , "Bash" ) ;
1278+ assert_eq ! ( map_codex_tool_name( "write_stdin" ) , "Bash" ) ;
12181279 assert_eq ! ( map_codex_tool_name( "batch_execute" ) , "batch_execute" ) ;
12191280 }
12201281
@@ -1545,6 +1606,120 @@ mod tests {
15451606 assert_eq ! ( msg. subtype. as_deref( ) , Some ( "microcompact_boundary" ) ) ;
15461607 }
15471608
1609+ #[ test]
1610+ fn convert_agent_reasoning_event_to_thinking_message ( ) {
1611+ let mut counter = 0u64 ;
1612+ let msg = convert_codex_event (
1613+ & json ! ( {
1614+ "type" : "agent_reasoning" ,
1615+ "text" : "**Inspecting parsers**"
1616+ } ) ,
1617+ "session-1" ,
1618+ "2026-02-19T12:00:00Z" ,
1619+ & mut counter,
1620+ )
1621+ . expect ( "agent_reasoning should be converted" ) ;
1622+
1623+ assert_eq ! ( msg. message_type, "assistant" ) ;
1624+ let arr = msg
1625+ . content
1626+ . as_ref ( )
1627+ . and_then ( Value :: as_array)
1628+ . expect ( "content should be an array" ) ;
1629+ assert_eq ! ( arr[ 0 ] . get( "type" ) . and_then( Value :: as_str) , Some ( "thinking" ) ) ;
1630+ assert_eq ! (
1631+ arr[ 0 ] . get( "thinking" ) . and_then( Value :: as_str) ,
1632+ Some ( "**Inspecting parsers**" )
1633+ ) ;
1634+ }
1635+
1636+ #[ test]
1637+ fn convert_agent_reasoning_event_skips_empty_text ( ) {
1638+ let mut counter = 0u64 ;
1639+ let msg = convert_codex_event (
1640+ & json ! ( {
1641+ "type" : "agent_reasoning" ,
1642+ "text" : " "
1643+ } ) ,
1644+ "session-1" ,
1645+ "2026-02-19T12:00:00Z" ,
1646+ & mut counter,
1647+ ) ;
1648+
1649+ assert ! ( msg. is_none( ) ) ;
1650+ assert_eq ! ( counter, 0 ) ;
1651+ }
1652+
1653+ #[ test]
1654+ fn convert_agent_message_event_to_assistant_text_message ( ) {
1655+ let mut counter = 0u64 ;
1656+ let msg = convert_codex_event (
1657+ & json ! ( {
1658+ "type" : "agent_message" ,
1659+ "message" : "Working on requested changes"
1660+ } ) ,
1661+ "session-1" ,
1662+ "2026-02-19T12:00:00Z" ,
1663+ & mut counter,
1664+ )
1665+ . expect ( "agent_message should be converted" ) ;
1666+
1667+ assert_eq ! ( msg. message_type, "assistant" ) ;
1668+ let arr = msg
1669+ . content
1670+ . as_ref ( )
1671+ . and_then ( Value :: as_array)
1672+ . expect ( "content should be an array" ) ;
1673+ assert_eq ! ( arr[ 0 ] . get( "type" ) . and_then( Value :: as_str) , Some ( "text" ) ) ;
1674+ assert_eq ! (
1675+ arr[ 0 ] . get( "text" ) . and_then( Value :: as_str) ,
1676+ Some ( "Working on requested changes" )
1677+ ) ;
1678+ }
1679+
1680+ #[ test]
1681+ fn convert_agent_message_event_skips_missing_field ( ) {
1682+ let mut counter = 0u64 ;
1683+ let msg = convert_codex_event (
1684+ & json ! ( {
1685+ "type" : "agent_message"
1686+ } ) ,
1687+ "session-1" ,
1688+ "2026-02-19T12:00:00Z" ,
1689+ & mut counter,
1690+ ) ;
1691+
1692+ assert ! ( msg. is_none( ) ) ;
1693+ assert_eq ! ( counter, 0 ) ;
1694+ }
1695+
1696+ #[ test]
1697+ fn convert_user_message_event_to_user_text_message ( ) {
1698+ let mut counter = 0u64 ;
1699+ let msg = convert_codex_event (
1700+ & json ! ( {
1701+ "type" : "user_message" ,
1702+ "message" : "Please patch this file"
1703+ } ) ,
1704+ "session-1" ,
1705+ "2026-02-19T12:00:00Z" ,
1706+ & mut counter,
1707+ )
1708+ . expect ( "user_message should be converted" ) ;
1709+
1710+ assert_eq ! ( msg. message_type, "user" ) ;
1711+ let arr = msg
1712+ . content
1713+ . as_ref ( )
1714+ . and_then ( Value :: as_array)
1715+ . expect ( "content should be an array" ) ;
1716+ assert_eq ! ( arr[ 0 ] . get( "type" ) . and_then( Value :: as_str) , Some ( "text" ) ) ;
1717+ assert_eq ! (
1718+ arr[ 0 ] . get( "text" ) . and_then( Value :: as_str) ,
1719+ Some ( "Please patch this file" )
1720+ ) ;
1721+ }
1722+
15481723 #[ test]
15491724 fn convert_compacted_line_to_system_message ( ) {
15501725 let mut counter = 0u64 ;
0 commit comments