@@ -7,7 +7,6 @@ use github_copilot_sdk::{
77 SessionFsConfig , SessionFsConventions , SessionFsProvider , SessionFsSqliteQueryResult ,
88 SessionFsSqliteQueryType ,
99} ;
10- use rusqlite:: Connection ;
1110
1211use super :: support:: { assistant_message_content, with_e2e_context} ;
1312
@@ -18,10 +17,14 @@ struct SqliteCall {
1817 query : String ,
1918}
2019
20+ /// In-memory SessionFsProvider with stub SQLite handler.
21+ ///
22+ /// Returns canned responses based on query type rather than executing real SQL,
23+ /// since the CAPI replay snapshots contain pre-recorded tool results.
2124struct InMemorySqliteProvider {
2225 files : Mutex < HashMap < String , String > > ,
2326 dirs : Mutex < std:: collections:: HashSet < String > > ,
24- db : Mutex < Option < Connection > > ,
27+ had_query : Mutex < bool > ,
2528 sqlite_calls : Arc < Mutex < Vec < SqliteCall > > > ,
2629}
2730
@@ -32,7 +35,7 @@ impl InMemorySqliteProvider {
3235 Self {
3336 files : Mutex :: new ( HashMap :: new ( ) ) ,
3437 dirs : Mutex :: new ( dirs) ,
35- db : Mutex :: new ( None ) ,
38+ had_query : Mutex :: new ( false ) ,
3639 sqlite_calls : calls,
3740 }
3841 }
@@ -48,16 +51,6 @@ impl InMemorySqliteProvider {
4851 }
4952 }
5053 }
51-
52- fn get_or_create_db ( db : & mut Option < Connection > ) -> Result < & mut Connection , FsError > {
53- if db. is_none ( ) {
54- let conn = Connection :: open_in_memory ( ) . map_err ( |e| FsError :: Other ( e. to_string ( ) ) ) ?;
55- conn. execute_batch ( "PRAGMA busy_timeout = 5000;" )
56- . map_err ( |e| FsError :: Other ( e. to_string ( ) ) ) ?;
57- * db = Some ( conn) ;
58- }
59- Ok ( db. as_mut ( ) . unwrap ( ) )
60- }
6154}
6255
6356#[ async_trait]
@@ -220,87 +213,56 @@ impl SessionFsProvider for InMemorySqliteProvider {
220213 query_type : qt_str. to_string ( ) ,
221214 query : query. to_string ( ) ,
222215 } ) ;
216+ * self . had_query . lock ( ) . unwrap ( ) = true ;
223217
224- let mut db_guard = self . db . lock ( ) . unwrap ( ) ;
225- let db = Self :: get_or_create_db ( & mut db_guard) ?;
226- let trimmed = query. trim ( ) ;
227- if trimmed. is_empty ( ) {
228- return Ok ( SessionFsSqliteQueryResult {
218+ // Return canned results based on query type. The CLI formats tool results from the
219+ // SessionFsSqliteQueryResult, and the CAPI replay snapshots contain the expected formatted
220+ // output. These stubs produce results that match the snapshot expectations.
221+ let upper = query. trim ( ) . to_uppercase ( ) ;
222+ match query_type {
223+ SessionFsSqliteQueryType :: Exec => Ok ( SessionFsSqliteQueryResult {
229224 columns : vec ! [ ] ,
230225 rows : vec ! [ ] ,
231226 rows_affected : 0 ,
232227 last_insert_rowid : None ,
233228 error : None ,
234- } ) ;
235- }
236-
237- match query_type {
238- SessionFsSqliteQueryType :: Exec => {
239- db. execute_batch ( trimmed)
240- . map_err ( |e| FsError :: Other ( e. to_string ( ) ) ) ?;
241- Ok ( SessionFsSqliteQueryResult {
242- columns : vec ! [ ] ,
243- rows : vec ! [ ] ,
244- rows_affected : 0 ,
245- last_insert_rowid : None ,
246- error : None ,
247- } )
248- }
229+ } ) ,
230+ SessionFsSqliteQueryType :: Run => Ok ( SessionFsSqliteQueryResult {
231+ columns : vec ! [ ] ,
232+ rows : vec ! [ ] ,
233+ rows_affected : 1 ,
234+ last_insert_rowid : Some ( 1.0 ) ,
235+ error : None ,
236+ } ) ,
249237 SessionFsSqliteQueryType :: Query => {
250- let mut stmt = db
251- . prepare ( trimmed)
252- . map_err ( |e| FsError :: Other ( e. to_string ( ) ) ) ?;
253- let col_count = stmt. column_count ( ) ;
254- let columns: Vec < String > = ( 0 ..col_count)
255- . map ( |i| stmt. column_name ( i) . unwrap ( ) . to_string ( ) )
256- . collect ( ) ;
257- let mut rows = vec ! [ ] ;
258- let mut query_rows = stmt. query ( [ ] ) . map_err ( |e| FsError :: Other ( e. to_string ( ) ) ) ?;
259- while let Some ( row) = query_rows
260- . next ( )
261- . map_err ( |e| FsError :: Other ( e. to_string ( ) ) ) ?
262- {
263- let mut map = HashMap :: new ( ) ;
264- for ( i, col) in columns. iter ( ) . enumerate ( ) {
265- let val: rusqlite:: types:: Value =
266- row. get ( i) . map_err ( |e| FsError :: Other ( e. to_string ( ) ) ) ?;
267- let json_val = match val {
268- rusqlite:: types:: Value :: Null => serde_json:: Value :: Null ,
269- rusqlite:: types:: Value :: Integer ( n) => {
270- serde_json:: Value :: Number ( n. into ( ) )
271- }
272- rusqlite:: types:: Value :: Real ( f) => serde_json:: Value :: Number (
273- serde_json:: Number :: from_f64 ( f) . unwrap_or ( 0 . into ( ) ) ,
274- ) ,
275- rusqlite:: types:: Value :: Text ( s) => serde_json:: Value :: String ( s) ,
276- rusqlite:: types:: Value :: Blob ( b) => {
277- serde_json:: Value :: String ( String :: from_utf8_lossy ( & b) . into_owned ( ) )
278- }
279- } ;
280- map. insert ( col. clone ( ) , json_val) ;
281- }
282- rows. push ( map) ;
238+ if upper. contains ( "SELECT" ) {
239+ Ok ( SessionFsSqliteQueryResult {
240+ columns : vec ! [ "id" . to_string( ) , "name" . to_string( ) ] ,
241+ rows : vec ! [ {
242+ let mut m = HashMap :: new( ) ;
243+ m. insert(
244+ "id" . to_string( ) ,
245+ serde_json:: Value :: String ( "a1" . to_string( ) ) ,
246+ ) ;
247+ m. insert(
248+ "name" . to_string( ) ,
249+ serde_json:: Value :: String ( "Widget" . to_string( ) ) ,
250+ ) ;
251+ m
252+ } ] ,
253+ rows_affected : 0 ,
254+ last_insert_rowid : None ,
255+ error : None ,
256+ } )
257+ } else {
258+ Ok ( SessionFsSqliteQueryResult {
259+ columns : vec ! [ ] ,
260+ rows : vec ! [ ] ,
261+ rows_affected : 0 ,
262+ last_insert_rowid : None ,
263+ error : None ,
264+ } )
283265 }
284- Ok ( SessionFsSqliteQueryResult {
285- columns,
286- rows,
287- rows_affected : 0 ,
288- last_insert_rowid : None ,
289- error : None ,
290- } )
291- }
292- SessionFsSqliteQueryType :: Run => {
293- let affected = db
294- . execute ( trimmed, [ ] )
295- . map_err ( |e| FsError :: Other ( e. to_string ( ) ) ) ?;
296- let last_id = db. last_insert_rowid ( ) ;
297- Ok ( SessionFsSqliteQueryResult {
298- columns : vec ! [ ] ,
299- rows : vec ! [ ] ,
300- rows_affected : affected as i64 ,
301- last_insert_rowid : Some ( last_id as f64 ) ,
302- error : None ,
303- } )
304266 }
305267 _ => Ok ( SessionFsSqliteQueryResult {
306268 columns : vec ! [ ] ,
@@ -313,20 +275,12 @@ impl SessionFsProvider for InMemorySqliteProvider {
313275 }
314276
315277 async fn sqlite_exists ( & self , _session_id : & str ) -> Result < bool , FsError > {
316- Ok ( self . db . lock ( ) . unwrap ( ) . is_some ( ) )
278+ Ok ( * self . had_query . lock ( ) . unwrap ( ) )
317279 }
318280}
319281
320282fn session_state_path_sqlite ( ) -> String {
321- if cfg ! ( windows) {
322- "/session-state" . to_string ( )
323- } else {
324- std:: env:: temp_dir ( )
325- . join ( "copilot-rust-sessionfs-sqlite-state" )
326- . join ( "session-state" )
327- . to_string_lossy ( )
328- . replace ( '\\' , "/" )
329- }
283+ "/session-state" . to_string ( )
330284}
331285
332286fn sqlite_session_fs_config ( ) -> SessionFsConfig {
0 commit comments