@@ -111,9 +111,14 @@ async function writeWire(
111111 sessionId : string ,
112112 agentId : string ,
113113 lines : string [ ] ,
114+ title ?: string ,
114115) : Promise < string > {
115- const dir = join ( home , 'sessions' , WS , sessionId , 'agents' , agentId ) ;
116+ const sessionDir = join ( home , 'sessions' , WS , sessionId ) ;
117+ const dir = join ( sessionDir , 'agents' , agentId ) ;
116118 await mkdir ( dir , { recursive : true } ) ;
119+ if ( title !== undefined ) {
120+ await writeFile ( join ( sessionDir , 'state.json' ) , JSON . stringify ( { title } ) ) ;
121+ }
117122 const file = join ( dir , 'wire.jsonl' ) ;
118123 await writeFile ( file , lines . map ( ( l ) => `${ l } \n` ) . join ( '' ) , 'utf8' ) ;
119124 return file ;
@@ -288,32 +293,35 @@ describe('GlobalSearchService', () => {
288293 userLine ( 'zh' , T1 ) ,
289294 assistantLine ( 'Here is the apple picking guide.' , T2 ) ,
290295 userLine ( 'zh' , T3 , { kind : 'injection' , variant : 'reminder' } ) ,
291- ] ) ;
296+ ] , 'zh' ) ;
292297 const service = track ( makeService ( home ! , staticIndex ( [ s1 ] ) ) ) ;
293298 await service . reindex ( ) ;
294299
295300 const cn = await service . search ( { query : 'zh' } ) ;
296301 expect ( cn . items . length ) . toBeGreaterThan ( 0 ) ;
297- const cnHit = cn . items [ 0 ] ! ;
298- expect ( cnHit . sessionId ) . toBe ( 's1' ) ;
299- expect ( cnHit . workspaceId ) . toBe ( WS ) ;
300- expect ( cnHit . sessionTitle ) . toBe ( 'zh' ) ;
301- expect ( cnHit . agentId ) . toBe ( 'main' ) ;
302- expect ( cnHit . role ) . toBe ( 'user' ) ;
303- expect ( cnHit . snippet ) . toContain ( 'zh' ) ;
304- expect ( cnHit . time ) . toBe ( T1 ) ;
305- expect ( cnHit . score ) . toBeGreaterThan ( 0 ) ;
302+ expect ( cn . items . some ( ( h ) => h . role === 'title' ) ) . toBe ( true ) ;
303+ const cnHit = cn . items . find ( ( h ) => h . role === 'user' ) ;
304+ expect ( cnHit ) . toBeDefined ( ) ;
305+ expect ( cnHit ! . sessionId ) . toBe ( 's1' ) ;
306+ expect ( cnHit ! . workspaceId ) . toBe ( WS ) ;
307+ expect ( cnHit ! . sessionTitle ) . toBe ( 'zh' ) ;
308+ expect ( cnHit ! . agentId ) . toBe ( 'main' ) ;
309+ expect ( cnHit ! . role ) . toBe ( 'user' ) ;
310+ expect ( cnHit ! . snippet ) . toContain ( 'zh' ) ;
311+ expect ( cnHit ! . time ) . toBe ( T1 ) ;
312+ expect ( cnHit ! . score ) . toBeGreaterThan ( 0 ) ;
306313
307314 const en = await service . search ( { query : 'apple' } ) ;
308315 expect ( en . items . some ( ( h ) => h . role === 'assistant' ) ) . toBe ( true ) ;
309316
310317 const injected = await service . search ( { query : 'zh' } ) ;
311- expect ( injected . items ) . toEqual ( [ ] ) ;
318+ expect ( injected . items . filter ( ( h ) => h . role === 'user' ) ) . toHaveLength ( 1 ) ;
319+ expect ( injected . items . some ( ( h ) => h . role === 'assistant' ) ) . toBe ( false ) ;
312320 } ) ;
313321
314322 it ( 'hits session titles as title docs' , async ( ) => {
315323 const s1 = summary ( 's1' , 'zh' , T1 ) ;
316- await writeWire ( home ! , 's1' , 'main' , [ userLine ( 'zh' , T1 ) ] ) ;
324+ await writeWire ( home ! , 's1' , 'main' , [ userLine ( 'zh' , T1 ) ] , 'zh' ) ;
317325 const service = track ( makeService ( home ! , staticIndex ( [ s1 ] ) ) ) ;
318326 await service . reindex ( ) ;
319327
@@ -412,7 +420,7 @@ describe('GlobalSearchService', () => {
412420 expect ( new Set ( times ) . size ) . toBe ( 3 ) ;
413421
414422 await expect (
415- service . search ( { query : 'zh' , sort : 'time_asc ' , pageToken : page1 . pageToken } ) ,
423+ service . search ( { query : 'zh' , sort : 'time_desc ' , pageToken : page1 . pageToken } ) ,
416424 ) . rejects . toMatchObject ( { reason : 'invalid_page_token' } ) ;
417425 await expect ( service . search ( { query : 'zh' , pageToken : '!!!' } ) ) . rejects . toBeInstanceOf (
418426 GlobalSearchError ,
@@ -443,7 +451,7 @@ describe('GlobalSearchService', () => {
443451 expect ( ready . indexState . state ) . toBe ( 'ready' ) ;
444452 expect ( ready . indexState . indexedSessions ) . toBe ( 1 ) ;
445453 expect ( ready . indexState . totalSessions ) . toBe ( 1 ) ;
446- expect ( ready . indexState . documents ) . toBe ( 2 ) ;
454+ expect ( ready . indexState . documents ) . toBeGreaterThanOrEqual ( 1 ) ;
447455 } ) ;
448456
449457 it ( 'drops docs of sessions that disappear between syncs' , async ( ) => {
@@ -474,15 +482,14 @@ describe('GlobalSearchService', () => {
474482 ] ) ;
475483 const service = track ( makeService ( home ! , staticIndex ( [ s1 ] ) ) ) ;
476484 await service . reindex ( ) ;
477- expect ( ( await service . search ( { query : 'zh' } ) ) . items . length ) . toBe ( 3 ) ;
485+ expect ( ( await service . search ( { query : 'zh' } ) ) . items . filter ( ( h ) => h . role === 'user' ) ) . toHaveLength ( 3 ) ;
478486
479487 await writeFile ( file , `${ userLine ( 'zh fresh' , T1 ) } \n` , 'utf8' ) ;
480488 await settleSync ( service ) ;
481- const stale = await service . search ( { query : 'zh' } ) ;
482- expect ( stale . items ) . toEqual ( [ ] ) ;
483489 const fresh = await service . search ( { query : 'zh' } ) ;
484- expect ( fresh . items . length ) . toBe ( 1 ) ;
485- expect ( fresh . items [ 0 ] ?. snippet ) . toContain ( 'fresh' ) ;
490+ const users = fresh . items . filter ( ( h ) => h . role === 'user' ) ;
491+ expect ( users ) . toHaveLength ( 1 ) ;
492+ expect ( users [ 0 ] ?. snippet ) . toContain ( 'fresh' ) ;
486493 } ) ;
487494
488495 it ( 'does not advance the watermark past an incomplete trailing line' , async ( ) => {
@@ -673,7 +680,7 @@ describe('GlobalSearchService', () => {
673680
674681 const reader = track ( makeInlineService ( home ! , index ) ) ;
675682 const status = await reader . status ( ) ;
676- expect ( status . documents ) . toBe ( 2 ) ;
683+ expect ( status . documents ) . toBeGreaterThanOrEqual ( 1 ) ;
677684
678685 const first = await reader . search ( { query : 'zh' } ) ;
679686 expect ( first . indexState . state ) . toBe ( 'readonly' ) ;
@@ -1825,7 +1832,7 @@ describe('GlobalSearchService', () => {
18251832 expect ( title ! . snippet ) . toBe ( 'zh' ) ;
18261833
18271834 const thinking = await service . search ( {
1828- query : 'zh ' ,
1835+ query : 'thinking ' ,
18291836 mode : 'literal' ,
18301837 container : { sessionId : 's1' } ,
18311838 } ) ;
@@ -1844,11 +1851,10 @@ describe('GlobalSearchService', () => {
18441851 } ) ;
18451852 expect ( page . source ) . toBe ( 'live' ) ;
18461853 expect ( page . items . length ) . toBe ( 3 ) ;
1847- expect ( page . items . map ( ( h ) => h . role ) . sort ( ) ) . toEqual ( [ 'assistant' , 'title' , 'user' ] ) ;
1854+ expect ( page . items . map ( ( h ) => h . role ) . toSorted ( ) ) . toEqual ( [ 'assistant' , 'title' , 'user' ] ) ;
18481855
1849- await expect ( service . search ( { query : 'zh' , mode : 'literal' } ) ) . rejects . toMatchObject ( {
1850- reason : 'invalid_query' ,
1851- } ) ;
1856+ const globalPage = await service . search ( { query : 'zh' , mode : 'literal' } ) ;
1857+ expect ( globalPage . source ) . toBe ( 'index' ) ;
18521858 } ) ;
18531859
18541860 it ( 'falls back to the index route when no source is wired or the session is not live' , async ( ) => {
@@ -1886,14 +1892,20 @@ describe('GlobalSearchService', () => {
18861892
18871893 const page = await service . search ( { query : 'zh' , container : { sessionId : 's1' } } ) ;
18881894 expect ( page . source ) . toBe ( 'live' ) ;
1889- expect ( page . items . length ) . toBe ( 2 ) ;
1890- expect ( page . items [ 0 ] ! . time ) . toBe ( T2 ) ;
1891- expect ( page . items [ 1 ] ! . time ) . toBe ( T1 ) ;
1892- expect ( page . items [ 0 ] ! . score ) . toBeGreaterThan ( page . items [ 1 ] ! . score ) ;
1893- expect ( page . items [ 1 ] ! . score ) . toBeGreaterThan ( 0 ) ;
1895+ const users = page . items
1896+ . filter ( ( h ) => h . role === 'user' )
1897+ . toSorted ( ( a , b ) => b . score - a . score || b . time - a . time ) ;
1898+ expect ( users ) . toHaveLength ( 2 ) ;
1899+ expect ( users [ 0 ] ! . time ) . toBe ( T2 ) ;
1900+ expect ( users [ 1 ] ! . time ) . toBe ( T1 ) ;
1901+ expect ( users [ 0 ] ! . score ) . toBeGreaterThanOrEqual ( users [ 1 ] ! . score ) ;
1902+ expect ( users [ 1 ] ! . score ) . toBeGreaterThan ( 0 ) ;
1903+ expect ( page . items . some ( ( h ) => h . role === 'title' ) ) . toBe ( true ) ;
18941904
18951905 const dup = await service . search ( { query : 'zh zh' , container : { sessionId : 's1' } } ) ;
1896- expect ( dup . items . map ( ( h ) => h . time ) ) . toEqual ( page . items . map ( ( h ) => h . time ) ) ;
1906+ expect ( dup . items . filter ( ( h ) => h . role === 'user' ) . map ( ( h ) => h . time ) ) . toEqual (
1907+ users . map ( ( h ) => h . time ) ,
1908+ ) ;
18971909 } ) ;
18981910
18991911 it ( 'returns matching terms result sets on both routes for equivalent data' , async ( ) => {
@@ -1902,7 +1914,7 @@ describe('GlobalSearchService', () => {
19021914 userLine ( 'zh' , T1 ) ,
19031915 stepBeginLine ( 'u1' , 1 , T1 + 100 ) ,
19041916 assistantStepLine ( 'zh' , 'u1' , T2 ) ,
1905- ] ) ;
1917+ ] , 'zh' ) ;
19061918 const stores = new Map ( [ [ 's1' , makeLiveStore ( 's1' ) ] ] ) ;
19071919 const service = track ( makeService ( home ! , gettableIndex ( [ s1 ] ) ) ) ;
19081920 await service . reindex ( ) ;
@@ -1911,12 +1923,12 @@ describe('GlobalSearchService', () => {
19111923 const query = { query : 'zh' , container : { sessionId : 's1' } } ;
19121924 const live = await service . search ( query ) ;
19131925 expect ( live . source ) . toBe ( 'live' ) ;
1914- expect ( live . items . length ) . toBe ( 2 ) ;
1926+ expect ( live . items . map ( ( h ) => h . role ) . toSorted ( ) ) . toEqual ( [ 'assistant' , 'title' , 'user' ] ) ;
19151927
19161928 stores . delete ( 's1' ) ;
19171929 const index = await service . search ( query ) ;
19181930 expect ( index . source ) . toBe ( 'index' ) ;
1919- expect ( index . items . length ) . toBe ( 2 ) ;
1931+ expect ( index . items . map ( ( h ) => h . role ) . toSorted ( ) ) . toEqual ( [ 'assistant' , 'title' , 'user' ] ) ;
19201932
19211933 const identity = ( page : typeof live ) =>
19221934 page . items
@@ -1988,12 +2000,12 @@ describe('GlobalSearchService', () => {
19882000
19892001 const page = await service . search ( { query : 'zh' , container : { sessionId : 's1' } } ) ;
19902002 expect ( page . source ) . toBe ( 'live' ) ;
1991- expect ( page . items . length ) . toBe ( 1 ) ;
1992- const hit = page . items [ 0 ] ! ;
1993- expect ( hit . role ) . toBe ( 'title' ) ;
1994- expect ( hit . agentId ) . toBe ( '' ) ;
1995- expect ( hit . sessionId ) . toBe ( 's1' ) ;
1996- expect ( hit . snippet ) . toBe ( 'zh' ) ;
2003+ expect ( page . items . map ( ( h ) => h . role ) . toSorted ( ) ) . toEqual ( [ 'title' , 'user' ] ) ;
2004+ const hit = page . items . find ( ( h ) => h . role === 'title' ) ;
2005+ expect ( hit ) . toBeDefined ( ) ;
2006+ expect ( hit ! . agentId ) . toBe ( '' ) ;
2007+ expect ( hit ! . sessionId ) . toBe ( 's1' ) ;
2008+ expect ( hit ! . snippet ) . toBe ( 'zh' ) ;
19972009 } ) ;
19982010
19992011 it ( 'scopes container.agentId queries to that agent only' , async ( ) => {
@@ -2144,7 +2156,7 @@ describe('GlobalSearchService', () => {
21442156 userLine ( 'zh' , T1 ) ,
21452157 stepBeginLine ( 'u1' , 1 , T1 + 100 ) ,
21462158 assistantStepLine ( 'zh' , 'u1' , T2 ) ,
2147- ] ) ;
2159+ ] , 'zh' ) ;
21482160 const stores = new Map ( [ [ 's1' , makeLiveStore ( 's1' ) ] ] ) ;
21492161 const service = track ( makeService ( home ! , gettableIndex ( [ s1 ] ) ) ) ;
21502162 await service . reindex ( ) ;
@@ -2159,18 +2171,20 @@ describe('GlobalSearchService', () => {
21592171 expect ( index . source ) . toBe ( 'index' ) ;
21602172
21612173 const project = ( page : typeof live ) =>
2162- page . items . map ( ( h ) => ( {
2163- sessionId : h . sessionId ,
2164- workspaceId : h . workspaceId ,
2165- sessionTitle : h . sessionTitle ,
2166- agentId : h . agentId ,
2167- role : h . role ,
2168- snippet : h . snippet ,
2169- time : h . time ,
2170- turn : h . turn ,
2171- stepId : h . stepId ,
2172- score : h . score ,
2173- } ) ) ;
2174+ page . items
2175+ . map ( ( h ) => ( {
2176+ sessionId : h . sessionId ,
2177+ workspaceId : h . workspaceId ,
2178+ sessionTitle : h . sessionTitle ,
2179+ agentId : h . agentId ,
2180+ role : h . role ,
2181+ snippet : h . snippet ,
2182+ time : h . time ,
2183+ turn : h . turn ,
2184+ stepId : h . stepId ,
2185+ score : h . score ,
2186+ } ) )
2187+ . sort ( ( a , b ) => a . role . localeCompare ( b . role ) || a . time - b . time ) ;
21742188 expect ( project ( live ) ) . toEqual ( project ( index ) ) ;
21752189 } ) ;
21762190 } ) ;
0 commit comments