@@ -6,6 +6,7 @@ import { initializeAssistantMode } from '@/api/repos'
66
77const mocks = vi . hoisted ( ( ) => ( {
88 listSessions : vi . fn ( ) ,
9+ listSessionsPage : vi . fn ( ) ,
910 createSession : vi . fn ( ) ,
1011 sendPromptAsync : vi . fn ( ) ,
1112 initializeAssistantMode : vi . fn ( ) ,
@@ -18,6 +19,7 @@ vi.mock('@/api/repos', () => ({
1819vi . mock ( '@/api/opencode' , ( ) => ( {
1920 OpenCodeClient : vi . fn ( ( ) => ( {
2021 listSessions : mocks . listSessions ,
22+ listSessionsPage : mocks . listSessionsPage ,
2123 createSession : mocks . createSession ,
2224 sendPromptAsync : mocks . sendPromptAsync ,
2325 } ) ) ,
@@ -35,12 +37,14 @@ describe('useAssistantSessionLauncher', () => {
3537 } )
3638
3739 it ( 'opens the latest root session in the assistant directory' , async ( ) => {
38- mocks . listSessions . mockResolvedValue ( [
39- { id : 'older' , directory : '/assistant' , time : { updated : 10 } } ,
40- { id : 'newest-child' , parentID : 'newest' , directory : '/assistant' , time : { updated : 40 } } ,
41- { id : 'different-directory' , directory : '/other' , time : { updated : 50 } } ,
42- { id : 'newest' , directory : '/assistant' , time : { updated : 30 } } ,
43- ] )
40+ mocks . listSessionsPage . mockResolvedValue ( {
41+ items : [
42+ { id : 'older' , directory : '/assistant' , time : { updated : 10 } } ,
43+ { id : 'newest-child' , parentID : 'newest' , directory : '/assistant' , time : { updated : 40 } } ,
44+ { id : 'different-directory' , directory : '/other' , time : { updated : 50 } } ,
45+ { id : 'newest' , directory : '/assistant' , time : { updated : 30 } } ,
46+ ] ,
47+ } )
4448 const onNavigate = vi . fn ( )
4549 const { result } = renderHook ( ( ) => useAssistantSessionLauncher ( {
4650 repoId : 123 ,
@@ -54,13 +58,44 @@ describe('useAssistantSessionLauncher', () => {
5458
5559 expect ( initializeAssistantMode ) . toHaveBeenCalledWith ( 123 )
5660 expect ( OpenCodeClient ) . toHaveBeenCalledWith ( 'http://localhost:5551' , '/assistant' )
57- expect ( mocks . listSessions ) . toHaveBeenCalledWith ( { limit : 1 , roots : true } )
61+ expect ( mocks . listSessionsPage ) . toHaveBeenCalledWith ( { limit : 25 , order : 'desc' } )
62+ expect ( mocks . listSessions ) . not . toHaveBeenCalled ( )
5863 expect ( onNavigate ) . toHaveBeenCalledWith ( 'newest' )
5964 expect ( localStorage . getItem ( 'ocm:assistant:last-session:123:/assistant' ) ) . toBe ( 'newest' )
6065 expect ( mocks . createSession ) . not . toHaveBeenCalled ( )
6166 expect ( mocks . sendPromptAsync ) . not . toHaveBeenCalled ( )
6267 } )
6368
69+ it ( 'paginates assistant sessions instead of making an unbounded session list request' , async ( ) => {
70+ mocks . listSessionsPage
71+ . mockResolvedValueOnce ( {
72+ items : [
73+ { id : 'newest-child' , parentID : 'newest' , directory : '/assistant' , time : { updated : 40 } } ,
74+ ] ,
75+ nextCursor : 'next-page' ,
76+ } )
77+ . mockResolvedValueOnce ( {
78+ items : [
79+ { id : 'newest' , directory : '/assistant' , time : { updated : 30 } } ,
80+ ] ,
81+ } )
82+ const onNavigate = vi . fn ( )
83+ const { result } = renderHook ( ( ) => useAssistantSessionLauncher ( {
84+ repoId : 123 ,
85+ opcodeUrl : 'http://localhost:5551' ,
86+ onNavigate,
87+ } ) )
88+
89+ await act ( async ( ) => {
90+ await result . current . openAssistant ( )
91+ } )
92+
93+ expect ( mocks . listSessionsPage ) . toHaveBeenNthCalledWith ( 1 , { limit : 25 , order : 'desc' } )
94+ expect ( mocks . listSessionsPage ) . toHaveBeenNthCalledWith ( 2 , { cursor : 'next-page' } )
95+ expect ( mocks . listSessions ) . not . toHaveBeenCalled ( )
96+ expect ( onNavigate ) . toHaveBeenCalledWith ( 'newest' )
97+ } )
98+
6499 it ( 'notifies an existing assistant session when some generated updates were preserved' , async ( ) => {
65100 mocks . initializeAssistantMode . mockResolvedValue ( {
66101 directory : '/assistant' ,
@@ -72,9 +107,11 @@ describe('useAssistantSessionLauncher', () => {
72107 } ,
73108 ] ,
74109 } )
75- mocks . listSessions . mockResolvedValue ( [
76- { id : 'existing' , directory : '/assistant' , time : { updated : 10 } } ,
77- ] )
110+ mocks . listSessionsPage . mockResolvedValue ( {
111+ items : [
112+ { id : 'existing' , directory : '/assistant' , time : { updated : 10 } } ,
113+ ] ,
114+ } )
78115 const onNavigate = vi . fn ( )
79116 const { result } = renderHook ( ( ) => useAssistantSessionLauncher ( {
80117 repoId : 123 ,
@@ -87,7 +124,7 @@ describe('useAssistantSessionLauncher', () => {
87124 } )
88125
89126 expect ( onNavigate ) . toHaveBeenCalledWith ( 'existing' )
90- expect ( mocks . listSessions ) . toHaveBeenCalledWith ( { limit : 1 , roots : true } )
127+ expect ( mocks . listSessionsPage ) . toHaveBeenCalledWith ( { limit : 25 , order : 'desc' } )
91128 expect ( mocks . sendPromptAsync ) . toHaveBeenCalledWith ( 'existing' , {
92129 parts : [
93130 expect . objectContaining ( {
@@ -101,9 +138,11 @@ describe('useAssistantSessionLauncher', () => {
101138 } )
102139
103140 it ( 'creates a session when the assistant directory has no root sessions' , async ( ) => {
104- mocks . listSessions . mockResolvedValue ( [
105- { id : 'other' , directory : '/other' , time : { updated : 50 } } ,
106- ] )
141+ mocks . listSessionsPage . mockResolvedValue ( {
142+ items : [
143+ { id : 'other' , directory : '/other' , time : { updated : 50 } } ,
144+ ] ,
145+ } )
107146 mocks . createSession . mockResolvedValue ( { id : 'created' } )
108147 const onNavigate = vi . fn ( )
109148 const { result } = renderHook ( ( ) => useAssistantSessionLauncher ( {
@@ -141,7 +180,7 @@ describe('useAssistantSessionLauncher', () => {
141180 } )
142181
143182 it ( 'navigates after creating a session without waiting for the welcome prompt to complete' , async ( ) => {
144- mocks . listSessions . mockResolvedValue ( [ ] )
183+ mocks . listSessionsPage . mockResolvedValue ( { items : [ ] } )
145184 let resolvePrompt : ( ) => void
146185 const promptPromise = new Promise < void > ( ( resolve ) => {
147186 resolvePrompt = resolve
@@ -172,7 +211,7 @@ describe('useAssistantSessionLauncher', () => {
172211 } )
173212
174213 it ( 'navigates even when welcome prompt fails' , async ( ) => {
175- mocks . listSessions . mockResolvedValue ( [ ] )
214+ mocks . listSessionsPage . mockResolvedValue ( { items : [ ] } )
176215 mocks . createSession . mockResolvedValue ( { id : 'created' } )
177216 mocks . sendPromptAsync . mockRejectedValueOnce ( new Error ( 'provider unavailable' ) )
178217 const onNavigate = vi . fn ( )
0 commit comments