@@ -105,6 +105,96 @@ describe('ACP server test', { timeout: 40_000 }, () => {
105105 expect ( newSessionResponse . sessionId ) . toBeDefined ( )
106106 } )
107107
108+ it ( 'prefetches session additional skill roots before thread start' , async ( ) => {
109+ const mockFixture = createCodexMockTestFixture ( ) ;
110+ const codexAcpClient = mockFixture . getCodexAcpClient ( ) ;
111+ const codexAppServerClient = mockFixture . getCodexAppServerClient ( ) ;
112+
113+ const listSkillsSpy = vi . spyOn ( codexAppServerClient , "listSkills" ) . mockResolvedValue ( { data : [ ] } ) ;
114+ const threadStartSpy = vi . spyOn ( codexAppServerClient , "threadStart" ) . mockResolvedValue ( {
115+ thread : { id : "thread-id" } as any ,
116+ model : "gpt-5" ,
117+ modelProvider : "openai" ,
118+ cwd : "/workspace" ,
119+ approvalPolicy : "on-request" ,
120+ sandbox : "workspace-write" ,
121+ reasoningEffort : "medium" ,
122+ } as any ) ;
123+ vi . spyOn ( codexAppServerClient , "listModels" ) . mockResolvedValue ( {
124+ data : [ {
125+ id : "gpt-5" ,
126+ model : "gpt-5" ,
127+ upgrade : null ,
128+ displayName : "gpt-5" ,
129+ description : "test model" ,
130+ supportedReasoningEfforts : [ { reasoningEffort : "medium" , description : "balanced" } ] ,
131+ defaultReasoningEffort : "medium" ,
132+ inputModalities : [ "text" ] ,
133+ supportsPersonality : false ,
134+ isDefault : true
135+ } ] ,
136+ nextCursor : null
137+ } ) ;
138+
139+ await codexAcpClient . newSession ( {
140+ cwd : "/workspace" ,
141+ mcpServers : [ ] ,
142+ _meta : {
143+ additionalRoots : [ "/skills/one" , " /skills/two " , 7 ]
144+ }
145+ } ) ;
146+
147+ expect ( listSkillsSpy ) . toHaveBeenCalledWith ( {
148+ cwds : [ "/workspace" ] ,
149+ forceReload : true ,
150+ perCwdExtraUserRoots : [ {
151+ cwd : "/workspace" ,
152+ extraUserRoots : [ "/skills/one" , "/skills/two" ]
153+ } ]
154+ } ) ;
155+ expect ( listSkillsSpy . mock . invocationCallOrder [ 0 ] ! ) . toBeLessThan ( threadStartSpy . mock . invocationCallOrder [ 0 ] ! ) ;
156+ } ) ;
157+
158+ it ( 'prefetches session additional skill roots before turn start' , async ( ) => {
159+ const mockFixture = createCodexMockTestFixture ( ) ;
160+ const codexAcpAgent = mockFixture . getCodexAcpAgent ( ) ;
161+ const codexAppServerClient = mockFixture . getCodexAppServerClient ( ) ;
162+
163+ const listSkillsSpy = vi . spyOn ( codexAppServerClient , "listSkills" ) . mockResolvedValue ( { data : [ ] } ) ;
164+ const turnStartSpy = vi . spyOn ( codexAppServerClient , "turnStart" ) . mockResolvedValue ( {
165+ turn : { id : "turn-id" , items : [ ] , status : "inProgress" , error : null }
166+ } as any ) ;
167+ vi . spyOn ( codexAppServerClient , "awaitTurnCompleted" ) . mockResolvedValue ( {
168+ threadId : "session-id" ,
169+ turn : { id : "turn-id" , items : [ ] , status : "completed" , error : null }
170+ } as any ) ;
171+
172+ vi . spyOn ( codexAcpAgent , "getSessionState" ) . mockReturnValue ( createTestSessionState ( {
173+ sessionId : "session-id" ,
174+ cwd : "/workspace"
175+ } ) ) ;
176+
177+ const promptRequest : acp . PromptRequest = {
178+ sessionId : "session-id" ,
179+ prompt : [ { type : "text" , text : "Hello" } ] ,
180+ _meta : {
181+ additionalRoots : [ "/skills/one" , " /skills/two " , 7 ] ,
182+ cwd : "/workspace"
183+ }
184+ } ;
185+ await codexAcpAgent . prompt ( promptRequest ) ;
186+
187+ expect ( listSkillsSpy ) . toHaveBeenCalledWith ( {
188+ cwds : [ "/workspace" ] ,
189+ forceReload : true ,
190+ perCwdExtraUserRoots : [ {
191+ cwd : "/workspace" ,
192+ extraUserRoots : [ "/skills/one" , "/skills/two" ]
193+ } ]
194+ } ) ;
195+ expect ( listSkillsSpy . mock . invocationCallOrder [ 0 ] ! ) . toBeLessThan ( turnStartSpy . mock . invocationCallOrder [ 0 ] ! ) ;
196+ } ) ;
197+
108198 function loadNotifications ( ) {
109199 //TODO collect logs form dev run and then load them from file to speedup
110200 const serverNotifications : ServerNotification [ ] = [
0 commit comments