@@ -15,6 +15,7 @@ const homedir = typeof os.homedir === "function" ? os.homedir : () => "";
1515const ODE_CONFIG_DIR = join ( homedir ( ) , ".config" , "ode" ) ;
1616const SESSIONS_DIR = join ( ODE_CONFIG_DIR , "sessions" ) ;
1717const SESSION_SAVE_DEBOUNCE_MS = 5000 ;
18+ const SESSION_RETENTION_MS = 7 * 24 * 60 * 60 * 1000 ;
1819
1920export interface TrackedTool {
2021 id : string ;
@@ -101,6 +102,21 @@ function getSessionFilePath(sessionKey: string): string {
101102 return join ( SESSIONS_DIR , `${ safeKey } .json` ) ;
102103}
103104
105+ function getSessionLastActiveAt ( session : PersistedSession ) : number {
106+ if ( Number . isFinite ( session . lastActivityAt ) ) {
107+ return session . lastActivityAt ;
108+ }
109+ if ( Number . isFinite ( session . createdAt ) ) {
110+ return session . createdAt ;
111+ }
112+ return 0 ;
113+ }
114+
115+ function isSessionExpired ( session : PersistedSession , now = Date . now ( ) ) : boolean {
116+ const lastActiveAt = getSessionLastActiveAt ( session ) ;
117+ return now - lastActiveAt >= SESSION_RETENTION_MS ;
118+ }
119+
104120function sanitizeSessionForStorage ( session : PersistedSession ) : PersistedSession {
105121 const snapshot = structuredClone ( session ) ;
106122 if ( snapshot . activeRequest ) {
@@ -158,7 +174,12 @@ export function loadSession(channelId: string, threadId: string): PersistedSessi
158174
159175 // Check cache first
160176 if ( activeSessions . has ( sessionKey ) ) {
161- return activeSessions . get ( sessionKey ) ! ;
177+ const cached = activeSessions . get ( sessionKey ) ! ;
178+ if ( isSessionExpired ( cached ) ) {
179+ deleteSession ( channelId , threadId ) ;
180+ return null ;
181+ }
182+ return cached ;
162183 }
163184
164185 const filePath = getSessionFilePath ( sessionKey ) ;
@@ -169,6 +190,10 @@ export function loadSession(channelId: string, threadId: string): PersistedSessi
169190 try {
170191 const data = readFileSync ( filePath , "utf-8" ) ;
171192 const session = JSON . parse ( data ) as PersistedSession ;
193+ if ( isSessionExpired ( session ) ) {
194+ deleteSession ( channelId , threadId ) ;
195+ return null ;
196+ }
172197 if ( session . activeRequest ) {
173198 const active = session . activeRequest as ActiveRequest & {
174199 settingsChannelId ?: string ;
@@ -340,7 +365,11 @@ export function loadAllSessions(): PersistedSession[] {
340365 ensureSessionsDir ( ) ;
341366 const sessionsByKey = new Map < string , PersistedSession > ( ) ;
342367
343- for ( const [ sessionKey , session ] of activeSessions . entries ( ) ) {
368+ for ( const [ sessionKey , session ] of Array . from ( activeSessions . entries ( ) ) ) {
369+ if ( isSessionExpired ( session ) ) {
370+ deleteSession ( session . channelId , session . threadId ) ;
371+ continue ;
372+ }
344373 sessionsByKey . set ( sessionKey , session ) ;
345374 }
346375
@@ -351,6 +380,10 @@ export function loadAllSessions(): PersistedSession[] {
351380 try {
352381 const data = readFileSync ( filePath , "utf-8" ) ;
353382 const session = JSON . parse ( data ) as PersistedSession ;
383+ if ( isSessionExpired ( session ) ) {
384+ deleteSession ( session . channelId , session . threadId ) ;
385+ continue ;
386+ }
354387 const sessionKey = getSessionKey ( session . channelId , session . threadId ) ;
355388 if ( ! sessionsByKey . has ( sessionKey ) ) {
356389 sessionsByKey . set ( sessionKey , session ) ;
0 commit comments