@@ -88,6 +88,23 @@ async function collectDiscoverySnapshot(sessionsDir: string): Promise<DiscoveryS
8888 const dirStat = await stat ( dirPath ) . catch ( ( ) => null )
8989 if ( ! dirStat ?. isDirectory ( ) ) continue
9090 snapshot . push ( { path : dirPath , mtimeMs : dirStat . mtimeMs } )
91+
92+ // Sub-agent sessions land in <project-dir>/<session-id>/ subdirectories.
93+ // Their mtimes must be tracked separately — adding files inside a subdir
94+ // does not bump the parent project dir's mtime.
95+ let subEntries : string [ ]
96+ try {
97+ subEntries = await readdir ( dirPath )
98+ } catch {
99+ continue
100+ }
101+ for ( const subName of subEntries ) {
102+ const subPath = join ( dirPath , subName )
103+ const subStat = await stat ( subPath ) . catch ( ( ) => null )
104+ if ( subStat ?. isDirectory ( ) ) {
105+ snapshot . push ( { path : subPath , mtimeMs : subStat . mtimeMs } )
106+ }
107+ }
91108 }
92109
93110 return snapshot
@@ -112,39 +129,58 @@ async function discoverSessionsInDir(sessionsDir: string, providerName: string):
112129 const dirStat = await stat ( dirPath ) . catch ( ( ) => null )
113130 if ( ! dirStat ?. isDirectory ( ) ) continue
114131
115- let files : string [ ]
116- try {
117- files = await readdir ( dirPath )
118- } catch {
119- continue
120- }
121-
122- for ( const file of files ) {
123- if ( ! file . endsWith ( '.jsonl' ) ) continue
124- const filePath = join ( dirPath , file )
125- const fileStat = await stat ( filePath ) . catch ( ( ) => null )
126- if ( ! fileStat ?. isFile ( ) ) continue
127-
128- const first = await readFirstEntry ( filePath )
129- if ( ! first || first . type !== 'session' ) continue
130-
131- const cwd = first . cwd ?? dirName
132- sources . push ( {
133- path : filePath ,
134- project : basename ( cwd ) ,
135- provider : providerName ,
136- fingerprintPath : filePath ,
137- cacheStrategy : 'append-jsonl' ,
138- progressLabel : basename ( filePath ) ,
139- parserVersion : `${ providerName } :v1` ,
140- } )
141- }
132+ await collectJsonlFromDir ( dirPath , dirName , providerName , sources )
142133 }
143134
144135 await saveDiscoveryCache ( providerName , sessionsDir , snapshot , sources )
145136 return sources
146137}
147138
139+ // Collects session sources from dirPath and one level of subdirectories.
140+ // Sub-agent sessions land in <project-dir>/<parent-session-id>/<agent-name>.jsonl,
141+ // so we must recurse one level deeper than the project directory.
142+ async function collectJsonlFromDir (
143+ dirPath : string ,
144+ projectDirName : string ,
145+ providerName : string ,
146+ sources : SessionSource [ ] ,
147+ ) : Promise < void > {
148+ let entries : string [ ]
149+ try {
150+ entries = await readdir ( dirPath )
151+ } catch {
152+ return
153+ }
154+
155+ for ( const entry of entries ) {
156+ const entryPath = join ( dirPath , entry )
157+ const entryStat = await stat ( entryPath ) . catch ( ( ) => null )
158+ if ( ! entryStat ) continue
159+
160+ if ( entryStat . isDirectory ( ) ) {
161+ // Sub-agent session dir: recurse one level, but don't go deeper
162+ await collectJsonlFromDir ( entryPath , projectDirName , providerName , sources )
163+ continue
164+ }
165+
166+ if ( ! entryStat . isFile ( ) || ! entry . endsWith ( '.jsonl' ) ) continue
167+
168+ const first = await readFirstEntry ( entryPath )
169+ if ( ! first || first . type !== 'session' ) continue
170+
171+ const cwd = first . cwd ?? projectDirName
172+ sources . push ( {
173+ path : entryPath ,
174+ project : basename ( cwd ) ,
175+ provider : providerName ,
176+ fingerprintPath : entryPath ,
177+ cacheStrategy : 'append-jsonl' ,
178+ progressLabel : basename ( entryPath ) ,
179+ parserVersion : `${ providerName } :v1` ,
180+ } )
181+ }
182+ }
183+
148184function createParser ( source : SessionSource , seenKeys : Set < string > ) : SessionParser {
149185 return {
150186 async * parse ( ) : AsyncGenerator < ParsedProviderCall > {
@@ -188,7 +224,7 @@ function createParser(source: SessionSource, seenKeys: Set<string>): SessionPars
188224
189225 const model = msg . model ?? 'gpt-5'
190226 const responseId = msg . responseId ?? ''
191- const dedupKey = `pi :${ source . path } :${ responseId || entry . id || entry . timestamp || String ( lineIdx ) } `
227+ const dedupKey = `${ source . provider } :${ source . path } :${ responseId || entry . id || entry . timestamp || String ( lineIdx ) } `
192228
193229 if ( seenKeys . has ( dedupKey ) ) continue
194230 seenKeys . add ( dedupKey )
@@ -289,4 +325,4 @@ export function createOmpProvider(sessionsDir?: string): Provider {
289325 }
290326}
291327
292- export const omp = createOmpProvider ( )
328+ export const omp = createOmpProvider ( )
0 commit comments