@@ -5,6 +5,14 @@ import { buildCodexMediaUnderstandingProvider } from "./media-understanding-prov
55import type { CodexAppServerClient } from "./src/app-server/client.js" ;
66import type { CodexServerNotification , JsonValue } from "./src/app-server/protocol.js" ;
77
8+ const sharedClientMocks = vi . hoisted ( ( ) => ( {
9+ createIsolatedCodexAppServerClient : vi . fn ( ) ,
10+ } ) ) ;
11+
12+ vi . mock ( "./src/app-server/shared-client.js" , ( ) => ( {
13+ createIsolatedCodexAppServerClient : sharedClientMocks . createIsolatedCodexAppServerClient ,
14+ } ) ) ;
15+
816function codexModel ( inputModalities : string [ ] = [ "text" , "image" ] ) {
917 return {
1018 id : "gpt-5.4" ,
@@ -169,6 +177,7 @@ function createFakeClient(options?: {
169177 requestHandlers . add ( handler ) ;
170178 return ( ) => requestHandlers . delete ( handler ) ;
171179 } ,
180+ close : vi . fn ( ) ,
172181 } as unknown as CodexAppServerClient ;
173182
174183 return { client, requests, approvalResponses } ;
@@ -178,13 +187,24 @@ describe("codex media understanding provider", () => {
178187 afterEach ( ( ) => {
179188 vi . useRealTimers ( ) ;
180189 vi . restoreAllMocks ( ) ;
190+ sharedClientMocks . createIsolatedCodexAppServerClient . mockReset ( ) ;
181191 } ) ;
182192
183193 it ( "runs image understanding through a bounded Codex app-server turn" , async ( ) => {
184194 const { client, requests } = createFakeClient ( ) ;
195+ const clientFactory = vi . fn (
196+ async ( _startOptions , _authProfileId , _agentDir , _config ) => client ,
197+ ) ;
185198 const provider = buildCodexMediaUnderstandingProvider ( {
186- clientFactory : async ( ) => client ,
199+ clientFactory,
187200 } ) ;
201+ const cfg = {
202+ auth : {
203+ order : {
204+ openai : [ "openai:work" ] ,
205+ } ,
206+ } ,
207+ } ;
188208
189209 const result = await provider . describeImage ?.( {
190210 buffer : Buffer . from ( "image-bytes" ) ,
@@ -194,7 +214,7 @@ describe("codex media understanding provider", () => {
194214 model : "gpt-5.4" ,
195215 prompt : "Describe briefly." ,
196216 timeoutMs : 30_000 ,
197- cfg : { } ,
217+ cfg,
198218 agentDir : "/tmp/openclaw-agent" ,
199219 } ) ;
200220
@@ -204,6 +224,12 @@ describe("codex media understanding provider", () => {
204224 "thread/start" ,
205225 "turn/start" ,
206226 ] ) ;
227+ expect ( clientFactory ) . toHaveBeenCalledWith (
228+ expect . any ( Object ) ,
229+ undefined ,
230+ "/tmp/openclaw-agent" ,
231+ cfg ,
232+ ) ;
207233 expect ( requests [ 1 ] ?. params ) . toEqual ( {
208234 model : "gpt-5.4" ,
209235 modelProvider : "openai" ,
@@ -236,6 +262,62 @@ describe("codex media understanding provider", () => {
236262 } ) ;
237263 } ) ;
238264
265+ it ( "treats a blank agent directory as absent when starting the app-server" , async ( ) => {
266+ const { client, requests } = createFakeClient ( ) ;
267+ const clientFactory = vi . fn ( async ( ) => client ) ;
268+ const provider = buildCodexMediaUnderstandingProvider ( { clientFactory } ) ;
269+ const cfg = { } ;
270+
271+ await provider . describeImage ?.( {
272+ buffer : Buffer . from ( "image-bytes" ) ,
273+ fileName : "image.png" ,
274+ mime : "image/png" ,
275+ provider : "codex" ,
276+ model : "gpt-5.4" ,
277+ timeoutMs : 30_000 ,
278+ cfg,
279+ agentDir : " " ,
280+ } ) ;
281+
282+ expect ( clientFactory ) . toHaveBeenCalledWith ( expect . any ( Object ) , undefined , undefined , cfg ) ;
283+ expect ( requests [ 1 ] ?. params ) . toEqual ( expect . objectContaining ( { cwd : process . cwd ( ) } ) ) ;
284+ expect ( requests [ 2 ] ?. params ) . toEqual ( expect . objectContaining ( { cwd : process . cwd ( ) } ) ) ;
285+ } ) ;
286+
287+ it ( "passes the scoped auth store into isolated app-server startup" , async ( ) => {
288+ const { client } = createFakeClient ( ) ;
289+ sharedClientMocks . createIsolatedCodexAppServerClient . mockResolvedValue ( client ) ;
290+ const provider = buildCodexMediaUnderstandingProvider ( ) ;
291+ const authStore = {
292+ version : 1 ,
293+ profiles : {
294+ "openai:scoped" : {
295+ type : "oauth" as const ,
296+ provider : "openai" ,
297+ access : "scoped-access" ,
298+ refresh : "scoped-refresh" ,
299+ expires : Date . now ( ) + 60_000 ,
300+ } ,
301+ } ,
302+ } ;
303+
304+ await provider . describeImage ?.( {
305+ buffer : Buffer . from ( "image-bytes" ) ,
306+ fileName : "image.png" ,
307+ mime : "image/png" ,
308+ provider : "codex" ,
309+ model : "gpt-5.4" ,
310+ timeoutMs : 30_000 ,
311+ cfg : { } ,
312+ authStore,
313+ agentDir : "/tmp/openclaw-agent" ,
314+ } ) ;
315+
316+ expect ( sharedClientMocks . createIsolatedCodexAppServerClient ) . toHaveBeenCalledWith (
317+ expect . objectContaining ( { authProfileStore : authStore } ) ,
318+ ) ;
319+ } ) ;
320+
239321 it ( "clamps oversized image understanding turn timeouts" , async ( ) => {
240322 const setTimeoutSpy = vi . spyOn ( globalThis , "setTimeout" ) ;
241323 try {
0 commit comments