@@ -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_providers : 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, "openai" ) ;
82+ assert_eq ! ( items[ 1 ] . model_provider, "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_providers : None ,
8592 } )
8693 . await
8794 . expect ( "send listConversations page 2" ) ;
@@ -99,7 +106,88 @@ 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, "openai" ) ;
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_providers : 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 ! ( filtered_items[ 0 ] . model_provider, "test-provider" ) ;
145+
146+ // Empty filter should include every session regardless of provider metadata.
147+ let unfiltered_req_id = mcp
148+ . send_list_conversations_request ( ListConversationsParams {
149+ page_size : Some ( 10 ) ,
150+ cursor : None ,
151+ model_providers : Some ( Vec :: new ( ) ) ,
152+ } )
153+ . await
154+ . expect ( "send listConversations unfiltered" ) ;
155+ let unfiltered_resp: JSONRPCResponse = timeout (
156+ DEFAULT_READ_TIMEOUT ,
157+ mcp. read_stream_until_response_message ( RequestId :: Integer ( unfiltered_req_id) ) ,
158+ )
159+ . await
160+ . expect ( "listConversations unfiltered timeout" )
161+ . expect ( "listConversations unfiltered resp" ) ;
162+ let ListConversationsResponse {
163+ items : unfiltered_items,
164+ next_cursor : unfiltered_next,
165+ } = to_response :: < ListConversationsResponse > ( unfiltered_resp)
166+ . expect ( "deserialize unfiltered response" ) ;
167+ assert_eq ! ( unfiltered_items. len( ) , 4 ) ;
168+ assert ! ( unfiltered_next. is_none( ) ) ;
169+
170+ let empty_req_id = mcp
171+ . send_list_conversations_request ( ListConversationsParams {
172+ page_size : Some ( 10 ) ,
173+ cursor : None ,
174+ model_providers : Some ( vec ! [ "other" . to_string( ) ] ) ,
175+ } )
176+ . await
177+ . expect ( "send listConversations filtered empty" ) ;
178+ let empty_resp: JSONRPCResponse = timeout (
179+ DEFAULT_READ_TIMEOUT ,
180+ mcp. read_stream_until_response_message ( RequestId :: Integer ( empty_req_id) ) ,
181+ )
182+ . await
183+ . expect ( "listConversations filtered empty timeout" )
184+ . expect ( "listConversations filtered empty resp" ) ;
185+ let ListConversationsResponse {
186+ items : empty_items,
187+ next_cursor : empty_next,
188+ } = to_response :: < ListConversationsResponse > ( empty_resp) . expect ( "deserialize filtered empty" ) ;
189+ assert ! ( empty_items. is_empty( ) ) ;
190+ assert ! ( empty_next. is_none( ) ) ;
103191
104192 // Now resume one of the sessions and expect a SessionConfigured notification and response.
105193 let resume_req_id = mcp
@@ -152,7 +240,13 @@ async fn test_list_and_resume_conversations() {
152240 assert ! ( !conversation_id. to_string( ) . is_empty( ) ) ;
153241}
154242
155- fn create_fake_rollout ( codex_home : & Path , filename_ts : & str , meta_rfc3339 : & str , preview : & str ) {
243+ fn create_fake_rollout (
244+ codex_home : & Path ,
245+ filename_ts : & str ,
246+ meta_rfc3339 : & str ,
247+ preview : & str ,
248+ model_provider : Option < & str > ,
249+ ) {
156250 let uuid = Uuid :: new_v4 ( ) ;
157251 // sessions/YYYY/MM/DD/ derived from filename_ts (YYYY-MM-DDThh-mm-ss)
158252 let year = & filename_ts[ 0 ..4 ] ;
@@ -164,18 +258,22 @@ fn create_fake_rollout(codex_home: &Path, filename_ts: &str, meta_rfc3339: &str,
164258 let file_path = dir. join ( format ! ( "rollout-{filename_ts}-{uuid}.jsonl" ) ) ;
165259 let mut lines = Vec :: new ( ) ;
166260 // Meta line with timestamp (flattened meta in payload for new schema)
261+ let mut payload = json ! ( {
262+ "id" : uuid,
263+ "timestamp" : meta_rfc3339,
264+ "cwd" : "/" ,
265+ "originator" : "codex" ,
266+ "cli_version" : "0.0.0" ,
267+ "instructions" : null,
268+ } ) ;
269+ if let Some ( provider) = model_provider {
270+ payload[ "model_provider" ] = json ! ( provider) ;
271+ }
167272 lines. push (
168273 json ! ( {
169274 "timestamp" : meta_rfc3339,
170275 "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- }
276+ "payload" : payload
179277 } )
180278 . to_string ( ) ,
181279 ) ;
0 commit comments