@@ -22,7 +22,11 @@ import {
2222 resolveAgentIdFromSessionStorePath ,
2323 resolveStateDirFromSessionStorePath ,
2424} from "./paths.js" ;
25- import type { SessionEntry } from "./types.js" ;
25+ import {
26+ mergeSessionEntry ,
27+ mergeSessionEntryPreserveActivity ,
28+ type SessionEntry ,
29+ } from "./types.js" ;
2630
2731const SESSION_STORE_SCOPE = "session_entries" ;
2832
@@ -176,6 +180,69 @@ export function replaceSqliteSessionStore(
176180 database . walMaintenance . checkpoint ( ) ;
177181}
178182
183+ export type PatchSqliteSessionEntryMode = "merge" | "preserve-activity" | "replace" ;
184+
185+ export function patchSqliteSessionEntry ( params : {
186+ storePath : string ;
187+ sessionKey : string ;
188+ fallbackEntry : SessionEntry ;
189+ patch : Partial < SessionEntry > ;
190+ mode ?: PatchSqliteSessionEntryMode ;
191+ } ) : SessionEntry {
192+ const databaseOptions = resolveSessionStoreDatabaseOptions ( params . storePath ) ;
193+ let persisted = params . fallbackEntry ;
194+ runOpenClawAgentWriteTransaction ( ( database ) => {
195+ const db = getNodeSqliteKysely < SessionStoreDatabase > ( database . db ) ;
196+ const row =
197+ executeSqliteQueryTakeFirstSync (
198+ database . db ,
199+ db
200+ . selectFrom ( "cache_entries" )
201+ . select ( [ "value_json" ] )
202+ . where ( "scope" , "=" , SESSION_STORE_SCOPE )
203+ . where ( "key" , "=" , params . sessionKey ) ,
204+ ) ?? null ;
205+ const current = row ? parseSessionEntryValue ( row . value_json ) : undefined ;
206+ persisted =
207+ params . mode === "replace"
208+ ? params . fallbackEntry
209+ : current
210+ ? params . mode === "preserve-activity"
211+ ? mergeSessionEntryPreserveActivity ( current , params . patch )
212+ : mergeSessionEntry ( current , params . patch )
213+ : params . fallbackEntry ;
214+ const valueJson = JSON . stringify ( persisted ) ;
215+ persisted = parseSessionEntryValue ( valueJson ) ?? persisted ;
216+ const updatedAt =
217+ typeof persisted . updatedAt === "number" && Number . isFinite ( persisted . updatedAt )
218+ ? persisted . updatedAt
219+ : Date . now ( ) ;
220+ executeSqliteQuerySync (
221+ database . db ,
222+ db
223+ . insertInto ( "cache_entries" )
224+ . values ( {
225+ scope : SESSION_STORE_SCOPE ,
226+ key : params . sessionKey ,
227+ value_json : valueJson ,
228+ blob : null ,
229+ expires_at : null ,
230+ updated_at : updatedAt ,
231+ } )
232+ . onConflict ( ( conflict ) =>
233+ conflict . columns ( [ "scope" , "key" ] ) . doUpdateSet ( {
234+ value_json : valueJson ,
235+ blob : null ,
236+ expires_at : null ,
237+ updated_at : updatedAt ,
238+ } ) ,
239+ ) ,
240+ ) ;
241+ } , databaseOptions ) ;
242+ openOpenClawAgentDatabase ( databaseOptions ) . walMaintenance . checkpoint ( ) ;
243+ return persisted ;
244+ }
245+
179246export function clearExistingSqliteSessionStore (
180247 storePath : string ,
181248 opts ?: { compact ?: boolean } ,
0 commit comments