11import { z } from "zod"
22import type { Logger } from "./logger"
33import type { PruningStrategy } from "./config"
4+ import type { PluginState } from "./state"
45import { buildAnalysisPrompt } from "./prompt"
56import { selectModel , extractModelFromSession } from "./model-selector"
67import { estimateTokensBatch , formatTokenCount } from "./tokenizer"
78import { detectDuplicates } from "./deduplicator"
89import { extractParameterKey } from "./display-utils"
10+ import { saveSessionState } from "./state-persistence"
11+ import { ensureSessionRestored } from "./state"
912
1013export interface SessionStats {
1114 totalToolsPruned : number
@@ -29,20 +32,28 @@ export interface PruningOptions {
2932}
3033
3134export class Janitor {
35+ private prunedIdsState : Map < string , string [ ] >
36+ private statsState : Map < string , SessionStats >
37+ private toolParametersCache : Map < string , any >
38+ private modelCache : Map < string , { providerID : string ; modelID : string } >
39+
3240 constructor (
3341 private client : any ,
34- private prunedIdsState : Map < string , string [ ] > ,
35- private statsState : Map < string , SessionStats > ,
42+ private state : PluginState ,
3643 private logger : Logger ,
37- private toolParametersCache : Map < string , any > ,
3844 private protectedTools : string [ ] ,
39- private modelCache : Map < string , { providerID : string ; modelID : string } > ,
4045 private configModel ?: string ,
4146 private showModelErrorToasts : boolean = true ,
4247 private strictModelSelection : boolean = false ,
4348 private pruningSummary : "off" | "minimal" | "detailed" = "detailed" ,
4449 private workingDirectory ?: string
45- ) { }
50+ ) {
51+ // Bind state references for convenience
52+ this . prunedIdsState = state . prunedIds
53+ this . statsState = state . stats
54+ this . toolParametersCache = state . toolParameters
55+ this . modelCache = state . model
56+ }
4657
4758 private async sendIgnoredMessage ( sessionID : string , text : string , agent ?: string ) {
4859 try {
@@ -85,6 +96,9 @@ export class Janitor {
8596 return null
8697 }
8798
99+ // Ensure persisted state is restored before processing
100+ await ensureSessionRestored ( this . state , sessionID , this . logger )
101+
88102 const [ sessionInfoResponse , messagesResponse ] = await Promise . all ( [
89103 this . client . session . get ( { path : { id : sessionID } } ) ,
90104 this . client . session . messages ( { path : { id : sessionID } , query : { limit : 100 } } )
@@ -97,8 +111,6 @@ export class Janitor {
97111 return null
98112 }
99113
100- // Extract the current agent from the last user message to preserve agent context
101- // Following the same pattern as OpenCode's server.ts
102114 let currentAgent : string | undefined = undefined
103115 for ( let i = messages . length - 1 ; i >= 0 ; i -- ) {
104116 const msg = messages [ i ]
@@ -330,6 +342,11 @@ export class Janitor {
330342 const allPrunedIds = [ ...new Set ( [ ...alreadyPrunedIds , ...finalPrunedIds ] ) ]
331343 this . prunedIdsState . set ( sessionID , allPrunedIds )
332344
345+ const sessionName = sessionInfo ?. title
346+ saveSessionState ( sessionID , new Set ( allPrunedIds ) , sessionStats , this . logger , sessionName ) . catch ( err => {
347+ this . logger . error ( "janitor" , "Failed to persist state" , { error : err . message } )
348+ } )
349+
333350 const prunedCount = finalNewlyPrunedIds . length
334351 const keptCount = candidateCount - prunedCount
335352 const hasBoth = deduplicatedIds . length > 0 && llmPrunedIds . length > 0
0 commit comments