77 type SessionStatus ,
88} from "../types/session"
99import type { Message } from "../types/message"
10- import type { SessionV2Info , V2SessionsResponse } from "@opencode-ai/sdk/v2/client"
10+ import type { Session as SDKSession , SessionListResponse } from "@opencode-ai/sdk/v2/client"
1111
1212import { instances } from "./instances"
1313import { preferences , setAgentModelPreference } from "./preferences"
@@ -36,8 +36,6 @@ import {
3636 cleanupBlankSessions ,
3737 syncInstanceSessionIndicator ,
3838 updateThreadTotalsForParent ,
39- SESSION_PAGE_SIZE ,
40- getSessionNextCursor ,
4139 setSessionPage ,
4240 prependSessionListId ,
4341 removeSessionListId ,
@@ -58,6 +56,7 @@ import { getRootClient } from "./opencode-client"
5856import { getWorktreeSlugForSession , migrateLegacyWorktreeMapToSessionMetadata , pruneStaleLegacyWorktreeMapEntries , removeLegacyParentSessionMapping , setWorktreeSlugForParentSession } from "./worktrees"
5957import { getOpenCodeWorkspaceIdForSession } from "./opencode-workspaces"
6058import { hydrateSessionMetadataWithClient } from "./session-metadata"
59+ import { PROJECT_SESSION_LIST_LIMIT , buildProjectSessionListOptions } from "./session-list-options"
6160
6261const log = getLogger ( "api" )
6362
@@ -111,17 +110,19 @@ interface SessionForkResponse {
111110
112111type V2SessionListOptions = {
113112 directory ?: string
114- limit ?: number
115113 search ?: string
116- cursor ?: string
117114}
118115
119- function getKnownParentId ( session : SessionV2Info | Session ) : string | null | undefined {
116+ type ProjectSessionListResponse = {
117+ data : SDKSession [ ]
118+ }
119+
120+ function getKnownParentId ( session : SDKSession | Session ) : string | null | undefined {
120121 return ( session as any ) . parentID ?? ( session as Session ) . parentId
121122}
122123
123- function hasMissingParentChain ( session : SessionV2Info , loaded : Map < string , SessionV2Info | Session > ) : boolean {
124- let current : SessionV2Info | Session = session
124+ function hasMissingParentChain ( session : SDKSession , loaded : Map < string , SDKSession | Session > ) : boolean {
125+ let current : SDKSession | Session = session
125126 const seen = new Set < string > ( )
126127
127128 while ( getKnownParentId ( current ) ) {
@@ -137,20 +138,17 @@ function hasMissingParentChain(session: SessionV2Info, loaded: Map<string, Sessi
137138 return false
138139}
139140
140- async function fetchV2Sessions ( instanceId : string , options : V2SessionListOptions ) : Promise < V2SessionsResponse > {
141+ async function fetchV2Sessions ( instanceId : string , options : V2SessionListOptions ) : Promise < ProjectSessionListResponse > {
141142 const client = getRootClient ( instanceId )
142- return requestData < V2SessionsResponse > ( client . v2 . session . list ( options ) , "v2.session.list" )
143+ const listOptions = buildProjectSessionListOptions ( options )
144+ const data = await requestData < SessionListResponse > ( client . session . list ( listOptions ) , "session.list" )
145+ return { data }
143146}
144147
145- function getV2SessionItems ( response : V2SessionsResponse ) : SessionV2Info [ ] {
148+ function getV2SessionItems ( response : ProjectSessionListResponse ) : SDKSession [ ] {
146149 return response . data
147150}
148151
149- function getV2NextCursor ( response : V2SessionsResponse ) : string | undefined {
150- const next = ( response as any ) ?. cursor ?. next
151- return typeof next === "string" && next . length > 0 ? next : undefined
152- }
153-
154152async function hydrateMissingSessionMetadata ( instanceId : string , sessionIds : string [ ] ) : Promise < void > {
155153 const uniqueIds = Array . from ( new Set ( sessionIds ) ) . filter ( Boolean )
156154 if ( uniqueIds . length === 0 ) return
@@ -167,43 +165,33 @@ async function hydrateMissingSessionMetadata(instanceId: string, sessionIds: str
167165 }
168166}
169167
170- async function ensureV2ParentChainsLoaded ( instanceId : string , apiSessions : SessionV2Info [ ] , directory ?: string ) : Promise < void > {
168+ async function ensureV2ParentChainsLoaded ( instanceId : string , apiSessions : SDKSession [ ] , directory ?: string ) : Promise < void > {
171169 const currentSessions = sessions ( ) . get ( instanceId ) ?? new Map < string , Session > ( )
172- const loaded = new Map < string , SessionV2Info | Session > ( currentSessions )
170+ const loaded = new Map < string , SDKSession | Session > ( currentSessions )
173171 for ( const session of apiSessions ) loaded . set ( session . id , session )
174172
175173 if ( ! apiSessions . some ( ( session ) => hasMissingParentChain ( session , loaded ) ) ) return
176174
177- const limit = SESSION_PAGE_SIZE
178- let cursor : string | undefined
179- let remainingPages = 25
180-
181- while ( apiSessions . some ( ( session ) => hasMissingParentChain ( session , loaded ) ) && remainingPages > 0 ) {
182- const page = await fetchV2Sessions ( instanceId , { directory, limit, ...( cursor ? { cursor } : { } ) } )
183- const items = getV2SessionItems ( page )
184- if ( items . length === 0 ) break
185-
186- setSessions ( ( prev ) => {
187- const next = new Map ( prev )
188- const instanceSessions = new Map ( next . get ( instanceId ) ?? new Map ( ) )
175+ const page = await fetchV2Sessions ( instanceId , { directory } )
176+ const items = getV2SessionItems ( page )
177+ if ( items . length === 0 ) return
189178
190- for ( const apiSession of items ) {
191- const existingSession = instanceSessions . get ( apiSession . id )
192- instanceSessions . set ( apiSession . id , toClientSessionV2 ( instanceId , apiSession , existingSession ) )
193- loaded . set ( apiSession . id , apiSession )
194- }
179+ setSessions ( ( prev ) => {
180+ const next = new Map ( prev )
181+ const instanceSessions = new Map ( next . get ( instanceId ) ?? new Map ( ) )
195182
196- next . set ( instanceId , instanceSessions )
197- return next
198- } )
183+ for ( const apiSession of items ) {
184+ const existingSession = instanceSessions . get ( apiSession . id )
185+ instanceSessions . set ( apiSession . id , toClientSessionV2 ( instanceId , apiSession , existingSession ) )
186+ loaded . set ( apiSession . id , apiSession )
187+ }
199188
200- cursor = getV2NextCursor ( page )
201- if ( ! cursor ) break
202- remainingPages -= 1
203- }
189+ next . set ( instanceId , instanceSessions )
190+ return next
191+ } )
204192}
205193
206- async function fetchSessions ( instanceId : string , options ?: { limit ?: number ; reset ?: boolean ; cursor ?: string } ) : Promise < void > {
194+ async function fetchSessions ( instanceId : string , options ?: { reset ?: boolean } ) : Promise < void > {
207195 const instance = instances ( ) . get ( instanceId )
208196 if ( ! instance || ! instance . client ) {
209197 throw new Error ( "Instance not ready" )
@@ -218,17 +206,10 @@ async function fetchSessions(instanceId: string, options?: { limit?: number; res
218206 } )
219207
220208 try {
221- const limit = Math . min ( options ?. limit ?? SESSION_PAGE_SIZE , 200 )
222-
223- const sessionListOptions : { directory ?: string ; limit ?: number ; cursor ?: string } = {
224- limit,
225- ...( instance . folder ? { directory : instance . folder } : { } ) ,
226- ...( options ?. cursor ? { cursor : options . cursor } : { } ) ,
227- }
209+ const sessionListOptions = instance . folder ? { directory : instance . folder } : { }
228210
229- log . info ( "v2. session.list" , { instanceId, limit, directory : sessionListOptions . directory , cursor : sessionListOptions . cursor } )
211+ log . info ( "session.list" , { instanceId, limit : PROJECT_SESSION_LIST_LIMIT , directory : sessionListOptions . directory , scope : "project" } )
230212 const response = await fetchV2Sessions ( instanceId , sessionListOptions )
231- const nextCursor = getV2NextCursor ( response )
232213
233214 let statusById : Record < string , any > = { }
234215 try {
@@ -298,7 +279,7 @@ async function fetchSessions(instanceId: string, options?: { limit?: number; res
298279 } )
299280 }
300281
301- setSessionPage ( instanceId , rootIds , Boolean ( nextCursor ) , options ?. reset ?? true , nextCursor )
282+ setSessionPage ( instanceId , rootIds , false , options ?. reset ?? true )
302283
303284 syncInstanceSessionIndicator ( instanceId )
304285
@@ -339,9 +320,7 @@ async function fetchSessions(instanceId: string, options?: { limit?: number; res
339320}
340321
341322async function loadMoreSessions ( instanceId : string ) : Promise < void > {
342- const cursor = getSessionNextCursor ( instanceId )
343- if ( ! cursor ) return
344- await fetchSessions ( instanceId , { limit : SESSION_PAGE_SIZE , reset : false , cursor } )
323+ return
345324}
346325
347326async function searchSessions ( instanceId : string , query : string ) : Promise < void > {
@@ -359,7 +338,6 @@ async function searchSessions(instanceId: string, query: string): Promise<void>
359338 log . info ( "v2.session.search" , { instanceId, query : trimmedQuery , directory : instance . folder } )
360339 const response = await fetchV2Sessions ( instanceId , {
361340 search : trimmedQuery ,
362- limit : SESSION_PAGE_SIZE ,
363341 directory : instance . folder ,
364342 } )
365343 if ( ! isLatestSessionSearch ( instanceId , trimmedQuery , requestId ) ) return
@@ -410,7 +388,7 @@ async function searchSessions(instanceId: string, query: string): Promise<void>
410388 }
411389}
412390
413- function toClientSessionV2 ( instanceId : string , apiSession : SessionV2Info , existingSession ?: Session ) : Session {
391+ function toClientSessionV2 ( instanceId : string , apiSession : SDKSession , existingSession ?: Session ) : Session {
414392 return {
415393 id : apiSession . id ,
416394 instanceId,
0 commit comments