@@ -30,18 +30,21 @@ async fn test_list_and_resume_conversations() {
3030 "2025-01-02T12-00-00" ,
3131 "2025-01-02T12:00:00Z" ,
3232 "Hello A" ,
33+ Some ( "openai" ) ,
3334 ) ;
3435 create_fake_rollout (
3536 codex_home. path ( ) ,
3637 "2025-01-01T13-00-00" ,
3738 "2025-01-01T13:00:00Z" ,
3839 "Hello B" ,
40+ Some ( "openai" ) ,
3941 ) ;
4042 create_fake_rollout (
4143 codex_home. path ( ) ,
4244 "2025-01-01T12-00-00" ,
4345 "2025-01-01T12:00:00Z" ,
4446 "Hello C" ,
47+ None ,
4548 ) ;
4649
4750 let mut mcp = McpProcess :: new ( codex_home. path ( ) )
@@ -57,6 +60,7 @@ async fn test_list_and_resume_conversations() {
5760 . send_list_conversations_request ( ListConversationsParams {
5861 page_size : Some ( 2 ) ,
5962 cursor : None ,
63+ model_provider : None ,
6064 } )
6165 . await
6266 . expect ( "send listConversations" ) ;
@@ -74,6 +78,8 @@ async fn test_list_and_resume_conversations() {
7478 // Newest first; preview text should match
7579 assert_eq ! ( items[ 0 ] . preview, "Hello A" ) ;
7680 assert_eq ! ( items[ 1 ] . preview, "Hello B" ) ;
81+ assert_eq ! ( items[ 0 ] . model_provider. as_deref( ) , Some ( "openai" ) ) ;
82+ assert_eq ! ( items[ 1 ] . model_provider. as_deref( ) , Some ( "openai" ) ) ;
7783 assert ! ( items[ 0 ] . path. is_absolute( ) ) ;
7884 assert ! ( next_cursor. is_some( ) ) ;
7985
@@ -82,6 +88,7 @@ async fn test_list_and_resume_conversations() {
8288 . send_list_conversations_request ( ListConversationsParams {
8389 page_size : Some ( 2 ) ,
8490 cursor : next_cursor,
91+ model_provider : None ,
8592 } )
8693 . await
8794 . expect ( "send listConversations page 2" ) ;
@@ -99,7 +106,91 @@ async fn test_list_and_resume_conversations() {
99106 } = to_response :: < ListConversationsResponse > ( resp2) . expect ( "deserialize response" ) ;
100107 assert_eq ! ( items2. len( ) , 1 ) ;
101108 assert_eq ! ( items2[ 0 ] . preview, "Hello C" ) ;
102- assert ! ( next2. is_some( ) ) ;
109+ assert_eq ! ( items2[ 0 ] . model_provider, None ) ;
110+ assert_eq ! ( next2, None ) ;
111+
112+ // Add a conversation with an explicit non-OpenAI provider for filter tests.
113+ create_fake_rollout (
114+ codex_home. path ( ) ,
115+ "2025-01-01T11-30-00" ,
116+ "2025-01-01T11:30:00Z" ,
117+ "Hello TP" ,
118+ Some ( "test-provider" ) ,
119+ ) ;
120+
121+ // Filtering by model provider should return only matching sessions.
122+ let filter_req_id = mcp
123+ . send_list_conversations_request ( ListConversationsParams {
124+ page_size : Some ( 10 ) ,
125+ cursor : None ,
126+ model_provider : Some ( vec ! [ "test-provider" . to_string( ) ] ) ,
127+ } )
128+ . await
129+ . expect ( "send listConversations filtered" ) ;
130+ let filter_resp: JSONRPCResponse = timeout (
131+ DEFAULT_READ_TIMEOUT ,
132+ mcp. read_stream_until_response_message ( RequestId :: Integer ( filter_req_id) ) ,
133+ )
134+ . await
135+ . expect ( "listConversations filtered timeout" )
136+ . expect ( "listConversations filtered resp" ) ;
137+ let ListConversationsResponse {
138+ items : filtered_items,
139+ next_cursor : filtered_next,
140+ } = to_response :: < ListConversationsResponse > ( filter_resp) . expect ( "deserialize filtered" ) ;
141+ assert_eq ! ( filtered_items. len( ) , 1 ) ;
142+ assert_eq ! ( filtered_next, None ) ;
143+ assert_eq ! ( filtered_items[ 0 ] . preview, "Hello TP" ) ;
144+ assert_eq ! (
145+ filtered_items[ 0 ] . model_provider. as_deref( ) ,
146+ Some ( "test-provider" )
147+ ) ;
148+
149+ // Empty filter should include every session regardless of provider metadata.
150+ let unfiltered_req_id = mcp
151+ . send_list_conversations_request ( ListConversationsParams {
152+ page_size : Some ( 10 ) ,
153+ cursor : None ,
154+ model_provider : Some ( Vec :: new ( ) ) ,
155+ } )
156+ . await
157+ . expect ( "send listConversations unfiltered" ) ;
158+ let unfiltered_resp: JSONRPCResponse = timeout (
159+ DEFAULT_READ_TIMEOUT ,
160+ mcp. read_stream_until_response_message ( RequestId :: Integer ( unfiltered_req_id) ) ,
161+ )
162+ . await
163+ . expect ( "listConversations unfiltered timeout" )
164+ . expect ( "listConversations unfiltered resp" ) ;
165+ let ListConversationsResponse {
166+ items : unfiltered_items,
167+ next_cursor : unfiltered_next,
168+ } = to_response :: < ListConversationsResponse > ( unfiltered_resp)
169+ . expect ( "deserialize unfiltered response" ) ;
170+ assert_eq ! ( unfiltered_items. len( ) , 4 ) ;
171+ assert ! ( unfiltered_next. is_none( ) ) ;
172+
173+ let empty_req_id = mcp
174+ . send_list_conversations_request ( ListConversationsParams {
175+ page_size : Some ( 10 ) ,
176+ cursor : None ,
177+ model_provider : Some ( vec ! [ "other" . to_string( ) ] ) ,
178+ } )
179+ . await
180+ . expect ( "send listConversations filtered empty" ) ;
181+ let empty_resp: JSONRPCResponse = timeout (
182+ DEFAULT_READ_TIMEOUT ,
183+ mcp. read_stream_until_response_message ( RequestId :: Integer ( empty_req_id) ) ,
184+ )
185+ . await
186+ . expect ( "listConversations filtered empty timeout" )
187+ . expect ( "listConversations filtered empty resp" ) ;
188+ let ListConversationsResponse {
189+ items : empty_items,
190+ next_cursor : empty_next,
191+ } = to_response :: < ListConversationsResponse > ( empty_resp) . expect ( "deserialize filtered empty" ) ;
192+ assert ! ( empty_items. is_empty( ) ) ;
193+ assert ! ( empty_next. is_none( ) ) ;
103194
104195 // Now resume one of the sessions and expect a SessionConfigured notification and response.
105196 let resume_req_id = mcp
@@ -152,7 +243,13 @@ async fn test_list_and_resume_conversations() {
152243 assert ! ( !conversation_id. to_string( ) . is_empty( ) ) ;
153244}
154245
155- fn create_fake_rollout ( codex_home : & Path , filename_ts : & str , meta_rfc3339 : & str , preview : & str ) {
246+ fn create_fake_rollout (
247+ codex_home : & Path ,
248+ filename_ts : & str ,
249+ meta_rfc3339 : & str ,
250+ preview : & str ,
251+ model_provider : Option < & str > ,
252+ ) {
156253 let uuid = Uuid :: new_v4 ( ) ;
157254 // sessions/YYYY/MM/DD/ derived from filename_ts (YYYY-MM-DDThh-mm-ss)
158255 let year = & filename_ts[ 0 ..4 ] ;
@@ -164,18 +261,22 @@ fn create_fake_rollout(codex_home: &Path, filename_ts: &str, meta_rfc3339: &str,
164261 let file_path = dir. join ( format ! ( "rollout-{filename_ts}-{uuid}.jsonl" ) ) ;
165262 let mut lines = Vec :: new ( ) ;
166263 // Meta line with timestamp (flattened meta in payload for new schema)
264+ let mut payload = json ! ( {
265+ "id" : uuid,
266+ "timestamp" : meta_rfc3339,
267+ "cwd" : "/" ,
268+ "originator" : "codex" ,
269+ "cli_version" : "0.0.0" ,
270+ "instructions" : null,
271+ } ) ;
272+ if let Some ( provider) = model_provider {
273+ payload[ "model_provider" ] = json ! ( provider) ;
274+ }
167275 lines. push (
168276 json ! ( {
169277 "timestamp" : meta_rfc3339,
170278 "type" : "session_meta" ,
171- "payload" : {
172- "id" : uuid,
173- "timestamp" : meta_rfc3339,
174- "cwd" : "/" ,
175- "originator" : "codex" ,
176- "cli_version" : "0.0.0" ,
177- "instructions" : null
178- }
279+ "payload" : payload
179280 } )
180281 . to_string ( ) ,
181282 ) ;
0 commit comments